From 32b40169881c2ebfd4c58acf718d8cc2b3fddcfe Mon Sep 17 00:00:00 2001 From: samobasquiat Date: Sat, 18 Nov 2023 14:44:41 +0000 Subject: [PATCH] feat: implement WidgetProvider & useWidget for accessing specific widget data --- widget/embedded/src/QueueManager.tsx | 6 +- widget/embedded/src/Widget.tsx | 106 ------------------ .../embedded/src/containers/App/App.styles.ts | 18 +++ widget/embedded/src/containers/App/App.tsx | 65 +++++++++++ widget/embedded/src/containers/App/index.ts | 1 + .../src/{ => containers/Wallets}/Wallets.tsx | 44 +++----- .../src/containers/Wallets/Wallets.types.ts | 12 ++ .../embedded/src/containers/Wallets/index.ts | 1 + .../embedded/src/containers/Widget/Widget.tsx | 19 ++++ .../src/containers/Widget/Widget.types.ts | 5 + .../embedded/src/containers/Widget/index.ts | 2 + .../src/containers/WidgetInfo/WidgetInfo.tsx | 36 ++++++ .../containers/WidgetInfo/WidgetInfo.types.ts | 5 + .../src/containers/WidgetInfo/index.ts | 1 + .../WidgetProvider/WidgetProvider.tsx | 19 ++++ .../WidgetProvider/WidgetProvider.types.ts | 7 ++ .../src/containers/WidgetProvider/index.ts | 1 + widget/embedded/src/index.ts | 13 ++- widget/playground/src/App.tsx | 13 +-- 19 files changed, 225 insertions(+), 149 deletions(-) delete mode 100644 widget/embedded/src/Widget.tsx create mode 100644 widget/embedded/src/containers/App/App.styles.ts create mode 100644 widget/embedded/src/containers/App/App.tsx create mode 100644 widget/embedded/src/containers/App/index.ts rename widget/embedded/src/{ => containers/Wallets}/Wallets.tsx (77%) create mode 100644 widget/embedded/src/containers/Wallets/Wallets.types.ts create mode 100644 widget/embedded/src/containers/Wallets/index.ts create mode 100644 widget/embedded/src/containers/Widget/Widget.tsx create mode 100644 widget/embedded/src/containers/Widget/Widget.types.ts create mode 100644 widget/embedded/src/containers/Widget/index.ts create mode 100644 widget/embedded/src/containers/WidgetInfo/WidgetInfo.tsx create mode 100644 widget/embedded/src/containers/WidgetInfo/WidgetInfo.types.ts create mode 100644 widget/embedded/src/containers/WidgetInfo/index.ts create mode 100644 widget/embedded/src/containers/WidgetProvider/WidgetProvider.tsx create mode 100644 widget/embedded/src/containers/WidgetProvider/WidgetProvider.types.ts create mode 100644 widget/embedded/src/containers/WidgetProvider/index.ts diff --git a/widget/embedded/src/QueueManager.tsx b/widget/embedded/src/QueueManager.tsx index b10c614bff..bbb7fabc80 100644 --- a/widget/embedded/src/QueueManager.tsx +++ b/widget/embedded/src/QueueManager.tsx @@ -17,7 +17,7 @@ import { useWalletsStore } from './store/wallets'; import { getConfig } from './utils/configs'; import { walletAndSupportedChainsNames } from './utils/wallets'; -function QueueManager(props: PropsWithChildren) { +function QueueManager(props: PropsWithChildren<{ apiKey?: string }>) { const { providers, getSigners, @@ -29,9 +29,9 @@ function QueueManager(props: PropsWithChildren) { const swapQueueDef = useMemo(() => { return makeQueueDefinition({ - API_KEY: getConfig('API_KEY'), + API_KEY: props.apiKey || getConfig('API_KEY'), }); - }, []); + }, [props.apiKey]); const blockchains = useAppStore().blockchains(); const connectedWallets = useWalletsStore.use.connectedWallets(); diff --git a/widget/embedded/src/Widget.tsx b/widget/embedded/src/Widget.tsx deleted file mode 100644 index f1031630c0..0000000000 --- a/widget/embedded/src/Widget.tsx +++ /dev/null @@ -1,106 +0,0 @@ -import type { WidgetConfig } from './types'; -import type { WalletType } from '@rango-dev/wallets-shared'; -import type { PropsWithChildren } from 'react'; - -import { I18nManager, styled } from '@rango-dev/ui'; -import React, { useContext, useEffect, useMemo, useState } from 'react'; - -import { AppRouter } from './components/AppRouter'; -import { AppRoutes } from './components/AppRoutes'; -import { WidgetEvents } from './components/WidgetEvents'; -import { globalFont } from './globalStyles'; -import { useLanguage } from './hooks/useLanguage'; -import { useTheme } from './hooks/useTheme'; -import QueueManager from './QueueManager'; -import { useAppStore } from './store/AppStore'; -import { useNotificationStore } from './store/notification'; -import { useSettingsStore } from './store/settings'; -import { DEFAULT_CONFIG } from './store/slices/config'; -import { initConfig } from './utils/configs'; -import { WidgetContext, WidgetWallets } from './Wallets'; - -const MainContainer = styled('div', { - width: '100%', - display: 'flex', - justifyContent: 'center', - alignItems: 'center', - fontFamily: '$widget', - boxSizing: 'border-box', - '& *, *::before, *::after': { - boxSizing: 'inherit', - listStyleType: 'none', - }, - '& *:focus-visible': { - outlineColor: '$info500', - transition: 'none', - }, -}); - -export type WidgetProps = { - config?: WidgetConfig; -}; - -export function Main() { - globalFont(); - - const { fetch: fetchMeta, config } = useAppStore(); - const { activeTheme } = useTheme(config?.theme || {}); - const { activeLanguage, changeLanguage } = useLanguage(); - const [lastConnectedWalletWithNetwork, setLastConnectedWalletWithNetwork] = - useState(''); - const [disconnectedWallet, setDisconnectedWallet] = useState(); - const widgetContext = useContext(WidgetContext); - - useMemo(() => { - if (config?.apiKey) { - initConfig({ - API_KEY: config?.apiKey, - }); - } - }, [config]); - useEffect(() => { - void fetchMeta().catch(); - void useSettingsStore.persist.rehydrate(); - void useNotificationStore.persist.rehydrate(); - widgetContext.onConnectWallet(setLastConnectedWalletWithNetwork); - }, []); - useEffect(() => { - if (config?.language) { - changeLanguage(config.language); - } - }, [config?.language]); - - return ( - - - - - { - setDisconnectedWallet(undefined); - }}> - - - - - - ); -} - -export function Widget(props: PropsWithChildren) { - if (!props.config?.externalWallets) { - return ( - -
- - ); - } - return
; -} diff --git a/widget/embedded/src/containers/App/App.styles.ts b/widget/embedded/src/containers/App/App.styles.ts new file mode 100644 index 0000000000..52a56343b0 --- /dev/null +++ b/widget/embedded/src/containers/App/App.styles.ts @@ -0,0 +1,18 @@ +import { styled } from '@rango-dev/ui'; + +export const MainContainer = styled('div', { + width: '100%', + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + fontFamily: '$widget', + boxSizing: 'border-box', + '& *, *::before, *::after': { + boxSizing: 'inherit', + listStyleType: 'none', + }, + '& *:focus-visible': { + outlineColor: '$info500', + transition: 'none', + }, +}); diff --git a/widget/embedded/src/containers/App/App.tsx b/widget/embedded/src/containers/App/App.tsx new file mode 100644 index 0000000000..b0e8077bee --- /dev/null +++ b/widget/embedded/src/containers/App/App.tsx @@ -0,0 +1,65 @@ +import type { WalletType } from '@rango-dev/wallets-shared'; + +import { I18nManager } from '@rango-dev/ui'; +import React, { useContext, useEffect, useMemo, useState } from 'react'; + +import { AppRouter } from '../../components/AppRouter'; +import { AppRoutes } from '../../components/AppRoutes'; +import { WidgetEvents } from '../../components/WidgetEvents'; +import { globalFont } from '../../globalStyles'; +import { useLanguage } from '../../hooks/useLanguage'; +import { useTheme } from '../../hooks/useTheme'; +import { useAppStore } from '../../store/AppStore'; +import { useNotificationStore } from '../../store/notification'; +import { useSettingsStore } from '../../store/settings'; +import { initConfig } from '../../utils/configs'; +import { WidgetContext } from '../Wallets'; + +import { MainContainer } from './App.styles'; + +export function Main() { + globalFont(); + + const { fetch: fetchMeta, config } = useAppStore(); + const { activeTheme } = useTheme(config?.theme || {}); + const { activeLanguage, changeLanguage } = useLanguage(); + const [lastConnectedWalletWithNetwork, setLastConnectedWalletWithNetwork] = + useState(''); + const [disconnectedWallet, setDisconnectedWallet] = useState(); + const widgetContext = useContext(WidgetContext); + + useMemo(() => { + if (config?.apiKey) { + initConfig({ + API_KEY: config?.apiKey, + }); + } + }, [config]); + useEffect(() => { + void fetchMeta().catch(); + void useSettingsStore.persist.rehydrate(); + void useNotificationStore.persist.rehydrate(); + widgetContext.onConnectWallet(setLastConnectedWalletWithNetwork); + }, []); + useEffect(() => { + if (config?.language) { + changeLanguage(config.language); + } + }, [config?.language]); + + return ( + + + + { + setDisconnectedWallet(undefined); + }}> + + + + + ); +} diff --git a/widget/embedded/src/containers/App/index.ts b/widget/embedded/src/containers/App/index.ts new file mode 100644 index 0000000000..bad96edcbb --- /dev/null +++ b/widget/embedded/src/containers/App/index.ts @@ -0,0 +1 @@ +export { Main } from './App'; diff --git a/widget/embedded/src/Wallets.tsx b/widget/embedded/src/containers/Wallets/Wallets.tsx similarity index 77% rename from widget/embedded/src/Wallets.tsx rename to widget/embedded/src/containers/Wallets/Wallets.tsx index 1aed9e8053..a055e89b1f 100644 --- a/widget/embedded/src/Wallets.tsx +++ b/widget/embedded/src/containers/Wallets/Wallets.tsx @@ -1,5 +1,9 @@ -import type { WidgetConfig } from './types'; -import type { ProvidersOptions } from './utils/providers'; +import type { + OnConnectHandler, + PropTypes, + WidgetContextInterface, +} from './Wallets.types'; +import type { ProvidersOptions } from '../../utils/providers'; import type { EventHandler } from '@rango-dev/wallets-react'; import type { Network } from '@rango-dev/wallets-shared'; import type { PropsWithChildren } from 'react'; @@ -8,18 +12,13 @@ import { Events, Provider } from '@rango-dev/wallets-react'; import { isEvmBlockchain } from 'rango-sdk'; import React, { createContext, useEffect, useRef } from 'react'; -import { useWalletProviders } from './hooks/useWalletProviders'; -import { AppStoreProvider, useAppStore } from './store/AppStore'; -import { useWalletsStore } from './store/wallets'; +import { useWalletProviders } from '../../hooks/useWalletProviders'; +import { AppStoreProvider, useAppStore } from '../../store/AppStore'; +import { useWalletsStore } from '../../store/wallets'; import { prepareAccountsForWalletStore, walletAndSupportedChainsNames, -} from './utils/wallets'; - -type OnConnectHandler = (key: string) => void; -interface WidgetContextInterface { - onConnectWallet(handler: OnConnectHandler): void; -} +} from '../../utils/wallets'; export const WidgetContext = createContext({ onConnectWallet: () => { @@ -27,18 +26,14 @@ export const WidgetContext = createContext({ }, }); -function Main( - props: PropsWithChildren<{ - providers: WidgetConfig['wallets']; - options?: ProvidersOptions; - onUpdateState?: EventHandler; - config: WidgetConfig; - }> -) { +function Main(props: PropsWithChildren) { const updateConfig = useAppStore().updateConfig; const blockchains = useAppStore().blockchains(); const tokens = useAppStore().tokens(); - const { providers } = useWalletProviders(props.providers, props?.options); + const walletOptions: ProvidersOptions = { + walletConnectProjectId: props.config?.walletConnectProjectId, + }; + const { providers } = useWalletProviders(props.config.wallets, walletOptions); const disconnectWallet = useWalletsStore.use.disconnectWallet(); const connectWallet = useWalletsStore.use.connectWallet(); const onConnectWalletHandler = useRef(); @@ -117,14 +112,7 @@ function Main( ); } -export function WidgetWallets( - props: PropsWithChildren<{ - providers: WidgetConfig['wallets']; - options?: ProvidersOptions; - onUpdateState?: EventHandler; - config: WidgetConfig; - }> -) { +export function WidgetWallets(props: PropsWithChildren) { const { config, ...otherProps } = props; return ( diff --git a/widget/embedded/src/containers/Wallets/Wallets.types.ts b/widget/embedded/src/containers/Wallets/Wallets.types.ts new file mode 100644 index 0000000000..b567b335b6 --- /dev/null +++ b/widget/embedded/src/containers/Wallets/Wallets.types.ts @@ -0,0 +1,12 @@ +import type { WidgetConfig } from '../../types'; +import type { EventHandler } from '@rango-dev/wallets-react'; + +export type OnConnectHandler = (key: string) => void; +export interface WidgetContextInterface { + onConnectWallet(handler: OnConnectHandler): void; +} + +export interface PropTypes { + onUpdateState?: EventHandler; + config: WidgetConfig; +} diff --git a/widget/embedded/src/containers/Wallets/index.ts b/widget/embedded/src/containers/Wallets/index.ts new file mode 100644 index 0000000000..06d0a0b2f0 --- /dev/null +++ b/widget/embedded/src/containers/Wallets/index.ts @@ -0,0 +1 @@ +export { WidgetWallets, WidgetContext } from './Wallets'; diff --git a/widget/embedded/src/containers/Widget/Widget.tsx b/widget/embedded/src/containers/Widget/Widget.tsx new file mode 100644 index 0000000000..fb0bec22c1 --- /dev/null +++ b/widget/embedded/src/containers/Widget/Widget.tsx @@ -0,0 +1,19 @@ +import type { WidgetProps } from './Widget.types'; +import type { PropsWithChildren } from 'react'; + +import React from 'react'; + +import { DEFAULT_CONFIG } from '../../store/slices/config'; +import { Main } from '../App'; +import { WidgetProvider } from '../WidgetProvider'; + +export function Widget(props: PropsWithChildren) { + if (!props.config?.externalWallets) { + return ( + +
+ + ); + } + return
; +} diff --git a/widget/embedded/src/containers/Widget/Widget.types.ts b/widget/embedded/src/containers/Widget/Widget.types.ts new file mode 100644 index 0000000000..034d2c54fb --- /dev/null +++ b/widget/embedded/src/containers/Widget/Widget.types.ts @@ -0,0 +1,5 @@ +import type { WidgetConfig } from '../../types'; + +export type WidgetProps = { + config?: WidgetConfig; +}; diff --git a/widget/embedded/src/containers/Widget/index.ts b/widget/embedded/src/containers/Widget/index.ts new file mode 100644 index 0000000000..c5d5dcb55f --- /dev/null +++ b/widget/embedded/src/containers/Widget/index.ts @@ -0,0 +1,2 @@ +export { Widget } from './Widget'; +export type { WidgetProps } from './Widget.types'; diff --git a/widget/embedded/src/containers/WidgetInfo/WidgetInfo.tsx b/widget/embedded/src/containers/WidgetInfo/WidgetInfo.tsx new file mode 100644 index 0000000000..8794197e62 --- /dev/null +++ b/widget/embedded/src/containers/WidgetInfo/WidgetInfo.tsx @@ -0,0 +1,36 @@ +import type { WidgetInfoContextInterface } from './WidgetInfo.types'; + +import { useManager } from '@rango-dev/queue-manager-react'; +import React, { createContext, useContext, useMemo } from 'react'; + +import { getPendingSwaps } from '../../utils/queue'; + +export const WidgetInfoContext = createContext< + WidgetInfoContextInterface | undefined +>(undefined); + +export function WidgetInfo(props: React.PropsWithChildren) { + const { manager } = useManager(); + const swaps = getPendingSwaps(manager); + const value = useMemo(() => { + return { + swaps, + }; + }, [manager, swaps]); + + return ( + + {props.children} + + ); +} + +export function useWidget(): WidgetInfoContextInterface { + const context = useContext(WidgetInfoContext); + if (!context) { + throw new Error( + 'useWidget can only be used within the WidgetProvider component' + ); + } + return context; +} diff --git a/widget/embedded/src/containers/WidgetInfo/WidgetInfo.types.ts b/widget/embedded/src/containers/WidgetInfo/WidgetInfo.types.ts new file mode 100644 index 0000000000..0939d7f52c --- /dev/null +++ b/widget/embedded/src/containers/WidgetInfo/WidgetInfo.types.ts @@ -0,0 +1,5 @@ +import type { PendingSwapWithQueueID } from '@rango-dev/queue-manager-rango-preset'; + +export interface WidgetInfoContextInterface { + swaps?: PendingSwapWithQueueID[]; +} diff --git a/widget/embedded/src/containers/WidgetInfo/index.ts b/widget/embedded/src/containers/WidgetInfo/index.ts new file mode 100644 index 0000000000..dcc6f77647 --- /dev/null +++ b/widget/embedded/src/containers/WidgetInfo/index.ts @@ -0,0 +1 @@ +export { WidgetInfo, useWidget } from './WidgetInfo'; diff --git a/widget/embedded/src/containers/WidgetProvider/WidgetProvider.tsx b/widget/embedded/src/containers/WidgetProvider/WidgetProvider.tsx new file mode 100644 index 0000000000..fa69004c29 --- /dev/null +++ b/widget/embedded/src/containers/WidgetProvider/WidgetProvider.tsx @@ -0,0 +1,19 @@ +import type { PropTypes } from './WidgetProvider.types'; +import type { PropsWithChildren } from 'react'; + +import React from 'react'; + +import QueueManager from '../../QueueManager'; +import { WidgetWallets } from '../Wallets'; +import { WidgetInfo } from '../WidgetInfo'; + +export function WidgetProvider(props: PropsWithChildren) { + const { onUpdateState, config } = props; + return ( + + + {props.children} + + + ); +} diff --git a/widget/embedded/src/containers/WidgetProvider/WidgetProvider.types.ts b/widget/embedded/src/containers/WidgetProvider/WidgetProvider.types.ts new file mode 100644 index 0000000000..7b848c9d8c --- /dev/null +++ b/widget/embedded/src/containers/WidgetProvider/WidgetProvider.types.ts @@ -0,0 +1,7 @@ +import type { WidgetConfig } from '../../types'; +import type { EventHandler } from '@rango-dev/wallets-react'; + +export type PropTypes = { + onUpdateState?: EventHandler; + config: WidgetConfig; +}; diff --git a/widget/embedded/src/containers/WidgetProvider/index.ts b/widget/embedded/src/containers/WidgetProvider/index.ts new file mode 100644 index 0000000000..dd39a3943b --- /dev/null +++ b/widget/embedded/src/containers/WidgetProvider/index.ts @@ -0,0 +1 @@ +export { WidgetProvider } from './WidgetProvider'; diff --git a/widget/embedded/src/index.ts b/widget/embedded/src/index.ts index 9e3cda7748..3c355ad02e 100644 --- a/widget/embedded/src/index.ts +++ b/widget/embedded/src/index.ts @@ -1,3 +1,4 @@ +import type { WidgetProps } from './containers/Widget'; import type { BlockchainAndTokenConfig, WidgetColors, @@ -5,7 +6,6 @@ import type { WidgetConfig, WidgetTheme, } from './types'; -import type { WidgetProps } from './Widget'; import type { Route, RouteEvent, @@ -39,8 +39,10 @@ import { } from '@rango-dev/queue-manager-rango-preset'; import { useWallets } from '@rango-dev/wallets-react'; -import { WidgetWallets } from './Wallets'; -import { Widget } from './Widget'; +import { WidgetWallets } from './containers/Wallets'; +import { Widget } from './containers/Widget'; +import { useWidget } from './containers/WidgetInfo'; +import { WidgetProvider } from './containers/WidgetProvider'; export type { WidgetConfig, @@ -70,7 +72,12 @@ export type { }; export { Widget, + /** + * @deprecated Use `WidgetProvider` instead. This component will be removed in future versions. + */ WidgetWallets, + WidgetProvider, + useWidget, useWallets, useWidgetEvents, MainEvents, diff --git a/widget/playground/src/App.tsx b/widget/playground/src/App.tsx index 0e7b9c0eac..f2ea7f775c 100644 --- a/widget/playground/src/App.tsx +++ b/widget/playground/src/App.tsx @@ -1,4 +1,4 @@ -import { Widget, WidgetWallets } from '@rango-dev/widget-embedded'; +import { Widget, WidgetProvider } from '@rango-dev/widget-embedded'; import React, { useEffect } from 'react'; import { Route, Routes } from 'react-router-dom'; @@ -6,7 +6,7 @@ import { ConfigContainer } from './containers/configContainer'; import { useTheme } from './hooks/useTheme'; import { useConfigStore } from './store/config'; import { useMetaStore } from './store/meta'; -import { RANGO_PUBLIC_API_KEY, WC_PROJECT_ID } from './utils/configs'; +import { RANGO_PUBLIC_API_KEY } from './utils/configs'; export function App() { const { activeStyle } = useTheme(); @@ -20,18 +20,13 @@ export function App() { return (
- + } /> - +
); }