From aaf9d6733d6dbb066c0b090ff4edb3948a6e2065 Mon Sep 17 00:00:00 2001 From: Martynas Kazlauskas Date: Mon, 16 Dec 2024 17:04:44 +0200 Subject: [PATCH] fix: align extension storage 'set' and 'observeAll' with pouchdb storage behavior deleting a wallet does: 1. delete it from repository 2. check remaining wallets, activate if some wallet exists there was a bug where step 2. would find the wallet that was just deleted and try to activate it, because extension storage 'observeAll' emits an slightly later --- .../storage/extension-document-store.ts | 30 ++++++++++++------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/apps/browser-extension-wallet/src/lib/scripts/background/storage/extension-document-store.ts b/apps/browser-extension-wallet/src/lib/scripts/background/storage/extension-document-store.ts index 1b8480a9a4..1b639b5af6 100644 --- a/apps/browser-extension-wallet/src/lib/scripts/background/storage/extension-document-store.ts +++ b/apps/browser-extension-wallet/src/lib/scripts/background/storage/extension-document-store.ts @@ -1,6 +1,6 @@ /* eslint-disable @typescript-eslint/ban-types */ import { storage as sdkStorage } from '@cardano-sdk/wallet'; -import { EMPTY, filter, from, map, mergeMap, Observable, of, share } from 'rxjs'; +import { EMPTY, filter, from, map, mergeMap, Observable, of, share, firstValueFrom } from 'rxjs'; import { Logger } from 'ts-log'; import { contextLogger, fromSerializableObject, toSerializableObject } from '@cardano-sdk/util'; import { ExtensionStore } from './extension-store'; @@ -10,8 +10,9 @@ export type DocumentChange = { newValue?: T; }; -const undefinedIfEmpty = (value: T | undefined): T | undefined => { - if (typeof value === 'object' && (value === null || Object.keys(value).length === 0)) return undefined; +const undefinedIfEmptyObj = (value: T | undefined): T | undefined => { + if (typeof value === 'object' && !Array.isArray(value) && (value === null || Object.keys(value).length === 0)) + return undefined; // eslint-disable-next-line consistent-return return value; }; @@ -28,14 +29,17 @@ export class ExtensionDocumentStore extends ExtensionStore impleme /** * @param docId unique document id within the store, used as extension storage key */ - constructor(protected docId: string, logger: Logger) { + constructor( + protected docId: string, + logger: Logger + ) { super(contextLogger(logger, `ExtensionStore(${docId})`)); this.documentChange$ = this.storageChange$.pipe( filter(({ key }) => key === docId), map( ({ change }): DocumentChange => ({ - oldValue: undefinedIfEmpty(change.oldValue), - newValue: undefinedIfEmpty(change.newValue) + oldValue: undefinedIfEmptyObj(change.oldValue), + newValue: undefinedIfEmptyObj(change.newValue) }) ), share() @@ -54,12 +58,18 @@ export class ExtensionDocumentStore extends ExtensionStore impleme set(doc: T): Observable { this.logger.debug('set', doc); + const storageChange = firstValueFrom(this.documentChange$); return from( - (this.idle = this.idle.then(() => - this.storage.set({ + (this.idle = this.idle.then(async () => { + await this.storage.set({ [this.docId]: toSerializableObject(doc) - }) - )) + }); + // do not emit until documentChange$ emits + // in order to avoid race conditions: + // users expect `observeAll` to emit new value when subscribing + // to it immediatelly after `set` emits + await storageChange; + })) ); }