-
Notifications
You must be signed in to change notification settings - Fork 4
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
Fix storage quota bug #489
Conversation
Ahh just remembered you wanted us to display the amount of memory used, let me add that now |
And is a playwright test possible? |
window.alert doesn't seem to reliably appear, so try a Modal instead
Not sure why a 1MB file is necessary for a test when we can do "x".repeat(123). Also don't think we need to force a browser to deal with 5MB in a form. We can just set some unused local storage key directly. I started a test in 7168bab. But the window.alerts appear to be quite flaky both in playwright and manually; they mostly haven't appeared for me, which is weird, because I thought window.alert is supposed to be blocking. What if we try a Modal instead and just have this handling in the sketch tool for now? |
Looks like a tricky one, happy to test locally if you guys want a second pair of eyes on it. |
b8bce1c
to
adc0134
Compare
src/lib/common/storage.ts
Outdated
} catch (error: any) { | ||
// Unfortunately it looks like we can't use typeof or instanceof to determine this one: | ||
// all we get is that it's an object so if someone changess the wording we may have to add new checks | ||
const isStorageQuotaError = |
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.
export function clearLocalStorage() { | ||
window.localStorage.clear(); | ||
} | ||
export function clearLocalStorageItem(key: string) { |
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.
Missing return type here. But actually, it might be simpler to not try and return this here. Export getStoredStrings
and call it from the modal. After clicking a button to clear something, over in the modal code, call the function again to refresh the list.
Then we don't need getEmptySetStorageErrorObject
or maybe even the setLocalStorageItem
wrapper at all. Just call the regular API, catch any error, launch the modal
@@ -0,0 +1,77 @@ | |||
const storageQuotaErrorMessage = |
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.
@@ -0,0 +1,77 @@ | |||
const storageQuotaErrorMessage = | |||
"Unable to save because the local storage quota has been exceeded: you may need to clear out your web browser's local storage for this app, or run in private mode before you can save again. Specific error here: "; |
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'd vote to move this to the modal. We don't need to console.log it, it's user-facing
src/lib/sidebar/StorageModal.svelte
Outdated
{#if storedStringDescriptor.key === currentAuthority} | ||
<p><WarningIcon text={"warning icon"} />This is the current sketch.</p> | ||
{/if} | ||
<SecondaryButton on:click={removeStorageItem(storedStringDescriptor.key)}> |
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.
If you do () => removeStorageItem(key)
then we don't need the weird indirection of returning a closure. Most Svelte examples make the closure here in the event handler
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.
an obvious trick I'm sure I've learned before that slipped my brain, thanks
src/lib/sidebar/StorageModal.svelte
Outdated
Be sure that you're not losing anything important before doing this! | ||
</p> | ||
<WarningButton on:click={clearLocalStorageAndCloseModal}> | ||
Delete All Sketch Data From Browser |
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.
Don't capitalize every word
src/lib/sidebar/StorageModal.svelte
Outdated
<p>Stored object: {storedStringDescriptor.key}</p> | ||
<p> | ||
Storage used (rounded to nearest 0.01MB): {storedStringDescriptor.storageUsedInMB | ||
.toString() |
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.
slice is weird -- toFixed(2)
?
Otherwise here is a breakdown of what is currently stored locally. You can | ||
delete individual items (normally storage quota is 5mb or 10mb): | ||
</p> | ||
{#each setStorageError.storedStrings as storedStringDescriptor} |
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.
It could be useful to show these from largest to smallest
src/lib/common/storage.ts
Outdated
return results; | ||
} | ||
|
||
function getLengthInMB(text: string) { |
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.
Missing return type, and you don't need the intermediate variable. Every intermediate var is something that needs a name and takes mental overhead to understand when reading
src/lib/common/storage.ts
Outdated
} | ||
|
||
function getStoredStrings(): StoredStringDescriptor[] { | ||
const itemsObject = { ...localStorage }; |
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 does seem to work, but it doesn't seem documented at https://developer.mozilla.org/en-US/docs/Web/API/Storage. Alternatively there's length
and key
, like https://github.com/dabreegster/ltn/blob/47187e79fb5bcddc823a6224a1f80d39d624f9b2/web/src/title/TitleMode.svelte#L30
src/lib/sidebar/StorageModal.svelte
Outdated
delete individual items (normally storage quota is 5mb or 10mb): | ||
</p> | ||
{#each setStorageError.storedStrings as storedStringDescriptor} | ||
<p>Stored object: {storedStringDescriptor.key}</p> |
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 is repetitive and visually confusing with a bunch of keys. Why not just have a bunch of buttons, keep the key in there, and add (123MB)
at the end or something?
472239d
to
17750c3
Compare
17750c3
to
c0c7aa3
Compare
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.
Sorted buttons feel much nicer, thanks! Still some open comments about simplifying the wording
src/lib/sidebar/StorageModal.svelte
Outdated
<SecondaryButton | ||
on:click={() => removeStorageItem(storedStringDescriptor.key)} | ||
> | ||
Remove stored item for {storedStringDescriptor.key}({storedStringDescriptor.storageUsedInMB}MB) |
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.
.toFixed(2)
on the number would look nicer and I think match the old slice previously
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.
Yep, did this at first then it got lost in refactor of how we're displaying this! Good spot!
tests/quota.spec.ts
Outdated
window.localStorage.setItem("huge", "x".repeat(4.999 * 1024 * 1024)), | ||
); | ||
|
||
await page.goto("/index.html?schema=pipeline"); |
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.
Could just go directly to the scheme page with the right query, to speed up the test. We test navigating from the homepage in other test cases, and local storage shouldn't really affect that
const storageQuotaErrorMessage = | ||
"Unable to save because the local storage quota has been exceeded: you may need to clear out your web browser's local storage for this app, or run in private mode before you can save again. Specific error here: "; | ||
|
||
export interface SetStorageErrorObject { |
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.
We maybe don't really need this, the empty object method, or setLocalStorageItem
. The caller in FileManagement
could call localStorage.setItem
directly, catch the exception, and trigger the modal when appropriate. We could export the method to summarize current local storage items, and make the modal call it again after clearing/removing an item. But up to you
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.
As mentioned in teams, I think it's plasuible we'd want to share this in the future and I prefer smaller files so I'd prefer to leave it this way.
No description provided.