From bdadb07cb99f2e23da7973bcfdebad1d773d2aa6 Mon Sep 17 00:00:00 2001 From: nuanyang233 Date: Wed, 20 Sep 2023 13:06:12 +0800 Subject: [PATCH] fix: bugfix for persona context (#10803) --- .../Mnemonic/DesktopMnemonicConfirm.tsx | 7 +- .../Restore/RestoreFromCloud/EmailField.tsx | 7 +- .../Restore/RestoreFromCloud/PhoneField.tsx | 1 + .../Restore/RestoreFromCloud/index.tsx | 8 +- .../Restore/RestoreFromPrivateKey.tsx | 7 +- .../mask/dashboard/hooks/usePersonaContext.ts | 46 -- .../pages/SetupPersona/Mnemonic/index.tsx | 7 +- .../pages/SetupPersona/Recovery/index.tsx | 8 +- .../pages/SetupPersona/SignUp/index.tsx | 8 +- .../pages/SignUp/steps/ConnectSocialMedia.tsx | 11 +- .../pages/SignUp/steps/MnemonicRevealForm.tsx | 7 +- .../pages/SignUp/steps/PersonaNameUI.tsx | 6 +- .../pages/SignUp/steps/PersonaRecovery.tsx | 9 +- .../mask/dashboard/pages/Wallets/StartUp.tsx | 39 -- .../components/AddCollectibleDialog/index.tsx | 204 -------- .../components/AddTokenConfirmUI/index.tsx | 88 ---- .../pages/Wallets/components/Assets/index.tsx | 147 ------ .../Wallets/components/Balance/index.tsx | 151 ------ .../Wallets/components/CreateWallet/index.tsx | 112 ----- .../components/EmptyPlaceholder/index.tsx | 33 -- .../components/FungibleTokenTable/index.tsx | 259 ---------- .../FungibleTokenTableRow/index.tsx | 140 ------ .../Wallets/components/History/index.tsx | 16 - .../Wallets/components/HistoryTable/index.tsx | 138 ------ .../components/HistoryTableRow/index.tsx | 168 ------- .../Wallets/components/ImportWallet/index.tsx | 99 ---- .../components/ReceiveDialog/index.tsx | 111 ----- .../components/TransactionIcon/index.tsx | 123 ----- .../Wallets/components/Transfer/NFTCard.tsx | 104 ---- .../components/Transfer/SelectNFTList.tsx | 69 --- .../components/Transfer/TransferERC20.tsx | 423 ---------------- .../components/Transfer/TransferERC721.tsx | 452 ------------------ .../Wallets/components/Transfer/index.tsx | 67 --- .../Wallets/components/Transfer/types.ts | 4 - .../components/WalletStateBar/index.tsx | 169 ------- .../dashboard/pages/Wallets/hooks/index.ts | 2 - .../pages/Wallets/hooks/useContext.ts | 57 --- .../pages/Wallets/hooks/useGasConfig.ts | 82 ---- .../pages/Wallets/hooks/useIsMatched.ts | 6 - .../mask/dashboard/pages/Wallets/index.tsx | 141 ------ packages/mask/dashboard/pages/routes.tsx | 4 - .../src/Components/PhoneNumberField/index.tsx | 12 +- .../src/Components/SendingCodeField/index.tsx | 6 +- 43 files changed, 64 insertions(+), 3494 deletions(-) delete mode 100644 packages/mask/dashboard/hooks/usePersonaContext.ts delete mode 100644 packages/mask/dashboard/pages/Wallets/StartUp.tsx delete mode 100644 packages/mask/dashboard/pages/Wallets/components/AddCollectibleDialog/index.tsx delete mode 100644 packages/mask/dashboard/pages/Wallets/components/AddTokenConfirmUI/index.tsx delete mode 100644 packages/mask/dashboard/pages/Wallets/components/Assets/index.tsx delete mode 100644 packages/mask/dashboard/pages/Wallets/components/Balance/index.tsx delete mode 100644 packages/mask/dashboard/pages/Wallets/components/CreateWallet/index.tsx delete mode 100644 packages/mask/dashboard/pages/Wallets/components/EmptyPlaceholder/index.tsx delete mode 100644 packages/mask/dashboard/pages/Wallets/components/FungibleTokenTable/index.tsx delete mode 100644 packages/mask/dashboard/pages/Wallets/components/FungibleTokenTableRow/index.tsx delete mode 100644 packages/mask/dashboard/pages/Wallets/components/History/index.tsx delete mode 100644 packages/mask/dashboard/pages/Wallets/components/HistoryTable/index.tsx delete mode 100644 packages/mask/dashboard/pages/Wallets/components/HistoryTableRow/index.tsx delete mode 100644 packages/mask/dashboard/pages/Wallets/components/ImportWallet/index.tsx delete mode 100644 packages/mask/dashboard/pages/Wallets/components/ReceiveDialog/index.tsx delete mode 100644 packages/mask/dashboard/pages/Wallets/components/TransactionIcon/index.tsx delete mode 100644 packages/mask/dashboard/pages/Wallets/components/Transfer/NFTCard.tsx delete mode 100644 packages/mask/dashboard/pages/Wallets/components/Transfer/SelectNFTList.tsx delete mode 100644 packages/mask/dashboard/pages/Wallets/components/Transfer/TransferERC20.tsx delete mode 100644 packages/mask/dashboard/pages/Wallets/components/Transfer/TransferERC721.tsx delete mode 100644 packages/mask/dashboard/pages/Wallets/components/Transfer/index.tsx delete mode 100644 packages/mask/dashboard/pages/Wallets/components/Transfer/types.ts delete mode 100644 packages/mask/dashboard/pages/Wallets/components/WalletStateBar/index.tsx delete mode 100644 packages/mask/dashboard/pages/Wallets/hooks/index.ts delete mode 100644 packages/mask/dashboard/pages/Wallets/hooks/useContext.ts delete mode 100644 packages/mask/dashboard/pages/Wallets/hooks/useGasConfig.ts delete mode 100644 packages/mask/dashboard/pages/Wallets/hooks/useIsMatched.ts delete mode 100644 packages/mask/dashboard/pages/Wallets/index.tsx diff --git a/packages/mask/dashboard/components/Mnemonic/DesktopMnemonicConfirm.tsx b/packages/mask/dashboard/components/Mnemonic/DesktopMnemonicConfirm.tsx index c5b25a5f93cc..525723b4cfef 100644 --- a/packages/mask/dashboard/components/Mnemonic/DesktopMnemonicConfirm.tsx +++ b/packages/mask/dashboard/components/Mnemonic/DesktopMnemonicConfirm.tsx @@ -1,7 +1,7 @@ import { memo, useCallback } from 'react' import { useDrop } from 'react-use' -import { MaskTextField, makeStyles } from '@masknet/theme' -import { Grid, Typography } from '@mui/material' +import { makeStyles } from '@masknet/theme' +import { Grid, TextField, Typography } from '@mui/material' const useStyles = makeStyles()((theme) => ({ input: { @@ -50,7 +50,7 @@ export const DesktopMnemonicConfirm = memo(function DesktopMnemonicConfirm(props const no = i + 1 return ( - {no}., + size: 'small', inputProps: { style: { textAlign: 'center', diff --git a/packages/mask/dashboard/components/Restore/RestoreFromCloud/EmailField.tsx b/packages/mask/dashboard/components/Restore/RestoreFromCloud/EmailField.tsx index d7f6ee517ff0..dc96857f05ed 100644 --- a/packages/mask/dashboard/components/Restore/RestoreFromCloud/EmailField.tsx +++ b/packages/mask/dashboard/components/Restore/RestoreFromCloud/EmailField.tsx @@ -1,5 +1,5 @@ -import { MaskTextField, SendingCodeField, useCustomSnackbar } from '@masknet/theme' -import { Box } from '@mui/material' +import { SendingCodeField, useCustomSnackbar } from '@masknet/theme' +import { Box, TextField } from '@mui/material' import { memo, useCallback, useLayoutEffect, useState } from 'react' import { useAsyncFn } from 'react-use' import { usePersonaRecovery } from '../../../contexts/RecoveryContext.js' @@ -79,7 +79,7 @@ export const EmailField = memo(function EmailField() { return ( <> - { setCodeError('') diff --git a/packages/mask/dashboard/components/Restore/RestoreFromCloud/index.tsx b/packages/mask/dashboard/components/Restore/RestoreFromCloud/index.tsx index 8e2de18a85f4..96bf6156661d 100644 --- a/packages/mask/dashboard/components/Restore/RestoreFromCloud/index.tsx +++ b/packages/mask/dashboard/components/Restore/RestoreFromCloud/index.tsx @@ -14,10 +14,10 @@ import { RestoreContext } from './RestoreProvider.js' import { RestoreStep } from './restoreReducer.js' import { InputForm } from './InputForm.js' import { ConfirmBackupInfo } from './ConfirmBackupInfo.js' -import { PersonaContext } from '../../../hooks/usePersonaContext.js' import { UserContext } from '../../../../shared-ui/index.js' import { AccountType } from '../../../type.js' import { BackupPreview } from '../../BackupPreview/index.js' +import { PersonaContext } from '@masknet/shared' interface RestoreProps { onRestore: () => Promise @@ -46,12 +46,14 @@ const RestoreFromCloudInner = memo(function RestoreFromCloudInner() { const navigate = useNavigate() const { showSnackbar } = useCustomSnackbar() const { user, updateUser } = UserContext.useContainer() - const { currentPersona, changeCurrentPersona } = PersonaContext.useContainer() + const { currentPersona } = PersonaContext.useContainer() const { state, dispatch } = RestoreContext.useContainer() const { account, accountType, backupSummary, password, backupDecrypted } = state const [openSynchronizePasswordDialog, toggleSynchronizePasswordDialog] = useState(false) + const changeCurrentPersona = useCallback(Services.Settings.setCurrentPersonaIdentifier, []) + const restoreCallback = useCallback(async () => { if (!currentPersona) { const lastedPersona = await Services.Identity.queryLastPersonaCreated() @@ -67,7 +69,7 @@ const RestoreFromCloudInner = memo(function RestoreFromCloudInner() { } } toggleSynchronizePasswordDialog(true) - }, [currentPersona, account, accountType, user, toggleSynchronizePasswordDialog, updateUser]) + }, [currentPersona, account, accountType, user, toggleSynchronizePasswordDialog, updateUser, changeCurrentPersona]) const handleRestore = useCallback(async () => { dispatch({ type: 'SET_LOADING', loading: true }) diff --git a/packages/mask/dashboard/components/Restore/RestoreFromPrivateKey.tsx b/packages/mask/dashboard/components/Restore/RestoreFromPrivateKey.tsx index 8dac8aafda77..b1d9b4aa5090 100644 --- a/packages/mask/dashboard/components/Restore/RestoreFromPrivateKey.tsx +++ b/packages/mask/dashboard/components/Restore/RestoreFromPrivateKey.tsx @@ -1,8 +1,8 @@ import { zodResolver } from '@hookform/resolvers/zod' import { Controller, useForm } from 'react-hook-form' import type { UseFormSetError, SubmitHandler } from 'react-hook-form' -import { MaskTextField, makeStyles } from '@masknet/theme' -import { Box } from '@mui/material' +import { makeStyles } from '@masknet/theme' +import { Box, TextField } from '@mui/material' import { memo, useCallback, useLayoutEffect } from 'react' import { useNavigate } from 'react-router-dom' import { z } from 'zod' @@ -73,11 +73,12 @@ export const RestoreFromPrivateKey = memo(function RestoreFromPrivateKey({ ( - x.identifier === currentPersonaIdentifier) - const [open, setOpen] = useState(false) - - const [, connectPersona] = useConnectSite() - const [, openProfilePage] = useOpenProfilePage() - const [, disconnectPersona] = useDisconnectSite() - const [, createPersona] = useCreatePersona() - const [, deleteBound] = useDeleteBound() - const renamePersona = Services.Identity.renamePersona - const changeCurrentPersona = useCallback(Services.Settings.setCurrentPersonaIdentifier, []) - - return { - connectPersona, - disconnectPersona, - createPersona, - renamePersona, - changeCurrentPersona, - deleteBound, - currentPersona, - definedSocialNetworkAdaptors, - personas, - openProfilePage, - drawerOpen: open, - toggleDrawer: () => setOpen((e) => !e), - } -} - -export const PersonaContext = createContainer(usePersonaContext) -PersonaContext.Provider.displayName = 'PersonaProvider' diff --git a/packages/mask/dashboard/pages/SetupPersona/Mnemonic/index.tsx b/packages/mask/dashboard/pages/SetupPersona/Mnemonic/index.tsx index 0560adcfb260..e8c015fdfcfd 100644 --- a/packages/mask/dashboard/pages/SetupPersona/Mnemonic/index.tsx +++ b/packages/mask/dashboard/pages/SetupPersona/Mnemonic/index.tsx @@ -13,7 +13,6 @@ import { SetupFrameController } from '../../../components/SetupFrame/index.js' import { useCreatePersonaV2 } from '../../../hooks/useCreatePersonaV2.js' import { useMnemonicWordsPuzzle } from '../../../hooks/useMnemonicWordsPuzzle.js' import { useDashboardI18N } from '../../../locales/index.js' -import { PersonaContext } from '../../../hooks/usePersonaContext.js' import { ComponentToPrint } from './ComponentToPrint.js' import { Words } from './Words.js' import urlcat from 'urlcat' @@ -92,7 +91,7 @@ export const SignUpMnemonic = memo(function SignUpMnemonic() { const navigate = useNavigate() const t = useDashboardI18N() const createPersona = useCreatePersonaV2() - const { changeCurrentPersona } = PersonaContext.useContainer() + const { classes } = useStyles() const { state } = useLocation() as { state: { @@ -117,6 +116,8 @@ export const SignUpMnemonic = memo(function SignUpMnemonic() { link.click() }, []) + const changeCurrentPersona = useCallback(Services.Settings.setCurrentPersonaIdentifier, []) + const [{ loading }, handleCreate] = useAsyncFn(async () => { try { const identifier = await createPersona(words.join(' '), state.personaName) @@ -125,7 +126,7 @@ export const SignUpMnemonic = memo(function SignUpMnemonic() { } catch (error) { showSnackbar((error as Error).message, { variant: 'error' }) } - }, [words]) + }, [words, changeCurrentPersona]) const handleRecovery = useCallback(() => { navigate(DashboardRoutes.RecoveryPersona) diff --git a/packages/mask/dashboard/pages/SetupPersona/Recovery/index.tsx b/packages/mask/dashboard/pages/SetupPersona/Recovery/index.tsx index 620191f1bdd5..7822ef21fc76 100644 --- a/packages/mask/dashboard/pages/SetupPersona/Recovery/index.tsx +++ b/packages/mask/dashboard/pages/SetupPersona/Recovery/index.tsx @@ -14,11 +14,11 @@ import { RestoreFromCloud } from '../../../components/Restore/RestoreFromCloud/i import { RecoveryProvider, RecoveryContext } from '../../../contexts/index.js' import { RestoreFromMnemonic } from '../../../components/Restore/RestoreFromMnemonic.js' import Services from '#services' -import { PersonaContext } from '../../../hooks/usePersonaContext.js' import { delay } from '@masknet/kit' import urlcat from 'urlcat' import { SignUpRoutePath } from '../../SignUp/routePath.js' +import { PersonaContext } from '@masknet/shared' const useStyles = makeStyles()((theme) => ({ header: { @@ -78,13 +78,15 @@ const useStyles = makeStyles()((theme) => ({ export const Recovery = memo(function Recovery() { const t = useDashboardI18N() const { classes } = useStyles() - const { currentPersona, changeCurrentPersona } = PersonaContext.useContainer() + const { currentPersona } = PersonaContext.useContainer() const tabPanelClasses = useMemo(() => ({ root: classes.panels }), [classes.panels]) const navigate = useNavigate() const [error, setError] = useState('') const [currentTab, onChange, tabs] = useTabs('mnemonic', 'privateKey', 'local', 'cloud') + const changeCurrentPersona = useCallback(Services.Settings.setCurrentPersonaIdentifier, []) + const handleRestoreFromMnemonic = useCallback( async (values: string[]) => { try { @@ -104,7 +106,7 @@ export const Recovery = memo(function Recovery() { setError(t.incorrect_identity_mnemonic()) } }, - [t, navigate], + [t, navigate, changeCurrentPersona], ) const handleRestoreFromPrivateKey = useCallback( diff --git a/packages/mask/dashboard/pages/SetupPersona/SignUp/index.tsx b/packages/mask/dashboard/pages/SetupPersona/SignUp/index.tsx index e850708e07d1..5a853b8db6ef 100644 --- a/packages/mask/dashboard/pages/SetupPersona/SignUp/index.tsx +++ b/packages/mask/dashboard/pages/SetupPersona/SignUp/index.tsx @@ -4,8 +4,8 @@ import { useNavigate } from 'react-router-dom' import Services from '#services' import { useDashboardI18N } from '../../../locales/i18n_generated.js' import { delay } from '@masknet/kit' -import { MaskTextField, makeStyles } from '@masknet/theme' -import { Typography, Button } from '@mui/material' +import { makeStyles } from '@masknet/theme' +import { Typography, Button, TextField } from '@mui/material' import { Box } from '@mui/system' import { PrimaryButton } from '../../../components/PrimaryButton/index.js' import { SecondaryButton } from '../../../components/SecondaryButton/index.js' @@ -97,7 +97,7 @@ export const SignUp = memo(function SignUp() { {t.persona_name()} - { if (error) setError('') setPersonaName(e.target.value) @@ -105,7 +105,7 @@ export const SignUp = memo(function SignUp() { autoFocus placeholder={t.persona_setup_persona_example()} required - InputProps={{ disableUnderline: true }} + InputProps={{ disableUnderline: true, size: 'large' }} inputProps={{ maxLength: 24 }} error={!!error} helperText={error} diff --git a/packages/mask/dashboard/pages/SignUp/steps/ConnectSocialMedia.tsx b/packages/mask/dashboard/pages/SignUp/steps/ConnectSocialMedia.tsx index 0311fd94dc4c..35b9f4f66fc7 100644 --- a/packages/mask/dashboard/pages/SignUp/steps/ConnectSocialMedia.tsx +++ b/packages/mask/dashboard/pages/SignUp/steps/ConnectSocialMedia.tsx @@ -2,7 +2,7 @@ import { useNavigate } from 'react-router-dom' import { upperFirst } from 'lodash-es' import { DashboardRoutes } from '@masknet/shared-base' import { Button, Stack } from '@mui/material' -import { SOCIAL_MEDIA_ICON_MAPPING } from '@masknet/shared' +import { PersonaContext, SOCIAL_MEDIA_ICON_MAPPING } from '@masknet/shared' import { Body, ColumnContentLayout, @@ -12,13 +12,18 @@ import { } from '../../../components/RegisterFrame/ColumnContentLayout.js' import { Header } from '../../../components/RegisterFrame/ColumnContentHeader.js' import { useDashboardI18N } from '../../../locales/index.js' -import { PersonaContext } from '../../../hooks/usePersonaContext.js' import { ActionCard } from '../../../components/ActionCard/index.js' +import { useConnectSite } from '../../../hooks/useConnectSite.js' +import { type SiteAdaptor, useSupportedSocialNetworkSites } from '../../../../shared-ui/index.js' export function ConnectSocialMedia() { const navigate = useNavigate() const t = useDashboardI18N() - const { currentPersona, connectPersona, definedSocialNetworkAdaptors } = PersonaContext.useContainer() + const { currentPersona } = PersonaContext.useContainer() + + const definedSocialNetworkAdaptors: SiteAdaptor[] = useSupportedSocialNetworkSites() + + const [, connectPersona] = useConnectSite() const handleConnect = async (networkIdentifier: string) => { if (currentPersona) { diff --git a/packages/mask/dashboard/pages/SignUp/steps/MnemonicRevealForm.tsx b/packages/mask/dashboard/pages/SignUp/steps/MnemonicRevealForm.tsx index c733b3ab58e7..baf3d4a791f7 100644 --- a/packages/mask/dashboard/pages/SignUp/steps/MnemonicRevealForm.tsx +++ b/packages/mask/dashboard/pages/SignUp/steps/MnemonicRevealForm.tsx @@ -1,4 +1,4 @@ -import { memo, useEffect, useState } from 'react' +import { memo, useCallback, useEffect, useState } from 'react' import { useLocation, useNavigate } from 'react-router-dom' import { Button, Stack, Box, IconButton, FormControlLabel, Checkbox } from '@mui/material' import { Refresh as RefreshIcon, Print as PrintIcon } from '@mui/icons-material' @@ -17,7 +17,6 @@ import { SignUpRoutePath } from '../routePath.js' import { ButtonContainer } from '../../../components/RegisterFrame/ButtonContainer.js' import { useMnemonicWordsPuzzle } from '../../../hooks/useMnemonicWordsPuzzle.js' import { useCreatePersonaV2 } from '../../../hooks/useCreatePersonaV2.js' -import { PersonaContext } from '../../../hooks/usePersonaContext.js' import Services from '#services' import { PreviewDialog } from './PreviewDialog.js' import { Icons } from '@masknet/icons' @@ -25,7 +24,7 @@ import { useAsync } from 'react-use' export const MnemonicRevealForm = memo(() => { const createPersona = useCreatePersonaV2() - const { changeCurrentPersona } = PersonaContext.useContainer() + const t = useDashboardI18N() const navigate = useNavigate() const { state } = useLocation() as { @@ -46,6 +45,8 @@ export const MnemonicRevealForm = memo(() => { const [privateKey, setPrivateKey] = useState('') const [checked, setChecked] = useState(false) + const changeCurrentPersona = useCallback(Services.Settings.setCurrentPersonaIdentifier, []) + const create = async () => { try { const identifier = await createPersona(words.join(' '), state.personaName) diff --git a/packages/mask/dashboard/pages/SignUp/steps/PersonaNameUI.tsx b/packages/mask/dashboard/pages/SignUp/steps/PersonaNameUI.tsx index 4ebf40488c31..174589f6357e 100644 --- a/packages/mask/dashboard/pages/SignUp/steps/PersonaNameUI.tsx +++ b/packages/mask/dashboard/pages/SignUp/steps/PersonaNameUI.tsx @@ -1,5 +1,5 @@ -import { MaskTextField, makeStyles } from '@masknet/theme' -import { Box, Typography } from '@mui/material' +import { makeStyles } from '@masknet/theme' +import { Box, TextField, Typography } from '@mui/material' import { useState } from 'react' import { PrimaryButton } from '../../../components/PrimaryButton/index.js' import { SetupFrameController } from '../../../components/SetupFrame/index.js' @@ -52,7 +52,7 @@ export function PersonaNameUI({ onNext, error, loading }: PersonaNameUIProps) { {t.persona_name()} - { setPersonaName(e.target.value) }} diff --git a/packages/mask/dashboard/pages/SignUp/steps/PersonaRecovery.tsx b/packages/mask/dashboard/pages/SignUp/steps/PersonaRecovery.tsx index eccb55d946bf..583b36906c56 100644 --- a/packages/mask/dashboard/pages/SignUp/steps/PersonaRecovery.tsx +++ b/packages/mask/dashboard/pages/SignUp/steps/PersonaRecovery.tsx @@ -1,4 +1,4 @@ -import { useState } from 'react' +import { useCallback, useState } from 'react' import { useLocation, useNavigate } from 'react-router-dom' import { useCustomSnackbar } from '@masknet/theme' import { DashboardRoutes, EMPTY_LIST, type ECKeyIdentifier, type EC_Public_JsonWebKey } from '@masknet/shared-base' @@ -10,7 +10,6 @@ import { delay } from '@masknet/kit' import { useAsync, useAsyncFn } from 'react-use' import { SmartPayBundler, SmartPayOwner } from '@masknet/web3-providers' import urlcat from 'urlcat' -import { PersonaContext } from '../../../hooks/usePersonaContext.js' export function PersonaRecovery() { const t = useDashboardI18N() @@ -19,7 +18,7 @@ export function PersonaRecovery() { const createPersona = useCreatePersonaV2() const createPersonaByPrivateKey = useCreatePersonaByPrivateKey() const { showSnackbar } = useCustomSnackbar() - const { changeCurrentPersona } = PersonaContext.useContainer() + const state = useLocation().state as { mnemonic?: string[] privateKey?: string @@ -33,6 +32,8 @@ export function PersonaRecovery() { navigate(DashboardRoutes.SignUp, { replace: true }) }, [state.mnemonic, state.privateKey]) + const changeCurrentPersona = useCallback(Services.Settings.setCurrentPersonaIdentifier, []) + const [{ loading }, onNext] = useAsyncFn( async (personaName: string) => { setError('') @@ -84,7 +85,7 @@ export function PersonaRecovery() { setError((error as Error).message) } }, - [state?.mnemonic, state?.privateKey], + [state?.mnemonic, state?.privateKey, changeCurrentPersona], ) return diff --git a/packages/mask/dashboard/pages/Wallets/StartUp.tsx b/packages/mask/dashboard/pages/Wallets/StartUp.tsx deleted file mode 100644 index 98cf739a696c..000000000000 --- a/packages/mask/dashboard/pages/Wallets/StartUp.tsx +++ /dev/null @@ -1,39 +0,0 @@ -import { styled } from '@mui/material/styles' -import { Icons } from '@masknet/icons' -import { useDashboardI18N } from '../../locales/index.js' -import { Paper, Stack, Box } from '@mui/material' -import { ActionCard } from '../../components/ActionCard/index.js' -import { SelectProviderModal } from '@masknet/shared' - -const Container = styled('div')` - display: flex; - justify-content: center; - align-items: center; - width: 100%; - height: 100%; -` - -export function StartUp() { - const t = useDashboardI18N() - - return ( - - - - - } - subtitle={t.wallets_startup_connect_desc()} - action={{ - type: 'primary', - text: t.wallets_startup_connect_action(), - handler: SelectProviderModal.open, - }} - /> - - - - - ) -} diff --git a/packages/mask/dashboard/pages/Wallets/components/AddCollectibleDialog/index.tsx b/packages/mask/dashboard/pages/Wallets/components/AddCollectibleDialog/index.tsx deleted file mode 100644 index 7c2699250e23..000000000000 --- a/packages/mask/dashboard/pages/Wallets/components/AddCollectibleDialog/index.tsx +++ /dev/null @@ -1,204 +0,0 @@ -import { type FormEvent, memo, useCallback, useEffect, useState } from 'react' -import { Controller, useForm } from 'react-hook-form' -import { z } from 'zod' -import { MaskDialog, MaskTextField } from '@masknet/theme' -import { Box, Button, DialogActions, DialogContent } from '@mui/material' -import { isSameAddress } from '@masknet/web3-shared-base' -import { NetworkPluginID } from '@masknet/shared-base' -import { zodResolver } from '@hookform/resolvers/zod' -import { - useNonFungibleTokenContract, - useChainContext, - useWeb3State, - useTrustedNonFungibleTokens, - useNetworkContext, -} from '@masknet/web3-hooks-base' -import type { Web3Helper } from '@masknet/web3-helpers' -import { isValidAddress, type ChainId } from '@masknet/web3-shared-evm' -import { Web3, Hub } from '@masknet/web3-providers' -import { useDashboardI18N } from '../../../../locales/index.js' - -export interface AddCollectibleDialogProps { - selectedNetwork: Web3Helper.NetworkDescriptorAll - open: boolean - onClose: () => void -} - -type FormInputs = { - address: string - tokenId: string -} - -enum FormErrorType { - Added = 'ADDED', - NotExist = 'NOT_EXIST', -} - -export const AddCollectibleDialog = memo(({ open, onClose, selectedNetwork }) => { - const { pluginID } = useNetworkContext() - const { account } = useChainContext() - const { Token } = useWeb3State<'all'>() - const trustedNonFungibleTokens = useTrustedNonFungibleTokens(pluginID) - - const [address, setAddress] = useState('') - const [tokenId, setTokenId] = useState('') - - const { value: contract, loading } = useNonFungibleTokenContract(NetworkPluginID.PLUGIN_EVM, address, undefined, { - chainId: selectedNetwork.chainId as ChainId, - }) - - const onSubmit = useCallback(async () => { - if (loading || !account) return - if (address && tokenId && !contract) throw new Error(FormErrorType.NotExist) - - // If the NonFungible token is added - const tokenInDB = trustedNonFungibleTokens.find( - (x) => - isSameAddress(x.contract?.owner, account) && x.tokenId === tokenId && isSameAddress(x.address, address), - ) - if (tokenInDB) throw new Error(FormErrorType.Added) - - const tokenAsset = await Hub.getNonFungibleAsset(address ?? '', tokenId, { - chainId: selectedNetwork.chainId as ChainId, - }) - const token = await Web3.getNonFungibleToken(address ?? '', tokenId, undefined, { - chainId: selectedNetwork.chainId as ChainId, - }) - const tokenDetailed = { ...token, ...tokenAsset } - const isOwner = await Web3.getNonFungibleTokenOwnership(address, tokenId, account, undefined, { - chainId: selectedNetwork.chainId as ChainId, - }) - - // If the NonFungible token is belong this account - if (!isOwner) { - throw new Error(FormErrorType.NotExist) - } else { - tokenDetailed.owner = { address: account } - tokenDetailed.ownerId = account - await Token?.addToken?.(account, tokenDetailed) - onClose() - } - }, [account, address, tokenId, contract, loading, trustedNonFungibleTokens.length]) - - return ( - - ) -}) - -export interface AddCollectibleDialogUIProps { - open: boolean - onClose: () => void - address: string - onAddressChange: (address: string) => void - onTokenIdChange: (tokenId: string) => void - onSubmit: () => void -} - -export const AddCollectibleDialogUI = memo( - ({ open, onClose, onAddressChange, onTokenIdChange, onSubmit }) => { - const t = useDashboardI18N() - - const schema = z.object({ - address: z - .string() - .min(1, t.wallets_collectible_field_contract_require()) - .refine((address) => isValidAddress(address), t.wallets_incorrect_address()), - tokenId: z.string().min(1, t.wallets_collectible_field_token_id_require()), - }) - - const { - control, - handleSubmit, - setError, - watch, - reset, - formState: { errors, isSubmitting, isDirty }, - } = useForm({ - resolver: zodResolver(schema), - defaultValues: { address: '', tokenId: '' }, - }) - - useEffect(() => { - const subscription = watch((value) => { - onAddressChange(value.address!) - onTokenIdChange(value.tokenId!) - }) - return () => subscription.unsubscribe() - }, [watch]) - - const handleFormSubmit = (event: FormEvent) => { - handleSubmit(onSubmit)(event).catch((error) => { - setError('tokenId', { - type: 'value', - message: - error.message === FormErrorType.Added - ? t.wallets_collectible_been_added() - : t.wallets_collectible_error_not_exist(), - }) - }) - } - - const handleClose = () => { - reset() - onClose() - } - - return ( - -
- - - ( - - )} - name="address" - /> - - - ( - - )} - name="tokenId" - /> - - - - - - -
-
- ) - }, -) diff --git a/packages/mask/dashboard/pages/Wallets/components/AddTokenConfirmUI/index.tsx b/packages/mask/dashboard/pages/Wallets/components/AddTokenConfirmUI/index.tsx deleted file mode 100644 index fb0371b4e1f3..000000000000 --- a/packages/mask/dashboard/pages/Wallets/components/AddTokenConfirmUI/index.tsx +++ /dev/null @@ -1,88 +0,0 @@ -import { memo } from 'react' -import { useDashboardI18N } from '../../../../locales/index.js' -import { Box, Button, DialogActions, DialogContent, Stack, Typography } from '@mui/material' -import { makeStyles } from '@masknet/theme' -import { TokenIcon } from '@masknet/shared' -import { useFormContext } from 'react-hook-form' -import type { FungibleToken } from '@masknet/web3-shared-base' -import type { ChainId, SchemaType } from '@masknet/web3-shared-evm' - -export interface AddTokenConfirmUIProps { - onBack: () => void - onConfirm: () => void - token?: FungibleToken - balance?: string -} - -const useStyles = makeStyles()((theme) => ({ - actions: { - padding: theme.spacing(1, 5, 6.75, 5), - display: 'grid', - gridTemplateColumns: 'repeat(2, 1fr)', - gap: theme.spacing(3.5), - }, - button: { - borderRadius: Number(theme.shape.borderRadius) * 5, - }, - content: { - padding: theme.spacing(3.5, 5, 5), - minWidth: 600, - }, - container: { - '& > *': { - display: 'flex', - justifyContent: 'space-between', - alignItems: 'center', - }, - }, - confirmTitle: { - fontWeight: 500, - }, -})) - -export const AddTokenConfirmUI = memo(({ token, balance, onBack, onConfirm }) => { - const t = useDashboardI18N() - const { classes } = useStyles() - const { getValues } = useFormContext() - - return ( - <> - - - - {t.wallets_assets_token()} - {t.wallets_assets_balance()} - - - - {token?.address ? ( - - ) : null} - - {getValues('symbol')} - - - - {balance} {token?.symbol} - - - - - - - - - - ) -}) diff --git a/packages/mask/dashboard/pages/Wallets/components/Assets/index.tsx b/packages/mask/dashboard/pages/Wallets/components/Assets/index.tsx deleted file mode 100644 index 907df12e32ee..000000000000 --- a/packages/mask/dashboard/pages/Wallets/components/Assets/index.tsx +++ /dev/null @@ -1,147 +0,0 @@ -import { memo, useCallback, useEffect, useState } from 'react' -import { useChainContext, useNetworkContext } from '@masknet/web3-hooks-base' -import type { Web3Helper } from '@masknet/web3-helpers' -import { makeStyles, useTabs } from '@masknet/theme' -import { TabContext, TabList, TabPanel } from '@mui/lab' -import { Box, Button, Tab } from '@mui/material' -import { - CollectionList, - UserAssetsProvider, - type CollectibleGridProps, - SelectFungibleTokenModal, -} from '@masknet/shared' -import { DashboardRoutes, NetworkPluginID } from '@masknet/shared-base' -import { ContentContainer } from '../../../../components/ContentContainer/index.js' -import { useDashboardI18N } from '../../../../locales/index.js' -import { AddCollectibleDialog } from '../AddCollectibleDialog/index.js' -import { FungibleTokenTable } from '../FungibleTokenTable/index.js' -import { useNavigate } from 'react-router-dom' -import { TransferTab } from '../Transfer/index.js' -import { Context } from '../../hooks/useContext.js' -import { useContainer } from 'unstated-next' - -const useStyles = makeStyles()((theme) => ({ - caption: { - paddingRight: theme.spacing(2.5), - display: 'flex', - alignItems: 'center', - justifyContent: 'space-between', - }, - addCustomTokenButton: { - borderRadius: Number(theme.shape.borderRadius) * 3.5, - fontSize: theme.typography.caption.fontSize, - }, -})) - -export enum AssetTab { - Token = 'Token', - Investment = 'Investment', - Collectibles = 'Collectibles', -} - -const assetTabs = [AssetTab.Token, AssetTab.Collectibles] as const - -export interface AssetsProps { - network: Web3Helper.NetworkDescriptorAll | null -} - -const gridProps: CollectibleGridProps = { - columns: 'repeat(auto-fill, minmax(180px, 1fr))', - gap: 4, -} -export const Assets = memo(({ network }) => { - const { chainId } = useContainer(Context) - const t = useDashboardI18N() - const navigate = useNavigate() - const { pluginID } = useNetworkContext() - const { account } = useChainContext() - const { classes } = useStyles() - const assetTabsLabel: Record = { - [AssetTab.Token]: t.wallets_assets_token(), - [AssetTab.Investment]: t.wallets_assets_investment(), - [AssetTab.Collectibles]: t.wallets_assets_collectibles(), - } - - const [currentTab, onChange, , setTab] = useTabs(AssetTab.Token, AssetTab.Collectibles) - - const [addCollectibleOpen, setAddCollectibleOpen] = useState(false) - - useEffect(() => { - setTab(AssetTab.Token) - }, [pluginID]) - - const showCollectibles = [NetworkPluginID.PLUGIN_EVM, NetworkPluginID.PLUGIN_SOLANA].includes(pluginID) - const handleActionClick = useCallback( - (asset: Web3Helper.NonFungibleAssetAll) => { - // Sending NFT is only available on EVM currently. - if (pluginID !== NetworkPluginID.PLUGIN_EVM) return - navigate(DashboardRoutes.WalletsTransfer, { - state: { - type: TransferTab.Collectibles, - nonFungibleToken: { ...asset, chainId }, - }, - }) - }, - [pluginID, chainId], - ) - - return ( - <> - - - - - {assetTabs - .filter((x) => showCollectibles || x === AssetTab.Token) - .map((key) => ( - - ))} - - {pluginID === NetworkPluginID.PLUGIN_EVM && network ? ( - - ) : null} - - - - - - - - - - - - {addCollectibleOpen && network ? ( - setAddCollectibleOpen(false)} /> - ) : null} - - ) -}) diff --git a/packages/mask/dashboard/pages/Wallets/components/Balance/index.tsx b/packages/mask/dashboard/pages/Wallets/components/Balance/index.tsx deleted file mode 100644 index 757a64d6feef..000000000000 --- a/packages/mask/dashboard/pages/Wallets/components/Balance/index.tsx +++ /dev/null @@ -1,151 +0,0 @@ -import { memo, useMemo } from 'react' -import { noop } from 'lodash-es' -import { BigNumber } from 'bignumber.js' -import { useContainer } from 'unstated-next' -import { Icons } from '@masknet/icons' -import { FormattedCurrency, MiniNetworkSelector } from '@masknet/shared' -import { DashboardRoutes } from '@masknet/shared-base' -import { MaskColorVar } from '@masknet/theme' -import { formatCurrency, getTokenUSDValue } from '@masknet/web3-shared-base' -import { Box, Button, buttonClasses, styled, Typography } from '@mui/material' -import { useDashboardI18N } from '../../../../locales/index.js' -import { useIsMatched } from '../../hooks/index.js' -import type { Web3Helper } from '@masknet/web3-helpers' -import { Context } from '../../hooks/useContext.js' - -const BalanceContainer = styled('div')( - ({ theme }) => ` - display: flex; - justify-content: space-between; - border-radius: 16px; - align-items: center; - padding: ${theme.spacing(2.5)}; - background: ${MaskColorVar.primaryBackground}; -`, -) - -const IconContainer = styled('div')` - width: 48px; - height: 48px; - display: flex; - justify-content: center; - align-items: center; - background: ${MaskColorVar.infoBackground}; - border-radius: 24px; -` - -const BalanceDisplayContainer = styled('div')( - ({ theme }) => ` - display: flex; - flex-direction: column; - justify-content: space-between; - margin-left: ${theme.spacing(1)}; -`, -) - -const BalanceTitle = styled(Typography)( - ({ theme }) => ` - font-size: ${theme.typography.subtitle2.fontSize}; - color: ${MaskColorVar.iconLight}; -`, -) - -const BalanceContent = styled(Typography)( - ({ theme }) => ` - font-size: ${theme.typography.h5.fontSize}; - color: ${MaskColorVar.textPrimary}; - line-height: ${theme.typography.h2.lineHeight}; -`, -) - -const ButtonGroup = styled('div')` - display: inline-grid; - gap: 10px; - grid-template-columns: repeat(4, 1fr); - & > * { - font-size: 12px; - white-space: nowrap; - & .${buttonClasses.endIcon} > *:nth-of-type(1) { - font-size: 0; - } - } -` - -export interface BalanceCardProps { - onSend(): void - onBuy(): void - onSwap(): void - onReceive(): void - networks: Web3Helper.NetworkDescriptorAll[] - selectedNetwork: Web3Helper.NetworkDescriptorAll | null - showOperations: boolean - onSelectNetwork(network: Web3Helper.NetworkDescriptorAll | null): void -} - -export const Balance = memo( - ({ onSend, onBuy, onSwap, onReceive, onSelectNetwork, networks, selectedNetwork, showOperations }) => { - const t = useDashboardI18N() - - const isWalletTransferPath = useIsMatched(DashboardRoutes.WalletsTransfer) - const isWalletHistoryPath = useIsMatched(DashboardRoutes.WalletsHistory) - - const { fungibleAssets } = useContainer(Context) - - const balance = useMemo(() => { - if (!fungibleAssets.data?.length) return 0 - - const values = fungibleAssets.data - .filter((x) => (selectedNetwork ? x.chainId === selectedNetwork.chainId : true)) - .map((y) => getTokenUSDValue(y.value)) - return BigNumber.sum(...values).toNumber() - }, [selectedNetwork, fungibleAssets.data]) - - const isHiddenAllButton = isWalletHistoryPath || isWalletTransferPath || networks.length <= 1 - - return ( - - - - - - - - {t.wallets_balance()} {selectedNetwork?.name ?? t.wallets_balance_all_chain()} - - - - - - networks.length <= 1 ? noop : onSelectNetwork(network) - } - /> - - - {showOperations ? ( - - - - - - - ) : null} - - ) - }, -) diff --git a/packages/mask/dashboard/pages/Wallets/components/CreateWallet/index.tsx b/packages/mask/dashboard/pages/Wallets/components/CreateWallet/index.tsx deleted file mode 100644 index 00d3f317b085..000000000000 --- a/packages/mask/dashboard/pages/Wallets/components/CreateWallet/index.tsx +++ /dev/null @@ -1,112 +0,0 @@ -import { Button, styled, FilledInput, Tab, Typography } from '@mui/material' -import { makeStyles, ButtonGroupTabList, MaskColorVar, useTabs } from '@masknet/theme' -import { memo } from 'react' -import { Icons } from '@masknet/icons' -import { MnemonicReveal } from '../../../../components/Mnemonic/index.js' -import { MaskAlert } from '../../../../components/MaskAlert/index.js' -import { useDashboardI18N } from '../../../../locales/index.js' -import { TabContext, TabPanel } from '@mui/lab' - -const Container = styled('div')` - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; -` - -const Refresh = styled('div')( - ({ theme }) => ` - display: flex; - align-items: center; - justify-content: flex-end; - width: 584px; - margin: ${theme.spacing(2, 0)}; - font-size: ${theme.typography.fontSize}; - line-height: 20px; - color: ${theme.palette.primary.main}; - cursor: pointer; -`, -) - -const MnemonicGeneratorContainer = styled('div')( - ({ theme }) => ` - padding: ${theme.spacing(4, 5)}; - background-color: ${theme.palette.background.default}; - border-radius: 8px; -`, -) - -const ControlContainer = styled('div')( - ({ theme }) => ` - margin-top: ${theme.spacing(6)}; - display: grid; - justify-content: center; - grid-template-columns: repeat(2, 180px); - gap: 24px; - width: 584px; -`, -) - -const AlertContainer = styled('div')( - ({ theme }) => ` - width: 676px; - margin-top: ${theme.spacing(7)}; - color: ${MaskColorVar.textSecondary}; -`, -) - -const PrivateKeyInput = styled(FilledInput)( - ({ theme }) => ` - width: 582px; - height: 182px; - margin-top: ${theme.spacing(3)}; -`, -) - -const useTabPanelStyles = makeStyles()({ - root: { - display: 'flex', - flexDirection: 'column', - alignItems: 'center', - padding: 0, - }, -}) - -export const CreateWallet = memo(() => { - const t = useDashboardI18N() - const { classes } = useTabPanelStyles() - const [currentTab, onChange, tabs] = useTabs('mnemonic', 'json', 'privateKey') - return ( - - - - - - - - - - - {t.wallets_create_wallet_refresh()} - - - - - - - TODO - - - - - - - - - - - - - - ) -}) diff --git a/packages/mask/dashboard/pages/Wallets/components/EmptyPlaceholder/index.tsx b/packages/mask/dashboard/pages/Wallets/components/EmptyPlaceholder/index.tsx deleted file mode 100644 index f2b38fd4739c..000000000000 --- a/packages/mask/dashboard/pages/Wallets/components/EmptyPlaceholder/index.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import { memo } from 'react' -import { Box, Typography } from '@mui/material' -import { makeStyles, MaskColorVar } from '@masknet/theme' -import { Icons } from '@masknet/icons' - -const useStyles = makeStyles()((theme) => ({ - container: { - width: '100%', - height: '100%', - display: 'flex', - flexDirection: 'column', - justifyContent: 'center', - alignItems: 'center', - }, - prompt: { - color: MaskColorVar.textLight, - fontSize: theme.typography.pxToRem(12), - lineHeight: theme.typography.pxToRem(16), - marginTop: theme.spacing(2.5), - }, -})) - -export interface EmptyPlaceholderProps extends React.PropsWithChildren<{}> {} - -export const EmptyPlaceholder = memo(({ children }) => { - const { classes } = useStyles() - return ( - - - {children} - - ) -}) diff --git a/packages/mask/dashboard/pages/Wallets/components/FungibleTokenTable/index.tsx b/packages/mask/dashboard/pages/Wallets/components/FungibleTokenTable/index.tsx deleted file mode 100644 index a6788f766f45..000000000000 --- a/packages/mask/dashboard/pages/Wallets/components/FungibleTokenTable/index.tsx +++ /dev/null @@ -1,259 +0,0 @@ -import { Icons } from '@masknet/icons' -import { CrossIsolationMessages, DashboardRoutes, EMPTY_LIST, NetworkPluginID } from '@masknet/shared-base' -import { MaskColorVar, makeStyles } from '@masknet/theme' -import type { Web3Helper } from '@masknet/web3-helpers' -import { useNativeToken, useNetworkContext } from '@masknet/web3-hooks-base' -import { CurrencyType, isGreaterThanOrEqualTo, isLessThan, leftShift, minus, toZero } from '@masknet/web3-shared-base' -import { isNativeTokenAddress } from '@masknet/web3-shared-evm' -import { Table, TableBody, TableCell, TableContainer, TableHead, TableRow, Typography } from '@mui/material' -import { memo, useCallback, useMemo, useState } from 'react' -import { useNavigate } from 'react-router-dom' -import { useContainer } from 'unstated-next' -import { LoadingPlaceholder } from '../../../../components/LoadingPlaceholder/index.js' -import { useDashboardI18N } from '../../../../locales/index.js' -import { Context } from '../../hooks/useContext.js' -import { EmptyPlaceholder } from '../EmptyPlaceholder/index.js' -import { FungibleTokenTableRow } from '../FungibleTokenTableRow/index.js' -import { TransferTab } from '../Transfer/types.js' - -const useStyles = makeStyles()((theme) => ({ - container: { - display: 'flex', - flexDirection: 'column', - }, - table: { - paddingLeft: theme.spacing(5), - paddingRight: theme.spacing(5), - [theme.breakpoints.down('lg')]: { - paddingLeft: theme.spacing(2), - paddingRight: theme.spacing(2), - }, - }, - header: { - color: MaskColorVar.normalText, - fontWeight: theme.typography.fontWeightRegular as any, - padding: '12px 0 12px', - backgroundColor: MaskColorVar.primaryBackground, - border: 'none', - }, - more: { - textAlign: 'center', - }, - moreBody: { - display: 'inline-flex', - flexDirection: 'row', - justifyContent: 'center', - alignItems: 'center', - padding: theme.spacing(1, 2), - gap: 2, - fontWeight: 600, - margin: theme.spacing(2, 'auto', 1), - cursor: 'pointer', - borderRadius: 20, - backgroundColor: theme.palette.maskColor.bg, - }, -})) - -export interface FungibleTokenTableProps { - selectedChainId?: Web3Helper.ChainIdAll -} - -export const FungibleTokenTable = memo(({ selectedChainId }) => { - const navigate = useNavigate() - const [isExpand, setIsExpand] = useState(false) - const { data: nativeToken } = useNativeToken<'all'>(NetworkPluginID.PLUGIN_EVM, { - chainId: selectedChainId, - }) - const { fungibleAssets } = useContainer(Context) - - const onSwap = useCallback((token: Web3Helper.FungibleAssetAll) => { - return CrossIsolationMessages.events.swapDialogEvent.sendToLocal({ - open: true, - traderProps: { - defaultInputCoin: { - name: token.name || '', - symbol: token.symbol || '', - address: token.address, - decimals: token.decimals, - }, - chainId: token.chainId, - }, - }) - }, []) - - const onSend = useCallback( - (token: Web3Helper.FungibleAssetAll) => - navigate(DashboardRoutes.WalletsTransfer, { state: { token, type: TransferTab.Token } }), - [], - ) - - const dataSource = useMemo(() => { - const results = - fungibleAssets.data?.filter((x) => !selectedChainId || x.chainId === selectedChainId) ?? EMPTY_LIST - - if (!selectedChainId) { - return results.sort((a, z) => { - const aUSD = toZero(a.value?.[CurrencyType.USD] ?? '0') - const zUSD = toZero(z.value?.[CurrencyType.USD] ?? '0') - - // token value - if (!aUSD.isEqualTo(zUSD)) return minus(zUSD, aUSD).isPositive() ? 1 : -1 - - return 0 - }) - } - - if (!results.length && nativeToken) { - return [ - { - ...nativeToken, - balance: '0', - }, - ] - } - - return results - }, [nativeToken, fungibleAssets.data, selectedChainId]) - - const handleSwitch = useCallback(() => { - setIsExpand((x) => !x) - }, []) - - const hasLowValueToken = useMemo(() => { - return !!dataSource.find((x) => !isNativeTokenAddress(x.address) && isLessThan(x.value?.usd ?? 0, 1)) - }, [dataSource]) - - const sortedData = useMemo(() => { - return dataSource.sort((first, second) => { - const firstValue = leftShift(first.balance, first.decimals) - const secondValue = leftShift(second.balance, second.decimals) - if (firstValue.isEqualTo(secondValue)) return 0 - return Number(firstValue.lt(secondValue)) - }) - }, [dataSource]) - - const visibleData = useMemo(() => { - if (isExpand) return sortedData - return sortedData.filter((asset) => { - return isNativeTokenAddress(asset.address) || isGreaterThanOrEqualTo(asset.value?.usd ?? 0, 1) - }) - }, [isExpand, sortedData]) - - return ( - <> - - - - ) -}) - -FungibleTokenTable.displayName = 'FungibleTokenTable' - -export interface MoreBarUIProps { - isEmpty: boolean - isExpand: boolean - isLoading: boolean - hasLowValueToken: boolean - onSwitch: () => void -} - -export const MoreBarUI = memo(({ isExpand, isEmpty, isLoading, hasLowValueToken, onSwitch }) => { - const t = useDashboardI18N() - const { classes } = useStyles() - - if (isEmpty || isLoading || !hasLowValueToken) return null - return ( - - - - {t.wallets_assets_more({ - symbol: '<', - context: isExpand ? 'expanded' : 'collapsed', - })} - - - - - ) -}) - -MoreBarUI.displayName = 'MoreBarUI' - -export interface TokenTableUIProps { - isEmpty: boolean - isExpand: boolean - isLoading: boolean - data: Web3Helper.FungibleAssetAll[] - onSwap(token: Web3Helper.FungibleAssetAll): void - onSend(token: Web3Helper.FungibleAssetAll): void -} - -export const TokenTableUI = memo(({ onSwap, onSend, isLoading, isEmpty, data }) => { - const t = useDashboardI18N() - const { classes } = useStyles() - const { pluginID: currentPluginId } = useNetworkContext() - - return ( - <> - - {isLoading ? ( - - ) : isEmpty ? ( - - ) : ( - - - - - {t.wallets_assets_asset()} - - - {t.wallets_assets_balance()} - - - {t.wallets_assets_price()} - - - {t.wallets_assets_value()} - - {currentPluginId === NetworkPluginID.PLUGIN_EVM && ( - - {t.wallets_assets_operation()} - - )} - - - - {data.length ? ( - - {data.map((asset) => ( - - ))} - - ) : null} -
- )} -
- - ) -}) - -TokenTableUI.displayName = 'TokenTableUI' diff --git a/packages/mask/dashboard/pages/Wallets/components/FungibleTokenTableRow/index.tsx b/packages/mask/dashboard/pages/Wallets/components/FungibleTokenTableRow/index.tsx deleted file mode 100644 index c684bde5ccaa..000000000000 --- a/packages/mask/dashboard/pages/Wallets/components/FungibleTokenTableRow/index.tsx +++ /dev/null @@ -1,140 +0,0 @@ -import { memo } from 'react' -import { Box, Button, TableCell, TableRow, Typography } from '@mui/material' -import { getMaskColor, makeStyles } from '@masknet/theme' -import { FormattedCurrency, ImageIcon, TokenIcon } from '@masknet/shared' -import { useNetworkContext, useNetworkDescriptor } from '@masknet/web3-hooks-base' -import { CurrencyType, formatBalance, formatCurrency, getTokenUSDValue } from '@masknet/web3-shared-base' -import { NetworkPluginID } from '@masknet/shared-base' -import { ChainId } from '@masknet/web3-shared-evm' -import type { Web3Helper } from '@masknet/web3-helpers' -import { useDashboardI18N } from '../../../../locales/index.js' -import { Context } from '../../hooks/useContext.js' -import { useContainer } from 'unstated-next' - -const useStyles = makeStyles()((theme) => ({ - icon: { - width: 36, - height: 36, - }, - symbol: { - marginLeft: 14, - fontSize: theme.typography.pxToRem(14), - maxWidth: '100px', - textOverflow: 'ellipsis', - textTransform: 'capitalize', - whiteSpace: 'nowrap', - overflow: 'hidden', - }, - row: { - '&:hover': { - backgroundColor: theme.palette.background.default, - }, - }, - cell: { - padding: theme.spacing(2), - border: 'none', - }, - button: { - color: theme.palette.mode === 'dark' ? getMaskColor(theme).white : getMaskColor(theme).primary, - '&:disabled': { - color: theme.palette.mode === 'dark' ? getMaskColor(theme).white : getMaskColor(theme).primary, - }, - }, - chainIcon: { - position: 'absolute', - right: -9, - bottom: 0, - height: 18, - width: 18, - border: `1px solid ${theme.palette.background.default}`, - borderRadius: '50%', - }, -})) - -export interface TokenTableRowProps { - asset: Web3Helper.FungibleAssetAll - onSwap(asset: Web3Helper.FungibleAssetAll): void - onSend(asset: Web3Helper.FungibleAssetAll): void -} - -export const FungibleTokenTableRow = memo(({ asset, onSend, onSwap }) => { - const t = useDashboardI18N() - const { classes } = useStyles() - - const { pluginID, setSelectedNetwork } = useContainer(Context) - const networkDescriptor = useNetworkDescriptor(pluginID, asset.chainId) - const { pluginID: currentPluginId } = useNetworkContext() - - return ( - - - - - - - - - - {asset.symbol} - - - - {formatBalance(asset.balance, asset.decimals, 6)} - - - - {asset.price?.[CurrencyType.USD] - ? formatCurrency(Number.parseFloat(asset.price[CurrencyType.USD] ?? '')) - : '-'} - - - - - {getTokenUSDValue(asset.value) < 0.01 ? ( - '<$0.01' - ) : ( - - )} - - - {currentPluginId === NetworkPluginID.PLUGIN_EVM && ( - - - - - )} - - ) -}) - -FungibleTokenTableRow.displayName = 'FungibleTokenTableRow' diff --git a/packages/mask/dashboard/pages/Wallets/components/History/index.tsx b/packages/mask/dashboard/pages/Wallets/components/History/index.tsx deleted file mode 100644 index 6aef2d4a627a..000000000000 --- a/packages/mask/dashboard/pages/Wallets/components/History/index.tsx +++ /dev/null @@ -1,16 +0,0 @@ -import { memo } from 'react' -import { ContentContainer } from '../../../../components/ContentContainer/index.js' -import { HistoryTable } from '../HistoryTable/index.js' -import type { Web3Helper } from '@masknet/web3-helpers' - -interface HistoryProps { - selectedChainId: Web3Helper.ChainIdAll -} - -export const History = memo(({ selectedChainId }) => { - return ( - - - - ) -}) diff --git a/packages/mask/dashboard/pages/Wallets/components/HistoryTable/index.tsx b/packages/mask/dashboard/pages/Wallets/components/HistoryTable/index.tsx deleted file mode 100644 index f08e66fd3545..000000000000 --- a/packages/mask/dashboard/pages/Wallets/components/HistoryTable/index.tsx +++ /dev/null @@ -1,138 +0,0 @@ -import { type Dispatch, memo, type SetStateAction, useMemo, useState } from 'react' -import { Box, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, Typography } from '@mui/material' -import { LoadingBase, makeStyles, MaskColorVar } from '@masknet/theme' -import type { Web3Helper } from '@masknet/web3-helpers' -import { useTransactions, useNetworkContext, useIterator } from '@masknet/web3-hooks-base' -import { ElementAnchor } from '@masknet/shared' -import { EMPTY_LIST } from '@masknet/shared-base' -import { Icons } from '@masknet/icons' -import type { Transaction } from '@masknet/web3-shared-base' -import { HistoryTableRow } from '../HistoryTableRow/index.js' -import { useDashboardI18N } from '../../../../locales/index.js' - -const useStyles = makeStyles()((theme) => ({ - container: { - display: 'flex', - flexDirection: 'column', - height: '100%', - }, - loading: { - justifyContent: 'center', - alignItems: 'center', - }, - header: { - color: MaskColorVar.normalText, - fontWeight: theme.typography.fontWeightRegular as any, - padding: '12px 0 12px', - border: 'none', - backgroundColor: MaskColorVar.primaryBackground, - }, - placeholder: { - display: 'flex', - flex: 1, - alignItems: 'center', - justifyContent: 'center', - flexDirection: 'column', - rowGap: 14, - }, - placeholderText: { - fontSize: 14, - lineHeight: '18px', - color: theme.palette.maskColor.second, - }, -})) - -interface HistoryTableProps { - selectedChainId: Web3Helper.ChainIdAll -} - -export const HistoryTable = memo(({ selectedChainId }) => { - const [page, setPage] = useState(0) - const { pluginID } = useNetworkContext() - const iterator = useTransactions(pluginID, { chainId: selectedChainId }) - const { - value = EMPTY_LIST, - next, - done, - loading, - } = useIterator>(iterator) - - const dataSource = useMemo(() => { - return value.filter((x) => x.chainId === selectedChainId) - }, [value, selectedChainId]) - - return ( - - ) -}) - -export interface HistoryTableUIProps { - page: number - next?: () => void - onPageChange: Dispatch> - done: boolean - isLoading: boolean - isEmpty: boolean - dataSource: Array> - selectedChainId: Web3Helper.ChainIdAll -} - -export const HistoryTableUI = memo(({ dataSource, next, isLoading, done, selectedChainId }) => { - const t = useDashboardI18N() - const { classes, cx } = useStyles() - - return ( - <> - {dataSource.length ? ( - - - - - - {t.wallets_history_types()} - - - {t.wallets_history_value()} - - - {t.wallets_history_receiver()} - - - - - - {dataSource.map((transaction) => ( - - ))} - -
-
- ) : isLoading || !done ? ( - - - - ) : !isLoading && dataSource.length === 0 ? ( - - - {t.wallet_history_no_data()} - - ) : null} - next?.()}> - {!done && dataSource?.length ? : null} - - - ) -}) diff --git a/packages/mask/dashboard/pages/Wallets/components/HistoryTableRow/index.tsx b/packages/mask/dashboard/pages/Wallets/components/HistoryTableRow/index.tsx deleted file mode 100644 index b06d949c8a02..000000000000 --- a/packages/mask/dashboard/pages/Wallets/components/HistoryTableRow/index.tsx +++ /dev/null @@ -1,168 +0,0 @@ -import { memo } from 'react' -import { BigNumber } from 'bignumber.js' -import formatDateTime from 'date-fns/format' -import fromUnixTime from 'date-fns/fromUnixTime' -import { Icons } from '@masknet/icons' -import { useReverseAddress, useWeb3Others } from '@masknet/web3-hooks-base' -import type { Web3Helper } from '@masknet/web3-helpers' -import { makeStyles, MaskColorVar } from '@masknet/theme' -import { TokenType, TransactionStatusType, type Transaction } from '@masknet/web3-shared-base' -import { Box, Link, Stack, TableCell, TableRow, Tooltip, Typography } from '@mui/material' -import { DebankTransactionDirection, ZerionTransactionDirection } from '@masknet/web3-providers/types' -import { TransactionIcon } from '../TransactionIcon/index.js' - -const useStyles = makeStyles()((theme) => ({ - type: { - maxWidth: '240px', - textOverflow: 'ellipsis', - textTransform: 'capitalize', - whiteSpace: 'nowrap', - overflow: 'hidden', - }, - cell: { - padding: `${theme.spacing(1.25)} ${theme.spacing(2.5)}`, - border: 'none', - fontSize: theme.typography.pxToRem(14), - }, - link: { - display: 'inline-flex', - alignItems: 'center', - justifyContent: 'center', - height: 21, - color: MaskColorVar.textPrimary, - }, - linkIcon: { - // TODO: replace with theme color - color: theme.palette.mode === 'dark' ? '#F5F5F5' : '#07101B', - marginLeft: 10, - }, - pair: { - color: MaskColorVar.greenMain, - }, - send: { - color: MaskColorVar.redMain, - }, - hover: { - '&:hover': { - backgroundColor: theme.palette.background.default, - }, - }, - nftName: { - whiteSpace: 'nowrap', - overflow: 'hidden', - textOverflow: 'ellipsis', - cursor: 'default', - }, -})) - -export interface HistoryTableRowProps { - transaction: Transaction - selectedChainId: Web3Helper.ChainIdAll -} - -export const HistoryTableRow = memo(({ transaction, selectedChainId }) => { - const { data: domain } = useReverseAddress(undefined, transaction.to) - const transactionType = (transaction.type ?? '').replaceAll('_', ' ') - - return ( - - ) -}) - -HistoryTableRow.displayName = 'HistoryTableRow' - -export interface HistoryTableRowUIProps extends HistoryTableRowProps { - selectedChainId: Web3Helper.ChainIdAll - formattedType: string - domain?: string | null -} - -export const HistoryTableRowUI = memo( - ({ transaction, selectedChainId, formattedType, domain }) => { - const { classes, cx } = useStyles() - const Others = useWeb3Others() - - return ( - - - - - - - {formattedType} - - - {formatDateTime(fromUnixTime(transaction.timestamp), 'yyyy-MM-dd HH:mm')} - - - - - - {transaction.assets.map((pair, index) => { - const direction = - pair.direction === DebankTransactionDirection.SEND || - pair.direction === ZerionTransactionDirection.OUT - return ( - - - {direction ? '-' : '+'} - - {new BigNumber(pair.amount).toFixed( - new BigNumber(pair.amount).toNumber() < 1 ? 6 : 2, - )} - - - - {pair.type === TokenType.NonFungible && ( - - - {pair.name} - - - )} - {pair.type === TokenType.Fungible && ( - - {pair.symbol} - - )} - - - ) - })} - - - - - {domain ? Others.formatDomainName?.(domain) : Others.formatAddress(transaction.to, 4)} - - - - - - - - ) - }, -) diff --git a/packages/mask/dashboard/pages/Wallets/components/ImportWallet/index.tsx b/packages/mask/dashboard/pages/Wallets/components/ImportWallet/index.tsx deleted file mode 100644 index 276c29df806b..000000000000 --- a/packages/mask/dashboard/pages/Wallets/components/ImportWallet/index.tsx +++ /dev/null @@ -1,99 +0,0 @@ -import { memo } from 'react' -import { TabContext, TabPanel } from '@mui/lab' -import { Button, styled, FilledInput, Tab } from '@mui/material' -import { makeStyles, ButtonGroupTabList, useTabs } from '@masknet/theme' -import { DesktopMnemonicConfirm } from '../../../../components/Mnemonic/index.js' -import { MaskAlert } from '../../../../components/MaskAlert/index.js' -import { useDashboardI18N } from '../../../../locales/index.js' - -const Container = styled('div')` - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; -` - -const ControlContainer = styled('div')( - ({ theme }) => ` - margin-top: ${theme.spacing(6)}; - display: grid; - justify-content: center; - grid-template-columns: repeat(2, 180px); - gap: 24px; - width: 584px; -`, -) - -const AlertContainer = styled('div')( - ({ theme }) => ` - width: 676px; - margin-top: ${theme.spacing(7)}; -`, -) - -const PrivateKeyInput = styled(FilledInput)( - ({ theme }) => ` - width: 582px; - height: 182px; - margin-top: ${theme.spacing(3)}; -`, -) - -const PasswordInput = styled(FilledInput)( - ({ theme }) => ` - width: 582px; - margin-top: ${theme.spacing(3)}; -`, -) - -const useStyles = makeStyles()((theme) => ({ - tabs: { width: 582, justifyContent: 'center' }, - panels: { - display: 'flex', - flexDirection: 'column', - alignItems: 'center', - padding: 0, - marginTop: theme.spacing(3), - width: 582, - }, -})) - -export const ImportWallet = memo(() => { - const { classes } = useStyles() - const t = useDashboardI18N() - const [currentTab, onChange, tabs] = useTabs('mnemonic', 'json', 'privateKey') - const tabPanelClasses = { root: classes.panels } - return ( - <> - - - - - - - - - {}} /> - - - TBD - - - - - - - - - - - - - - - - ) -}) diff --git a/packages/mask/dashboard/pages/Wallets/components/ReceiveDialog/index.tsx b/packages/mask/dashboard/pages/Wallets/components/ReceiveDialog/index.tsx deleted file mode 100644 index 4ab9ad957dda..000000000000 --- a/packages/mask/dashboard/pages/Wallets/components/ReceiveDialog/index.tsx +++ /dev/null @@ -1,111 +0,0 @@ -import { memo } from 'react' -import { useCopyToClipboard } from 'react-use' -import { MaskColorVar, MaskDialog, makeStyles } from '@masknet/theme' -import { QRCode, useSnackbarCallback } from '@masknet/shared' -import { DialogContent, Typography, DialogActions, Button } from '@mui/material' -import { ChainResolver } from '@masknet/web3-providers' -import type { Web3Helper } from '@masknet/web3-helpers' -import { useChainContext, useReverseAddress, useWeb3Others } from '@masknet/web3-hooks-base' -import { WalletQRCodeContainer } from '../../../../components/WalletQRCodeContainer/index.js' -import { useDashboardI18N } from '../../../../locales/index.js' - -const useStyles = makeStyles()((theme) => ({ - paper: { - width: '100%', - }, - container: { - display: 'flex', - flexDirection: 'column', - alignItems: 'center', - }, - addressTitle: { - marginTop: theme.spacing(1.5), - color: MaskColorVar.normalText, - }, - address: { - marginTop: theme.spacing(1.5), - fontWeight: 600, - }, -})) - -export interface ReceiveDialogProps { - open: boolean - address: string - onClose: () => void -} - -export const ReceiveDialog = memo(({ open, address, onClose }) => { - const Others = useWeb3Others() - - const { chainId } = useChainContext() - const { data: domain } = useReverseAddress(undefined, address) - - return ( - - ) -}) - -export interface ReceiveDialogUIProps extends ReceiveDialogProps { - chainId: Web3Helper.ChainIdAll - domain?: string -} - -export const ReceiveDialogUI = memo(({ open, chainId, address, domain, onClose }) => { - const t = useDashboardI18N() - const { classes } = useStyles() - const [, copyToClipboard] = useCopyToClipboard() - const copyAddress = useSnackbarCallback({ - executor: async (address: string) => copyToClipboard(address), - deps: [], - successText: t.wallets_address_copied(), - }) - // TODO: The text prop protocol maybe correct and requires confirmation - return ( - - - - {t.wallets_receive_tips({ chainName: ChainResolver.chainName(chainId as number) ?? '' })} - - - - - - {t.wallets_address()} - - - {domain ? ( - - {domain} - - ) : null} - - - {address} - - - - - - - ) -}) diff --git a/packages/mask/dashboard/pages/Wallets/components/TransactionIcon/index.tsx b/packages/mask/dashboard/pages/Wallets/components/TransactionIcon/index.tsx deleted file mode 100644 index bc27d1e719a8..000000000000 --- a/packages/mask/dashboard/pages/Wallets/components/TransactionIcon/index.tsx +++ /dev/null @@ -1,123 +0,0 @@ -import { memo, useMemo } from 'react' -import { Box } from '@mui/material' -import { Icons } from '@masknet/icons' -import { makeStyles, MaskColorVar } from '@masknet/theme' -import { isSameAddress } from '@masknet/web3-shared-base' -import type { NetworkPluginID } from '@masknet/shared-base' -import { useRedPacketConstants } from '@masknet/web3-shared-evm' -import { useChainContext } from '@masknet/web3-hooks-base' -import { HistoryAPI } from '@masknet/web3-providers/types' - -const useStyles = makeStyles()(() => ({ - container: { - width: 36, - height: 36, - borderRadius: '50%', - display: 'flex', - justifyContent: 'center', - alignItems: 'center', - // default backgroundColor - background: MaskColorVar.warning.alpha(0.1), - }, - success: { - background: MaskColorVar.success.alpha(0.1), - }, - warning: { - background: MaskColorVar.warning.alpha(0.1), - }, - error: { - background: MaskColorVar.redMain.alpha(0.1), - }, - icon: { - height: 20, - width: 20, - }, -})) - -export interface TransactionIconProps { - type?: string - transactionType?: string - address: string - failed: boolean -} - -export const TransactionIcon = memo(({ address, failed, type, transactionType }) => { - const { chainId } = useChainContext() - const { - HAPPY_RED_PACKET_ADDRESS_V1, - HAPPY_RED_PACKET_ADDRESS_V2, - HAPPY_RED_PACKET_ADDRESS_V3, - HAPPY_RED_PACKET_ADDRESS_V4, - } = useRedPacketConstants(chainId) - - const isRedPacket = - isSameAddress(HAPPY_RED_PACKET_ADDRESS_V1, address) || - isSameAddress(HAPPY_RED_PACKET_ADDRESS_V2, address) || - isSameAddress(HAPPY_RED_PACKET_ADDRESS_V3, address) || - isSameAddress(HAPPY_RED_PACKET_ADDRESS_V4, address) - - return ( - - ) -}) - -export interface TransactionIconUIProps { - isRedPacket: boolean - isFailed: boolean - type?: string - transactionType?: string -} - -export const TransactionIconUI = memo(({ isFailed, isRedPacket, type, transactionType }) => { - const { classes, cx } = useStyles() - const icon = useMemo(() => { - if (isFailed) return - if (isRedPacket) return - - switch (type) { - case HistoryAPI.TransactionType.SEND: - return - case HistoryAPI.TransactionType.TRANSFER: - return - case HistoryAPI.TransactionType.WITHDRAW: - case HistoryAPI.TransactionType.RECEIVE: - return - case HistoryAPI.TransactionType.CREATE_LUCKY_DROP: - case HistoryAPI.TransactionType.CREATE_RED_PACKET: - return - default: - return - } - }, [isFailed, isRedPacket, type]) - - const isNotFailed = !isFailed && !!transactionType - const isSuccess = - isNotFailed && - [ - HistoryAPI.TransactionType.RECEIVE, - HistoryAPI.TransactionType.CLAIM, - HistoryAPI.TransactionType.WITHDRAW, - ].includes(transactionType as HistoryAPI.TransactionType) - const isWarning = - isNotFailed && - [ - HistoryAPI.TransactionType.SEND, - HistoryAPI.TransactionType.FILL_POOL, - HistoryAPI.TransactionType.CREATE_RED_PACKET, - HistoryAPI.TransactionType.CREATE_LUCKY_DROP, - HistoryAPI.TransactionType.TRANSFER, - HistoryAPI.TransactionType.SWAP, - ].includes(transactionType as HistoryAPI.TransactionType) - - return ( - - {icon} - - ) -}) diff --git a/packages/mask/dashboard/pages/Wallets/components/Transfer/NFTCard.tsx b/packages/mask/dashboard/pages/Wallets/components/Transfer/NFTCard.tsx deleted file mode 100644 index 5d94826a21e4..000000000000 --- a/packages/mask/dashboard/pages/Wallets/components/Transfer/NFTCard.tsx +++ /dev/null @@ -1,104 +0,0 @@ -import { memo, useMemo, useState } from 'react' -import { Checkbox, ImageListItem, ImageListItemBar, Box } from '@mui/material' -import { getMaskColor, makeStyles, MaskColorVar } from '@masknet/theme' -import { Icons } from '@masknet/icons' -import { AssetPreviewer } from '@masknet/shared' -import { NetworkPluginID } from '@masknet/shared-base' -import { useNonFungibleAsset } from '@masknet/web3-hooks-base' -import type { NonFungibleToken } from '@masknet/web3-shared-base' -import type { ChainId, SchemaType } from '@masknet/web3-shared-evm' - -const useStyles = makeStyles()({ - checkbox: { - position: 'absolute', - top: 0, - right: 0, - }, - disabled: { - filter: 'opacity(0.5)', - cursor: 'not-allowed', - }, - barTitle: { - padding: 0, - lineHeight: '16px', - }, - fallbackImage: { - minHeight: '0 !important', - maxWidth: 'none', - transform: 'translateY(10px)', - width: 64, - height: 64, - }, -}) - -export interface NFTCardProps { - token: NonFungibleToken - selectedTokenId: string - onSelect(tokenId: string): void -} - -export const NFTCard = memo(({ token, selectedTokenId, onSelect }) => { - const { classes } = useStyles() - const [checked, setChecked] = useState(!!selectedTokenId && selectedTokenId === token.tokenId) - const isDisabled = useMemo( - () => !!selectedTokenId && selectedTokenId !== token.tokenId, - [selectedTokenId, token.tokenId], - ) - - const { data: NFTDetailed } = useNonFungibleAsset<'all'>(NetworkPluginID.PLUGIN_EVM, token.address, token.tokenId, { - chainId: token.chainId, - }) - - const NFTNameBar = useMemo(() => { - return ( - (theme.palette.mode === 'dark' ? MaskColorVar.primaryBackground : '#F9F9FA'), - }} - classes={{ titleWrap: classes.barTitle }} - subtitle={{token.tokenId}} - position="below" - /> - ) - }, [token.tokenId]) - - return ( - (theme.palette.mode === 'dark' ? getMaskColor(theme).white : '#F9F9FA'), - }} - className={isDisabled ? classes.disabled : ''}> - - {NFTNameBar} - - {/* TODO: replace to mask checkbox component */} - } - checkedIcon={} - onChange={(e) => { - const value = e.target.checked - onSelect(value ? token.tokenId : '') - setChecked(value) - }} - /> - - - ) -}) diff --git a/packages/mask/dashboard/pages/Wallets/components/Transfer/SelectNFTList.tsx b/packages/mask/dashboard/pages/Wallets/components/Transfer/SelectNFTList.tsx deleted file mode 100644 index 45e72f76aa14..000000000000 --- a/packages/mask/dashboard/pages/Wallets/components/Transfer/SelectNFTList.tsx +++ /dev/null @@ -1,69 +0,0 @@ -import { memo, useMemo } from 'react' -import { Box, ImageList, Typography, ImageListItem, Stack } from '@mui/material' -import { MaskColorVar, LoadingBase } from '@masknet/theme' -import type { NonFungibleToken } from '@masknet/web3-shared-base' -import type { ChainId, SchemaType } from '@masknet/web3-shared-evm' -import { useDashboardI18N } from '../../../../locales/index.js' -import { NFTCard } from './NFTCard.js' - -interface SelectNFTListProps { - list: Array> - selectedTokenId: string - loading: boolean - error?: boolean - onSelect(tokenId: string): void -} - -export const SelectNFTList = memo(({ list, onSelect, selectedTokenId, loading, error }) => { - const t = useDashboardI18N() - - const renderStatus = useMemo(() => { - if (loading) { - return ( - - - - - - ) - } - - return ( - - - - {t.wallets_collectible_load_end()} - - - - ) - }, [loading]) - - return ( - - theme.palette.mode === 'dark' ? MaskColorVar.lightBackground : MaskColorVar.normalBackground, - }}> - - {list.map((token) => ( - - ))} - {renderStatus} - - - ) -}) diff --git a/packages/mask/dashboard/pages/Wallets/components/Transfer/TransferERC20.tsx b/packages/mask/dashboard/pages/Wallets/components/Transfer/TransferERC20.tsx deleted file mode 100644 index 3111f86d5716..000000000000 --- a/packages/mask/dashboard/pages/Wallets/components/Transfer/TransferERC20.tsx +++ /dev/null @@ -1,423 +0,0 @@ -import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react' -import { useAsync, useAsyncFn, useUpdateEffect } from 'react-use' -import { useLocation, useNavigate } from 'react-router-dom' -import { BigNumber } from 'bignumber.js' -import { useContainer } from 'unstated-next' -import { Tune as TuneIcon } from '@mui/icons-material' -import { Box, Button, IconButton, Link, Popover, Stack, Typography } from '@mui/material' -import { Icons } from '@masknet/icons' -import { MaskColorVar, MaskTextField, ShadowRootTooltip, makeStyles } from '@masknet/theme' -import { useGasLimit } from '@masknet/web3-hooks-evm' -import { - useFungibleTokenBalance, - useGasPrice, - useLookupAddress, - useNetworkDescriptor, - useWeb3Others, - useNativeToken, - useNativeTokenPrice, - useMaskTokenAddress, - useWallet, - useWeb3Connection, -} from '@masknet/web3-hooks-base' -import { FormattedAddress, SelectFungibleTokenModal, TokenAmountPanel } from '@masknet/shared' -import { DashboardRoutes, NetworkPluginID } from '@masknet/shared-base' -import { ChainResolver, DepositPaymaster, SmartPayBundler } from '@masknet/web3-providers' -import { - TokenType, - type FungibleToken, - isGreaterThan, - isZero, - multipliedBy, - rightShift, - isSameAddress, - minus, - toFixed, -} from '@masknet/web3-shared-base' -import { - SchemaType, - formatWeiToEther, - type ChainId, - isValidDomain, - isValidAddress, - NetworkType, - isNativeTokenAddress, - addGasMargin, - type EIP1559GasConfig, -} from '@masknet/web3-shared-evm' -import { useDashboardI18N } from '../../../../locales/index.js' -import { useGasConfig } from '../../hooks/useGasConfig.js' -import { Context } from '../../hooks/useContext.js' -import { TransferTab } from './types.js' - -export interface TransferERC20Props { - token: FungibleToken -} - -const GAS_LIMIT = 21000 - -const useStyles = makeStyles()((theme) => { - return { - tooltip: { - backgroundColor: theme.palette.maskColor.publicMain, - color: theme.palette.maskColor.white, - }, - arrow: { - color: theme.palette.maskColor.publicMain, - }, - } -}) - -export const TransferERC20 = memo(({ token }) => { - const t = useDashboardI18N() - const wallet = useWallet() - const anchorEl = useRef(null) - const navigate = useNavigate() - const { state } = useLocation() as { - state: { - token?: FungibleToken - } | null - } - const [amount, setAmount] = useState('') - const [address, setAddress] = useState('') - const [message, setMessage] = useState('') - const [popoverOpen, setPopoverOpen] = useState(false) - const [minPopoverWidth, setMinPopoverWidth] = useState(0) - - const { classes } = useStyles() - - const { data: defaultGasPrice = '0' } = useGasPrice(NetworkPluginID.PLUGIN_EVM) - - const [selectedToken, setSelectedToken] = useState(token) - - const { account, chainId, pluginID, isWalletConnectNetworkNotMatch } = useContainer(Context) - - const network = useNetworkDescriptor(pluginID, state?.token?.chainId) - const Others = useWeb3Others() - const Web3 = useWeb3Connection(pluginID, { - account, - chainId, - }) - - const is1559Supported = useMemo(() => Others?.chainResolver.isFeatureSupported(chainId, 'EIP1559'), [chainId]) - useEffect(() => { - token.chainId === chainId - ? setSelectedToken(token) - : setSelectedToken(ChainResolver.nativeCurrency(chainId as ChainId)) - }, [token, chainId]) - - // workaround: transferERC20 should support non-evm network - const isNativeToken = isNativeTokenAddress(selectedToken.address) - - const { data: nativeToken } = useNativeToken(pluginID, { chainId }) - const { data: nativeTokenPrice = 0 } = useNativeTokenPrice(pluginID, { chainId }) - - // balance - const { data: tokenBalance = '0', refetch: tokenBalanceRetry } = useFungibleTokenBalance( - pluginID, - selectedToken?.address ?? '', - { chainId }, - ) - - // #region resolve ENS domain - const { - value: registeredAddress = '', - error: resolveDomainError, - loading: resolveDomainLoading, - } = useLookupAddress(pluginID, address, chainId) - // #endregion - - // transfer amount - const transferAmount = rightShift(amount || '0', selectedToken.decimals).toFixed() - const { data: erc20GasLimit = 0 } = useGasLimit( - selectedToken.type === TokenType.Fungible - ? selectedToken.symbol === nativeToken?.symbol - ? SchemaType.Native - : SchemaType.ERC20 - : selectedToken.schema, - selectedToken.address, - transferAmount, - isValidAddress(address) ? address : registeredAddress, - ) - const gasLimit_ = isNativeToken ? GAS_LIMIT : erc20GasLimit - const { gasConfig, onCustomGasSetting, gasLimit, maxFee } = useGasConfig(gasLimit_, GAS_LIMIT) - - const gasPrice = gasConfig.gasPrice || defaultGasPrice - - const gasFee = useMemo(() => { - const price = is1559Supported && maxFee ? new BigNumber(maxFee) : gasPrice - return multipliedBy(gasLimit, price) - }, [gasLimit, gasPrice, maxFee, is1559Supported]) - const gasFeeInUsd = formatWeiToEther(gasFee).multipliedBy(nativeTokenPrice) - - // #region hack for smartPay, will be removed - const maskTokenAddress = useMaskTokenAddress() - - const { value: smartPayConfig } = useAsync(async () => { - const smartPayChainId = await SmartPayBundler.getSupportedChainId() - const depositPaymaster = new DepositPaymaster(smartPayChainId) - const ratio = await depositPaymaster.getRatio() - - return { - ratio, - smartPayChainId, - } - }, []) - - const actualBalance = useMemo(() => { - if ( - !wallet?.owner || - chainId !== smartPayConfig?.smartPayChainId || - !isSameAddress(selectedToken?.address, maskTokenAddress) - ) - return tokenBalance - - return toFixed( - minus( - tokenBalance, - - new BigNumber((gasConfig as EIP1559GasConfig).maxFeePerGas) - .multipliedBy(!isZero(gasLimit) ? addGasMargin(gasLimit) : '200000') - .integerValue() - .multipliedBy(smartPayConfig?.ratio ?? 1), - ), - 0, - ) - }, [gasConfig, wallet, selectedToken?.address, maskTokenAddress, smartPayConfig, chainId, tokenBalance, gasLimit]) - // #endregion - - const maxAmount = useMemo(() => { - const price = is1559Supported && maxFee ? new BigNumber(maxFee) : gasPrice - const gasFee = multipliedBy(gasLimit, price) - - let amount_ = new BigNumber(actualBalance || '0') - amount_ = selectedToken.schema === SchemaType.Native ? amount_.minus(gasFee) : amount_ - return BigNumber.max(0, amount_).toFixed() - }, [actualBalance, gasPrice, selectedToken?.type, amount, gasLimit, maxFee, is1559Supported]) - - const [{ loading: isTransferring }, transferCallback] = useAsyncFn(async () => { - if (!selectedToken.address) return - let recipient: string | undefined - - if (isValidAddress(address)) recipient = address - else if (Others.isValidDomain(address)) recipient = registeredAddress - if (!recipient) return - - const totalAmount = rightShift(amount, token.decimals).toFixed() - return Web3.transferFungibleToken(selectedToken.address, recipient, totalAmount, '') - }, [account, selectedToken.address, token?.decimals, amount, Web3, address, registeredAddress]) - - const onTransfer = useCallback(async () => { - const hash = transferCallback() - if (typeof hash === 'string') { - setMessage('') - setAddress('') - setAmount('') - tokenBalanceRetry() - } - }, [transferCallback]) - - // #region validation - const validationMessage = useMemo(() => { - if (!transferAmount || isZero(transferAmount)) return t.wallets_transfer_error_amount_absence() - if (isGreaterThan(rightShift(amount, selectedToken.decimals), maxAmount)) - return t.wallets_transfer_error_insufficient_balance({ symbol: selectedToken.symbol ?? '' }) - if (!address) return t.wallets_transfer_error_address_absence() - if (!(isValidAddress(address) || isValidDomain(address))) return t.wallets_transfer_error_invalid_address() - if (isValidDomain(address) && (resolveDomainError || !registeredAddress)) { - if (network?.type !== NetworkType.Ethereum) return t.wallet_transfer_error_no_ens_support() - return t.wallet_transfer_error_no_address_has_been_set_name() - } - return '' - }, [ - transferAmount, - maxAmount, - address, - tokenBalance, - selectedToken, - amount, - registeredAddress, - resolveDomainError, - network, - Others, - ]) - // #endregion - - const ensContent = useMemo(() => { - if (resolveDomainLoading) return - if (registeredAddress) { - return ( - - - - {address} - - - - - - - - ) - } - - if (address.includes('.eth')) { - if (network?.type !== NetworkType.Ethereum) { - return ( - - - {t.wallet_transfer_error_no_ens_support()} - - - ) - } - if (Others.isValidDomain(address) && resolveDomainError) { - return ( - - - {t.wallet_transfer_error_no_address_has_been_set_name()} - - - ) - } - } - - return - }, [ - registeredAddress, - address, - Others.isValidDomain, - MaskColorVar, - resolveDomainError, - network?.type, - resolveDomainLoading, - ]) - - useUpdateEffect(() => { - setPopoverOpen(!!ensContent && !!anchorEl.current) - }, [ensContent]) - - return ( - - - - { - if (!anchorEl.current) anchorEl.current = event.currentTarget - if (ensContent) setPopoverOpen(true) - setMinPopoverWidth(event.currentTarget.clientWidth) - }, - spellCheck: false, - }} - onChange={(e) => setAddress(e.currentTarget.value)} - label={t.wallets_transfer_to_address()} - /> - - setPopoverOpen(false)} - PaperProps={{ - style: { minWidth: `${minPopoverWidth}px`, borderRadius: 4 }, - }} - anchorOrigin={{ - vertical: 'bottom', - horizontal: 'left', - }} - open={popoverOpen}> - {ensContent} - - - - { - const picked = await SelectFungibleTokenModal.openAndWaitForClose({ - disableNativeToken: false, - chainId, - pluginID: NetworkPluginID.PLUGIN_EVM, - }) - if (!picked) return - setSelectedToken(picked as FungibleToken) - // Update the previous location state of the token. - navigate(DashboardRoutes.WalletsTransfer, { - state: { type: TransferTab.Token, token: picked }, - }) - }, - }, - }} - /> - - - - {t.gas_fee()} - - - - {t.transfer_cost({ - gasFee: formatWeiToEther(gasFee).toFixed(6), - symbol: nativeToken?.symbol ?? '', - usd: gasFeeInUsd.toFixed(2), - })} - - - - - - - {isNativeToken ? ( - - setMessage(e.currentTarget.value)} - label={t.wallets_transfer_memo()} - /> - - ) : null} - - {isWalletConnectNetworkNotMatch ? ( - -
- -
-
- ) : ( - - )} -
-
-
- ) -}) diff --git a/packages/mask/dashboard/pages/Wallets/components/Transfer/TransferERC721.tsx b/packages/mask/dashboard/pages/Wallets/components/Transfer/TransferERC721.tsx deleted file mode 100644 index 60f5a6638bd1..000000000000 --- a/packages/mask/dashboard/pages/Wallets/components/Transfer/TransferERC721.tsx +++ /dev/null @@ -1,452 +0,0 @@ -import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react' -import { useAsync, useAsyncFn, useUpdateEffect } from 'react-use' -import { useNavigate, useLocation } from 'react-router-dom' -import { unionBy } from 'lodash-es' -import { z } from 'zod' -import { Controller, useForm } from 'react-hook-form' -import { Icons } from '@masknet/icons' -import { makeStyles, MaskColorVar, MaskTextField, ShadowRootTooltip } from '@masknet/theme' -import { Box, Button, IconButton, Link, Popover, Stack, Typography } from '@mui/material' -import { - isSameAddress, - type NonFungibleToken, - type NonFungibleTokenContract, - multipliedBy, -} from '@masknet/web3-shared-base' -import { NetworkPluginID, DashboardRoutes } from '@masknet/shared-base' -import { - SchemaType, - formatWeiToEther, - NetworkType, - type ChainId, - isValidDomain, - isValidAddress, - formatEthereumAddress, -} from '@masknet/web3-shared-evm' -import { FormattedAddress, SelectNonFungibleContractModal } from '@masknet/shared' -import { zodResolver } from '@hookform/resolvers/zod' -import { KeyboardArrowDown as KeyboardArrowDownIcon, Tune as TuneIcon } from '@mui/icons-material' -import { - useGasPrice, - useLookupAddress, - useNetworkDescriptor, - useNativeToken, - useNativeTokenPrice, - useWeb3Others, - useWeb3Connection, -} from '@masknet/web3-hooks-base' -import { useGasLimit, useNonFungibleOwnerTokens } from '@masknet/web3-hooks-evm' -import { SelectNFTList } from './SelectNFTList.js' -import { LoadingPlaceholder } from '../../../../components/LoadingPlaceholder/index.js' -import { useDashboardI18N } from '../../../../locales/index.js' -import { useGasConfig } from '../../hooks/index.js' -import { TransferTab } from './types.js' -import { Context } from '../../hooks/useContext.js' -import { useContainer } from 'unstated-next' - -const useStyles = makeStyles()((theme) => ({ - disabled: { - opacity: 1, - }, - tooltip: { - backgroundColor: theme.palette.maskColor.publicMain, - color: theme.palette.maskColor.white, - }, - arrow: { - color: theme.palette.maskColor.publicMain, - }, -})) - -type FormInputs = { - recipient: string - contract: string - tokenId: string -} - -const GAS_LIMIT = 30000 - -export const TransferERC721 = memo(() => { - const t = useDashboardI18N() - const { chainId, pluginID, isWalletConnectNetworkNotMatch, account } = useContainer(Context) - const anchorEl = useRef(null) - - const { state } = useLocation() as { - state: { - nonFungibleToken?: NonFungibleToken - type?: TransferTab - chainId?: ChainId - } | null - } - - const { classes } = useStyles() - const Others = useWeb3Others() - const [defaultToken, setDefaultToken] = useState | null>(null) - const navigate = useNavigate() - const [popoverOpen, setPopoverOpen] = useState(false) - const [recipientError, setRecipientError] = useState<{ - type: 'account' | 'contractAddress' - message: string - } | null>(null) - const [minPopoverWidth, setMinPopoverWidth] = useState(0) - const [contract, setContract] = useState>() - const network = useNetworkDescriptor() - - const { data: nativeToken } = useNativeToken(pluginID, { chainId }) - const { data: nativeTokenPrice = 0 } = useNativeTokenPrice(pluginID, { chainId }) - // form - const schema = z.object({ - recipient: z - .string() - .refine((address) => isValidAddress(address) || isValidDomain(address), t.wallets_incorrect_address()), - contract: z.string().min(1, t.wallets_collectible_contract_is_empty()), - tokenId: z.string().min(1, t.wallets_collectible_token_id_is_empty()), - }) - - const { - control, - handleSubmit, - setValue, - watch, - clearErrors, - formState: { errors, isSubmitting }, - } = useForm({ - resolver: zodResolver(schema), - defaultValues: { recipient: '', contract: '', tokenId: '' }, - }) - - const [contractAddress, setContractAddress] = useState(state?.nonFungibleToken?.address || '') - - useEffect(() => { - if ( - !state?.nonFungibleToken || - state?.nonFungibleToken.chainId !== chainId || - state?.type !== TransferTab.Collectibles - ) { - setContract(undefined) - setContractAddress('') - setValue('contract', '') - setValue('tokenId', '') - setDefaultToken(null) - return - } - - setContract(state.nonFungibleToken.contract) - setContractAddress(state.nonFungibleToken.address) - setValue('contract', state.nonFungibleToken.contract?.name ?? '') - setValue('tokenId', state.nonFungibleToken.tokenId) - setDefaultToken(state.nonFungibleToken) - }, [state, chainId]) - - const allFormFields = watch() - - // #region resolve ENS domain - const { - value: registeredAddress = '', - error: resolveDomainError, - loading: resolveDomainLoading, - } = useLookupAddress(NetworkPluginID.PLUGIN_EVM, allFormFields.recipient) - // #endregion - - // #region check contract address and account address - useAsync(async () => { - const recipient = allFormFields.recipient - setRecipientError(null) - if (!recipient && !registeredAddress) return - if (!isValidAddress(recipient) && !isValidAddress(registeredAddress)) return - clearErrors() - if (isSameAddress(recipient, account) || isSameAddress(registeredAddress, account)) { - setRecipientError({ - type: 'account', - message: t.wallets_transfer_error_same_address_with_current_account(), - }) - } - const result = await Web3.getCode(recipient) - if (result !== '0x') { - setRecipientError({ - type: 'contractAddress', - message: t.wallets_transfer_error_is_contract_address(), - }) - } - }, [allFormFields.recipient, clearErrors, registeredAddress]) - // #endregion - - const { data: erc721GasLimit } = useGasLimit( - SchemaType.ERC721, - contract?.address, - undefined, - isValidAddress(allFormFields.recipient) ? allFormFields.recipient : registeredAddress, - allFormFields.tokenId, - ) - - const gasLimit_ = erc721GasLimit ? erc721GasLimit : GAS_LIMIT - const { gasConfig, onCustomGasSetting, gasLimit } = useGasConfig(gasLimit_, GAS_LIMIT) - - const Web3 = useWeb3Connection(pluginID, { - account, - chainId, - }) - - const [{ loading: isTransferring }, transferCallback] = useAsyncFn(async () => { - if (!contractAddress) return - if (pluginID === NetworkPluginID.PLUGIN_EVM && !allFormFields.tokenId) return - return Web3.transferNonFungibleToken(contractAddress, allFormFields.tokenId ?? '', allFormFields.recipient, '1') - }, [account, allFormFields.tokenId, pluginID, contractAddress, allFormFields.recipient, Web3]) - - const handleSelectNFT = () => { - SelectNonFungibleContractModal.open({ - pluginID: NetworkPluginID.PLUGIN_EVM, - schemaType: SchemaType.ERC721, - chainId, - onSubmit: (contract) => { - setContractAddress(contract.address || '') - if (contract && defaultToken && !isSameAddress(contract.address, defaultToken.address)) { - setDefaultToken(null) - } - if (contract) { - setValue('contract', contract.name || contract.address || '', { - shouldValidate: true, - }) - setContract(contract as NonFungibleTokenContract) - setValue('tokenId', '') - } - }, - }) - } - - // gas price - const { data: defaultGasPrice = '0' } = useGasPrice() - const gasPrice = gasConfig.gasPrice || defaultGasPrice - const gasFee = useMemo(() => multipliedBy(gasLimit, gasPrice), [gasLimit, gasPrice]) - const gasFeeInUsd = formatWeiToEther(gasFee).multipliedBy(nativeTokenPrice) - - const { loading: loadingOwnerList, value: tokenDetailedOwnerList = [] } = useNonFungibleOwnerTokens( - contract?.address ?? '', - account, - chainId as ChainId, - ) - - const onTransfer = useCallback( - async (data: FormInputs) => { - let hash: string | undefined - if (isValidAddress(data.recipient)) { - hash = await transferCallback() - } else if (isValidDomain(data.recipient) && isValidAddress(registeredAddress)) { - hash = await transferCallback() - } - if (typeof hash === 'string') { - navigate(DashboardRoutes.WalletsHistory) - } - }, - [transferCallback, contract?.address, gasConfig, registeredAddress], - ) - - const ensContent = useMemo(() => { - if (resolveDomainLoading) return - if (registeredAddress) { - return ( - - - - {allFormFields.recipient} - - - - - - - - ) - } - - if (allFormFields.recipient.includes('.eth')) { - if (network?.type !== NetworkType.Ethereum) { - return ( - - - {t.wallet_transfer_error_no_ens_support()} - - - ) - } - if (isValidDomain(allFormFields.recipient) && resolveDomainError) { - return ( - - - {t.wallet_transfer_error_no_address_has_been_set_name()} - - - ) - } - } - return - }, [allFormFields.recipient, resolveDomainError, resolveDomainLoading, network, registeredAddress]) - - useUpdateEffect(() => { - setPopoverOpen(!!ensContent && !!anchorEl.current) - }, [ensContent]) - - const contractIcon = useMemo(() => { - if (!contract?.logoURL) return null - return ( - - - - ) - }, [contract]) - - return ( - -
- - - ( - setValue('recipient', e.currentTarget.value)} - helperText={errors.recipient?.message || recipientError?.message} - error={ - !!errors.recipient || - (!!recipientError && recipientError.type === 'contractAddress') - } - value={field.field.value} - InputProps={{ - onClick: (event) => { - if (!anchorEl.current) anchorEl.current = event.currentTarget - if (ensContent) setPopoverOpen(true) - setMinPopoverWidth(event.currentTarget.clientWidth) - }, - spellCheck: false, - }} - label={t.wallets_transfer_to_address()} - /> - )} - name="recipient" - /> - setPopoverOpen(false)} - PaperProps={{ - style: { minWidth: `${minPopoverWidth}px`, borderRadius: 4 }, - }} - anchorOrigin={{ - vertical: 'bottom', - horizontal: 'left', - }} - open={popoverOpen}> - {ensContent} - - - - ( - - , - }} - inputProps={{ - sx: { cursor: 'pointer' }, - }} - label={t.wallets_transfer_contract()} - value={field.field.value} - /> - - )} - name="contract" - /> - - {loadingOwnerList && tokenDetailedOwnerList.length === 0 ? ( - - - - ) : null} - - {tokenDetailedOwnerList.length > 0 && !loadingOwnerList && ( - ( - setValue('tokenId', value)} - list={ - defaultToken - ? unionBy([defaultToken, ...tokenDetailedOwnerList], 'tokenId') - : tokenDetailedOwnerList - } - selectedTokenId={field.field.value} - loading={loadingOwnerList} - /> - )} - name="tokenId" - /> - )} - - - - {t.gas_fee()} - - - - {t.transfer_cost({ - gasFee: formatWeiToEther(gasFee).toFixed(6), - symbol: nativeToken?.symbol ?? '', - usd: gasFeeInUsd.toFixed(2), - })} - - - - - - - - {isWalletConnectNetworkNotMatch ? ( - -
- -
-
- ) : ( - - )} -
-
-
-
- ) -}) diff --git a/packages/mask/dashboard/pages/Wallets/components/Transfer/index.tsx b/packages/mask/dashboard/pages/Wallets/components/Transfer/index.tsx deleted file mode 100644 index 73b5bdc5067d..000000000000 --- a/packages/mask/dashboard/pages/Wallets/components/Transfer/index.tsx +++ /dev/null @@ -1,67 +0,0 @@ -import { memo, useEffect } from 'react' -import { ContentContainer } from '../../../../components/ContentContainer/index.js' -import { Box, Tab } from '@mui/material' -import { useTabs } from '@masknet/theme' -import { TabContext, TabList, TabPanel } from '@mui/lab' -import { TransferERC20 } from './TransferERC20.js' -import { useLocation } from 'react-router-dom' -import { useDashboardI18N } from '../../../../locales/index.js' -import { TransferERC721 } from './TransferERC721.js' -import { TransferTab } from './types.js' -import type { FungibleToken } from '@masknet/web3-shared-base' -import type { ChainId, SchemaType } from '@masknet/web3-shared-evm' -import { useWeb3Others } from '@masknet/web3-hooks-base' -import { useContainer } from 'unstated-next' -import { Context } from '../../hooks/useContext.js' - -const assetTabs = [TransferTab.Token, TransferTab.Collectibles] as const - -export * from './types.js' - -export const Transfer = memo(() => { - const t = useDashboardI18N() - const { state } = useLocation() as { - state: { - token?: FungibleToken - nonFungibleToken?: FungibleToken - type?: TransferTab - } | null - } - const { chainId } = useContainer(Context) - const Others = useWeb3Others() - const nativeToken = Others.createNativeToken(chainId) - const transferTabsLabel: Record = { - [TransferTab.Token]: t.wallets_assets_token(), - [TransferTab.Collectibles]: t.wallets_assets_collectibles(), - } - const [currentTab, onChange, , setTab] = useTabs(TransferTab.Token, TransferTab.Collectibles) - - useEffect(() => { - if (!state) return - if (!state.nonFungibleToken || state.type !== TransferTab.Collectibles) return - - setTab(TransferTab.Collectibles) - }, [state]) - - if (!nativeToken && !state?.token) return null - - return ( - - - - - {assetTabs.map((key) => ( - - ))} - - - - - - - - - - - ) -}) diff --git a/packages/mask/dashboard/pages/Wallets/components/Transfer/types.ts b/packages/mask/dashboard/pages/Wallets/components/Transfer/types.ts deleted file mode 100644 index 61e77868c144..000000000000 --- a/packages/mask/dashboard/pages/Wallets/components/Transfer/types.ts +++ /dev/null @@ -1,4 +0,0 @@ -export enum TransferTab { - Token = 'Token', - Collectibles = 'Collectibles', -} diff --git a/packages/mask/dashboard/pages/Wallets/components/WalletStateBar/index.tsx b/packages/mask/dashboard/pages/Wallets/components/WalletStateBar/index.tsx deleted file mode 100644 index 93fdf9957339..000000000000 --- a/packages/mask/dashboard/pages/Wallets/components/WalletStateBar/index.tsx +++ /dev/null @@ -1,169 +0,0 @@ -import { memo } from 'react' -import { Box, Button, Stack, Typography } from '@mui/material' -import { ProviderType } from '@masknet/web3-shared-evm' -import { makeStyles, MaskColorVar, LoadingBase } from '@masknet/theme' -import { FormattedAddress, WalletIcon, SelectProviderModal, WalletStatusModal } from '@masknet/shared' -import { - useNetworkDescriptor, - useProviderDescriptor, - useWallet, - useReverseAddress, - useRecentTransactions, - useChainContext, -} from '@masknet/web3-hooks-base' -import { Others } from '@masknet/web3-providers' -import { NetworkPluginID } from '@masknet/shared-base' -import { TransactionStatusType } from '@masknet/web3-shared-base' -import type { Web3Helper } from '@masknet/web3-helpers' -import { useDashboardI18N } from '../../../../locales/index.js' - -const useStyles = makeStyles()((theme) => ({ - bar: { - minWidth: 80, - lineHeight: '28px', - height: '28px', - cursor: 'pointer', - position: 'relative', - '&::after': { - borderRadius: 30, - pointerEvents: 'none', - content: '""', - inset: 0, - margin: 'auto', - position: 'absolute', - backgroundColor: 'var(--network-icon-color, transparent)', - opacity: 0.1, - zIndex: 0, - }, - '& > span': { - position: 'relative', - zIndex: 1, - }, - }, - dot: { - position: 'relative', - top: 0, - display: 'inline-block', - marginRight: theme.spacing(0.8), - lineHeight: '28px', - width: 10, - height: 10, - borderRadius: 5, - }, - domain: { - fontSize: 14, - marginLeft: 20, - background: theme.palette.mode === 'dark' ? 'rgba(73, 137, 255, 0.2)' : 'rgba(28, 104, 243, 0.1)', - padding: '2px 8px', - borderRadius: 4, - }, -})) - -export const WalletStateBar = memo(() => { - const t = useDashboardI18N() - - const { account } = useChainContext() - const wallet = useWallet() - const networkDescriptor = useNetworkDescriptor() - const providerDescriptor = useProviderDescriptor() - const pendingTransactions = useRecentTransactions(NetworkPluginID.PLUGIN_EVM, TransactionStatusType.NOT_DEPEND) - - const { data: domain } = useReverseAddress(NetworkPluginID.PLUGIN_EVM, account) - - if (!account) { - return - } - return ( - - ) -}) - -interface WalletStateBarUIProps { - isPending: boolean - network?: Web3Helper.NetworkDescriptorAll - provider?: Web3Helper.ProviderDescriptorAll - name?: string - address?: string - domain?: string | null - openConnectWalletDialog(): void -} - -export function WalletStateBarUI({ - isPending, - network, - provider, - name, - address, - domain, - openConnectWalletDialog, -}: WalletStateBarUIProps) { - const t = useDashboardI18N() - const { classes } = useStyles() - - if (!network || !provider) return null - - return ( - - - - - {network.name} - - - {isPending ? ( - - - - {t.wallet_transactions_pending()} - - - ) : null} - - - - - - {provider.type !== ProviderType.MaskWallet ? ( - - {domain ? Others.formatDomainName(domain) : provider.name} - - ) : ( - - {name} - {domain ? ( - {Others.formatDomainName(domain)} - ) : null} - - )} - - - - - - - ) -} diff --git a/packages/mask/dashboard/pages/Wallets/hooks/index.ts b/packages/mask/dashboard/pages/Wallets/hooks/index.ts deleted file mode 100644 index 4106243ea699..000000000000 --- a/packages/mask/dashboard/pages/Wallets/hooks/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export * from './useGasConfig.js' -export * from './useIsMatched.js' diff --git a/packages/mask/dashboard/pages/Wallets/hooks/useContext.ts b/packages/mask/dashboard/pages/Wallets/hooks/useContext.ts deleted file mode 100644 index 8f4e1f9c8a4d..000000000000 --- a/packages/mask/dashboard/pages/Wallets/hooks/useContext.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { createContainer } from 'unstated-next' -import { NetworkPluginID, EMPTY_LIST } from '@masknet/shared-base' -import { - useFungibleAssets, - useChainContext, - useFungibleTokensFromTokenList, - useWeb3Others, -} from '@masknet/web3-hooks-base' -import type { Web3Helper } from '@masknet/web3-helpers' -import { isNativeTokenAddress, ProviderType } from '@masknet/web3-shared-evm' -import { isSameAddress } from '@masknet/web3-shared-base' -import { useMemo } from 'react' - -function useContext(initialState?: { - account?: string - chainId?: Web3Helper.ChainIdAll - setSelectedNetwork?: (x: Web3Helper.NetworkDescriptorAll | null) => void - pluginID?: NetworkPluginID - connectedChainId?: Web3Helper.ChainIdAll -}) { - const { account, chainId, providerType } = useChainContext({ - account: initialState?.account, - chainId: initialState?.chainId, - }) - const Others = useWeb3Others(initialState?.pluginID) - const fungibleAssets = useFungibleAssets<'all'>(initialState?.pluginID, undefined, { - account, - chainId, - }) - const { value: fungibleTokens = EMPTY_LIST, loading } = useFungibleTokensFromTokenList(initialState?.pluginID, { - chainId, - }) - - const assets = useMemo(() => { - if (!fungibleAssets?.data) return EMPTY_LIST - return fungibleAssets.data.map((x) => { - if (isNativeTokenAddress(x.address)) - return { ...x, logoURL: Others.chainResolver.nativeCurrency(x.chainId)?.logoURL || x.logoURL } - const token = fungibleTokens.find((y) => isSameAddress(x.address, y.address) && x.chainId === y.chainId) - if (!token?.logoURL) return x - return { ...x, logoURL: token.logoURL } - }) - }, [fungibleAssets.data, fungibleTokens, Others.chainResolver.nativeCurrency]) - return { - account, - chainId, - setSelectedNetwork: initialState?.setSelectedNetwork, - isWalletConnectNetworkNotMatch: - (providerType === ProviderType.WalletConnect || providerType === ProviderType.WalletConnectV2) && - initialState?.connectedChainId !== chainId, - pluginID: initialState?.pluginID ?? NetworkPluginID.PLUGIN_EVM, - fungibleAssets: { ...fungibleAssets, data: assets, isLoading: loading || fungibleAssets.isLoading }, - } -} - -export const Context = createContainer(useContext) -Context.Provider.displayName = 'FungibleAssetsProvider' diff --git a/packages/mask/dashboard/pages/Wallets/hooks/useGasConfig.ts b/packages/mask/dashboard/pages/Wallets/hooks/useGasConfig.ts deleted file mode 100644 index a005024843c6..000000000000 --- a/packages/mask/dashboard/pages/Wallets/hooks/useGasConfig.ts +++ /dev/null @@ -1,82 +0,0 @@ -import { useEffect, useMemo, useState } from 'react' -import { toHex } from 'web3-utils' -import { BigNumber } from 'bignumber.js' -import { ChainResolver } from '@masknet/web3-providers' -import { type GasConfig } from '@masknet/web3-shared-evm' -import { useChainContext, useGasOptions, useGasPrice } from '@masknet/web3-hooks-base' -import { GasOptionType } from '@masknet/web3-shared-base' -import { NetworkPluginID } from '@masknet/shared-base' -import { GasSettingModal } from '@masknet/shared' - -interface GasConfigProps { - gasConfig: GasConfig - gasLimit: number - maxFee: BigNumber.Value - onCustomGasSetting: () => ReturnType -} - -export function useGasConfig(gasLimit: number, minGasLimit: number): GasConfigProps { - const { chainId } = useChainContext() - - const [gasLimit_, setGasLimit_] = useState(0) - const [customGasPrice, setCustomGasPrice] = useState(0) - const [gasOption, setGasOption] = useState(GasOptionType.NORMAL) - const [maxFee, setMaxFee] = useState(0) - const [priorityFee, setPriorityFee] = useState(0) - - const is1559Supported = useMemo(() => ChainResolver.isFeatureSupported(chainId, 'EIP1559'), [chainId]) - const { data: defaultGasPrice = '0' } = useGasPrice(NetworkPluginID.PLUGIN_EVM) - const gasPrice = customGasPrice || defaultGasPrice - const { data: gasOptions } = useGasOptions(NetworkPluginID.PLUGIN_EVM) - - useEffect(() => GasSettingModal.close(), []) - - useEffect(() => { - setGasLimit_(gasLimit) - }, [gasLimit]) - - useEffect(() => { - if (!gasOptions) return - - if (is1559Supported) { - const gasLevel = gasOptions.normal - setMaxFee((oldVal) => { - return !oldVal ? gasLevel?.suggestedMaxFeePerGas ?? '0' : oldVal - }) - setPriorityFee((oldVal) => { - return !oldVal ? gasLevel?.suggestedMaxPriorityFeePerGas ?? '0' : oldVal - }) - } else { - setCustomGasPrice((oldVal) => (!oldVal ? gasOptions.normal.suggestedMaxFeePerGas : oldVal)) - } - }, [is1559Supported, gasOptions?.normal]) - - useEffect(() => { - if (!gasOptions) return - if (is1559Supported) { - const gasLevel = gasOptions.normal - setMaxFee(gasLevel?.suggestedMaxFeePerGas ?? 0) - setPriorityFee(gasLevel?.suggestedMaxPriorityFeePerGas ?? 0) - } else { - setCustomGasPrice(gasOptions.normal.suggestedMaxFeePerGas) - } - }, [chainId, gasOptions?.normal]) - - const gasConfig: GasConfig = useMemo(() => { - return is1559Supported - ? { - gas: toHex(gasLimit_), - maxFeePerGas: toHex(new BigNumber(maxFee).integerValue().toFixed()), - maxPriorityFeePerGas: toHex(new BigNumber(priorityFee).integerValue().toFixed()), - } - : { gas: toHex(gasLimit_), gasPrice: toHex(new BigNumber(gasPrice).toString()) } - }, [is1559Supported, gasLimit_, maxFee, priorityFee, gasPrice, chainId]) - - return { - gasConfig, - gasLimit: gasLimit_, - maxFee, - onCustomGasSetting: async () => - GasSettingModal.openAndWaitForClose({ gasLimit: gasLimit_, gasOption, minGasLimit }), - } -} diff --git a/packages/mask/dashboard/pages/Wallets/hooks/useIsMatched.ts b/packages/mask/dashboard/pages/Wallets/hooks/useIsMatched.ts deleted file mode 100644 index 1556206c8a1c..000000000000 --- a/packages/mask/dashboard/pages/Wallets/hooks/useIsMatched.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { useMatch } from 'react-router-dom' - -export function useIsMatched(...args: Parameters) { - const match = useMatch(...args) - return match !== null -} diff --git a/packages/mask/dashboard/pages/Wallets/index.tsx b/packages/mask/dashboard/pages/Wallets/index.tsx deleted file mode 100644 index 9991026a7ea2..000000000000 --- a/packages/mask/dashboard/pages/Wallets/index.tsx +++ /dev/null @@ -1,141 +0,0 @@ -import { useEffect, useMemo, useState, useCallback } from 'react' -import { Route, Routes, useLocation, useNavigate } from 'react-router-dom' -import { getRegisteredWeb3Networks } from '@masknet/plugin-infra' -import { PluginTransakMessages } from '@masknet/plugin-transak' -import type { Web3Helper } from '@masknet/web3-helpers' -import { useChainContext, useNetworkDescriptor, useNetworkContext, useWallets } from '@masknet/web3-hooks-base' -import { DashboardRoutes, relativeRouteOf, CrossIsolationMessages, NetworkPluginID } from '@masknet/shared-base' -import { useRemoteControlledDialog } from '@masknet/shared-base-ui' -import { ChainId, type SchemaType } from '@masknet/web3-shared-evm' -import { ChainResolver } from '@masknet/web3-providers' -import type { FungibleToken, NonFungibleToken } from '@masknet/web3-shared-base' -import { useDashboardI18N } from '../../locales/index.js' -import { PageFrame } from '../../components/PageFrame/index.js' -import { Assets } from './components/Assets/index.js' -import { Balance } from './components/Balance/index.js' -import { History } from './components/History/index.js' -import { ReceiveDialog } from './components/ReceiveDialog/index.js' -import { Transfer, TransferTab } from './components/Transfer/index.js' -import { WalletStateBar } from './components/WalletStateBar/index.js' -import { useIsMatched } from './hooks/index.js' -import { Context } from './hooks/useContext.js' -import { StartUp } from './StartUp.js' - -const r = relativeRouteOf(DashboardRoutes.Wallets) - -function Wallets() { - const t = useDashboardI18N() - const navigate = useNavigate() - const { account, chainId } = useChainContext() - const wallets = useWallets() - - const { pathname, state } = useLocation() as { - state: { - token?: FungibleToken - nonFungibleToken?: NonFungibleToken - type?: TransferTab - } | null - pathname: string - } - const isWalletPath = useIsMatched(DashboardRoutes.Wallets) - const isWalletTransferPath = useIsMatched(DashboardRoutes.WalletsTransfer) - const isWalletHistoryPath = useIsMatched(DashboardRoutes.WalletsHistory) - - const [receiveOpen, setReceiveOpen] = useState(false) - - const { pluginID } = useNetworkContext() - const networks = getRegisteredWeb3Networks(pluginID).filter((x) => x.isMainnet) - - // If show one network only, set it as default network - const defaultNetwork = networks.length !== 1 ? null : networks[0] - - const networkDescriptor = useNetworkDescriptor( - NetworkPluginID.PLUGIN_EVM, - (state?.type === TransferTab.Token ? state?.token?.chainId : state?.nonFungibleToken?.chainId) ?? - ChainId.Mainnet, - ) - const [selectedNetwork, setSelectedNetwork] = useState( - networkDescriptor ?? null, - ) - - const { openDialog: openBuyDialog } = useRemoteControlledDialog(PluginTransakMessages.buyTokenDialogUpdated) - - const openSwapDialog = useCallback(() => { - CrossIsolationMessages.events.swapDialogEvent.sendToLocal({ - open: true, - }) - }, []) - - useEffect(() => { - if (isWalletPath) return - setSelectedNetwork(networkDescriptor || defaultNetwork) - }, [isWalletPath, networkDescriptor, defaultNetwork]) - - useEffect(() => { - if (isWalletTransferPath || isWalletHistoryPath) { - setSelectedNetwork(networkDescriptor || defaultNetwork) - return - } - setSelectedNetwork(defaultNetwork) - }, [pathname, defaultNetwork]) - - const pateTitle = useMemo(() => { - if (!account && wallets.length === 0) return t.create_wallet_form_title() - if (isWalletPath) return t.wallets_assets() - if (isWalletTransferPath) return t.wallets_transfer() - if (isWalletHistoryPath) return t.wallets_history() - - return t.wallets() - }, [isWalletPath, isWalletHistoryPath, isWalletTransferPath, account, wallets.length]) - - return ( - }> - {!account ? ( - - ) : ( - - - navigate(DashboardRoutes.WalletsTransfer, { - state: { - type: TransferTab.Token, - token: ChainResolver.nativeCurrency( - (selectedNetwork?.chainId ?? chainId) as ChainId, - ), - }, - }) - } - onBuy={openBuyDialog} - onSwap={openSwapDialog} - onReceive={() => setReceiveOpen(true)} - networks={networks} - selectedNetwork={selectedNetwork} - onSelectNetwork={setSelectedNetwork} - showOperations={pluginID === NetworkPluginID.PLUGIN_EVM} - /> - - } /> - } /> - } - /> - - - )} - {account ? ( - setReceiveOpen(false)} /> - ) : null} - - ) -} - -export default function () { - return -} diff --git a/packages/mask/dashboard/pages/routes.tsx b/packages/mask/dashboard/pages/routes.tsx index ea16ba83f90c..01334c4af7a2 100644 --- a/packages/mask/dashboard/pages/routes.tsx +++ b/packages/mask/dashboard/pages/routes.tsx @@ -1,6 +1,5 @@ import React, { lazy, Suspense } from 'react' import { Route, Routes, Navigate, HashRouter } from 'react-router-dom' -import { useCustomSnackbar } from '@masknet/theme' import { DashboardRoutes } from '@masknet/shared-base' import { useDashboardI18N } from '../locales/index.js' import { TermsGuard } from './TermsGuard.js' @@ -8,7 +7,6 @@ import { DashboardFrame } from '../components/DashboardFrame/index.js' import { Modals } from '../modals/index.js' const SetupPersona = lazy(() => import(/* webpackPrefetch: true */ './SetupPersona/index.js')) -const Wallets = lazy(() => import(/* webpackPrefetch: true */ './Wallets/index.js')) const SignUp = lazy(() => import('./SignUp/index.js')) const PrivacyPolicy = lazy(() => import('./PrivacyPolicy/index.js')) @@ -16,7 +14,6 @@ const CreateWallet = lazy(() => import('./CreateMaskWallet/index.js')) export function Pages() { const t = useDashboardI18N() - const { showSnackbar } = useCustomSnackbar() return ( @@ -26,7 +23,6 @@ export function Pages() { } /> } /> } /> - )} /> } /> } /> diff --git a/packages/theme/src/Components/PhoneNumberField/index.tsx b/packages/theme/src/Components/PhoneNumberField/index.tsx index 37d63a21737a..0225f063348b 100644 --- a/packages/theme/src/Components/PhoneNumberField/index.tsx +++ b/packages/theme/src/Components/PhoneNumberField/index.tsx @@ -11,6 +11,7 @@ import { type InputBaseProps, type PopoverProps, type Popper, + TextField, } from '@mui/material' import Fuse from 'fuse.js' import guessCallingCode from 'guess-calling-code' @@ -154,7 +155,7 @@ export function PhoneNumberField({ value, error, placeholder, onBlur, onChange } return ( <> - +