fix: display error message when local storage quota is exceeded (#9961)
Tests / test (push) Has been cancelled
Details
Auto release excalidraw next / Auto-release-excalidraw-next (push) Has been cancelled
Details
Build Docker image / build-docker (push) Has been cancelled
Details
Cancel previous runs / cancel (push) Has been cancelled
Details
Publish Docker / publish-docker (push) Has been cancelled
Details
New Sentry production release / sentry (push) Has been cancelled
Details
Tests / test (push) Has been cancelled
Details
Auto release excalidraw next / Auto-release-excalidraw-next (push) Has been cancelled
Details
Build Docker image / build-docker (push) Has been cancelled
Details
Cancel previous runs / cancel (push) Has been cancelled
Details
Publish Docker / publish-docker (push) Has been cancelled
Details
New Sentry production release / sentry (push) Has been cancelled
Details
* fix: display error message when local storage quota is exceeded * add danger alert instead of toast * tweak text --------- Co-authored-by: dwelle <5153846+dwelle@users.noreply.github.com>
This commit is contained in:
parent
fde796a7a0
commit
835eb8d2fd
|
@ -119,6 +119,7 @@ import {
|
||||||
LibraryIndexedDBAdapter,
|
LibraryIndexedDBAdapter,
|
||||||
LibraryLocalStorageMigrationAdapter,
|
LibraryLocalStorageMigrationAdapter,
|
||||||
LocalData,
|
LocalData,
|
||||||
|
localStorageQuotaExceededAtom,
|
||||||
} from "./data/LocalData";
|
} from "./data/LocalData";
|
||||||
import { isBrowserStorageStateNewer } from "./data/tabSync";
|
import { isBrowserStorageStateNewer } from "./data/tabSync";
|
||||||
import { ShareDialog, shareDialogStateAtom } from "./share/ShareDialog";
|
import { ShareDialog, shareDialogStateAtom } from "./share/ShareDialog";
|
||||||
|
@ -727,6 +728,8 @@ const ExcalidrawWrapper = () => {
|
||||||
|
|
||||||
const isOffline = useAtomValue(isOfflineAtom);
|
const isOffline = useAtomValue(isOfflineAtom);
|
||||||
|
|
||||||
|
const localStorageQuotaExceeded = useAtomValue(localStorageQuotaExceededAtom);
|
||||||
|
|
||||||
const onCollabDialogOpen = useCallback(
|
const onCollabDialogOpen = useCallback(
|
||||||
() => setShareDialogState({ isOpen: true, type: "collaborationOnly" }),
|
() => setShareDialogState({ isOpen: true, type: "collaborationOnly" }),
|
||||||
[setShareDialogState],
|
[setShareDialogState],
|
||||||
|
@ -901,10 +904,15 @@ const ExcalidrawWrapper = () => {
|
||||||
|
|
||||||
<TTDDialogTrigger />
|
<TTDDialogTrigger />
|
||||||
{isCollaborating && isOffline && (
|
{isCollaborating && isOffline && (
|
||||||
<div className="collab-offline-warning">
|
<div className="alertalert--warning">
|
||||||
{t("alerts.collabOfflineWarning")}
|
{t("alerts.collabOfflineWarning")}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
{localStorageQuotaExceeded && (
|
||||||
|
<div className="alert alert--danger">
|
||||||
|
{t("alerts.localStorageQuotaExceeded")}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
{latestShareableLink && (
|
{latestShareableLink && (
|
||||||
<ShareableLinkDialog
|
<ShareableLinkDialog
|
||||||
link={latestShareableLink}
|
link={latestShareableLink}
|
||||||
|
|
|
@ -27,6 +27,8 @@ import {
|
||||||
get,
|
get,
|
||||||
} from "idb-keyval";
|
} from "idb-keyval";
|
||||||
|
|
||||||
|
import { appJotaiStore, atom } from "excalidraw-app/app-jotai";
|
||||||
|
|
||||||
import type { LibraryPersistedData } from "@excalidraw/excalidraw/data/library";
|
import type { LibraryPersistedData } from "@excalidraw/excalidraw/data/library";
|
||||||
import type { ImportedDataState } from "@excalidraw/excalidraw/data/types";
|
import type { ImportedDataState } from "@excalidraw/excalidraw/data/types";
|
||||||
import type { ExcalidrawElement, FileId } from "@excalidraw/element/types";
|
import type { ExcalidrawElement, FileId } from "@excalidraw/element/types";
|
||||||
|
@ -45,6 +47,8 @@ import { updateBrowserStateVersion } from "./tabSync";
|
||||||
|
|
||||||
const filesStore = createStore("files-db", "files-store");
|
const filesStore = createStore("files-db", "files-store");
|
||||||
|
|
||||||
|
export const localStorageQuotaExceededAtom = atom(false);
|
||||||
|
|
||||||
class LocalFileManager extends FileManager {
|
class LocalFileManager extends FileManager {
|
||||||
clearObsoleteFiles = async (opts: { currentFileIds: FileId[] }) => {
|
clearObsoleteFiles = async (opts: { currentFileIds: FileId[] }) => {
|
||||||
await entries(filesStore).then((entries) => {
|
await entries(filesStore).then((entries) => {
|
||||||
|
@ -69,6 +73,9 @@ const saveDataStateToLocalStorage = (
|
||||||
elements: readonly ExcalidrawElement[],
|
elements: readonly ExcalidrawElement[],
|
||||||
appState: AppState,
|
appState: AppState,
|
||||||
) => {
|
) => {
|
||||||
|
const localStorageQuotaExceeded = appJotaiStore.get(
|
||||||
|
localStorageQuotaExceededAtom,
|
||||||
|
);
|
||||||
try {
|
try {
|
||||||
const _appState = clearAppStateForLocalStorage(appState);
|
const _appState = clearAppStateForLocalStorage(appState);
|
||||||
|
|
||||||
|
@ -88,12 +95,22 @@ const saveDataStateToLocalStorage = (
|
||||||
JSON.stringify(_appState),
|
JSON.stringify(_appState),
|
||||||
);
|
);
|
||||||
updateBrowserStateVersion(STORAGE_KEYS.VERSION_DATA_STATE);
|
updateBrowserStateVersion(STORAGE_KEYS.VERSION_DATA_STATE);
|
||||||
|
if (localStorageQuotaExceeded) {
|
||||||
|
appJotaiStore.set(localStorageQuotaExceededAtom, false);
|
||||||
|
}
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
// Unable to access window.localStorage
|
// Unable to access window.localStorage
|
||||||
console.error(error);
|
console.error(error);
|
||||||
|
if (isQuotaExceededError(error) && !localStorageQuotaExceeded) {
|
||||||
|
appJotaiStore.set(localStorageQuotaExceededAtom, true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const isQuotaExceededError = (error: any) => {
|
||||||
|
return error instanceof DOMException && error.name === "QuotaExceededError";
|
||||||
|
};
|
||||||
|
|
||||||
type SavingLockTypes = "collaboration";
|
type SavingLockTypes = "collaboration";
|
||||||
|
|
||||||
export class LocalData {
|
export class LocalData {
|
||||||
|
|
|
@ -58,7 +58,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.collab-offline-warning {
|
.alert {
|
||||||
pointer-events: none;
|
pointer-events: none;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 6.5rem;
|
top: 6.5rem;
|
||||||
|
@ -69,10 +69,18 @@
|
||||||
text-align: center;
|
text-align: center;
|
||||||
line-height: 1.5;
|
line-height: 1.5;
|
||||||
border-radius: var(--border-radius-md);
|
border-radius: var(--border-radius-md);
|
||||||
background-color: var(--color-warning);
|
|
||||||
color: var(--color-text-warning);
|
|
||||||
z-index: 6;
|
z-index: 6;
|
||||||
white-space: pre;
|
white-space: pre;
|
||||||
|
|
||||||
|
&--warning {
|
||||||
|
background-color: var(--color-warning);
|
||||||
|
color: var(--color-text-warning);
|
||||||
|
}
|
||||||
|
|
||||||
|
&--danger {
|
||||||
|
background-color: var(--color-danger-dark);
|
||||||
|
color: var(--color-danger-text);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -260,7 +260,8 @@
|
||||||
"resetLibrary": "This will clear your library. Are you sure?",
|
"resetLibrary": "This will clear your library. Are you sure?",
|
||||||
"removeItemsFromsLibrary": "Delete {{count}} item(s) from library?",
|
"removeItemsFromsLibrary": "Delete {{count}} item(s) from library?",
|
||||||
"invalidEncryptionKey": "Encryption key must be of 22 characters. Live collaboration is disabled.",
|
"invalidEncryptionKey": "Encryption key must be of 22 characters. Live collaboration is disabled.",
|
||||||
"collabOfflineWarning": "No internet connection available.\nYour changes will not be saved!"
|
"collabOfflineWarning": "No internet connection available.\nYour changes will not be saved!",
|
||||||
|
"localStorageQuotaExceeded": "Browser storage quota exceeded. Changes will not be saved."
|
||||||
},
|
},
|
||||||
"errors": {
|
"errors": {
|
||||||
"unsupportedFileType": "Unsupported file type.",
|
"unsupportedFileType": "Unsupported file type.",
|
||||||
|
|
Loading…
Reference in New Issue