diff --git a/.changeset/angry-cats-help.md b/.changeset/angry-cats-help.md new file mode 100644 index 000000000..e0a01496b --- /dev/null +++ b/.changeset/angry-cats-help.md @@ -0,0 +1,6 @@ +--- +"@fuel-wallet/types": patch +"fuels-wallet": patch +--- + +fixes service worker services not restarting communication protocol when DB closes or blocks diff --git a/packages/app/src/App.tsx b/packages/app/src/App.tsx index 1a1de3d46..9a4098a7e 100644 --- a/packages/app/src/App.tsx +++ b/packages/app/src/App.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { Providers } from '~/systems/Core'; +import { Providers, useRecoverWelcomeFromError } from '~/systems/Core'; import { IS_DEVELOPMENT, IS_TEST } from './config'; import { getRoutes } from './routes'; @@ -9,6 +9,8 @@ const ThrowError = React.lazy( ); export function App() { + useRecoverWelcomeFromError(); + return ( {getRoutes()} diff --git a/packages/app/src/systems/CRX/background/actions/onInstall.ts b/packages/app/src/systems/CRX/background/actions/onInstall.ts index ec33b3505..3408f65f9 100644 --- a/packages/app/src/systems/CRX/background/actions/onInstall.ts +++ b/packages/app/src/systems/CRX/background/actions/onInstall.ts @@ -4,9 +4,19 @@ import { executeContentScript } from '../../scripts/executeContentScript'; // Execute everytime the background service starts executeContentScript(); -chrome.runtime.onInstalled.addListener((object) => { - if (object.reason === chrome.runtime.OnInstalledReason.INSTALL) { +chrome.runtime.onInstalled.addListener(async (object) => { + const { shouldRecoverWelcomeFromError } = await chrome.storage.local.get( + 'shouldRecoverWelcomeFromError' + ); + + chrome.storage.local.remove('shouldRecoverWelcomeFromError'); + + if ( + shouldRecoverWelcomeFromError || + object.reason === chrome.runtime.OnInstalledReason.INSTALL + ) { chrome.tabs.create({ url: welcomeLink() }); } + executeContentScript(); }); diff --git a/packages/app/src/systems/CRX/background/services/VaultService.ts b/packages/app/src/systems/CRX/background/services/VaultService.ts index a07726ff5..606507094 100644 --- a/packages/app/src/systems/CRX/background/services/VaultService.ts +++ b/packages/app/src/systems/CRX/background/services/VaultService.ts @@ -1,5 +1,9 @@ -import { POPUP_SCRIPT_NAME, VAULT_SCRIPT_NAME } from '@fuel-wallet/types'; -import { MessageTypes } from '@fuels/connectors'; +import { + type DatabaseRestartEvent, + POPUP_SCRIPT_NAME, + VAULT_SCRIPT_NAME, +} from '@fuel-wallet/types'; +import { MessageTypes, type RequestMessage } from '@fuels/connectors'; import { AUTO_LOCK_IN_MINUTES } from '~/config'; import { VaultServer } from '~/systems/Vault/services/VaultServer'; @@ -11,6 +15,7 @@ import { saveSecret, } from '../../utils'; +import { db } from '../../../../systems/Core/utils/database'; import type { CommunicationProtocol } from './CommunicationProtocol'; export class VaultService extends VaultServer { @@ -24,6 +29,15 @@ export class VaultService extends VaultServer { this.setupListeners(); } + async checkVaultIntegrity() { + // Ensure integrity of database connection + const dbLoadedCorrectly = (await db.open().catch(() => false)) && true; + const secret = await loadSecret().catch(() => null); + const isLocked = await super.isLocked().catch(() => true); + + return dbLoadedCorrectly && (!isLocked || !!(secret && isLocked)); + } + async unlock({ password }: { password: string }): Promise { await super.unlock({ password }); saveSecret(password, AUTO_LOCK_IN_MINUTES); @@ -72,7 +86,7 @@ export class VaultService extends VaultServer { } setupListeners() { - this.communicationProtocol.on(MessageTypes.request, async (event) => { + const handleRequest = async (event: RequestMessage) => { if (!event.sender?.origin?.includes(chrome.runtime.id)) return; if (event.sender?.id !== chrome.runtime.id) return; if (event.target !== VAULT_SCRIPT_NAME) return; @@ -85,7 +99,22 @@ export class VaultService extends VaultServer { response, }); } - }); + }; + + const handleRestartEvent = async (message: DatabaseRestartEvent) => { + const { type: eventType, payload } = message ?? {}; + const integrity = await this.checkVaultIntegrity(); + + if ( + eventType === 'DB_EVENT' && + payload.event === 'restarted' && + !integrity + ) { + this.resetAndReload(); + } + }; + chrome.runtime.onMessage.addListener(handleRestartEvent); + this.communicationProtocol.on(MessageTypes.request, handleRequest); } emitLockEvent() { diff --git a/packages/app/src/systems/Core/hooks/index.tsx b/packages/app/src/systems/Core/hooks/index.tsx index 29df4d45e..08283896c 100644 --- a/packages/app/src/systems/Core/hooks/index.tsx +++ b/packages/app/src/systems/Core/hooks/index.tsx @@ -1,2 +1,3 @@ export * from './useIsLogged'; export * from './useUnlockForm'; +export * from './useRecoverWelcomeFromError'; diff --git a/packages/app/src/systems/Core/hooks/useRecoverWelcomeFromError.ts b/packages/app/src/systems/Core/hooks/useRecoverWelcomeFromError.ts new file mode 100644 index 000000000..b3730205c --- /dev/null +++ b/packages/app/src/systems/Core/hooks/useRecoverWelcomeFromError.ts @@ -0,0 +1,28 @@ +import type { DatabaseRestartEvent } from '@fuel-wallet/types'; +import { useLayoutEffect } from 'react'; +import { IS_CRX } from '../../../config'; + +/** + * @description This hook is used to detect and flag the welcome screen to be recovered from an error state. + */ +export function useRecoverWelcomeFromError() { + useLayoutEffect(() => { + const handleRestartEvent = async (message: DatabaseRestartEvent) => { + const { type: eventType, payload } = message ?? {}; + const isErrorState = + eventType === 'DB_EVENT' && payload.event === 'restarted'; + + if (isErrorState) { + chrome.storage.local.set({ shouldRecoverWelcomeFromError: true }); + } + }; + + if (IS_CRX) { + chrome.runtime.onMessage.addListener(handleRestartEvent); + } + + return () => { + chrome.runtime.onMessage.removeListener(handleRestartEvent); + }; + }, []); +} diff --git a/packages/app/src/systems/Core/utils/database.ts b/packages/app/src/systems/Core/utils/database.ts index d7b2e0142..6596b0189 100644 --- a/packages/app/src/systems/Core/utils/database.ts +++ b/packages/app/src/systems/Core/utils/database.ts @@ -4,6 +4,7 @@ import type { Account, AssetData, Connection, + DatabaseRestartEvent, FuelWalletError, NetworkData, Vault, @@ -23,6 +24,7 @@ export class FuelDB extends Dexie { assets!: Table; abis!: Table; errors!: Table; + readonly alwaysOpen = true; constructor() { super('FuelDB'); @@ -49,6 +51,26 @@ export class FuelDB extends Dexie { id: createUUID(), }); }); + this.on('blocked', () => this.restart('closed')); + this.on('close', () => this.restart('blocked')); + } + + async restart(eventName: 'blocked' | 'closed') { + if (!this.alwaysOpen) { + return; + } + if (eventName !== 'closed') { + this.close(); + } + + this.open(); + + chrome.runtime.sendMessage({ + type: 'DB_EVENT', + payload: { + event: 'restarted', + }, + } as DatabaseRestartEvent); } async clear() { diff --git a/packages/app/src/systems/Vault/services/VaultServer.ts b/packages/app/src/systems/Vault/services/VaultServer.ts index 3663ac159..6e1a48c7f 100644 --- a/packages/app/src/systems/Vault/services/VaultServer.ts +++ b/packages/app/src/systems/Vault/services/VaultServer.ts @@ -46,7 +46,7 @@ export type VaultInputs = { export class VaultServer extends EventEmitter { readonly server: JSONRPCServer; - readonly manager: WalletManager; + manager: WalletManager; static readonly methods: Array = [ 'isLocked', 'unlock', @@ -194,6 +194,13 @@ export class VaultServer extends EventEmitter { await this.manager.removeVault(vault.vaultId); } } + + resetAndReload() { + const storage = new IndexedDBStorage(); + const manager = new WalletManager({ storage }); + this.manager = manager; + chrome.runtime.reload(); + } } export type VaultMethods = { diff --git a/packages/types/src/database.ts b/packages/types/src/database.ts new file mode 100644 index 000000000..2bc36f583 --- /dev/null +++ b/packages/types/src/database.ts @@ -0,0 +1,6 @@ +export interface DatabaseRestartEvent { + type: 'DB_EVENT'; + payload: { + event: 'restarted'; + }; +} diff --git a/packages/types/src/index.ts b/packages/types/src/index.ts index 8e96b6466..465a3a111 100644 --- a/packages/types/src/index.ts +++ b/packages/types/src/index.ts @@ -7,3 +7,4 @@ export * from './fuel'; export * from './constants'; export * from './abi'; export * from './error'; +export * from './database';