From 84b93c7e2ec4e90c2883220f3c5a52eab1055cb6 Mon Sep 17 00:00:00 2001 From: IsaccoSordo Date: Thu, 6 Feb 2025 13:58:25 +0100 Subject: [PATCH 01/25] fix: p2p connection failure --- .../beacon-dapp/src/dapp-client/DAppClient.ts | 24 ++- packages/beacon-dapp/src/events.ts | 190 ++++++++++-------- .../P2PCommunicationClient.ts | 30 ++- .../beacon-ui/src/components/info/index.tsx | 2 +- 4 files changed, 145 insertions(+), 101 deletions(-) diff --git a/packages/beacon-dapp/src/dapp-client/DAppClient.ts b/packages/beacon-dapp/src/dapp-client/DAppClient.ts index 55cb68089..404abd41e 100644 --- a/packages/beacon-dapp/src/dapp-client/DAppClient.ts +++ b/packages/beacon-dapp/src/dapp-client/DAppClient.ts @@ -306,7 +306,8 @@ export class DAppClient extends Client { .catch(async (storageError) => { logger.error(storageError) await this.resetInvalidState(false) - this.events.emit(BeaconEvent.INVALID_ACCOUNT_DEACTIVATED) + this.events.emit(BeaconEvent.GENERIC_ERROR, storageError.message) + return undefined }) @@ -824,12 +825,11 @@ export class DAppClient extends Client { p2pTransport .connect() .then() - .catch((err) => { + .catch(async (err) => { logger.error(err) - if (err.message === 'The account is deactivated.') { - this.hideUI(['alert']) - abortHandler() - } + await this.hideUI(['alert']) // hide pairing alert + setTimeout(() => this.events.emit(BeaconEvent.GENERIC_ERROR, err.message), 1000) + abortHandler() }) return p2pTransport.getPairingRequestInfo() }, @@ -866,7 +866,17 @@ export class DAppClient extends Client { private async resetInvalidState(emit: boolean = true) { this.accountManager.removeAllAccounts() this._activeAccount = ExposedPromise.resolve(undefined) - this.storage.set(StorageKey.ACTIVE_ACCOUNT, undefined) + + Object.values(StorageKey) + .filter( + (key) => + !key.includes('wc@2') && + !key.includes('secret') && + !key.includes('sdk_version') && + !key.includes('user-id') + ) + .forEach((key) => this.storage.delete(key)) + emit && this.events.emit(BeaconEvent.INVALID_ACTIVE_ACCOUNT_STATE) !emit && this.hideUI(['alert']) await Promise.all([ diff --git a/packages/beacon-dapp/src/events.ts b/packages/beacon-dapp/src/events.ts index f8ef74604..df0654db0 100644 --- a/packages/beacon-dapp/src/events.ts +++ b/packages/beacon-dapp/src/events.ts @@ -96,6 +96,7 @@ export enum BeaconEvent { HIDE_UI = 'HIDE_UI', INVALID_ACTIVE_ACCOUNT_STATE = 'INVALID_ACTIVE_ACCOUNT_STATE', INVALID_ACCOUNT_DEACTIVATED = 'INVALID_ACCOUNT_DEACTIVATED', + GENERIC_ERROR = 'GENERIC_ERROR', PAIR_INIT = 'PAIR_INIT', PAIR_SUCCESS = 'PAIR_SUCCESS', CHANNEL_CLOSED = 'CHANNEL_CLOSED', @@ -196,8 +197,9 @@ export interface BeaconEventType { [BeaconEvent.NO_PERMISSIONS]: undefined [BeaconEvent.ACTIVE_ACCOUNT_SET]: AccountInfo [BeaconEvent.ACTIVE_TRANSPORT_SET]: Transport - [BeaconEvent.INVALID_ACTIVE_ACCOUNT_STATE]: undefined + [BeaconEvent.INVALID_ACTIVE_ACCOUNT_STATE]: undefined [BeaconEvent.INVALID_ACCOUNT_DEACTIVATED]: undefined + [BeaconEvent.GENERIC_ERROR]: string [BeaconEvent.SHOW_PREPARE]: { walletInfo?: WalletInfo } [BeaconEvent.HIDE_UI]: ('alert' | 'toast')[] | undefined [BeaconEvent.PAIR_INIT]: { @@ -211,9 +213,9 @@ export interface BeaconEventType { featuredWallets?: string[] } [BeaconEvent.PAIR_SUCCESS]: - | ExtendedPostMessagePairingResponse - | ExtendedP2PPairingResponse - | ExtendedWalletConnectPairingResponse + | ExtendedPostMessagePairingResponse + | ExtendedP2PPairingResponse + | ExtendedWalletConnectPairingResponse [BeaconEvent.CHANNEL_CLOSED]: string [BeaconEvent.INTERNAL_ERROR]: { text: string; buttons?: AlertButton[] } [BeaconEvent.UNKNOWN]: undefined @@ -331,11 +333,23 @@ const showInvalidActiveAccountState = async (): Promise => { */ const showInvalidAccountDeactivated = async (): Promise => { await openAlert({ - title: 'Error', + title: 'Info', body: `Your session has expired. Please pair with your wallet again.` }) } +/** + * Show an "Invalid state" alert + */ +const showGenericErrorAlert = async (errorMessage: string): Promise => { + await openAlert({ + title: 'Error', + body: `${errorMessage}.
Please try again.
If this problem persists please reach out support@walletbeacon.io` + }) +} + /** * Show an error toast * @@ -531,22 +545,22 @@ const showProofOfEventChallengeSuccessAlert = async ( state: 'finished', actions: output.isAccepted ? [ - { - text: `Payload hash: ${output.payloadHash}`, - actionText: 'Copy to clipboard', - actionCallback: async (): Promise => { - navigator.clipboard.writeText(output.payloadHash).then( - () => { - logger.log('showSignSuccessAlert', 'Copying to clipboard was successful!') - }, - (err) => { - logger.error('showSignSuccessAlert', 'Could not copy text to clipboard: ', err) - } - ) - await closeToast() + { + text: `Payload hash: ${output.payloadHash}`, + actionText: 'Copy to clipboard', + actionCallback: async (): Promise => { + navigator.clipboard.writeText(output.payloadHash).then( + () => { + logger.log('showSignSuccessAlert', 'Copying to clipboard was successful!') + }, + (err) => { + logger.error('showSignSuccessAlert', 'Could not copy text to clipboard: ', err) + } + ) + await closeToast() + } } - } - ] + ] : [] }) } @@ -565,22 +579,22 @@ const showSimulatedProofOfEventChallengeSuccessAlert = async ( state: 'finished', actions: !output.errorMessage ? [ - { - text: 'Operation list', - actionText: 'Copy to clipboard', - actionCallback: async (): Promise => { - navigator.clipboard.writeText(output.operationsList).then( - () => { - logger.log('showSignSuccessAlert', 'Copying to clipboard was successful!') - }, - (err) => { - logger.error('showSignSuccessAlert', 'Could not copy text to clipboard: ', err) - } - ) - await closeToast() + { + text: 'Operation list', + actionText: 'Copy to clipboard', + actionCallback: async (): Promise => { + navigator.clipboard.writeText(output.operationsList).then( + () => { + logger.log('showSignSuccessAlert', 'Copying to clipboard was successful!') + }, + (err) => { + logger.error('showSignSuccessAlert', 'Could not copy text to clipboard: ', err) + } + ) + await closeToast() + } } - } - ] + ] : [{ text: 'Error message', actionText: output.errorMessage }] }) } @@ -769,6 +783,7 @@ export const defaultEventCallbacks: { [BeaconEvent.ACTIVE_TRANSPORT_SET]: emptyHandler(), [BeaconEvent.INVALID_ACTIVE_ACCOUNT_STATE]: showInvalidActiveAccountState, [BeaconEvent.INVALID_ACCOUNT_DEACTIVATED]: showInvalidAccountDeactivated, + [BeaconEvent.GENERIC_ERROR]: showGenericErrorAlert, [BeaconEvent.SHOW_PREPARE]: showPrepare, [BeaconEvent.HIDE_UI]: hideUI, [BeaconEvent.PAIR_INIT]: showPairAlert, @@ -787,59 +802,58 @@ export class BeaconEventHandler { private readonly callbackMap: { [key in BeaconEvent]: BeaconEventHandlerFunction[] // TODO: Fix type } = { - [BeaconEvent.PERMISSION_REQUEST_SENT]: [defaultEventCallbacks.PERMISSION_REQUEST_SENT], - [BeaconEvent.PERMISSION_REQUEST_SUCCESS]: [defaultEventCallbacks.PERMISSION_REQUEST_SUCCESS], - [BeaconEvent.PERMISSION_REQUEST_ERROR]: [defaultEventCallbacks.PERMISSION_REQUEST_ERROR], - [BeaconEvent.PROOF_OF_EVENT_CHALLENGE_REQUEST_SENT]: [ - defaultEventCallbacks.PERMISSION_REQUEST_SENT - ], - [BeaconEvent.PROOF_OF_EVENT_CHALLENGE_REQUEST_SUCCESS]: [ - defaultEventCallbacks.PROOF_OF_EVENT_CHALLENGE_REQUEST_SUCCESS - ], - [BeaconEvent.PROOF_OF_EVENT_CHALLENGE_REQUEST_ERROR]: [ - defaultEventCallbacks.PROOF_OF_EVENT_CHALLENGE_REQUEST_ERROR - ], - [BeaconEvent.SIMULATED_PROOF_OF_EVENT_CHALLENGE_REQUEST_SENT]: [ - defaultEventCallbacks.SIMULATED_PROOF_OF_EVENT_CHALLENGE_REQUEST_SENT - ], - [BeaconEvent.SIMULATED_PROOF_OF_EVENT_CHALLENGE_REQUEST_SUCCESS]: [ - defaultEventCallbacks.SIMULATED_PROOF_OF_EVENT_CHALLENGE_REQUEST_SUCCESS - ], - [BeaconEvent.SIMULATED_PROOF_OF_EVENT_CHALLENGE_REQUEST_ERROR]: [ - defaultEventCallbacks.SIMULATED_PROOF_OF_EVENT_CHALLENGE_REQUEST_ERROR - ], - [BeaconEvent.OPERATION_REQUEST_SENT]: [defaultEventCallbacks.OPERATION_REQUEST_SENT], - [BeaconEvent.OPERATION_REQUEST_SUCCESS]: [defaultEventCallbacks.OPERATION_REQUEST_SUCCESS], - [BeaconEvent.OPERATION_REQUEST_ERROR]: [defaultEventCallbacks.OPERATION_REQUEST_ERROR], - [BeaconEvent.SIGN_REQUEST_SENT]: [defaultEventCallbacks.SIGN_REQUEST_SENT], - [BeaconEvent.SIGN_REQUEST_SUCCESS]: [defaultEventCallbacks.SIGN_REQUEST_SUCCESS], - [BeaconEvent.SIGN_REQUEST_ERROR]: [defaultEventCallbacks.SIGN_REQUEST_ERROR], - // TODO: ENCRYPTION - // [BeaconEvent.ENCRYPT_REQUEST_SENT]: [defaultEventCallbacks.ENCRYPT_REQUEST_SENT], - // [BeaconEvent.ENCRYPT_REQUEST_SUCCESS]: [defaultEventCallbacks.ENCRYPT_REQUEST_SUCCESS], - // [BeaconEvent.ENCRYPT_REQUEST_ERROR]: [defaultEventCallbacks.ENCRYPT_REQUEST_ERROR], - [BeaconEvent.BROADCAST_REQUEST_SENT]: [defaultEventCallbacks.BROADCAST_REQUEST_SENT], - [BeaconEvent.BROADCAST_REQUEST_SUCCESS]: [defaultEventCallbacks.BROADCAST_REQUEST_SUCCESS], - [BeaconEvent.BROADCAST_REQUEST_ERROR]: [defaultEventCallbacks.BROADCAST_REQUEST_ERROR], - [BeaconEvent.ACKNOWLEDGE_RECEIVED]: [defaultEventCallbacks.ACKNOWLEDGE_RECEIVED], - [BeaconEvent.LOCAL_RATE_LIMIT_REACHED]: [defaultEventCallbacks.LOCAL_RATE_LIMIT_REACHED], - [BeaconEvent.NO_PERMISSIONS]: [defaultEventCallbacks.NO_PERMISSIONS], - [BeaconEvent.ACTIVE_ACCOUNT_SET]: [defaultEventCallbacks.ACTIVE_ACCOUNT_SET], - [BeaconEvent.ACTIVE_TRANSPORT_SET]: [defaultEventCallbacks.ACTIVE_TRANSPORT_SET], - [BeaconEvent.INVALID_ACTIVE_ACCOUNT_STATE]: [ - defaultEventCallbacks.INVALID_ACTIVE_ACCOUNT_STATE - ], - [BeaconEvent.INVALID_ACCOUNT_DEACTIVATED]: [ - defaultEventCallbacks.INVALID_ACCOUNT_DEACTIVATED - ], - [BeaconEvent.SHOW_PREPARE]: [defaultEventCallbacks.SHOW_PREPARE], - [BeaconEvent.HIDE_UI]: [defaultEventCallbacks.HIDE_UI], - [BeaconEvent.PAIR_INIT]: [defaultEventCallbacks.PAIR_INIT], - [BeaconEvent.PAIR_SUCCESS]: [defaultEventCallbacks.PAIR_SUCCESS], - [BeaconEvent.CHANNEL_CLOSED]: [defaultEventCallbacks.CHANNEL_CLOSED], - [BeaconEvent.INTERNAL_ERROR]: [defaultEventCallbacks.INTERNAL_ERROR], - [BeaconEvent.UNKNOWN]: [defaultEventCallbacks.UNKNOWN] - } + [BeaconEvent.PERMISSION_REQUEST_SENT]: [defaultEventCallbacks.PERMISSION_REQUEST_SENT], + [BeaconEvent.PERMISSION_REQUEST_SUCCESS]: [defaultEventCallbacks.PERMISSION_REQUEST_SUCCESS], + [BeaconEvent.PERMISSION_REQUEST_ERROR]: [defaultEventCallbacks.PERMISSION_REQUEST_ERROR], + [BeaconEvent.PROOF_OF_EVENT_CHALLENGE_REQUEST_SENT]: [ + defaultEventCallbacks.PERMISSION_REQUEST_SENT + ], + [BeaconEvent.PROOF_OF_EVENT_CHALLENGE_REQUEST_SUCCESS]: [ + defaultEventCallbacks.PROOF_OF_EVENT_CHALLENGE_REQUEST_SUCCESS + ], + [BeaconEvent.PROOF_OF_EVENT_CHALLENGE_REQUEST_ERROR]: [ + defaultEventCallbacks.PROOF_OF_EVENT_CHALLENGE_REQUEST_ERROR + ], + [BeaconEvent.SIMULATED_PROOF_OF_EVENT_CHALLENGE_REQUEST_SENT]: [ + defaultEventCallbacks.SIMULATED_PROOF_OF_EVENT_CHALLENGE_REQUEST_SENT + ], + [BeaconEvent.SIMULATED_PROOF_OF_EVENT_CHALLENGE_REQUEST_SUCCESS]: [ + defaultEventCallbacks.SIMULATED_PROOF_OF_EVENT_CHALLENGE_REQUEST_SUCCESS + ], + [BeaconEvent.SIMULATED_PROOF_OF_EVENT_CHALLENGE_REQUEST_ERROR]: [ + defaultEventCallbacks.SIMULATED_PROOF_OF_EVENT_CHALLENGE_REQUEST_ERROR + ], + [BeaconEvent.OPERATION_REQUEST_SENT]: [defaultEventCallbacks.OPERATION_REQUEST_SENT], + [BeaconEvent.OPERATION_REQUEST_SUCCESS]: [defaultEventCallbacks.OPERATION_REQUEST_SUCCESS], + [BeaconEvent.OPERATION_REQUEST_ERROR]: [defaultEventCallbacks.OPERATION_REQUEST_ERROR], + [BeaconEvent.SIGN_REQUEST_SENT]: [defaultEventCallbacks.SIGN_REQUEST_SENT], + [BeaconEvent.SIGN_REQUEST_SUCCESS]: [defaultEventCallbacks.SIGN_REQUEST_SUCCESS], + [BeaconEvent.SIGN_REQUEST_ERROR]: [defaultEventCallbacks.SIGN_REQUEST_ERROR], + // TODO: ENCRYPTION + // [BeaconEvent.ENCRYPT_REQUEST_SENT]: [defaultEventCallbacks.ENCRYPT_REQUEST_SENT], + // [BeaconEvent.ENCRYPT_REQUEST_SUCCESS]: [defaultEventCallbacks.ENCRYPT_REQUEST_SUCCESS], + // [BeaconEvent.ENCRYPT_REQUEST_ERROR]: [defaultEventCallbacks.ENCRYPT_REQUEST_ERROR], + [BeaconEvent.BROADCAST_REQUEST_SENT]: [defaultEventCallbacks.BROADCAST_REQUEST_SENT], + [BeaconEvent.BROADCAST_REQUEST_SUCCESS]: [defaultEventCallbacks.BROADCAST_REQUEST_SUCCESS], + [BeaconEvent.BROADCAST_REQUEST_ERROR]: [defaultEventCallbacks.BROADCAST_REQUEST_ERROR], + [BeaconEvent.ACKNOWLEDGE_RECEIVED]: [defaultEventCallbacks.ACKNOWLEDGE_RECEIVED], + [BeaconEvent.LOCAL_RATE_LIMIT_REACHED]: [defaultEventCallbacks.LOCAL_RATE_LIMIT_REACHED], + [BeaconEvent.NO_PERMISSIONS]: [defaultEventCallbacks.NO_PERMISSIONS], + [BeaconEvent.ACTIVE_ACCOUNT_SET]: [defaultEventCallbacks.ACTIVE_ACCOUNT_SET], + [BeaconEvent.ACTIVE_TRANSPORT_SET]: [defaultEventCallbacks.ACTIVE_TRANSPORT_SET], + [BeaconEvent.INVALID_ACTIVE_ACCOUNT_STATE]: [ + defaultEventCallbacks.INVALID_ACTIVE_ACCOUNT_STATE + ], + [BeaconEvent.INVALID_ACCOUNT_DEACTIVATED]: [defaultEventCallbacks.INVALID_ACCOUNT_DEACTIVATED], + [BeaconEvent.GENERIC_ERROR]: [defaultEventCallbacks.GENERIC_ERROR], + [BeaconEvent.SHOW_PREPARE]: [defaultEventCallbacks.SHOW_PREPARE], + [BeaconEvent.HIDE_UI]: [defaultEventCallbacks.HIDE_UI], + [BeaconEvent.PAIR_INIT]: [defaultEventCallbacks.PAIR_INIT], + [BeaconEvent.PAIR_SUCCESS]: [defaultEventCallbacks.PAIR_SUCCESS], + [BeaconEvent.CHANNEL_CLOSED]: [defaultEventCallbacks.CHANNEL_CLOSED], + [BeaconEvent.INTERNAL_ERROR]: [defaultEventCallbacks.INTERNAL_ERROR], + [BeaconEvent.UNKNOWN]: [defaultEventCallbacks.UNKNOWN] + } constructor( eventsToOverride: { [key in BeaconEvent]?: { diff --git a/packages/beacon-transport-matrix/src/communication-client/P2PCommunicationClient.ts b/packages/beacon-transport-matrix/src/communication-client/P2PCommunicationClient.ts index 77a9005e9..f729863fc 100644 --- a/packages/beacon-transport-matrix/src/communication-client/P2PCommunicationClient.ts +++ b/packages/beacon-transport-matrix/src/communication-client/P2PCommunicationClient.ts @@ -47,18 +47,38 @@ const RESPONSE_WAIT_TIME_MS: number = 1000 const REGIONS_AND_SERVERS: NodeDistributions = { [Regions.EUROPE_WEST]: [ 'beacon-node-1.diamond.papers.tech', + 'beacon-node-1.diamond.walletbeacon.io', 'beacon-node-1.sky.papers.tech', + 'beacon-node-1.sky.walletbeacon.io', 'beacon-node-2.sky.papers.tech', + 'beacon-node-2.sky.walletbeacon.io', 'beacon-node-1.hope.papers.tech', + 'beacon-node-1.hope.walletbeacon.io', 'beacon-node-1.hope-2.papers.tech', + 'beacon-node-1.hope-2.walletbeacon.io', 'beacon-node-1.hope-3.papers.tech', + 'beacon-node-1.hope-3.walletbeacon.io', 'beacon-node-1.hope-4.papers.tech', - 'beacon-node-1.hope-5.papers.tech' + 'beacon-node-1.hope-4.walletbeacon.io', + 'beacon-node-1.hope-5.papers.tech', + 'beacon-node-1.hope-5.walletbeacon.io' ], - [Regions.NORTH_AMERICA_EAST]: ['beacon-node-1.beacon-server-1.papers.tech'], - [Regions.NORTH_AMERICA_WEST]: ['beacon-node-1.beacon-server-2.papers.tech'], - [Regions.ASIA_EAST]: ['beacon-node-1.beacon-server-3.papers.tech'], - [Regions.AUSTRALIA]: ['beacon-node-1.beacon-server-4.papers.tech'] + [Regions.NORTH_AMERICA_EAST]: [ + 'beacon-node-1.beacon-server-1.papers.tech', + 'beacon-node-1.beacon-server-1.walletbeacon.io' + ], + [Regions.NORTH_AMERICA_WEST]: [ + 'beacon-node-1.beacon-server-2.papers.tech', + 'beacon-node-1.beacon-server-2.walletbeacon.io' + ], + [Regions.ASIA_EAST]: [ + 'beacon-node-1.beacon-server-3.papers.tech', + 'beacon-node-1.beacon-server-3.walletbeacon.io' + ], + [Regions.AUSTRALIA]: [ + 'beacon-node-1.beacon-server-4.papers.tech', + 'beacon-node-1.beacon-server-4.walletbeacon.io' + ] } interface BeaconInfoResponse { diff --git a/packages/beacon-ui/src/components/info/index.tsx b/packages/beacon-ui/src/components/info/index.tsx index 8ce757534..8270bfbae 100644 --- a/packages/beacon-ui/src/components/info/index.tsx +++ b/packages/beacon-ui/src/components/info/index.tsx @@ -27,7 +27,7 @@ const Info: Component = (props: InfoProps) => { )}

{props.title}

- {props.description &&
{props.description}
} + {props.description &&
} {props.data &&
{props.data}
}
From 812e7d90d943de941ba3544f557bce13be7d72a3 Mon Sep 17 00:00:00 2001 From: IsaccoSordo Date: Thu, 6 Feb 2025 14:02:18 +0100 Subject: [PATCH 02/25] chore: removed INVALID_ACCOUNT_DEACTIVATED --- packages/beacon-dapp/src/events.ts | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/packages/beacon-dapp/src/events.ts b/packages/beacon-dapp/src/events.ts index df0654db0..ff9e17fd7 100644 --- a/packages/beacon-dapp/src/events.ts +++ b/packages/beacon-dapp/src/events.ts @@ -95,7 +95,6 @@ export enum BeaconEvent { SHOW_PREPARE = 'SHOW_PREPARE', HIDE_UI = 'HIDE_UI', INVALID_ACTIVE_ACCOUNT_STATE = 'INVALID_ACTIVE_ACCOUNT_STATE', - INVALID_ACCOUNT_DEACTIVATED = 'INVALID_ACCOUNT_DEACTIVATED', GENERIC_ERROR = 'GENERIC_ERROR', PAIR_INIT = 'PAIR_INIT', PAIR_SUCCESS = 'PAIR_SUCCESS', @@ -198,7 +197,6 @@ export interface BeaconEventType { [BeaconEvent.ACTIVE_ACCOUNT_SET]: AccountInfo [BeaconEvent.ACTIVE_TRANSPORT_SET]: Transport [BeaconEvent.INVALID_ACTIVE_ACCOUNT_STATE]: undefined - [BeaconEvent.INVALID_ACCOUNT_DEACTIVATED]: undefined [BeaconEvent.GENERIC_ERROR]: string [BeaconEvent.SHOW_PREPARE]: { walletInfo?: WalletInfo } [BeaconEvent.HIDE_UI]: ('alert' | 'toast')[] | undefined @@ -328,16 +326,6 @@ const showInvalidActiveAccountState = async (): Promise => { }) } -/** - * Show an "account deactivated" error alert - */ -const showInvalidAccountDeactivated = async (): Promise => { - await openAlert({ - title: 'Info', - body: `Your session has expired. Please pair with your wallet again.` - }) -} - /** * Show an "Invalid state" alert */ @@ -782,7 +770,6 @@ export const defaultEventCallbacks: { [BeaconEvent.ACTIVE_ACCOUNT_SET]: emptyHandler(), [BeaconEvent.ACTIVE_TRANSPORT_SET]: emptyHandler(), [BeaconEvent.INVALID_ACTIVE_ACCOUNT_STATE]: showInvalidActiveAccountState, - [BeaconEvent.INVALID_ACCOUNT_DEACTIVATED]: showInvalidAccountDeactivated, [BeaconEvent.GENERIC_ERROR]: showGenericErrorAlert, [BeaconEvent.SHOW_PREPARE]: showPrepare, [BeaconEvent.HIDE_UI]: hideUI, @@ -844,7 +831,6 @@ export class BeaconEventHandler { [BeaconEvent.INVALID_ACTIVE_ACCOUNT_STATE]: [ defaultEventCallbacks.INVALID_ACTIVE_ACCOUNT_STATE ], - [BeaconEvent.INVALID_ACCOUNT_DEACTIVATED]: [defaultEventCallbacks.INVALID_ACCOUNT_DEACTIVATED], [BeaconEvent.GENERIC_ERROR]: [defaultEventCallbacks.GENERIC_ERROR], [BeaconEvent.SHOW_PREPARE]: [defaultEventCallbacks.SHOW_PREPARE], [BeaconEvent.HIDE_UI]: [defaultEventCallbacks.HIDE_UI], From 52ba9f028f368aaa12af8dabc3e39e22d2817c35 Mon Sep 17 00:00:00 2001 From: IsaccoSordo Date: Tue, 11 Feb 2025 14:51:06 +0100 Subject: [PATCH 03/25] feat: made bug report available everywhere --- packages/beacon-dapp/src/events.ts | 24 ++- .../beacon-ui/src/components/info/index.tsx | 2 +- packages/beacon-ui/src/index.ts | 2 +- packages/beacon-ui/src/ui/alert/index.tsx | 170 +++++++----------- 4 files changed, 88 insertions(+), 110 deletions(-) diff --git a/packages/beacon-dapp/src/events.ts b/packages/beacon-dapp/src/events.ts index ff9e17fd7..507e40e7b 100644 --- a/packages/beacon-dapp/src/events.ts +++ b/packages/beacon-dapp/src/events.ts @@ -40,7 +40,7 @@ import { // EncryptionOperation } from '@airgap/beacon-core' import { shortenString } from './utils/shorten-string' -import { isMobile, isMobileOS } from '@airgap/beacon-ui' +import { isMobile, isMobileOS, openBugReport } from '@airgap/beacon-ui' const logger = new Logger('BeaconEvents') @@ -100,6 +100,8 @@ export enum BeaconEvent { PAIR_SUCCESS = 'PAIR_SUCCESS', CHANNEL_CLOSED = 'CHANNEL_CLOSED', + OPEN_BUG_REPORT = 'OPEN_BUG_REPORT', + INTERNAL_ERROR = 'INTERNAL_ERROR', UNKNOWN = 'UNKNOWN' } @@ -216,6 +218,7 @@ export interface BeaconEventType { | ExtendedWalletConnectPairingResponse [BeaconEvent.CHANNEL_CLOSED]: string [BeaconEvent.INTERNAL_ERROR]: { text: string; buttons?: AlertButton[] } + [BeaconEvent.OPEN_BUG_REPORT]: undefined [BeaconEvent.UNKNOWN]: undefined } @@ -331,10 +334,15 @@ const showInvalidActiveAccountState = async (): Promise => { */ const showGenericErrorAlert = async (errorMessage: string): Promise => { await openAlert({ - title: 'Error', - body: `${errorMessage}.
Please try again.
If this problem persists please reach out support@walletbeacon.io` + title: `${errorMessage}`, + body: 'Please try again. If this problem persists please send us a bug report here', + buttons: [ + { + label: 'Send Report', + type: 'primary', + onClick: () => openBugReport() + } + ] as any }) } @@ -436,6 +444,10 @@ const showExtensionConnectedAlert = async (): Promise => { await closeAlerts() } +const showBugReportForm = () => { + openBugReport() +} + /** * Show a "channel closed" alert for 1.5 seconds */ @@ -775,6 +787,7 @@ export const defaultEventCallbacks: { [BeaconEvent.HIDE_UI]: hideUI, [BeaconEvent.PAIR_INIT]: showPairAlert, [BeaconEvent.PAIR_SUCCESS]: showExtensionConnectedAlert, + [BeaconEvent.OPEN_BUG_REPORT]: showBugReportForm, [BeaconEvent.CHANNEL_CLOSED]: showChannelClosedAlert, [BeaconEvent.INTERNAL_ERROR]: showInternalErrorAlert, [BeaconEvent.UNKNOWN]: emptyHandler() @@ -836,6 +849,7 @@ export class BeaconEventHandler { [BeaconEvent.HIDE_UI]: [defaultEventCallbacks.HIDE_UI], [BeaconEvent.PAIR_INIT]: [defaultEventCallbacks.PAIR_INIT], [BeaconEvent.PAIR_SUCCESS]: [defaultEventCallbacks.PAIR_SUCCESS], + [BeaconEvent.OPEN_BUG_REPORT]: [defaultEventCallbacks.OPEN_BUG_REPORT], [BeaconEvent.CHANNEL_CLOSED]: [defaultEventCallbacks.CHANNEL_CLOSED], [BeaconEvent.INTERNAL_ERROR]: [defaultEventCallbacks.INTERNAL_ERROR], [BeaconEvent.UNKNOWN]: [defaultEventCallbacks.UNKNOWN] diff --git a/packages/beacon-ui/src/components/info/index.tsx b/packages/beacon-ui/src/components/info/index.tsx index 8270bfbae..8ce757534 100644 --- a/packages/beacon-ui/src/components/info/index.tsx +++ b/packages/beacon-ui/src/components/info/index.tsx @@ -27,7 +27,7 @@ const Info: Component = (props: InfoProps) => {
)}

{props.title}

- {props.description &&
} + {props.description &&
{props.description}
} {props.data &&
{props.data}
}
diff --git a/packages/beacon-ui/src/index.ts b/packages/beacon-ui/src/index.ts index 0c0d230b5..a9aa4a5f7 100644 --- a/packages/beacon-ui/src/index.ts +++ b/packages/beacon-ui/src/index.ts @@ -1,4 +1,4 @@ -export { openAlert, closeAlerts } from './ui/alert' +export { openAlert, closeAlerts, openBugReport } from './ui/alert' export type { AlertButton, AlertConfig } from './ui/alert' export { diff --git a/packages/beacon-ui/src/ui/alert/index.tsx b/packages/beacon-ui/src/ui/alert/index.tsx index 345bb3355..ad765f773 100644 --- a/packages/beacon-ui/src/ui/alert/index.tsx +++ b/packages/beacon-ui/src/ui/alert/index.tsx @@ -122,7 +122,7 @@ const closeAlert = (_: string): Promise => { */ const closeAlerts = async (): Promise => { if (currentInfo() === 'help') { - console.log('setting status as pairing expired.') + logger.log('closeAlerts', 'setting status as pairing expired.') setPairingExpired(true) return } @@ -144,6 +144,11 @@ const closeAlerts = async (): Promise => { }) } +const openBugReport = () => { + setPreviousInfo(currentInfo()) + setCurrentInfo('help') +} + /** * Show an alert * @@ -155,9 +160,6 @@ const openAlert = async (config: AlertConfig): Promise => { const p2pPayload = config.pairingPayload?.p2pSyncCode() const wcPayload = config.pairingPayload?.walletConnectSyncCode() const isOnline = navigator.onLine - const areMetricsEnabled = localStorage - ? localStorage.getItem(StorageKey.ENABLE_METRICS) === 'true' - : false setAnalytics(config.analytics) @@ -1069,61 +1071,11 @@ const openAlert = async (config: AlertConfig): Promise => { } } > - {areMetricsEnabled && ( - { - handleCloseAlert() - }} - /> - )} - {!areMetricsEnabled && ( - <> - - - - - } - title="What is a wallet?" - description="Wallets let you send, receive, store and interact with digital assets. Your wallet can be used as an easy way to login, instead of having to remember a password." - /> - - - - - - } - title="Not sure where to start?" - description="If you are new to the Web3, we recommend that you start by creating a Kukai wallet. Kukai is a fast way of creating your first wallet using your preferred social account." - /> - - )} + { + handleCloseAlert() + }} + />
=> { - - - - - } - title={config.title || 'No title'} - description={config.body || 'No description'} - data={config.data} - buttons={[ - { - label: 'Close', - type: 'primary', - onClick: () => handleCloseAlert() - } - ]} - /> + <> + {currentInfo() === 'help' ? ( + { + handleCloseAlert() + }} + /> + ) : ( + + + + + + } + title={config.title || 'No title'} + description={config.body || 'No description'} + data={config.data} + buttons={ + (config.buttons as any) ?? [ + { + label: 'Close', + type: 'primary', + onClick: () => handleCloseAlert() + } + ] + } + /> + )} + } onCloseClick={() => handleCloseAlert()} /> @@ -1263,4 +1227,4 @@ const openAlert = async (config: AlertConfig): Promise => { return '' } -export { closeAlert, closeAlerts, openAlert } +export { closeAlert, closeAlerts, openAlert, openBugReport } From 37393e85c57968e5c328aa88e6e868098ce53a83 Mon Sep 17 00:00:00 2001 From: IsaccoSordo Date: Tue, 11 Feb 2025 16:04:51 +0100 Subject: [PATCH 04/25] fix: min length --- packages/beacon-ui/src/components/bug-report-form/index.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/beacon-ui/src/components/bug-report-form/index.tsx b/packages/beacon-ui/src/components/bug-report-form/index.tsx index 7bdd56e20..1f3015fbe 100644 --- a/packages/beacon-ui/src/components/bug-report-form/index.tsx +++ b/packages/beacon-ui/src/components/bug-report-form/index.tsx @@ -40,21 +40,21 @@ const BugReportForm = (props: any) => { const db = new IndexedDBStorage('beacon', 'bug_report') const isTitleValid = () => { - const check = title().replace(/ /gi, '').length > 10 + const check = title().replace(/ /gi, '').length > 2 const invalidText = check ? '' : 'The title must be at least 10 characters long.' setTitleErrorMsg(invalidText) return check } const isDescriptionValid = () => { - const check = description().replace(/ /gi, '').length >= 30 + const check = description().replace(/ /gi, '').length > 2 const invalidText = check ? '' : 'The description must be at least 30 characters long.' setDescriptionErrorMsg(invalidText) return check } const areStepsValid = () => { - const check = steps().replace(/ /gi, '').length >= 30 + const check = steps().replace(/ /gi, '').length > 2 const invalidText = check ? '' : 'Write at least 30 characters to describe the steps to reproduce.' From c0fdd46db8240609467ce57e7a9729e9d8e81cc2 Mon Sep 17 00:00:00 2001 From: IsaccoSordo Date: Wed, 12 Feb 2025 15:41:50 +0100 Subject: [PATCH 05/25] fix: clean storages --- .../src/components/bug-report-form/index.tsx | 89 +++++++++---------- 1 file changed, 42 insertions(+), 47 deletions(-) diff --git a/packages/beacon-ui/src/components/bug-report-form/index.tsx b/packages/beacon-ui/src/components/bug-report-form/index.tsx index 1f3015fbe..92eee40c7 100644 --- a/packages/beacon-ui/src/components/bug-report-form/index.tsx +++ b/packages/beacon-ui/src/components/bug-report-form/index.tsx @@ -25,13 +25,10 @@ interface BugReportRequest { const BugReportForm = (props: any) => { const [title, setTitle] = createSignal('') const [titleTouched, setTitleTouched] = createSignal(false) - const [titleErrorMsg, setTitleErrorMsg] = createSignal('') const [description, setDescription] = createSignal('') const [descriptionTouched, setDescriptionTouched] = createSignal(false) - const [descriptionErrorMsg, setDescriptionErrorMsg] = createSignal('') const [steps, setSteps] = createSignal('') const [stepsTouched, setStepsTouched] = createSignal(false) - const [stepsErrorMsg, setStepsErrorMsg] = createSignal('') const [isFormValid, setFormValid] = createSignal(false) const [isLoading, setIsLoading] = createSignal(false) const [didUserAllow, setDidUserAllow] = createSignal(false) @@ -39,29 +36,6 @@ const BugReportForm = (props: any) => { const [showThankYou, setShowThankYou] = createSignal(false) const db = new IndexedDBStorage('beacon', 'bug_report') - const isTitleValid = () => { - const check = title().replace(/ /gi, '').length > 2 - const invalidText = check ? '' : 'The title must be at least 10 characters long.' - setTitleErrorMsg(invalidText) - return check - } - - const isDescriptionValid = () => { - const check = description().replace(/ /gi, '').length > 2 - const invalidText = check ? '' : 'The description must be at least 30 characters long.' - setDescriptionErrorMsg(invalidText) - return check - } - - const areStepsValid = () => { - const check = steps().replace(/ /gi, '').length > 2 - const invalidText = check - ? '' - : 'Write at least 30 characters to describe the steps to reproduce.' - setStepsErrorMsg(invalidText) - return check - } - const indexDBToMetadata = async () => { const wcResult: StorageObject = {} const beaconResult: StorageObject = {} @@ -96,13 +70,45 @@ const BugReportForm = (props: any) => { } createEffect(() => { - const titleValid = isTitleValid(), - descriptionValid = isDescriptionValid(), - stepsValid = areStepsValid(), - userAllow = didUserAllow() - setFormValid(titleValid && descriptionValid && stepsValid && userAllow) + setFormValid(didUserAllow()) }) + /** + * Recursively removes all properties whose key contains "seed" + * from an object/array. Also, if a string is valid JSON, + * it will attempt to clean its parsed value. + */ + const clean = (value: any): any => { + if (Array.isArray(value)) { + // Process each element in the array. + return value.map(clean) + } else if (value !== null && typeof value === 'object') { + // Process an object by filtering its keys. + const result: Record = {} + for (const key in value) { + if (Object.prototype.hasOwnProperty.call(value, key)) { + // Remove any property with "seed" in its name (case insensitive) + if (key.toLowerCase().includes('seed')) continue + result[key] = clean(value[key]) + } + } + return result + } else if (typeof value === 'string') { + // Try to parse the string as JSON. + try { + const parsed = JSON.parse(value) + // If it parsed successfully, clean the parsed value + // and re-stringify it so that we preserve the original type. + return JSON.stringify(clean(parsed)) + } catch (err) { + // If parsing fails, just return the original string. + return value + } + } + // For primitives (number, boolean, etc.), just return the value. + return value + } + const handleSubmit = async (event: Event) => { event.preventDefault() setStatus(null) @@ -122,8 +128,8 @@ const BugReportForm = (props: any) => { steps: steps(), os: currentOS(), browser: currentBrowser(), - localStorage: JSON.stringify(beaconState), - wcStorage: JSON.stringify(wcState) + localStorage: JSON.stringify(clean(beaconState)), + wcStorage: JSON.stringify(clean(wcState)) } const options = { @@ -172,11 +178,8 @@ const BugReportForm = (props: any) => { !titleTouched() && setTitleTouched(true) setTitle(e.currentTarget.value) }} - class={`input-style ${titleTouched() && titleErrorMsg().length ? 'invalid' : ''}`} + class={`input-style`} /> - {titleTouched() && titleErrorMsg().length && ( - - )}
From b5a9b0650918d9519c66f118463bba9ea669c049 Mon Sep 17 00:00:00 2001 From: IsaccoSordo Date: Fri, 14 Feb 2025 17:24:42 +0100 Subject: [PATCH 06/25] feat: multiple object stores --- .../src/storage/IndexedDBStorage.ts | 120 ++++++++++++++---- .../beacon-dapp/src/dapp-client/DAppClient.ts | 18 ++- .../src/components/bug-report-form/index.tsx | 2 +- 3 files changed, 109 insertions(+), 31 deletions(-) diff --git a/packages/beacon-core/src/storage/IndexedDBStorage.ts b/packages/beacon-core/src/storage/IndexedDBStorage.ts index cfadd22e0..1909b0dfb 100644 --- a/packages/beacon-core/src/storage/IndexedDBStorage.ts +++ b/packages/beacon-core/src/storage/IndexedDBStorage.ts @@ -7,9 +7,14 @@ export class IndexedDBStorage extends Storage { private db: IDBDatabase | null = null private isSupported: boolean = true + /** + * @param dbName Name of the database. + * @param storeNames An array of object store names to create in the database. + * The first store in the array will be used as the default if none is specified. + */ constructor( private readonly dbName: string = 'WALLET_CONNECT_V2_INDEXED_DB', - private readonly storeName: string = 'keyvaluestorage' + private readonly storeNames: string[] = ['keyvaluestorage'] ) { super() this.initDB() @@ -39,23 +44,32 @@ export class IndexedDBStorage extends Storage { openRequest.onupgradeneeded = () => { const db = openRequest.result - if (!db.objectStoreNames.contains(this.storeName)) { - db.createObjectStore(this.storeName) - } + // Create all required object stores + this.storeNames.forEach((storeName) => { + if (!db.objectStoreNames.contains(storeName)) { + db.createObjectStore(storeName) + } + }) } openRequest.onsuccess = (event: any) => { const db = event.target.result as IDBDatabase - if (!db.objectStoreNames.contains(this.storeName)) { - // Close the current connection + // Check if all stores exist – if not, perform an upgrade. + const missingStores = this.storeNames.filter( + (storeName) => !db.objectStoreNames.contains(storeName) + ) + if (missingStores.length > 0) { db.close() - // Re-open the database with an incremented version number const newVersion = db.version + 1 const upgradeRequest = indexedDB.open(this.dbName, newVersion) upgradeRequest.onupgradeneeded = () => { const upgradedDB = upgradeRequest.result - upgradedDB.createObjectStore(this.storeName) + missingStores.forEach((storeName) => { + if (!upgradedDB.objectStoreNames.contains(storeName)) { + upgradedDB.createObjectStore(storeName) + } + }) } upgradeRequest.onsuccess = (event: any) => { @@ -74,28 +88,44 @@ export class IndexedDBStorage extends Storage { }) } + /** + * Performs a transaction on the given object store. + * @param mode Transaction mode. + * @param storeName The name of the object store. + * @param operation The operation to perform with the object store. + */ private async transaction( mode: IDBTransactionMode, + storeName: string, operation: (store: IDBObjectStore) => Promise ): Promise { return new Promise((resolve, reject) => { if (!this.isSupported) { reject('IndexedDB is not supported.') + return } - if (!this.db?.objectStoreNames.contains(this.storeName)) { - reject(`${this.storeName} not found. error: ${new Error().stack}`) + if (!this.db?.objectStoreNames.contains(storeName)) { + reject(`${storeName} not found. error: ${new Error().stack}`) + return } - const transaction = this.db?.transaction(this.storeName, mode) - const objectStore = transaction?.objectStore(this.storeName) - objectStore && operation(objectStore).then(resolve).catch(reject) + const transaction = this.db.transaction(storeName, mode) + const objectStore = transaction.objectStore(storeName) + operation(objectStore).then(resolve).catch(reject) }) } - public get(key: K): Promise { + /** + * Retrieves a value by key from the specified object store. + * If no store is specified, the default (first in the list) is used. + */ + public get(key: K, storeName?: string): Promise + public get(key: string, storeName?: string): Promise + public get(key: StorageKey | string, storeName: string = this.storeNames[0]): Promise { return this.transaction( 'readonly', + storeName, (store) => new Promise((resolve, reject) => { const getRequest = store.get(key) @@ -105,9 +135,23 @@ export class IndexedDBStorage extends Storage { ) } - public set(key: K, value: StorageKeyReturnType[K]): Promise { + /** + * Stores a key/value pair in the specified object store. + */ + public set( + key: K, + value: StorageKeyReturnType[K], + storeName?: string + ): Promise + public set(key: string, value: string, storeName?: string): Promise + public set( + key: StorageKey | string, + value: any, + storeName: string = this.storeNames[0] + ): Promise { return this.transaction( 'readwrite', + storeName, (store) => new Promise((resolve, reject) => { const putRequest = store.put(value, key) @@ -117,9 +161,15 @@ export class IndexedDBStorage extends Storage { ) } - public delete(key: K): Promise { + /** + * Deletes an entry by key from the specified object store. + */ + public delete(key: K, storeName?: string): Promise + public delete(key: string, storeName?: string): Promise + public delete(key: StorageKey | string, storeName: string = this.storeNames[0]): Promise { return this.transaction( 'readwrite', + storeName, (store) => new Promise((resolve, reject) => { const deleteRequest = store.delete(key) @@ -129,9 +179,13 @@ export class IndexedDBStorage extends Storage { ) } - public getAll(): Promise { + /** + * Retrieves all values from the specified object store. + */ + public getAll(storeName?: string): Promise { return this.transaction( 'readonly', + storeName || this.storeNames[0], (store) => new Promise((resolve, reject) => { const getAllRequest = store.getAll() @@ -141,9 +195,13 @@ export class IndexedDBStorage extends Storage { ) } - public getAllKeys(): Promise { + /** + * Retrieves all keys from the specified object store. + */ + public getAllKeys(storeName?: string): Promise { return this.transaction( 'readonly', + storeName || this.storeNames[0], (store) => new Promise((resolve, reject) => { const getAllKeysRequest = store.getAllKeys() @@ -153,9 +211,13 @@ export class IndexedDBStorage extends Storage { ) } - public clearStore(): Promise { + /** + * Clears all entries from the specified object store. + */ + public clearStore(storeName?: string): Promise { return this.transaction( 'readwrite', + storeName || this.storeNames[0], (store) => new Promise((resolve, reject) => { const clearRequest = store.clear() @@ -178,20 +240,22 @@ export class IndexedDBStorage extends Storage { newValue: string | null }) => {} ): Promise { - logger.debug('subscriveToStorageEvent', callback) + logger.debug('subscribeToStorageEvent', callback) throw new Error('Method not implemented.') } /** - * it copies over all key value pairs from a source store into a target one - * @param targetDBName the name of the target DB - * @param targetStoreName the name of the target store - * @param skipKeys all the keys to ignore + * Copies all key/value pairs from the source store into a target store. + * @param targetDBName Name of the target database. + * @param targetStoreName Name of the target object store. + * @param skipKeys Keys to skip. + * @param sourceStoreName (Optional) Source store name – defaults to the default store. */ public async fillStore( targetDBName: string, targetStoreName: string, - skipKeys: string[] = [] + skipKeys: string[] = [], + sourceStoreName: string = this.storeNames[0] ): Promise { if (!this.isSupported) { logger.error('fillStore', 'IndexedDB not supported.') @@ -209,8 +273,8 @@ export class IndexedDBStorage extends Storage { targetDBRequest.onerror = (event: any) => reject(event.target.error) }) - // Copy all items from the source store to the target store, skipping specified keys - await this.transaction('readonly', async (sourceStore) => { + // Copy items from the source store to the target store, skipping any specified keys. + await this.transaction('readonly', sourceStoreName, async (sourceStore) => { const getAllRequest = sourceStore.getAll() const getAllKeysRequest = sourceStore.getAllKeys() @@ -220,7 +284,7 @@ export class IndexedDBStorage extends Storage { const keys = getAllKeysRequest.result if (!targetDB.objectStoreNames.contains(targetStoreName)) { - logger.error(`${this.storeName} not found. ${new Error().stack}`) + logger.error(`${targetStoreName} not found. ${new Error().stack}`) return } diff --git a/packages/beacon-dapp/src/dapp-client/DAppClient.ts b/packages/beacon-dapp/src/dapp-client/DAppClient.ts index 404abd41e..3277c00b9 100644 --- a/packages/beacon-dapp/src/dapp-client/DAppClient.ts +++ b/packages/beacon-dapp/src/dapp-client/DAppClient.ts @@ -223,7 +223,7 @@ export class DAppClient extends Client { private readonly storageValidator: StorageValidator - private readonly bugReportStorage = new IndexedDBStorage('beacon', 'bug_report') + private readonly beaconIDB = new IndexedDBStorage('beacon', ['bug_report', 'metrics']) private debounceSetActiveAccount: boolean = false @@ -548,7 +548,7 @@ export class DAppClient extends Client { try { for (const key of keys) { - await this.bugReportStorage.set(key, await this.storage.get(key)) + await this.beaconIDB.set(key, this.storage.getPrefixedKey(key)) } } catch (err: any) { logger.error('createStateSnapshot', err.message) @@ -1092,15 +1092,29 @@ export class DAppClient extends Client { } } + private async updateMetricsStorage(payload: string) { + const size = (await this.beaconIDB.getAll('metrics')).length + + if (size >= 1000) { + await this.beaconIDB.clearStore('metrics') + } + + this.beaconIDB.set(String(Date.now()), payload, 'metrics') + } + private sendMetrics( uri: string, options?: RequestInit, thenHandler?: (res: Response) => void, catchHandler?: (err: Error) => void ) { + if (!this.enableMetrics && uri === 'performance-metrics/save') { + options && this.updateMetricsStorage(options.body as string) + } if (!this.enableMetrics) { return } + fetch(`https://beacon-backend.prod.gke.papers.tech/${uri}`, options) .then((res) => thenHandler && thenHandler(res)) .catch((err: Error) => { diff --git a/packages/beacon-ui/src/components/bug-report-form/index.tsx b/packages/beacon-ui/src/components/bug-report-form/index.tsx index 92eee40c7..567489cc3 100644 --- a/packages/beacon-ui/src/components/bug-report-form/index.tsx +++ b/packages/beacon-ui/src/components/bug-report-form/index.tsx @@ -34,7 +34,7 @@ const BugReportForm = (props: any) => { const [didUserAllow, setDidUserAllow] = createSignal(false) const [status, setStatus] = createSignal<'success' | 'error' | null>(null) const [showThankYou, setShowThankYou] = createSignal(false) - const db = new IndexedDBStorage('beacon', 'bug_report') + const db = new IndexedDBStorage('beacon', ['bug_report', 'metrics']) const indexDBToMetadata = async () => { const wcResult: StorageObject = {} From 4e9f9eaa56395bcb03d67f8af7dc3b0218ca9a56 Mon Sep 17 00:00:00 2001 From: IsaccoSordo Date: Mon, 17 Feb 2025 12:17:28 +0100 Subject: [PATCH 07/25] feat: saveAll --- .../src/components/bug-report-form/index.tsx | 45 +++++++++++++------ .../src/components/top-wallets/index.tsx | 23 +++------- 2 files changed, 37 insertions(+), 31 deletions(-) diff --git a/packages/beacon-ui/src/components/bug-report-form/index.tsx b/packages/beacon-ui/src/components/bug-report-form/index.tsx index 567489cc3..f0adef379 100644 --- a/packages/beacon-ui/src/components/bug-report-form/index.tsx +++ b/packages/beacon-ui/src/components/bug-report-form/index.tsx @@ -109,6 +109,36 @@ const BugReportForm = (props: any) => { return value } + const sendRequest = ( + url: string, + method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE', + body: any + ) => { + const options = { + method, + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify(body) + } + + return fetch(url, method === 'GET' ? undefined : options) + } + + const sendMetrics = async () => { + const metrics = await db.getAll('metrics') + const payload = metrics.map((metric) => JSON.parse(metric)) + + sendRequest('http://localhost:9001/performance-metrics/saveAll', 'POST', payload) + .then(() => { + db.clearStore('metrics') + }) + .catch((error) => { + console.error('Error while sending metrics:', error.message) + setStatus('error') + }) + } + const handleSubmit = async (event: Event) => { event.preventDefault() setStatus(null) @@ -132,25 +162,14 @@ const BugReportForm = (props: any) => { wcStorage: JSON.stringify(clean(wcState)) } - const options = { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify(request) - } - - fetch('https://beacon-backend.prod.gke.papers.tech/bug-report/save', options) + sendRequest('https://beacon-backend.prod.gke.papers.tech/bug-report/save', 'POST', request) .then((response) => { if (!response.ok) { throw new Error('Network response was not ok') } setStatus('success') setTimeout(() => setShowThankYou(true), 600) - return response.json() - }) - .then((data) => { - console.log(data) + sendMetrics() }) .catch((error) => { console.error('Error while sending report:', error.message) diff --git a/packages/beacon-ui/src/components/top-wallets/index.tsx b/packages/beacon-ui/src/components/top-wallets/index.tsx index 974f69418..35409dc18 100644 --- a/packages/beacon-ui/src/components/top-wallets/index.tsx +++ b/packages/beacon-ui/src/components/top-wallets/index.tsx @@ -2,7 +2,6 @@ import { Component, For } from 'solid-js' import { MergedWallet } from '../../utils/wallets' import Wallet from '../wallet' import styles from './styles.css' -import { StorageKey } from '@airgap/beacon-types' interface TopWalletsProps { wallets: MergedWallet[] @@ -14,28 +13,16 @@ interface TopWalletsProps { } const TopWallets: Component = (props: TopWalletsProps) => { - const enableBugReport = localStorage ? localStorage.getItem(StorageKey.ENABLE_METRICS) : 'false' - return (

Connect Wallet

- {enableBugReport === 'true' && ( - - Do you wish to report a bug?{' '} - props.onClickLearnMore()}> - Click here - - - )} - {enableBugReport !== 'true' && ( - - If you don't have a wallet, you can select a provider and create one now.{' '} - props.onClickLearnMore()}> - Learn more - + + Do you wish to report a bug?{' '} + props.onClickLearnMore()}> + Click here - )} +
From 0c86b7a10bdee1f309645706a3b9b7962ab4dec0 Mon Sep 17 00:00:00 2001 From: IsaccoSordo Date: Mon, 17 Feb 2025 12:18:42 +0100 Subject: [PATCH 08/25] fix: no metrics found --- packages/beacon-ui/src/components/bug-report-form/index.tsx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/beacon-ui/src/components/bug-report-form/index.tsx b/packages/beacon-ui/src/components/bug-report-form/index.tsx index f0adef379..372c42dcc 100644 --- a/packages/beacon-ui/src/components/bug-report-form/index.tsx +++ b/packages/beacon-ui/src/components/bug-report-form/index.tsx @@ -127,6 +127,11 @@ const BugReportForm = (props: any) => { const sendMetrics = async () => { const metrics = await db.getAll('metrics') + + if (!metrics || metrics.length === 0) { + return + } + const payload = metrics.map((metric) => JSON.parse(metric)) sendRequest('http://localhost:9001/performance-metrics/saveAll', 'POST', payload) From 8eba55000b27b93305ab8d4f1ef2b171e619fe8d Mon Sep 17 00:00:00 2001 From: IsaccoSordo Date: Mon, 17 Feb 2025 12:28:06 +0100 Subject: [PATCH 09/25] fix: url --- packages/beacon-ui/src/components/bug-report-form/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/beacon-ui/src/components/bug-report-form/index.tsx b/packages/beacon-ui/src/components/bug-report-form/index.tsx index 372c42dcc..ef610c04b 100644 --- a/packages/beacon-ui/src/components/bug-report-form/index.tsx +++ b/packages/beacon-ui/src/components/bug-report-form/index.tsx @@ -134,7 +134,7 @@ const BugReportForm = (props: any) => { const payload = metrics.map((metric) => JSON.parse(metric)) - sendRequest('http://localhost:9001/performance-metrics/saveAll', 'POST', payload) + sendRequest('https://beacon-backend.prod.gke.papers.tech/performance-metrics/saveAll', 'POST', payload) .then(() => { db.clearStore('metrics') }) From 3de926877f3471e6f33f65e2a57bc0b10f12343d Mon Sep 17 00:00:00 2001 From: IsaccoSordo Date: Mon, 17 Feb 2025 15:14:59 +0100 Subject: [PATCH 10/25] feat: toast open bug report --- packages/beacon-dapp/src/events.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/packages/beacon-dapp/src/events.ts b/packages/beacon-dapp/src/events.ts index 507e40e7b..cfda28209 100644 --- a/packages/beacon-dapp/src/events.ts +++ b/packages/beacon-dapp/src/events.ts @@ -261,6 +261,19 @@ const showSentToast = async (data: RequestSentInfo): Promise => { } } }) + actions.push({ + text: 'Do you wish to report a bug?', + actionText: 'Open', + actionCallback: async (): Promise => { + await closeToast() + await openAlert({ + title: '', + body: '', + buttons: [] + }) + openBugReport() + } + }) openToast({ body: `Request sent to\u00A0 {{wallet}}`, From e6b6da735865fb7fc2ef1a3f5cbe172d4764034a Mon Sep 17 00:00:00 2001 From: IsaccoSordo Date: Mon, 17 Feb 2025 16:29:28 +0100 Subject: [PATCH 11/25] fix: buttons --- .../beacon-ui/src/components/info/index.tsx | 11 ++++++-- packages/beacon-ui/src/ui/alert/index.tsx | 26 ++++++++++++------- 2 files changed, 26 insertions(+), 11 deletions(-) diff --git a/packages/beacon-ui/src/components/info/index.tsx b/packages/beacon-ui/src/components/info/index.tsx index 8ce757534..1b528905f 100644 --- a/packages/beacon-ui/src/components/info/index.tsx +++ b/packages/beacon-ui/src/components/info/index.tsx @@ -10,11 +10,17 @@ interface InfoProps { border?: boolean iconBadge?: boolean bigIcon?: boolean - buttons?: { label: string; type: 'primary' | 'secondary'; onClick: () => void }[] + buttons?: InfoButton[] downloadLink?: { url: string; label: string } onShowQRCodeClick?: (() => void) | (() => Promise) } +export interface InfoButton { + label: string + type: 'primary' | 'secondary' + onClick: () => void +} + const Info: Component = (props: InfoProps) => { return (
@@ -33,10 +39,11 @@ const Info: Component = (props: InfoProps) => { {(button) => ( )} diff --git a/packages/beacon-ui/src/ui/alert/index.tsx b/packages/beacon-ui/src/ui/alert/index.tsx index ad765f773..4ea100af7 100644 --- a/packages/beacon-ui/src/ui/alert/index.tsx +++ b/packages/beacon-ui/src/ui/alert/index.tsx @@ -770,6 +770,7 @@ const openAlert = async (config: AlertConfig): Promise => { ) const isConnected = !currentWallet()?.supportedInteractionStandards?.includes('wallet_connect') || isWCWorking() + return ( <> {isConnected ? ( @@ -792,6 +793,21 @@ const openAlert = async (config: AlertConfig): Promise => { const colorMode = getColorMode() + const buttons = ( + config.buttons ?? [ + { + label: 'Close', + type: 'primary' + } + ] + ).map((btn: any) => { + if (btn.onClick) { + return { ...btn } + } + + return { ...btn, onClick: () => closeAlert('') } + }) + dispose = render( () => (
@@ -1199,15 +1215,7 @@ const openAlert = async (config: AlertConfig): Promise => { title={config.title || 'No title'} description={config.body || 'No description'} data={config.data} - buttons={ - (config.buttons as any) ?? [ - { - label: 'Close', - type: 'primary', - onClick: () => handleCloseAlert() - } - ] - } + buttons={buttons as any} /> )} From 099c14a5aea579a1f2e6725277551d196576f61f Mon Sep 17 00:00:00 2001 From: IsaccoSordo Date: Mon, 17 Feb 2025 16:54:20 +0100 Subject: [PATCH 12/25] fix: types --- .../beacon-dapp/src/dapp-client/DAppClient.ts | 16 +++++++----- packages/beacon-dapp/src/events.ts | 4 +-- .../beacon-ui/src/components/info/index.tsx | 9 ++----- packages/beacon-ui/src/ui/alert/index.tsx | 26 +++++++------------ 4 files changed, 22 insertions(+), 33 deletions(-) diff --git a/packages/beacon-dapp/src/dapp-client/DAppClient.ts b/packages/beacon-dapp/src/dapp-client/DAppClient.ts index 3277c00b9..acc110c2d 100644 --- a/packages/beacon-dapp/src/dapp-client/DAppClient.ts +++ b/packages/beacon-dapp/src/dapp-client/DAppClient.ts @@ -2059,7 +2059,7 @@ export class DAppClient extends Client { } } - buttons.push({ text: 'Remove account', actionCallback }) + buttons.push({ label: 'Remove account', type: 'primary', onClick: actionCallback }) } const peer = await this.getPeer() @@ -2403,11 +2403,12 @@ export class DAppClient extends Client { text: 'Unable to send message. If this problem persists, please reset the connection and pair your wallet again.', buttons: [ { - text: 'Reset Connection', - actionCallback: async (): Promise => { + label: 'Reset Connection', + onClick: async (): Promise => { await closeToast() this.disconnect() - } + }, + type: 'secondary' } ] }) @@ -2519,11 +2520,12 @@ export class DAppClient extends Client { text: 'Unable to send message. If this problem persists, please reset the connection and pair your wallet again.', buttons: [ { - text: 'Reset Connection', - actionCallback: async (): Promise => { + label: 'Reset Connection', + onClick: async (): Promise => { await closeToast() this.disconnect() - } + }, + type: 'secondary' } ] }) diff --git a/packages/beacon-dapp/src/events.ts b/packages/beacon-dapp/src/events.ts index cfda28209..49954b115 100644 --- a/packages/beacon-dapp/src/events.ts +++ b/packages/beacon-dapp/src/events.ts @@ -445,7 +445,7 @@ const showRateLimitReached = async (): Promise => { openAlert({ title: 'Error', body: 'Rate limit reached. Please slow down', - buttons: [{ text: 'Done', style: 'outline' }], + buttons: [{ label: 'Done', type: 'primary', onClick: () => closeAlerts() }], timer: 3000 }).catch((toastError) => console.error(toastError)) } @@ -478,7 +478,7 @@ const showInternalErrorAlert = async ( ): Promise => { const buttons: AlertButton[] = [...(data.buttons ?? [])] - buttons.push({ text: 'Done', style: 'outline' }) + buttons.push({ label: 'Done', type: 'primary', onClick: () => closeAlerts() }) const alertConfig: AlertConfig = { title: 'Internal Error', diff --git a/packages/beacon-ui/src/components/info/index.tsx b/packages/beacon-ui/src/components/info/index.tsx index 1b528905f..c333c85b2 100644 --- a/packages/beacon-ui/src/components/info/index.tsx +++ b/packages/beacon-ui/src/components/info/index.tsx @@ -1,6 +1,7 @@ import { Component, For } from 'solid-js' import styles from './styles.css' import { QRCodeIcon } from '../icons' +import { AlertButton } from 'src/ui/alert' interface InfoProps { title: string @@ -10,17 +11,11 @@ interface InfoProps { border?: boolean iconBadge?: boolean bigIcon?: boolean - buttons?: InfoButton[] + buttons?: AlertButton[] downloadLink?: { url: string; label: string } onShowQRCodeClick?: (() => void) | (() => Promise) } -export interface InfoButton { - label: string - type: 'primary' | 'secondary' - onClick: () => void -} - const Info: Component = (props: InfoProps) => { return (
diff --git a/packages/beacon-ui/src/ui/alert/index.tsx b/packages/beacon-ui/src/ui/alert/index.tsx index 4ea100af7..bf7a12588 100644 --- a/packages/beacon-ui/src/ui/alert/index.tsx +++ b/packages/beacon-ui/src/ui/alert/index.tsx @@ -48,11 +48,10 @@ import BugReportForm from '../../components/bug-report-form' const logger = new Logger('Alert') -// Interfaces export interface AlertButton { - text: string - style?: 'solid' | 'outline' - actionCallback?(): Promise + label: string + type: 'primary' | 'secondary' + onClick: () => void } export interface AlertConfig { @@ -793,20 +792,13 @@ const openAlert = async (config: AlertConfig): Promise => { const colorMode = getColorMode() - const buttons = ( - config.buttons ?? [ - { - label: 'Close', - type: 'primary' - } - ] - ).map((btn: any) => { - if (btn.onClick) { - return { ...btn } + const buttons: AlertButton[] = config.buttons ?? [ + { + label: 'Close', + type: 'primary', + onClick: () => closeAlert('') } - - return { ...btn, onClick: () => closeAlert('') } - }) + ] dispose = render( () => ( From c64d5ef89ef39626bc81b4e3edfc4de962a29a3c Mon Sep 17 00:00:00 2001 From: IsaccoSordo Date: Tue, 18 Feb 2025 10:10:10 +0100 Subject: [PATCH 13/25] fix: disable backdrop onClick close --- packages/beacon-ui/src/components/alert/index.tsx | 5 +++++ packages/beacon-ui/src/ui/alert/index.tsx | 2 ++ 2 files changed, 7 insertions(+) diff --git a/packages/beacon-ui/src/components/alert/index.tsx b/packages/beacon-ui/src/components/alert/index.tsx index 3a1949031..d11858208 100644 --- a/packages/beacon-ui/src/components/alert/index.tsx +++ b/packages/beacon-ui/src/components/alert/index.tsx @@ -11,6 +11,7 @@ export interface AlertProps { onCloseClick: () => void onClickShowMore?: () => void onBackClick?: () => void + closeOnBackdropClick: boolean } const Alert: Component = (props: AlertProps) => { @@ -30,6 +31,10 @@ const Alert: Component = (props: AlertProps) => {
{ + if (!props.closeOnBackdropClick) { + return + } + props.onCloseClick() }} > diff --git a/packages/beacon-ui/src/ui/alert/index.tsx b/packages/beacon-ui/src/ui/alert/index.tsx index bf7a12588..f9b37f3a2 100644 --- a/packages/beacon-ui/src/ui/alert/index.tsx +++ b/packages/beacon-ui/src/ui/alert/index.tsx @@ -807,6 +807,7 @@ const openAlert = async (config: AlertConfig): Promise => { {config.pairingPayload && ( => { {!config.pairingPayload && ( {currentInfo() === 'help' ? ( From 515d68311892a46ba746b8d32368d22551beb7b2 Mon Sep 17 00:00:00 2001 From: IsaccoSordo Date: Tue, 18 Feb 2025 10:51:01 +0100 Subject: [PATCH 14/25] fix: prefill bug report --- .../src/components/bug-report-form/index.tsx | 53 ++++++++++++------- packages/beacon-ui/src/ui/alert/index.tsx | 12 +++-- 2 files changed, 42 insertions(+), 23 deletions(-) diff --git a/packages/beacon-ui/src/components/bug-report-form/index.tsx b/packages/beacon-ui/src/components/bug-report-form/index.tsx index ef610c04b..8578694fb 100644 --- a/packages/beacon-ui/src/components/bug-report-form/index.tsx +++ b/packages/beacon-ui/src/components/bug-report-form/index.tsx @@ -27,8 +27,6 @@ const BugReportForm = (props: any) => { const [titleTouched, setTitleTouched] = createSignal(false) const [description, setDescription] = createSignal('') const [descriptionTouched, setDescriptionTouched] = createSignal(false) - const [steps, setSteps] = createSignal('') - const [stepsTouched, setStepsTouched] = createSignal(false) const [isFormValid, setFormValid] = createSignal(false) const [isLoading, setIsLoading] = createSignal(false) const [didUserAllow, setDidUserAllow] = createSignal(false) @@ -36,6 +34,35 @@ const BugReportForm = (props: any) => { const [showThankYou, setShowThankYou] = createSignal(false) const db = new IndexedDBStorage('beacon', ['bug_report', 'metrics']) + createEffect(() => { + setDescription(prefillDescriptionField()) + }) + + const prefillDescriptionField = () => { + if (!localStorage) { + return '' + } + + const wcKey = Object.keys(localStorage).find((key) => key.includes('wc-init-error')) + const beaconKey = Object.keys(localStorage).find((key) => key.includes('beacon-last-error')) + + if (!wcKey || !beaconKey) { + return '' + } + + let output = '' + + if (wcKey) { + output += `WalletConnect error: ${localStorage.getItem(wcKey)} \n\n` + } + + if (beaconKey) { + output += `Beacon error: ${localStorage.getItem(beaconKey)} \n\n` + } + + return output + } + const indexDBToMetadata = async () => { const wcResult: StorageObject = {} const beaconResult: StorageObject = {} @@ -134,7 +161,11 @@ const BugReportForm = (props: any) => { const payload = metrics.map((metric) => JSON.parse(metric)) - sendRequest('https://beacon-backend.prod.gke.papers.tech/performance-metrics/saveAll', 'POST', payload) + sendRequest( + 'https://beacon-backend.prod.gke.papers.tech/performance-metrics/saveAll', + 'POST', + payload + ) .then(() => { db.clearStore('metrics') }) @@ -160,7 +191,7 @@ const BugReportForm = (props: any) => { title: title(), sdkVersion: SDK_VERSION, description: description(), - steps: steps(), + steps: '<#EMPTY#>', os: currentOS(), browser: currentBrowser(), localStorage: JSON.stringify(clean(beaconState)), @@ -219,20 +250,6 @@ const BugReportForm = (props: any) => { class={`textarea-style `} />
-
- -