From 2c076a776574b1da29304be14efd5f8d7c7b01e6 Mon Sep 17 00:00:00 2001 From: darianm <47156919+DarianM@users.noreply.github.com> Date: Fri, 31 Jan 2025 10:19:06 +0200 Subject: [PATCH] refactor(PostInstall): use `ConnectWalletForm` in the last step (#872) --- src/_locales/en/messages.json | 6 ++ src/background/container.ts | 22 ++++-- src/background/services/background.ts | 28 +++++++- src/background/services/index.ts | 2 +- .../{sendToPopup.ts => sendToPort.ts} | 31 ++++----- src/pages/app/App.tsx | 7 +- src/pages/app/lib/context.tsx | 69 +++++++++++++++++++ src/pages/app/lib/store.ts | 35 ++++++++++ src/pages/app/pages/PostInstall.tsx | 69 ++++++++++++++++--- src/pages/popup/Popup.tsx | 2 +- src/pages/popup/lib/context.tsx | 4 +- src/pages/popup/lib/store.ts | 4 +- src/pages/popup/pages/ConnectWallet.tsx | 14 ++-- src/shared/messages.ts | 37 +++++++++- src/shared/types.ts | 4 ++ 15 files changed, 281 insertions(+), 53 deletions(-) rename src/background/services/{sendToPopup.ts => sendToPort.ts} (53%) create mode 100644 src/pages/app/lib/store.ts diff --git a/src/_locales/en/messages.json b/src/_locales/en/messages.json index a715d36e..7f1a1427 100755 --- a/src/_locales/en/messages.json +++ b/src/_locales/en/messages.json @@ -260,5 +260,11 @@ }, "postInstall_action_submit": { "message": "Connect your wallet" + }, + "postInstall_text_wallet_connected_1": { + "message": "You've already connected your wallet." + }, + "postInstall_text_wallet_connected_2": { + "message": "Access the extension from the browser toolbar." } } diff --git a/src/background/container.ts b/src/background/container.ts index fae138ba..74f7d1b4 100644 --- a/src/background/container.ts +++ b/src/background/container.ts @@ -15,7 +15,7 @@ import { TabEvents, TabState, WindowState, - SendToPopup, + SendToPort, EventsService, Heartbeat, Deduplicator, @@ -29,8 +29,12 @@ import { type Translation, } from '@/shared/helpers'; import { - MessageManager, + type BackgroundToAppMessagesMap, + type BackgroundToPopupMessagesMap, type BackgroundToContentMessage, + MessageManager, + BACKGROUND_TO_POPUP_CONNECTION_NAME, + BACKGROUND_TO_APP_CONNECTION_NAME, } from '@/shared/messages'; export interface Cradle { @@ -46,7 +50,8 @@ export interface Cradle { walletService: WalletService; monetizationService: MonetizationService; message: MessageManager; - sendToPopup: SendToPopup; + sendToPopup: SendToPort; + sendToApp: SendToPort; tabEvents: TabEvents; background: Background; t: Translation; @@ -101,7 +106,16 @@ export const configureContainer = () => { })), message: asClass(MessageManager).singleton(), tabEvents: asClass(TabEvents).singleton(), - sendToPopup: asClass(SendToPopup).singleton(), + sendToPopup: asClass(SendToPort) + .singleton() + .inject(() => ({ + connectionName: BACKGROUND_TO_POPUP_CONNECTION_NAME, + })), + sendToApp: asClass(SendToPort) + .singleton() + .inject(() => ({ + connectionName: BACKGROUND_TO_APP_CONNECTION_NAME, + })), background: asClass(Background) .singleton() .inject(() => ({ diff --git a/src/background/services/background.ts b/src/background/services/background.ts index 9476a3aa..2c1acb9a 100644 --- a/src/background/services/background.ts +++ b/src/background/services/background.ts @@ -14,6 +14,7 @@ import { getTab } from '@/background/utils'; import { PERMISSION_HOSTS } from '@/shared/defines'; import { APP_URL } from '@/background/constants'; import type { Cradle } from '@/background/container'; +import type { AppStore } from '@/shared/types'; type AlarmCallback = Parameters[0]; const ALARM_RESET_OUT_OF_FUNDS = 'reset-out-of-funds'; @@ -27,6 +28,7 @@ export class Background { private tabEvents: Cradle['tabEvents']; private windowState: Cradle['windowState']; private sendToPopup: Cradle['sendToPopup']; + private sendToApp: Cradle['sendToApp']; private events: Cradle['events']; private heartbeat: Cradle['heartbeat']; @@ -39,6 +41,7 @@ export class Background { tabEvents, windowState, sendToPopup, + sendToApp, events, heartbeat, }: Cradle) { @@ -48,6 +51,7 @@ export class Background { monetizationService, storage, sendToPopup, + sendToApp, tabEvents, windowState, logger, @@ -67,6 +71,7 @@ export class Background { this.bindTabHandlers(); this.bindWindowHandlers(); this.sendToPopup.start(); + this.sendToApp.start(); await KeyAutoAddService.registerContentScripts({ browser: this.browser }); } @@ -128,6 +133,19 @@ export class Background { this.browser.alarms.onAlarm.addListener(resetOutOfFundsState); } + async getAppData(): Promise { + const { connected, publicKey } = await this.storage.get([ + 'connected', + 'publicKey', + ]); + + return { + connected, + publicKey, + transientState: this.storage.getPopupTransientState(), + }; + } + bindWindowHandlers() { this.browser.windows.onCreated.addListener( this.windowState.onWindowCreated, @@ -143,7 +161,7 @@ export class Background { windowTypes: ['normal'], }); const popupWasOpen = popupOpen; - popupOpen = this.sendToPopup.isPopupOpen; + popupOpen = this.sendToPopup.isPortOpen; if (popupWasOpen || popupOpen) { // This is intentionally called after windows.getAll, to add a little // delay for popup port to open @@ -189,7 +207,7 @@ export class Background { try { switch (message.action) { // region Popup - case 'GET_CONTEXT_DATA': + case 'GET_DATA_POPUP': return success( await this.monetizationService.getPopupData( await this.windowState.getCurrentTab(), @@ -286,6 +304,11 @@ export class Background { // endregion + // region App + case 'GET_DATA_APP': + return success(await this.getAppData()); + // endregion + default: return; } @@ -330,6 +353,7 @@ export class Background { this.events.on('storage.popup_transient_state_update', (state) => { this.sendToPopup.send('SET_TRANSIENT_STATE', state); + this.sendToApp.send('SET_TRANSIENT_STATE', state); }); this.events.on('request_popup_close', () => { diff --git a/src/background/services/index.ts b/src/background/services/index.ts index 1884d59f..6bae3fb9 100644 --- a/src/background/services/index.ts +++ b/src/background/services/index.ts @@ -7,7 +7,7 @@ export { Background } from './background'; export { TabEvents } from './tabEvents'; export { TabState } from './tabState'; export { WindowState } from './windowState'; -export { SendToPopup } from './sendToPopup'; +export { SendToPort } from './sendToPort'; export { EventsService } from './events'; export { Deduplicator } from './deduplicator'; export { Heartbeat } from './heartbeat'; diff --git a/src/background/services/sendToPopup.ts b/src/background/services/sendToPort.ts similarity index 53% rename from src/background/services/sendToPopup.ts rename to src/background/services/sendToPort.ts index 863182ea..1673921d 100644 --- a/src/background/services/sendToPopup.ts +++ b/src/background/services/sendToPort.ts @@ -1,24 +1,23 @@ import type { Runtime } from 'webextension-polyfill'; -import { - BACKGROUND_TO_POPUP_CONNECTION_NAME as CONNECTION_NAME, - type BackgroundToPopupMessage, - type BackgroundToPopupMessagesMap, -} from '@/shared/messages'; import type { Cradle } from '@/background/container'; +import type { BackgroundToPortMessagesMap } from '@/shared/messages'; -export class SendToPopup { +export class SendToPort { private browser: Cradle['browser']; - - private isConnected = false; private port: Runtime.Port; + private isConnected = false; + private connectionName: string; - constructor({ browser }: Cradle) { - Object.assign(this, { browser }); + constructor({ + browser, + connectionName, + }: Cradle & { connectionName: string }) { + Object.assign(this, { browser, connectionName }); } start() { this.browser.runtime.onConnect.addListener((port) => { - if (port.name !== CONNECTION_NAME) return; + if (port.name !== this.connectionName) return; if (port.error) { return; } @@ -30,18 +29,16 @@ export class SendToPopup { }); } - get isPopupOpen() { + get isPortOpen() { return this.isConnected; } - async send( - type: T, - data: BackgroundToPopupMessagesMap[T], - ) { + async send(type: K, data: Message[K]) { if (!this.isConnected) { return; } - const message = { type, data } as BackgroundToPopupMessage; + + const message = { type, data }; this.port.postMessage(message); } } diff --git a/src/pages/app/App.tsx b/src/pages/app/App.tsx index aa0df56a..c6cc2fac 100644 --- a/src/pages/app/App.tsx +++ b/src/pages/app/App.tsx @@ -3,6 +3,7 @@ import { BrowserContextProvider, TranslationContextProvider, } from '@/pages/shared/lib/context'; +import { MessageContextProvider, WaitForStateLoad } from '@/app/lib/context'; import browser from 'webextension-polyfill'; import { createHashRouter, @@ -28,7 +29,11 @@ export const App = () => { return ( - + + + + + ); diff --git a/src/pages/app/lib/context.tsx b/src/pages/app/lib/context.tsx index aaf25e09..f187a788 100644 --- a/src/pages/app/lib/context.tsx +++ b/src/pages/app/lib/context.tsx @@ -1 +1,70 @@ +import React from 'react'; +import { + type BackgroundToAppMessage, + BACKGROUND_TO_APP_CONNECTION_NAME as CONNECTION_NAME, + MessageManager, + type AppToBackgroundMessage, +} from '@/shared/messages'; +import { useBrowser } from '@/pages/shared/lib/context'; +import { dispatch } from './store'; + export { useBrowser, useTranslation } from '@/pages/shared/lib/context'; + +export function WaitForStateLoad({ children }: React.PropsWithChildren) { + const message = useMessage(); + const [isLoading, setIsLoading] = React.useState(true); + + React.useEffect(() => { + async function get() { + const response = await message.send('GET_DATA_APP'); + + if (response.success) { + dispatch({ type: 'SET_DATA_APP', data: response.payload }); + setIsLoading(false); + } + } + + get(); + }, [message]); + + if (isLoading) { + return <>Loading; + } + + return <>{children}; +} + +const MessageContext = React.createContext< + MessageManager +>({} as MessageManager); + +export const useMessage = () => React.useContext(MessageContext); + +export const MessageContextProvider = ({ + children, +}: React.PropsWithChildren) => { + const browser = useBrowser(); + const message = new MessageManager({ browser }); + + React.useEffect(() => { + const port = browser.runtime.connect({ name: CONNECTION_NAME }); + port.onMessage.addListener((message: BackgroundToAppMessage) => { + switch (message.type) { + case 'SET_TRANSIENT_STATE': + return dispatch(message); + } + }); + port.onDisconnect.addListener(() => { + // nothing to do + }); + return () => { + port.disconnect(); + }; + }, [browser]); + + return ( + + {children} + + ); +}; diff --git a/src/pages/app/lib/store.ts b/src/pages/app/lib/store.ts new file mode 100644 index 00000000..5dd87182 --- /dev/null +++ b/src/pages/app/lib/store.ts @@ -0,0 +1,35 @@ +import type { + AppStore, + DeepNonNullable, + PopupTransientState, +} from '@/shared/types'; +import { proxy, useSnapshot } from 'valtio'; + +export type AppState = Required>; + +export const store = proxy({ + transientState: {} as PopupTransientState, +} as AppState); + +// easier access to the store via this hook +export const useAppState = () => useSnapshot(store); + +export const dispatch = async ({ type, data }: Actions) => { + switch (type) { + case 'SET_DATA_APP': + for (const key of Object.keys(data) as Array) { + // @ts-expect-error we know TypeScript + store[key] = data[key]; + } + break; + case 'SET_TRANSIENT_STATE': + store.transientState = data; + break; + default: + throw new Error('Unknown action'); + } +}; + +type Actions = + | { type: 'SET_TRANSIENT_STATE'; data: PopupTransientState } + | { type: 'SET_DATA_APP'; data: Pick }; diff --git a/src/pages/app/pages/PostInstall.tsx b/src/pages/app/pages/PostInstall.tsx index 26bb7166..80428fb4 100644 --- a/src/pages/app/pages/PostInstall.tsx +++ b/src/pages/app/pages/PostInstall.tsx @@ -4,8 +4,16 @@ import { CaretDownIcon, ExternalIcon, } from '@/pages/shared/components/Icons'; -import { cn, getBrowserName, type BrowserName } from '@/shared/helpers'; +import { + cn, + getBrowserName, + getWalletInformation, + type BrowserName, +} from '@/shared/helpers'; import { useBrowser, useTranslation } from '@/app/lib/context'; +import { ConnectWalletForm } from '@/popup/components/ConnectWalletForm'; +import { useMessage } from '@/app/lib/context'; +import { useAppState } from '@/app/lib/store'; export const Component = () => { return ( @@ -62,10 +70,9 @@ const Steps = () => { const browser = useBrowser(); const t = useTranslation(); const isPinnedToToolbar = usePinnedStatus(); - const [isOpen, setIsOpen] = React.useState(0); const browserName = getBrowserName(browser, navigator.userAgent); - const popupUrl = browser.runtime.getURL('popup/index.html'); + const [isOpen, setIsOpen] = React.useState(0); const onClick = React.useCallback((index: number, open: boolean) => { setIsOpen((prev) => (!open ? index : prev + 1)); }, []); @@ -150,14 +157,7 @@ const Steps = () => { onClick={onClick} title={t('postInstall_action_submit')} > -
-