From fede284f62d66ea0c35eff659f73f9119d8c96ca Mon Sep 17 00:00:00 2001 From: darian Date: Tue, 28 Jan 2025 23:43:49 +0200 Subject: [PATCH 1/7] use ConnectWalletForm in app postinstall screen --- src/background/container.ts | 3 ++ src/background/services/background.ts | 5 ++ src/background/services/index.ts | 1 + src/background/services/sendToApp.ts | 43 +++++++++++++++++ src/pages/app/App.tsx | 11 +++-- src/pages/app/lib/context.tsx | 69 +++++++++++++++++++++++++++ src/pages/app/lib/store.ts | 26 ++++++++++ src/pages/app/pages/PostInstall.tsx | 43 ++++++++++++++--- src/pages/popup/Popup.tsx | 2 +- src/shared/messages.ts | 28 +++++++++++ src/shared/types.ts | 4 ++ 11 files changed, 224 insertions(+), 11 deletions(-) create mode 100644 src/background/services/sendToApp.ts create mode 100644 src/pages/app/lib/store.ts diff --git a/src/background/container.ts b/src/background/container.ts index fae138ba..8b4fb414 100644 --- a/src/background/container.ts +++ b/src/background/container.ts @@ -16,6 +16,7 @@ import { TabState, WindowState, SendToPopup, + SendToApp, EventsService, Heartbeat, Deduplicator, @@ -47,6 +48,7 @@ export interface Cradle { monetizationService: MonetizationService; message: MessageManager; sendToPopup: SendToPopup; + sendToApp: SendToApp; tabEvents: TabEvents; background: Background; t: Translation; @@ -102,6 +104,7 @@ export const configureContainer = () => { message: asClass(MessageManager).singleton(), tabEvents: asClass(TabEvents).singleton(), sendToPopup: asClass(SendToPopup).singleton(), + sendToApp: asClass(SendToApp).singleton(), background: asClass(Background) .singleton() .inject(() => ({ diff --git a/src/background/services/background.ts b/src/background/services/background.ts index 9476a3aa..3ee90fa7 100644 --- a/src/background/services/background.ts +++ b/src/background/services/background.ts @@ -27,6 +27,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 +40,7 @@ export class Background { tabEvents, windowState, sendToPopup, + sendToApp, events, heartbeat, }: Cradle) { @@ -48,6 +50,7 @@ export class Background { monetizationService, storage, sendToPopup, + sendToApp, tabEvents, windowState, logger, @@ -67,6 +70,7 @@ export class Background { this.bindTabHandlers(); this.bindWindowHandlers(); this.sendToPopup.start(); + this.sendToApp.start(); await KeyAutoAddService.registerContentScripts({ browser: this.browser }); } @@ -330,6 +334,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..e20f3111 100644 --- a/src/background/services/index.ts +++ b/src/background/services/index.ts @@ -8,6 +8,7 @@ export { TabEvents } from './tabEvents'; export { TabState } from './tabState'; export { WindowState } from './windowState'; export { SendToPopup } from './sendToPopup'; +export { SendToApp } from './sendToApp'; export { EventsService } from './events'; export { Deduplicator } from './deduplicator'; export { Heartbeat } from './heartbeat'; diff --git a/src/background/services/sendToApp.ts b/src/background/services/sendToApp.ts new file mode 100644 index 00000000..f5ffc30c --- /dev/null +++ b/src/background/services/sendToApp.ts @@ -0,0 +1,43 @@ +import type { Runtime } from 'webextension-polyfill'; +import { + BACKGROUND_TO_APP_CONNECTION_NAME as CONNECTION_NAME, + type BackgroundToAppMessage, + type BackgroundToAppMessagesMap, +} from '@/shared/messages'; +import type { Cradle } from '@/background/container'; + +export class SendToApp { + private browser: Cradle['browser']; + + private isConnected = false; + private port: Runtime.Port; + + constructor({ browser }: Cradle) { + Object.assign(this, { browser }); + } + + start() { + this.browser.runtime.onConnect.addListener((port) => { + if (port.name !== CONNECTION_NAME) return; + if (port.error) { + return; + } + this.port = port; + this.isConnected = true; + port.onDisconnect.addListener(() => { + this.isConnected = false; + }); + }); + } + + async send( + type: T, + data: BackgroundToAppMessagesMap[T], + ) { + if (!this.isConnected) { + return; + } + const message = { type, data } as BackgroundToAppMessage; + this.port.postMessage(message); + } +} diff --git a/src/pages/app/App.tsx b/src/pages/app/App.tsx index aa0df56a..c0afadfb 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, @@ -27,9 +28,13 @@ export const App = () => { }); return ( - - - + + + + + + + ); }; diff --git a/src/pages/app/lib/context.tsx b/src/pages/app/lib/context.tsx index aaf25e09..ebff3a95 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_CONTEXT_DATA'); + + if (response.success) { + dispatch({ type: 'SET_DATA', 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..80115cd7 --- /dev/null +++ b/src/pages/app/lib/store.ts @@ -0,0 +1,26 @@ +import type { AppStore, PopupTransientState } from '@/shared/types'; +import { proxy, useSnapshot } from 'valtio'; + +export const store = proxy({ + transientState: {} as PopupTransientState, +} as AppStore); + +// 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': + store.publicKey = data.publicKey; + 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'; data: Pick }; diff --git a/src/pages/app/pages/PostInstall.tsx b/src/pages/app/pages/PostInstall.tsx index 26bb7166..ff50f848 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 ( @@ -64,7 +72,9 @@ const Steps = () => { const isPinnedToToolbar = usePinnedStatus(); const [isOpen, setIsOpen] = React.useState(0); const browserName = getBrowserName(browser, navigator.userAgent); - const popupUrl = browser.runtime.getURL('popup/index.html'); + const { transientState, publicKey } = useAppState(); + const connectState = transientState.connect; + const message = useMessage(); const onClick = React.useCallback((index: number, open: boolean) => { setIsOpen((prev) => (!open ? index : prev + 1)); @@ -151,11 +161,30 @@ const Steps = () => { title={t('postInstall_action_submit')} >
-