-
Notifications
You must be signed in to change notification settings - Fork 580
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Store Rubric in IndexedDB #9840
Store Rubric in IndexedDB #9840
Conversation
…happens in a centralized place that listens for rubric state changes. Also still need to save when a criteria is removed.
… have additional fields we'd want to store here, like autosave and autorun preferences.
@@ -1,11 +1,14 @@ | |||
namespace pxt.blocks { | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@srietkerk The changes in this file are responding to your comments in #9835. They're not related to the indexed db change, I actually forgot I'd made them in this branch, but I suppose here they are 😅
...teacherTool.rubric, | ||
name, | ||
}; | ||
dispatch(Actions.setRubric(newRubric)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
May want to provide feedback here that the name was saved. Post a notification?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, a notification feels a little too heavy-weight here to me. Ideally, I think the user would just feel like the name is always saved once they type it, similarly to how criteria adding/removing works, no real need for a "saved" notification. Maybe I should just save the name whenever the text is changed and debounce it a little, rather than requiring them to hit enter and/or click away from the Input?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably fine as is for now. Let's see what kind of feedback we get.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I did end up adding a debounced input to handle this instead...but I can change it back if you want :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(also considered just wrapping the onChange call in pxt.Util.debounce but I got worried it wouldn't play nicely with React re-rendering things and possibly resetting fields)
|
||
return ( | ||
<div className="rubric-display"> | ||
<h3>{lf("Rubric")}</h3> | ||
{teacherTool.selectedCriteria?.map(criteriaInstance => { | ||
<Input |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
needs ariaLabel
|
||
const db = new TeacherToolDb(); | ||
|
||
let initializeAsync = async () => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This code actually doesn't work as intended. Due to the async, the function can be called multiple times before the reassignment happens. This is my bug :). I introduced it in Kiosk. One fix would be to have a global promise that does the db init and then resolves. getLastActiveRubricAsync, etc. would wait on that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You could create a global promise that is the only source of the db instance. API calls have to await it to get the db instance. This would ensure no calls can be done on db
before it's initialized.
} | ||
|
||
export async function saveRubricAsync(rubric: Rubric) { | ||
await db.saveRubric(rubric); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Need to await the db init here.
placeholder={lf("Rubric Name")} | ||
initialValue={inProgressName} | ||
onEnterKey={handleConfirmName} | ||
onBlur={handleConfirmName} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Normally you would need to include the preserveValueOnBlur={true}
prop here, but you might be getting around it by setting initialValue={inProgressName}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, yeah. I think that was masking the issue. I'll still add it.
…ks/teacher_tool/store_rubric_in_indexedDb
… happens exactly once and all code that references the db must go through the promise to get it.
Co-authored-by: Eric Anderson <[email protected]>
…ps://github.com/microsoft/pxt into thsparks/teacher_tool/store_rubric_in_indexedDb
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks great.
…quiring the user to click away or click enter
clearTimeout(timerId.current); | ||
} | ||
|
||
timerId.current = setTimeout(() => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is the expected behavior if the component unmounts while a timeout is pending?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm good question. I'll add some code to fire the onChange immediately if we're umounting and the timeout is pending.
import { Input, InputProps } from "react-common/components/controls/Input"; | ||
|
||
export interface DebouncedInputProps extends InputProps { | ||
intervalMs?: number | undefined; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider assigning the default value here so it's less buried and available to intellisense.
intervalMs = 500
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think I can set it here, since this is an interface, but I've moved it up to the DebouncedInput parameter definition and added a comment here instead.
…the component, move default interval value for better visibility)
This change stores the rubric in the indexed db and loads the last-active rubric automatically when the page loads. Currently only one rubric is really stored at a time, but the schema can support multiple if we want to evolve this over time.
Indexed DB has two tables:
A note on code reuse:
The indexed db code is borrowed from Kiosk. I considered moving some of it into a shared class, but noticed we already have IDBWrapper in BrowserUtils. Then I tried switching to that, but I ultimately needed more code to manage the error handling than I saved by re-using the wrapper code, so I moved away from that approach as well. (It also wasn't working cleanly with custom key fields.) I think the right long-term move here is to evolve the IDBWrapper class to work better for these scenarios, but I think it'd require quite a lot of testing outside of the teachertool, so I was hesitant to make that change now.