diff --git a/packages/mask/src/components/shared/WalletStatusBox.tsx b/packages/mask/src/components/shared/WalletStatusBox.tsx
index f75f919884b7..7adeb276572a 100644
--- a/packages/mask/src/components/shared/WalletStatusBox.tsx
+++ b/packages/mask/src/components/shared/WalletStatusBox.tsx
@@ -17,7 +17,7 @@ import {
useReverseAddress,
useWallet,
} from '@masknet/plugin-infra/web3'
-import { FormattedAddress, useSnackbarCallback, WalletIcon } from '@masknet/shared'
+import { FormattedAddress, useShowConfirm, useSnackbarCallback, WalletIcon } from '@masknet/shared'
import { WalletMessages } from '../../plugins/Wallet/messages'
import { useI18N } from '../../utils'
import Services from '../../extension/service'
@@ -136,6 +136,7 @@ export function WalletStatusBox(props: WalletStatusBox) {
)
// #endregion
+ const showConfirm = useShowConfirm()
const onDisconnect = useCallback(async () => {
switch (providerType) {
case ProviderType.WalletConnect:
@@ -199,6 +200,12 @@ export function WalletStatusBox(props: WalletStatusBox) {
+
{!props.disableChange && (
{providerType === ProviderType.WalletConnect || providerType === ProviderType.Fortmatic ? (
diff --git a/packages/shared/src/contexts/SharedContextProvider.tsx b/packages/shared/src/contexts/SharedContextProvider.tsx
index 67838690496c..8ea25d438baa 100644
--- a/packages/shared/src/contexts/SharedContextProvider.tsx
+++ b/packages/shared/src/contexts/SharedContextProvider.tsx
@@ -1,11 +1,14 @@
import type { FC, PropsWithChildren } from 'react'
import { BaseSharedUIProvider } from './base'
+import { CommonUIProvider } from './common'
import { EvmSharedUIProvider } from './evm'
export const SharedContextProvider: FC> = ({ children }) => {
return (
- {children}
+
+ {children}
+
)
}
diff --git a/packages/shared/src/contexts/UITaskManager.tsx b/packages/shared/src/contexts/UITaskManager.tsx
new file mode 100644
index 000000000000..97c4a01fef9d
--- /dev/null
+++ b/packages/shared/src/contexts/UITaskManager.tsx
@@ -0,0 +1,82 @@
+import { defer, DeferTuple } from '@dimensiondev/kit'
+import { EMPTY_LIST } from '@masknet/shared-base'
+import { createContext, createElement, FC, FunctionComponent, PropsWithChildren, useMemo, useState } from 'react'
+import type { InjectedDialogProps } from './components'
+
+interface ContextOptionsGeneric {
+ show(options: T): Promise
+}
+
+type BaseDialogProps = Pick
+
+export const createUITaskManager = <
+ TaskOptions,
+ Result,
+ Props extends BaseDialogProps,
+ ResolveProp extends keyof Props,
+>(
+ Component: FunctionComponent,
+ taskResolveFieldKey: ResolveProp,
+) => {
+ type ContextOptions = ContextOptionsGeneric
+ const TaskManagerContext = createContext(null!)
+ let id = 0
+
+ type TaskDeferTuple = DeferTuple
+ interface TaskBase {
+ id: number
+ promise: TaskDeferTuple[0]
+ resolve: TaskDeferTuple[1]
+ reject: TaskDeferTuple[2]
+ }
+ interface Task extends TaskBase {
+ taskOptions: TaskOptions
+ }
+
+ const TaskManagerProvider: FC> = ({ children }) => {
+ const [tasks, setTasks] = useState(EMPTY_LIST)
+
+ const contextValue = useMemo(() => {
+ const removeTask = (task: Task) => {
+ setTasks((list) => list.filter((t) => t !== task))
+ }
+
+ return {
+ show: (options: TaskOptions) => {
+ const [promise, resolve, reject] = defer()
+ id += 1
+ const newTask: Task = { id, promise, resolve, reject, taskOptions: options }
+ setTasks((list) => [...list, newTask])
+ promise.then(() => {
+ removeTask(newTask)
+ })
+ return promise
+ },
+ }
+ }, [])
+
+ return (
+
+ {children}
+ {tasks.map((task) => {
+ return createElement(
+ Component,
+ {
+ key: task.id,
+ open: true,
+ ...task.taskOptions,
+ [taskResolveFieldKey]: (result: Result | null) => {
+ task.resolve(result)
+ },
+ onClose: () => {
+ task.resolve(null)
+ },
+ } as unknown as Props,
+ children,
+ )
+ })}
+
+ )
+ }
+ return { TaskManagerContext, TaskManagerProvider }
+}
diff --git a/packages/shared/src/contexts/common/Confirm/Dialog.tsx b/packages/shared/src/contexts/common/Confirm/Dialog.tsx
new file mode 100644
index 000000000000..fdcff6a28776
--- /dev/null
+++ b/packages/shared/src/contexts/common/Confirm/Dialog.tsx
@@ -0,0 +1,39 @@
+import { makeStyles } from '@masknet/theme'
+import { Button, DialogActions, DialogContent } from '@mui/material'
+import type { FC } from 'react'
+import { useSharedI18N } from '../../../locales'
+import { InjectedDialog, InjectedDialogProps } from '../../components'
+import type { ConfirmOptions } from './types'
+
+const useStyles = makeStyles()((theme) => ({
+ content: {
+ display: 'flex',
+ alignItems: 'center',
+ justifyContent: 'center',
+ minHeight: 80,
+ margin: theme.spacing(0, 1),
+ fontSize: 18,
+ fontWeight: 'bold',
+ },
+ button: {
+ margin: theme.spacing(1),
+ },
+}))
+
+export interface ConfirmDialogProps extends ConfirmOptions, Omit {}
+
+export const ConfirmDialog: FC = ({ title, confirmLabel, content, onConfirm, ...rest }) => {
+ const t = useSharedI18N()
+ const { classes } = useStyles()
+
+ return (
+
+ {content}
+
+
+
+
+ )
+}
diff --git a/packages/shared/src/contexts/common/Confirm/index.tsx b/packages/shared/src/contexts/common/Confirm/index.tsx
new file mode 100644
index 000000000000..796c7c4a0bd4
--- /dev/null
+++ b/packages/shared/src/contexts/common/Confirm/index.tsx
@@ -0,0 +1,16 @@
+import { useContext } from 'react'
+import { createUITaskManager } from '../../UITaskManager'
+import { ConfirmDialog, ConfirmDialogProps } from './Dialog'
+import type { ConfirmOptions } from './types'
+
+const { TaskManagerContext, TaskManagerProvider: ConfirmProvider } = createUITaskManager<
+ ConfirmOptions,
+ void,
+ ConfirmDialogProps,
+ 'onConfirm'
+>(ConfirmDialog, 'onConfirm')
+
+export const useShowConfirm = () => {
+ return useContext(TaskManagerContext).show
+}
+export { ConfirmProvider }
diff --git a/packages/shared/src/contexts/common/Confirm/types.ts b/packages/shared/src/contexts/common/Confirm/types.ts
new file mode 100644
index 000000000000..00b45f4825cc
--- /dev/null
+++ b/packages/shared/src/contexts/common/Confirm/types.ts
@@ -0,0 +1,6 @@
+export interface ConfirmOptions {
+ title?: string
+ content: string
+ confirmLabel?: string
+ onConfirm?(): void
+}
diff --git a/packages/shared/src/contexts/common/index.tsx b/packages/shared/src/contexts/common/index.tsx
new file mode 100644
index 000000000000..b02969f64623
--- /dev/null
+++ b/packages/shared/src/contexts/common/index.tsx
@@ -0,0 +1,8 @@
+import type { FC, PropsWithChildren } from 'react'
+import { ConfirmProvider } from './Confirm'
+
+export * from './Confirm'
+
+export const CommonUIProvider: FC> = ({ children }) => {
+ return {children}
+}
diff --git a/packages/shared/src/contexts/components/DialogDismissIcon.tsx b/packages/shared/src/contexts/components/DialogDismissIcon.tsx
index b6b0af2ddd4c..4636c45cb52a 100644
--- a/packages/shared/src/contexts/components/DialogDismissIcon.tsx
+++ b/packages/shared/src/contexts/components/DialogDismissIcon.tsx
@@ -1,5 +1,3 @@
-// see https://github.com/import-js/eslint-plugin-import/issues/2288
-// eslint-disable-next-line import/no-deprecated
import { useTheme, useMediaQuery } from '@mui/material'
import CloseIcon from '@mui/icons-material/Close'
import ArrowBackRoundedIcon from '@mui/icons-material/ArrowBackRounded'
@@ -11,8 +9,6 @@ export interface DialogDismissIconUIProps {
export function DialogDismissIcon(props: DialogDismissIconUIProps) {
const close =
const back =
- // see https://github.com/import-js/eslint-plugin-import/issues/2288
- // eslint-disable-next-line import/no-deprecated
const auto = useMediaQuery(`(min-width: ${useTheme().breakpoints.values.sm}px)`)
if (!props.style || props.style === 'auto') return auto ? close : back
if (props.style === 'back') return back
diff --git a/packages/shared/src/contexts/evm/TokenPicker/SelectTokenDialog.tsx b/packages/shared/src/contexts/evm/TokenPicker/SelectTokenDialog.tsx
index 37c841bc85e3..ebe0ce1adc7d 100644
--- a/packages/shared/src/contexts/evm/TokenPicker/SelectTokenDialog.tsx
+++ b/packages/shared/src/contexts/evm/TokenPicker/SelectTokenDialog.tsx
@@ -2,13 +2,11 @@ import { EMPTY_LIST } from '@masknet/shared-base'
import { ERC20TokenList, useSharedI18N } from '@masknet/shared'
import { makeStyles, MaskColorVar } from '@masknet/theme'
import { ChainId, FungibleTokenDetailed, useTokenConstants } from '@masknet/web3-shared-evm'
-// see https://github.com/import-js/eslint-plugin-import/issues/2288
-// eslint-disable-next-line import/no-deprecated
import { DialogContent, Theme, useMediaQuery } from '@mui/material'
import type { FC } from 'react'
import { useBaseUIRuntime } from '../../base'
import { MINDS_ID } from '../../../constants'
-import { InjectedDialog } from '../../components'
+import { InjectedDialog, InjectedDialogProps } from '../../components'
import { useRowSize } from './useRowSize'
interface StyleProps {
@@ -50,14 +48,11 @@ export interface PickTokenOptions {
blacklist?: string[]
tokens?: FungibleTokenDetailed[]
selectedTokens?: string[]
-}
-
-export interface SelectTokenDialogProps extends PickTokenOptions {
- open: boolean
onSelect?(token: FungibleTokenDetailed): void
- onClose?(): void
}
+export interface SelectTokenDialogProps extends PickTokenOptions, Omit {}
+
export const SelectTokenDialog: FC = ({
open,
chainId,
@@ -76,7 +71,6 @@ export const SelectTokenDialog: FC = ({
const compact = networkIdentifier === MINDS_ID
const { classes } = useStyles({ compact, disablePaddingTop: isDashboard })
const { NATIVE_TOKEN_ADDRESS } = useTokenConstants(chainId)
- // eslint-disable-next-line import/no-deprecated
const isMdScreen = useMediaQuery((theme) => theme.breakpoints.down('md'))
const rowSize = useRowSize()
diff --git a/packages/shared/src/contexts/evm/TokenPicker/index.tsx b/packages/shared/src/contexts/evm/TokenPicker/index.tsx
index b5abae98c24f..0832ace429f6 100644
--- a/packages/shared/src/contexts/evm/TokenPicker/index.tsx
+++ b/packages/shared/src/contexts/evm/TokenPicker/index.tsx
@@ -1,66 +1,16 @@
-import { defer, DeferTuple } from '@dimensiondev/kit'
-import { EMPTY_LIST } from '@masknet/shared-base'
import type { FungibleTokenDetailed } from '@masknet/web3-shared-evm'
-import { createContext, FC, PropsWithChildren, useCallback, useContext, useMemo, useState } from 'react'
-import { PickTokenOptions, SelectTokenDialog } from './SelectTokenDialog'
+import { useContext } from 'react'
+import { createUITaskManager } from '../../UITaskManager'
+import { PickTokenOptions, SelectTokenDialog, SelectTokenDialogProps } from './SelectTokenDialog'
-interface ContextOptions {
- pickToken: (options: PickTokenOptions) => Promise
-}
-const TokenPickerContext = createContext(null!)
-
-type PickerDeferTuple = DeferTuple
-
-interface Task {
- id: number
- promise: PickerDeferTuple[0]
- resolve: PickerDeferTuple[1]
- reject: PickerDeferTuple[2]
- pickerOptions: PickTokenOptions
-}
-
-let id = 0
-export const TokenPickerProvider: FC> = ({ children }) => {
- const [tasks, setTasks] = useState(EMPTY_LIST)
-
- const removeTask = useCallback((task: Task) => {
- setTasks((list) => list.filter((t) => t !== task))
- }, [])
-
- const contextValue = useMemo(() => {
- return {
- pickToken: (options: PickTokenOptions) => {
- const [promise, resolve, reject] = defer()
- id += 1
- const newTask: Task = { id, promise, resolve, reject, pickerOptions: options }
- setTasks((list) => [...list, newTask])
- return promise
- },
- }
- }, [])
-
- return (
-
- {children}
- {tasks.map((task) => (
- {
- task.resolve(token)
- removeTask(task)
- }}
- onClose={() => {
- task.resolve(null)
- removeTask(task)
- }}
- />
- ))}
-
- )
-}
+const { TaskManagerContext, TaskManagerProvider: TokenPickerProvider } = createUITaskManager<
+ PickTokenOptions,
+ FungibleTokenDetailed,
+ SelectTokenDialogProps,
+ 'onSelect'
+>(SelectTokenDialog, 'onSelect')
export const usePickToken = () => {
- return useContext(TokenPickerContext).pickToken
+ return useContext(TaskManagerContext).show
}
+export { TokenPickerProvider }
diff --git a/packages/shared/src/contexts/index.ts b/packages/shared/src/contexts/index.ts
index f56942ef31c8..036e11d535e1 100644
--- a/packages/shared/src/contexts/index.ts
+++ b/packages/shared/src/contexts/index.ts
@@ -1,4 +1,5 @@
-export * from './evm'
export * from './base'
+export * from './common'
export * from './components'
+export * from './evm'
export * from './SharedContextProvider'
diff --git a/packages/shared/src/locales/en-US.json b/packages/shared/src/locales/en-US.json
index c0aca43099c6..c61a23329f2b 100644
--- a/packages/shared/src/locales/en-US.json
+++ b/packages/shared/src/locales/en-US.json
@@ -20,5 +20,7 @@
"address_viewer_address_name_rns": "RNS",
"address_viewer_address_name_address": "address",
"address_viewer_address_name_twitter": "twitter blue",
- "dialog_dismiss": "Dismiss"
+ "dialog_dismiss": "Dismiss",
+ "dialog_confirm": "Confirm",
+ "dialog_confirm_label": "Confirm"
}