From cc1e2945593a62fbb20ae0a49829838d1038790a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antti=20M=C3=A4ki?= Date: Fri, 16 Dec 2022 17:07:03 +0200 Subject: [PATCH 1/2] Await asynchronous Dexie operations This is an attempt to avoid the corruption of settings stored in IndexedDB, which sometimes occurs when users update the manager. Refs TS-1385 --- src/r2mm/manager/SettingsDexieStore.ts | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/r2mm/manager/SettingsDexieStore.ts b/src/r2mm/manager/SettingsDexieStore.ts index 47394f276..3f8bce98f 100644 --- a/src/r2mm/manager/SettingsDexieStore.ts +++ b/src/r2mm/manager/SettingsDexieStore.ts @@ -42,7 +42,7 @@ export default class SettingsDexieStore extends Dexie { } public async getLatestGlobal(): Promise { - return this.global.toArray().then(result => { + return this.global.toArray().then(async result => { if (result.length > 0) { const globalEntry = result[result.length - 1]; const parsed = JSON.parse(globalEntry.settings); @@ -52,22 +52,22 @@ export default class SettingsDexieStore extends Dexie { } else { // Is legacy. const legacyToV2 = this.mapLegacyToV2(parsed, this.activeGame); - this.global.put({ settings: JSON.stringify(legacyToV2.global) }); - this.gameSpecific.put({ settings: JSON.stringify(legacyToV2.gameSpecific) }); + await this.global.put({ settings: JSON.stringify(legacyToV2.global) }); + await this.gameSpecific.put({ settings: JSON.stringify(legacyToV2.gameSpecific) }); return legacyToV2.global; } } else { ManagerSettings.NEEDS_MIGRATION = true; const obj = this.createNewSettingsInstance(); - this.global.put({ settings: JSON.stringify(obj.global) }); - this.gameSpecific.put({ settings: JSON.stringify(obj.gameSpecific) }); + await this.global.put({ settings: JSON.stringify(obj.global) }); + await this.gameSpecific.put({ settings: JSON.stringify(obj.gameSpecific) }); return obj.global; } }); } public async getLatestGameSpecific(): Promise { - return this.gameSpecific.toArray().then(result => { + return this.gameSpecific.toArray().then(async result => { if (result.length > 0) { const globalEntry = result[result.length - 1]; const parsed = JSON.parse(globalEntry.settings); @@ -80,7 +80,7 @@ export default class SettingsDexieStore extends Dexie { } } else { const obj = this.createNewSettingsInstance(); - this.gameSpecific.put({ settings: JSON.stringify(obj.gameSpecific) }); + await this.gameSpecific.put({ settings: JSON.stringify(obj.gameSpecific) }); return obj.gameSpecific; } }); @@ -125,14 +125,14 @@ export default class SettingsDexieStore extends Dexie { } public async save(holder: ManagerSettingsInterfaceHolder) { - await this.global.toArray().then(result => { + await this.global.toArray().then(async result => { for (let settingsInterface of result) { - this.global.update(settingsInterface.id!, {settings: JSON.stringify(holder.global)}); + await this.global.update(settingsInterface.id!, {settings: JSON.stringify(holder.global)}); } }); - await this.gameSpecific.toArray().then(result => { + await this.gameSpecific.toArray().then(async result => { for (let settingsInterface of result) { - this.gameSpecific.update(settingsInterface.id!, {settings: JSON.stringify(holder.gameSpecific)}); + await this.gameSpecific.update(settingsInterface.id!, {settings: JSON.stringify(holder.gameSpecific)}); } }); } From 3faa53580b67140346daaf9fd9759b239ae216b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Antti=20M=C3=A4ki?= Date: Thu, 22 Dec 2022 11:15:17 +0200 Subject: [PATCH 2/2] Use transactions on Dexie write operations This is an attempt to avoid the corruption of settings stored in IndexedDB, which sometimes occurs when users update the manager. Refs TS-1385 --- src/r2mm/manager/SettingsDexieStore.ts | 38 ++++++++++++++++---------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/src/r2mm/manager/SettingsDexieStore.ts b/src/r2mm/manager/SettingsDexieStore.ts index 3f8bce98f..f3479279c 100644 --- a/src/r2mm/manager/SettingsDexieStore.ts +++ b/src/r2mm/manager/SettingsDexieStore.ts @@ -87,12 +87,16 @@ export default class SettingsDexieStore extends Dexie { } public async getLatest(): Promise { - const latestGlobal = await this.getLatestGlobal(); - const latestGameSpecific = await this.getLatestGameSpecific(); - return { - global: latestGlobal, - gameSpecific: latestGameSpecific + const get = async () => { + const latestGlobal = await this.getLatestGlobal(); + const latestGameSpecific = await this.getLatestGameSpecific(); + return { + global: latestGlobal, + gameSpecific: latestGameSpecific + }; }; + + return await this.transaction("rw!", this.global, this.gameSpecific, get); } private createNewSettingsInstance(): ManagerSettingsInterfaceHolder { @@ -125,16 +129,20 @@ export default class SettingsDexieStore extends Dexie { } public async save(holder: ManagerSettingsInterfaceHolder) { - await this.global.toArray().then(async result => { - for (let settingsInterface of result) { - await this.global.update(settingsInterface.id!, {settings: JSON.stringify(holder.global)}); - } - }); - await this.gameSpecific.toArray().then(async result => { - for (let settingsInterface of result) { - await this.gameSpecific.update(settingsInterface.id!, {settings: JSON.stringify(holder.gameSpecific)}); - } - }); + const update = async () => { + await this.global.toArray().then(async result => { + for (let settingsInterface of result) { + await this.global.update(settingsInterface.id!, {settings: JSON.stringify(holder.global)}); + } + }); + await this.gameSpecific.toArray().then(async result => { + for (let settingsInterface of result) { + await this.gameSpecific.update(settingsInterface.id!, {settings: JSON.stringify(holder.gameSpecific)}); + } + }); + } + + await this.transaction("rw!", this.global, this.gameSpecific, update); } private mapLegacyToV2(itf: ManagerSettingsInterface_Legacy, game: Game): ManagerSettingsInterfaceHolder {