From c4686d4b99fe4f74d65c01070a91bad3302daea5 Mon Sep 17 00:00:00 2001 From: Andreas Gassmann Date: Wed, 8 Nov 2023 10:09:15 +0100 Subject: [PATCH] feat(): sync active account across tabs --- .../beacon-core/src/storage/ChromeStorage.ts | 15 +++++++++ .../beacon-core/src/storage/LocalStorage.ts | 33 ++++++++++++++++++- .../beacon-dapp/src/dapp-client/DAppClient.ts | 17 ++++++++++ .../beacon-types/src/types/storage/Storage.ts | 21 ++++++++++++ 4 files changed, 85 insertions(+), 1 deletion(-) diff --git a/packages/beacon-core/src/storage/ChromeStorage.ts b/packages/beacon-core/src/storage/ChromeStorage.ts index 6cc04ed67..64573f658 100644 --- a/packages/beacon-core/src/storage/ChromeStorage.ts +++ b/packages/beacon-core/src/storage/ChromeStorage.ts @@ -47,4 +47,19 @@ export class ChromeStorage implements Storage { }) }) } + + public async subscribeToStorageChanged( + _callback: (arg: { + eventType: 'storageCleared' | 'entryModified' + key: string | null + oldValue: string | null + newValue: string | null + }) => {} + ): Promise { + // TODO + } + + public getPrefixedKey(key: string): string { + return key + } } diff --git a/packages/beacon-core/src/storage/LocalStorage.ts b/packages/beacon-core/src/storage/LocalStorage.ts index 9af62fbd1..fe294bcf8 100644 --- a/packages/beacon-core/src/storage/LocalStorage.ts +++ b/packages/beacon-core/src/storage/LocalStorage.ts @@ -42,7 +42,38 @@ export class LocalStorage extends Storage { return Promise.resolve(localStorage.removeItem(this.getPrefixedKey(key))) } - private getPrefixedKey(key: string): string { + public async subscribeToStorageChanged( + callback: (arg: { + eventType: 'storageCleared' | 'entryModified' + key: string | null + oldValue: string | null + newValue: string | null + }) => {} + ): Promise { + window.addEventListener( + 'storage', + (event) => { + if (!event.key) { + callback({ + eventType: 'storageCleared', + key: null, + oldValue: null, + newValue: null + }) + } else { + callback({ + eventType: 'entryModified', + key: this.getPrefixedKey(event.key), + oldValue: event.oldValue, + newValue: event.newValue + }) + } + }, + false + ) + } + + public getPrefixedKey(key: string): string { return this.prefix ? `${this.prefix}-${key}` : key } } diff --git a/packages/beacon-dapp/src/dapp-client/DAppClient.ts b/packages/beacon-dapp/src/dapp-client/DAppClient.ts index 92c4e4fb7..782dbaa4d 100644 --- a/packages/beacon-dapp/src/dapp-client/DAppClient.ts +++ b/packages/beacon-dapp/src/dapp-client/DAppClient.ts @@ -215,6 +215,23 @@ export class DAppClient extends Client { this.appMetadataManager = new AppMetadataManager(this.storage) + // Subscribe to storage changes and update the active account if it changes on other tabs + this.storage.subscribeToStorageChanged(async (event) => { + if (event.eventType === 'storageCleared') { + this.setActiveAccount(undefined) + } else if (event.eventType === 'entryModified') { + if (event.key === this.storage.getPrefixedKey(StorageKey.ACTIVE_ACCOUNT)) { + const accountIdentifier = event.newValue + if (!accountIdentifier || accountIdentifier === 'undefined') { + this.setActiveAccount(undefined) + } else { + const account = await this.getAccount(accountIdentifier) + this.setActiveAccount(account) + } + } + } + }) + this.activeAccountLoaded = this.storage .get(StorageKey.ACTIVE_ACCOUNT) .then(async (activeAccountIdentifier) => { diff --git a/packages/beacon-types/src/types/storage/Storage.ts b/packages/beacon-types/src/types/storage/Storage.ts index 774e2cdbb..ed59c1fad 100644 --- a/packages/beacon-types/src/types/storage/Storage.ts +++ b/packages/beacon-types/src/types/storage/Storage.ts @@ -34,4 +34,25 @@ export abstract class Storage { * @param key The storage key */ abstract delete(key: K): Promise + + /** + * This event will fire if the storage was modified by someone else, eg. on another tab + * + * @param callback The callback to be called when a storage value changes + */ + abstract subscribeToStorageChanged( + callback: (arg: { + eventType: 'storageCleared' | 'entryModified' + key: string | null + oldValue: string | null + newValue: string | null + }) => {} + ): Promise + + /** + * Get the key with the internal prefix + * + * @param key the storage key + */ + abstract getPrefixedKey(key: K): string }