From 4502fd443e65c6909c2aa4f8d45244050fd063a1 Mon Sep 17 00:00:00 2001 From: David Walker Date: Fri, 19 Jul 2024 19:27:04 -0700 Subject: [PATCH] BUGFIX: Don't spin forever if IDB can't be loaded (#1500) Our IndexDB handling did not have very good error handling. It wasn't reporting the actual errors that occured, nor was it using actual Error objects. In some cases it also had overly convoluted Promise use, and it didn't need to be .tsx either. The biggest issue was that if any problem occured during the main load(), this would end up as an unhandled rejection and so it would only be logged to the console. This extends the previous catch to also cover this, so that the recovery screen is activated. --- src/{db.tsx => db.ts} | 34 ++++++++++++++----------------- src/ui/LoadingScreen.tsx | 19 ++++++++--------- src/ui/React/DeleteGameButton.tsx | 2 +- 3 files changed, 24 insertions(+), 31 deletions(-) rename src/{db.tsx => db.ts} (65%) diff --git a/src/db.tsx b/src/db.ts similarity index 65% rename from src/db.tsx rename to src/db.ts index 0e7953d7e..a47aa4c59 100644 --- a/src/db.tsx +++ b/src/db.ts @@ -19,14 +19,14 @@ function getDB(): Promise { db.createObjectStore("savestring"); }; - indexedDbRequest.onerror = function (this: IDBRequest, ev: Event) { - reject(`Failed to get IDB ${ev}`); + indexedDbRequest.onerror = function (this: IDBRequest) { + reject(new Error("Failed to get IDB", { cause: this.error })); }; indexedDbRequest.onsuccess = function (this: IDBRequest) { const db = this.result; if (!db) { - reject("database loading result was undefined"); + reject(new Error("database loading result was undefined")); return; } resolve(db.transaction(["savestring"], "readwrite").objectStore("savestring")); @@ -35,21 +35,17 @@ function getDB(): Promise { } export function load(): Promise { - return new Promise((resolve, reject) => { - getDB() - .then((db) => { - return new Promise((resolve, reject) => { - const request: IDBRequest = db.get("save"); - request.onerror = function (this: IDBRequest, ev: Event) { - reject("Error in Database request to get save data: " + ev); - }; + return getDB().then((db) => { + return new Promise((resolve, reject) => { + const request: IDBRequest = db.get("save"); + request.onerror = function (this: IDBRequest) { + reject(new Error("Error in Database request to get save data", { cause: this.error })); + }; - request.onsuccess = function (this: IDBRequest) { - resolve(this.result); - }; - }).then((saveData) => resolve(saveData)); - }) - .catch((r) => reject(r)); + request.onsuccess = function (this: IDBRequest) { + resolve(this.result); + }; + }); }); } @@ -59,8 +55,8 @@ export function save(saveData: SaveData): Promise { // We'll save to IndexedDB const request = db.put(saveData, "save"); - request.onerror = function (e) { - reject("Error saving game to IndexedDB: " + e); + request.onerror = function (this: IDBRequest) { + reject(new Error("Error saving game to IndexedDB", { cause: this.error })); }; request.onsuccess = () => resolve(); diff --git a/src/ui/LoadingScreen.tsx b/src/ui/LoadingScreen.tsx index 09e35dcb3..729e169ab 100644 --- a/src/ui/LoadingScreen.tsx +++ b/src/ui/LoadingScreen.tsx @@ -32,21 +32,18 @@ export function LoadingScreen(): React.ReactElement { }); useEffect(() => { - load().then(async (saveData) => { - try { - await initSwc(); - await Engine.load(saveData); - } catch (error) { + load() + .then((saveData) => Promise.all([initSwc(), Engine.load(saveData)])) + .then(() => { + pushGameReady(); + setLoaded(true); + }) + .catch(async (error) => { console.error(error); ActivateRecoveryMode(error); await Engine.load(""); setLoaded(true); - return; - } - - pushGameReady(); - setLoaded(true); - }); + }); }, []); return loaded ? ( diff --git a/src/ui/React/DeleteGameButton.tsx b/src/ui/React/DeleteGameButton.tsx index 59228ac56..d5b13860b 100644 --- a/src/ui/React/DeleteGameButton.tsx +++ b/src/ui/React/DeleteGameButton.tsx @@ -29,7 +29,7 @@ export function DeleteGameButton({ color = "primary" }: IProps): React.ReactElem pushDisableRestore(); setTimeout(() => location.reload(), 1000); }) - .catch((r) => console.error(`Could not delete game: ${r}`)); + .catch((r) => console.error("Could not delete game: %o", r)); }} open={modalOpened} onClose={() => setModalOpened(false)}