Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion packages/mask/.webpack/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -434,7 +434,9 @@ export async function createConfiguration(

const entries = (baseConfig.entry = {
dashboard: withReactDevTools(join(import.meta.dirname, '../dashboard/initialization/index.ts')),
popups: withReactDevTools(join(import.meta.dirname, '../popups/initialization/index.ts')),
// Browser-action popups are short-lived and particularly sensitive to startup cost.
// Keep React DevTools on long-lived extension surfaces instead of loading it before the popup can open.
popups: normalizeEntryDescription(join(import.meta.dirname, '../popups/initialization/index.ts')),
contentScript: withReactDevTools(join(import.meta.dirname, '../content-script/index.ts')),
background: normalizeEntryDescription(join(import.meta.dirname, '../background/initialization/mv3-entry.ts')),
backgroundWorker: normalizeEntryDescription(
Expand Down
45 changes: 35 additions & 10 deletions packages/mask/popups/Popup.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,15 @@ import Services from '#services'
import { LoadingPlaceholder } from '@masknet/injected-ui/LoadingPlaceholder'
import { PopupLayout } from './components/PopupLayout/index.js'
import { PopupContext, PageTitleContext } from './hooks/index.js'
import { Modals } from './modals/index.js'
import { UserContext, queryPersistOptions, usePageThemePalette, useThemeLanguage } from '../shared-ui/index.js'
import { ReactQueryDevtools } from '@tanstack/react-query-devtools'
import { Modals } from './modals/index.js'
import { PersistQueryClientProvider } from '@tanstack/react-query-persist-client'
import { queryClient, ErrorBoundary } from '@masknet/shared-base-ui'
import { PersonaFrame, personaRoute } from './pages/Personas/index.js'
import { WalletFrame, walletRoutes } from './pages/Wallet/index.js'
import { ContactsFrame, contactsRoutes } from './pages/Friends/index.js'
import { walletRoutes } from './pages/Wallet/routes.js'
import { contactsRoutes } from './pages/Friends/routes.js'
import { traderRoutes } from './pages/Trader/routes.js'
import { ErrorBoundaryUIOfError } from '../../shared-base-ui/src/components/ErrorBoundary/ErrorBoundary.js'
import { TraderFrame, traderRoutes } from './pages/Trader/index.js'
import { StyledEngineProvider } from '@mui/material/styles'
import { i18n } from '@lingui/core'
import { PopupSnackbarProvider } from './components/PopupSnackbarProvider/index.js'
Expand All @@ -43,7 +42,17 @@ const pending = (
<LoadingPlaceholder />
</Box>
)
// Modals hosts the imperative SingletonModal targets; it must mount with the
// first commit; otherwise openAndWaitForClose callers hang when the host has
// not registered yet.
const RoutedModals = lazy(() => import('./modals/modals.js'))
const ReactQueryDevtools = lazy(async () => {
const { ReactQueryDevtools } = await import('@tanstack/react-query-devtools')
return { default: ReactQueryDevtools }
})
const loadTrader = () => import('./pages/Trader/index.js')
const loadWallet = () => import('./pages/Wallet/index.js')
const loadContacts = () => import('./pages/Friends/index.js')
const PopupShell = memo(function PopupShell() {
const [searchParams] = useSearchParams()
const modal = searchParams.get('modal')
Expand All @@ -66,7 +75,9 @@ const PopupShell = memo(function PopupShell() {
<Modals />
<Suspense>
{modal ?
<RoutedModals path={modal} />
<ErrorBoundary>
<RoutedModals path={modal} />
</ErrorBoundary>
: null}
</Suspense>
</UserContext>
Expand All @@ -84,10 +95,22 @@ const router = createHashRouter([
element: <PopupLayout />,
children: [
{ path: PopupRoutes.Personas, element: <PersonaFrame />, children: personaRoute },
{ path: PopupRoutes.Wallet, element: <WalletFrame />, children: walletRoutes },
{ path: PopupRoutes.Friends, element: <ContactsFrame />, children: contactsRoutes },
{
path: PopupRoutes.Wallet,
lazy: async () => ({ Component: (await loadWallet()).WalletFrame }),
children: walletRoutes,
},
{
path: PopupRoutes.Friends,
lazy: async () => ({ Component: (await loadContacts()).ContactsFrame }),
children: contactsRoutes,
},
{ path: PopupRoutes.Settings, lazy: () => import('./pages/Settings/index.js') },
{ path: PopupRoutes.Trader, element: <TraderFrame />, children: traderRoutes },
{
path: PopupRoutes.Trader,
lazy: async () => ({ Component: (await loadTrader()).TraderFrame }),
children: traderRoutes,
},
],
},
{ path: PopupRoutes.RequestPermission, lazy: () => import('./pages/RequestPermission/index.js') },
Expand Down Expand Up @@ -130,7 +153,9 @@ export default function Popups() {
<PrivySetup />
{/* https://github.com/TanStack/query/issues/5417 */}
{process.env.NODE_ENV === 'development' ?
<ReactQueryDevtools buttonPosition="bottom-right" />
<Suspense fallback={null}>
<ReactQueryDevtools buttonPosition="bottom-right" />
</Suspense>
: null}
<RouterProvider router={router} />
</>,
Expand Down
5 changes: 3 additions & 2 deletions packages/mask/popups/initialization/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
/// <reference types="@masknet/global-types/web-extension" />

import '../../shared-ui/initialization/index.js'
await import(/* webpackMode: 'eager' */ './render.js')
void Promise.all([import('../../shared-ui/initialization/index.js'), import('./render.js')])
.then(([, { renderPopup }]) => renderPopup())
.catch((error: unknown) => console.error('Failed to initialize the popup.', error))
55 changes: 28 additions & 27 deletions packages/mask/popups/initialization/render.tsx
Original file line number Diff line number Diff line change
@@ -1,34 +1,35 @@
import { createNormalReactRoot } from '../../shared-ui/utils/createNormalReactRoot.js'
import Popups from '../Popup.js'
import { setupUIContext } from '../../shared-ui/initUIContext.js'
import './trader.js'

setupUIContext()
if (location.hash === '') location.assign('#/personas')
export function renderPopup() {
setupUIContext()
if (location.hash === '') location.assign('#/personas')

/**
* Firefox will not help popup fixed width when user click browser action
* So this will determine if the user has set maxWidth to 'unset' when resizing in the window
*/
if (navigator.userAgent.includes('Firefox')) {
setTimeout(() => {
document.body.style.maxWidth = '350px'
/**
* Firefox will not help popup fixed width when user click browser action
* So this will determine if the user has set maxWidth to 'unset' when resizing in the window
*/
if (navigator.userAgent.includes('Firefox')) {
setTimeout(() => {
document.body.style.maxWidth = '350px'

window.addEventListener(
// eslint-disable-next-line unicorn/prefer-observer-apis -- once: true, therefore no perf issue
'resize',
() => {
if (window.innerWidth === 400) return
document.body.style.maxWidth = 'unset'
},
{ once: true },
)
}, 200)
window.addEventListener(
// eslint-disable-next-line unicorn/prefer-observer-apis -- once: true, therefore no perf issue
'resize',
() => {
if (window.innerWidth === 400) return
document.body.style.maxWidth = 'unset'
},
{ once: true },
)
}, 200)
}
// Resize window if page gets zoom in
const { innerWidth, outerWidth } = window
if (innerWidth !== outerWidth && innerWidth < outerWidth) {
const ratio = outerWidth / innerWidth
window.resizeTo(400 * ratio, 600)
}
createNormalReactRoot(<Popups />)
}
// Resize window if page gets zoom in
const { innerWidth, outerWidth } = window
if (innerWidth !== outerWidth && innerWidth < outerWidth) {
const ratio = outerWidth / innerWidth
window.resizeTo(400 * ratio, 600)
}
createNormalReactRoot(<Popups />)
25 changes: 23 additions & 2 deletions packages/mask/popups/modals/modals.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { PopupModalRoutes } from '@masknet/shared-base'
import { use, type PropsWithChildren } from 'react'
import { Route, Routes } from 'react-router-dom'
import { wrapModal } from '../components/index.js'
import { allStatesReadySettled } from '../../shared-ui/initialization/walletSetup.js'
import { ChangeBackupPasswordModal } from './ChangeBackupPasswordModal/index.js'
import { ChooseCurrencyModal } from './ChooseCurrencyModal/index.js'
import { ChooseNetworkModal } from './ChooseNetworkModal/index.js'
Expand All @@ -17,6 +19,11 @@ import { VerifyBackupPasswordModal } from './VerifyBackupPasswordModal/index.js'
import { SelectProviderModal } from './SelectProviderModal/index.js'
import { UpdatePermissionModal } from './UpdatePermissionModal/index.js'

function AllWalletStatesReady(props: PropsWithChildren) {
use(allStatesReadySettled)
return props.children
}

export default function RoutedModals(props: { path: string }) {
return (
<Routes location={props.path}>
Expand All @@ -29,8 +36,22 @@ export default function RoutedModals(props: { path: string }) {
<Route path={PopupModalRoutes.ChooseCurrency} element={wrapModal(<ChooseCurrencyModal />)} />
<Route path={PopupModalRoutes.ChooseNetwork} element={wrapModal(<ChooseNetworkModal />)} />
<Route path={PopupModalRoutes.ConnectSocialAccount} element={wrapModal(<ConnectSocialAccountModal />)} />
<Route path={PopupModalRoutes.SelectProvider} element={wrapModal(<SelectProviderModal />)} />
<Route path={PopupModalRoutes.ConnectProvider} element={wrapModal(<ConnectProviderModal />)} />
<Route
path={PopupModalRoutes.SelectProvider}
element={wrapModal(
<AllWalletStatesReady>
<SelectProviderModal />
</AllWalletStatesReady>,
)}
/>
<Route
path={PopupModalRoutes.ConnectProvider}
element={wrapModal(
<AllWalletStatesReady>
<ConnectProviderModal />
</AllWalletStatesReady>,
)}
/>
<Route path={PopupModalRoutes.SelectLanguage} element={wrapModal(<SelectLanguageModal />)} />
<Route path={PopupModalRoutes.SelectAppearance} element={wrapModal(<SelectAppearanceModal />)} />
<Route path={PopupModalRoutes.SupportedSitesModal} element={wrapModal(<SupportedSitesModal />)} />
Expand Down
12 changes: 2 additions & 10 deletions packages/mask/popups/pages/Friends/index.tsx
Original file line number Diff line number Diff line change
@@ -1,18 +1,10 @@
import { RestorableScrollContext } from '@masknet/shared'
import { PopupModalRoutes, PopupRoutes, relativeRouteOf } from '@masknet/shared-base'
import { PopupModalRoutes, PopupRoutes } from '@masknet/shared-base'
import { memo, Suspense, useEffect } from 'react'
import { Navigate, Outlet, useMatch, useSearchParams, type RouteObject } from 'react-router-dom'
import { Outlet, useMatch, useSearchParams } from 'react-router-dom'
import { LoadingPlaceholder } from '@masknet/injected-ui/LoadingPlaceholder'
import { NormalHeader, useModalNavigate } from '../../components/index.js'

const r = relativeRouteOf(PopupRoutes.Friends)

export const contactsRoutes: RouteObject[] = [
{ index: true, lazy: () => import('./Home/index.js') },
{ path: `${r(PopupRoutes.FriendsDetail)}/:id?`, lazy: () => import('./Detail/index.js') },
{ path: '*', element: <Navigate to={PopupRoutes.Contacts} /> },
]

export const ContactsFrame = memo(function Contacts() {
const modalNavigate = useModalNavigate()
const [params] = useSearchParams()
Expand Down
10 changes: 10 additions & 0 deletions packages/mask/popups/pages/Friends/routes.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { PopupRoutes, relativeRouteOf } from '@masknet/shared-base'
import { Navigate, type RouteObject } from 'react-router-dom'

const r = relativeRouteOf(PopupRoutes.Friends)

export const contactsRoutes: RouteObject[] = [
{ index: true, lazy: () => import('./Home/index.js') },
{ path: `${r(PopupRoutes.FriendsDetail)}/:id?`, lazy: () => import('./Detail/index.js') },
{ path: '*', element: <Navigate to={PopupRoutes.Contacts} /> },
]
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { useUpdateEffect } from '@react-hookz/web'
import { PopupRoutes } from '@masknet/shared-base'
import { PersonaContext } from '@masknet/shared'
import { useLocation, useMatch } from 'react-router-dom'
import { NormalHeader } from '../../../../components/index.js'
import { NormalHeader } from '../../../../components/NormalHeader/index.js'
import { PageTitleContext } from '../../../../hooks/index.js'
import { PersonaHeaderUI } from './UI.js'

Expand Down
38 changes: 10 additions & 28 deletions packages/mask/popups/pages/Trader/index.tsx
Original file line number Diff line number Diff line change
@@ -1,43 +1,25 @@
import {
import { Providers, RuntimeProvider } from '@masknet/plugin-trader'
import { RestorableScrollContext } from '@masknet/shared'
import { memo, Suspense, type PropsWithChildren } from 'react'
import { Outlet } from 'react-router-dom'
import { LoadingPlaceholder } from '@masknet/injected-ui/LoadingPlaceholder'
import { Header } from './Header.js'
import { useImplementRuntime } from './useImplementRuntime.js'
import '../../initialization/trader.js'

export {
BridgeConfirm,
BridgeQuoteRoute,
Confirm,
HistoryView,
NetworkFee,
QuoteRoute,
RoutePaths,
SelectLiquidity,
Slippage,
TradeView,
TradingRoute,
Transaction,
Providers,
RuntimeProvider,
} from '@masknet/plugin-trader'
import { RestorableScrollContext } from '@masknet/shared'
import { memo, Suspense, type PropsWithChildren } from 'react'
import { Outlet, type RouteObject } from 'react-router-dom'
import { LoadingPlaceholder } from '@masknet/injected-ui/LoadingPlaceholder'
import { Header } from './Header.js'
import { useImplementRuntime } from './useImplementRuntime.js'

export const traderRoutes: RouteObject[] = [
{ index: true, element: <TradeView /> },
{ path: RoutePaths.Trade, element: <TradeView /> },
{ path: RoutePaths.History, element: <HistoryView /> },
{ path: RoutePaths.Confirm, element: <Confirm /> },
{ path: RoutePaths.BridgeConfirm, element: <BridgeConfirm /> },
{ path: RoutePaths.SelectLiquidity, element: <SelectLiquidity /> },
{ path: RoutePaths.Slippage, element: <Slippage /> },
{ path: RoutePaths.QuoteRoute, element: <QuoteRoute /> },
{ path: RoutePaths.BridgeQuoteRoute, element: <BridgeQuoteRoute /> },
{ path: RoutePaths.TradingRoute, element: <TradingRoute /> },
{ path: RoutePaths.NetworkFee, element: <NetworkFee /> },
{ path: RoutePaths.Transaction, element: <Transaction /> },
].map((x) => ({
...x,
path: x.path ? x.path.slice(1) : undefined,
}))

function Runtime({ children }: PropsWithChildren) {
const runtime = useImplementRuntime()
Expand Down
31 changes: 31 additions & 0 deletions packages/mask/popups/pages/Trader/routes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { RoutePaths } from '@masknet/plugin-trader'
import type { RouteObject } from 'react-router-dom'

const loadTrader = () => import('./index.js')

export const traderRoutes: RouteObject[] = [
{ index: true, lazy: async () => ({ Component: (await loadTrader()).TradeView }) },
{ path: RoutePaths.Trade.slice(1), lazy: async () => ({ Component: (await loadTrader()).TradeView }) },
{ path: RoutePaths.History.slice(1), lazy: async () => ({ Component: (await loadTrader()).HistoryView }) },
{ path: RoutePaths.Confirm.slice(1), lazy: async () => ({ Component: (await loadTrader()).Confirm }) },
{ path: RoutePaths.BridgeConfirm.slice(1), lazy: async () => ({ Component: (await loadTrader()).BridgeConfirm }) },
{
path: RoutePaths.SelectLiquidity.slice(1),
lazy: async () => ({ Component: (await loadTrader()).SelectLiquidity }),
},
{ path: RoutePaths.Slippage.slice(1), lazy: async () => ({ Component: (await loadTrader()).Slippage }) },
{ path: RoutePaths.QuoteRoute.slice(1), lazy: async () => ({ Component: (await loadTrader()).QuoteRoute }) },
{
path: RoutePaths.BridgeQuoteRoute.slice(1),
lazy: async () => ({ Component: (await loadTrader()).BridgeQuoteRoute }),
},
{
path: RoutePaths.TradingRoute.slice(1),
lazy: async () => ({ Component: (await loadTrader()).TradingRoute }),
},
{ path: RoutePaths.NetworkFee.slice(1), lazy: async () => ({ Component: (await loadTrader()).NetworkFee }) },
{
path: RoutePaths.Transaction.slice(1),
lazy: async () => ({ Component: (await loadTrader()).Transaction }),
},
]
37 changes: 1 addition & 36 deletions packages/mask/popups/pages/Wallet/index.tsx
Original file line number Diff line number Diff line change
@@ -1,43 +1,8 @@
import { Suspense } from 'react'
import { Navigate, Outlet, type RouteObject } from 'react-router-dom'
import { PopupRoutes, relativeRouteOf } from '@masknet/shared-base'
import { Outlet } from 'react-router-dom'
import { RestorableScrollContext } from '@masknet/shared'
import { LoadingPlaceholder } from '@masknet/injected-ui/LoadingPlaceholder'
import { WalletGuard } from './WalletGuard/index.js'
import { NoWalletGuard } from './NoWalletGuard/index.js'

const r = relativeRouteOf(PopupRoutes.Wallet)
export const walletRoutes: RouteObject[] = [
{
element: <WalletGuard />,
children: [
{ index: true, lazy: () => import('./components/WalletAssets/index.js') },
{
path: r(`${PopupRoutes.Contacts}/:address?` as PopupRoutes),
lazy: () => import('./ContactList/index.js'),
},
{
path: r(`${PopupRoutes.AddToken}/:chainId/:assetType` as PopupRoutes),
lazy: () => import('./AddToken/index.js'),
},
{ path: r(PopupRoutes.Transfer), lazy: () => import('./Transfer/index.js') },
{ path: r(PopupRoutes.ContractInteraction), lazy: () => import('./Interaction/page.js') },
{ path: r(PopupRoutes.NetworkManagement), lazy: () => import('./NetworkManagement/index.js') },
{ path: r(PopupRoutes.AddNetwork), lazy: () => import('./EditNetwork/index.js') },
{ path: r(`${PopupRoutes.EditNetwork}/:id?` as PopupRoutes), lazy: () => import('./EditNetwork/index.js') },
{ path: r(PopupRoutes.Receive), lazy: () => import('./Receive/index.js') },
{ path: r(PopupRoutes.SyncTwitterCookies), lazy: () => import('./SyncTwitterCookies/index.js') },
],
},
{
element: <NoWalletGuard />,
children: [
{ path: r(PopupRoutes.TokenDetail), lazy: () => import('./TokenDetail/index.js') },
{ path: r(PopupRoutes.TransactionDetail), lazy: () => import('./TransactionDetail/index.js') },
{ path: '*', element: <Navigate to={PopupRoutes.Wallet} /> },
],
},
]
export function WalletFrame() {
return (
<Suspense fallback={<LoadingPlaceholder />}>
Expand Down
Loading
Loading