From a6d845390466e40db5b32558416b86017ce39ac6 Mon Sep 17 00:00:00 2001 From: unclebill Date: Thu, 31 Mar 2022 19:01:38 +0800 Subject: [PATCH 01/19] fix(tip): set current chain as default tip chain --- .../src/plugins/NextID/contexts/TargetChainIdContext.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/packages/mask/src/plugins/NextID/contexts/TargetChainIdContext.ts b/packages/mask/src/plugins/NextID/contexts/TargetChainIdContext.ts index 17df3f66514f..ad2d904a03a7 100644 --- a/packages/mask/src/plugins/NextID/contexts/TargetChainIdContext.ts +++ b/packages/mask/src/plugins/NextID/contexts/TargetChainIdContext.ts @@ -1,11 +1,15 @@ import { ChainId, useChainId } from '@masknet/web3-shared-evm' -import { useState } from 'react' +import { useEffect, useState } from 'react' import { createContainer } from 'unstated-next' function useTargetChainId() { const chainId = useChainId() const [targetChainId, setTargetChainId] = useState(chainId) + useEffect(() => { + setTargetChainId(chainId) + }, [chainId]) + return { targetChainId, setTargetChainId, From ef3282fc24864b7db0463b3399c97c3e0ab31129 Mon Sep 17 00:00:00 2001 From: unclebill Date: Wed, 30 Mar 2022 18:18:27 +0800 Subject: [PATCH 02/19] feat: tip NFT --- packages/icons/general/Edit2.tsx | 13 +++ packages/icons/general/index.ts | 1 + .../components/Tip/NFTSection/NFTList.tsx | 93 ++++++++-------- .../components/Tip/NFTSection/index.tsx | 87 ++++++--------- .../NextID/components/Tip/TipButton.tsx | 5 +- .../NextID/components/Tip/TipDialog.tsx | 104 ++++++++++++++++-- .../plugins/NextID/components/Tip/TipForm.tsx | 73 ++++++++---- .../NextID/contexts/Tip/useTipValidate.ts | 4 +- .../contexts/components/InjectedDialog.tsx | 15 ++- 9 files changed, 265 insertions(+), 130 deletions(-) create mode 100644 packages/icons/general/Edit2.tsx diff --git a/packages/icons/general/Edit2.tsx b/packages/icons/general/Edit2.tsx new file mode 100644 index 000000000000..0e05c13325dd --- /dev/null +++ b/packages/icons/general/Edit2.tsx @@ -0,0 +1,13 @@ +import { createIcon } from '../utils' +import type { SvgIcon } from '@mui/material' + +export const Edit2Icon: typeof SvgIcon = createIcon( + 'Edit2Icon', + + + , + '0 0 24 24', +) diff --git a/packages/icons/general/index.ts b/packages/icons/general/index.ts index a3d4ce6901cf..28e046e8bb0d 100644 --- a/packages/icons/general/index.ts +++ b/packages/icons/general/index.ts @@ -12,6 +12,7 @@ export * from './Link' export * from './Author' export * from './Settings' export * from './Edit' +export * from './Edit2' export * from './ArrowDownRound' export * from './ArrowUpRound' export * from './Empty' diff --git a/packages/mask/src/plugins/NextID/components/Tip/NFTSection/NFTList.tsx b/packages/mask/src/plugins/NextID/components/Tip/NFTSection/NFTList.tsx index f19c33b3bcbf..cd7c0d99b4ae 100644 --- a/packages/mask/src/plugins/NextID/components/Tip/NFTSection/NFTList.tsx +++ b/packages/mask/src/plugins/NextID/components/Tip/NFTSection/NFTList.tsx @@ -1,15 +1,15 @@ +import type { Web3Plugin } from '@masknet/plugin-infra' import { NFTCardStyledAssetPlayer } from '@masknet/shared' -import { makeStyles } from '@masknet/theme' -import { ERC721TokenDetailed, formatNFT_TokenId, useChainId } from '@masknet/web3-shared-evm' -import { Checkbox, List, ListItem, Radio, Typography } from '@mui/material' +import { makeStyles, ShadowRootTooltip } from '@masknet/theme' +import { formatNFT_TokenId, useChainId } from '@masknet/web3-shared-evm' +import { Checkbox, List, ListItem, Radio, Tooltip } from '@mui/material' import classnames from 'classnames' import { noop } from 'lodash-unified' import { FC, useCallback } from 'react' interface Props { selectedIds: string[] - tokens: ERC721TokenDetailed[] - enableTokenIds?: string[] + tokens: Web3Plugin.NonFungibleToken[] onChange?: (ids: string[]) => void limit?: number className: string @@ -21,6 +21,11 @@ const useStyles = makeStyles()((theme) => ({ right: 0, top: 0, }, + list: { + gridGap: 13, + display: 'grid', + gridTemplateColumns: 'repeat(5, 1fr)', + }, nftItem: { position: 'relative', cursor: 'pointer', @@ -30,9 +35,9 @@ const useStyles = makeStyles()((theme) => ({ padding: 0, flexDirection: 'column', borderRadius: 8, - height: 180, + height: 100, userSelect: 'none', - width: 120, + width: 100, justifyContent: 'center', }, disabled: { @@ -51,31 +56,26 @@ function arrayRemove(arr: T[], item: T): T[] { } interface NFTItemProps { - token: ERC721TokenDetailed + token: Web3Plugin.NonFungibleToken } export const NFTItem: FC = ({ token }) => { const { classes } = useStyles() const chainId = useChainId() return ( - <> - - - {formatNFT_TokenId(token.tokenId, 2)} - - + ) } -export const NFTList: FC = ({ selectedIds, tokens, enableTokenIds = [], onChange, limit = 1, className }) => { +export const NFTList: FC = ({ selectedIds, tokens, onChange, limit = 1, className }) => { const { classes } = useStyles() const isRadio = limit === 1 @@ -99,33 +99,38 @@ export const NFTList: FC = ({ selectedIds, tokens, enableTokenIds = [], o const SelectComponent = isRadio ? Radio : Checkbox return ( - + {tokens.map((token) => { - const isNotOwned = !enableTokenIds.includes(token.tokenId) - const disabled = (!isRadio && reachedLimit && !selectedIds.includes(token.tokenId)) || isNotOwned + const disabled = !isRadio && reachedLimit && !selectedIds.includes(token.tokenId) return ( - { - if (disabled) return - toggleItem(token.tokenId) - }}> - - + { if (disabled) return toggleItem(token.tokenId) - }} - className={classes.checkbox} - checked={selectedIds.includes(token.tokenId)} - /> - + }}> + + { + if (disabled) return + toggleItem(token.tokenId) + }} + className={classes.checkbox} + checked={selectedIds.includes(token.tokenId)} + /> + + ) })} diff --git a/packages/mask/src/plugins/NextID/components/Tip/NFTSection/index.tsx b/packages/mask/src/plugins/NextID/components/Tip/NFTSection/index.tsx index 8232f6a1b325..459142f9383e 100644 --- a/packages/mask/src/plugins/NextID/components/Tip/NFTSection/index.tsx +++ b/packages/mask/src/plugins/NextID/components/Tip/NFTSection/index.tsx @@ -1,14 +1,15 @@ +import { useNetworkDescriptor, useWeb3State as useWeb3PluginState, Web3Plugin } from '@masknet/plugin-infra' +import { EMPTY_LIST } from '@masknet/shared-base' import { makeStyles } from '@masknet/theme' -import { ERC721TokenDetailed, useAccount, useERC721TokenDetailedCallback } from '@masknet/web3-shared-evm' -import { useERC721TokenDetailedOwnerList } from '@masknet/web3-providers' -import { Button, FormControl, Typography } from '@mui/material' +import { useAccount } from '@masknet/web3-shared-evm' +import { FormControl } from '@mui/material' import classnames from 'classnames' -import { FC, HTMLProps, useCallback, useMemo, useState } from 'react' -import { SearchInput } from '../../../../../extension/options-page/DashboardComponents/SearchInput' +import { FC, HTMLProps, useEffect, useMemo, useState } from 'react' +import { useAsyncRetry } from 'react-use' import { ERC721ContractSelectPanel } from '../../../../../web3/UI/ERC721ContractSelectPanel' import { TargetChainIdContext, useTip } from '../../../contexts' -import { NFTList } from './NFTList' import { useI18N } from '../../../locales' +import { NFTList } from './NFTList' export * from './NFTList' @@ -27,14 +28,10 @@ const useStyles = makeStyles()((theme) => ({ list: { flexGrow: 1, marginTop: theme.spacing(2), - display: 'grid', - gridTemplateColumns: 'repeat(4, 1fr)', maxHeight: 400, overflow: 'auto', - gridGap: 18, backgroundColor: theme.palette.background.default, borderRadius: 4, - padding: theme.spacing(1), }, keyword: { borderRadius: 8, @@ -62,21 +59,27 @@ export const NFTSection: FC = ({ className, ...rest }) => { const t = useI18N() const { targetChainId: chainId } = TargetChainIdContext.useContainer() const { erc721Contract, setErc721Contract, erc721TokenId, setErc721TokenId, isSending } = useTip() - const [tokenId, setTokenId, erc721TokenDetailedCallback] = useERC721TokenDetailedCallback(erc721Contract) const { classes } = useStyles() const account = useAccount() - const { tokenDetailedOwnerList: myTokens = [] } = useERC721TokenDetailedOwnerList(erc721Contract, account) const selectedIds = useMemo(() => (erc721TokenId ? [erc721TokenId] : []), [erc721TokenId]) - const [searchedToken, setSearchedToken] = useState(null) - const onSearch = useCallback(async () => { - const token = await erc721TokenDetailedCallback() - setSearchedToken(token?.info.owner ? token : null) - }, [erc721TokenDetailedCallback]) + const { Asset } = useWeb3PluginState() + + const [page, setPage] = useState(0) + + const networkDescriptor = useNetworkDescriptor() + const [selectedNetwork, setSelectedNetwork] = useState( + networkDescriptor ?? null, + ) + const { value = { data: EMPTY_LIST, hasNextPage: false }, retry } = useAsyncRetry( + async () => Asset?.getNonFungibleAssets?.(account, { page, size: 20 }, undefined, selectedNetwork || undefined), + [account, Asset?.getNonFungibleAssets, selectedNetwork], + ) - const tokens = useMemo(() => (searchedToken ? [searchedToken] : myTokens), [searchedToken, myTokens]) - const enableTokenIds = useMemo(() => myTokens.map((t) => t.tokenId), [myTokens]) + console.log('nft data', value.data) + + useEffect(retry, [chainId]) return (
@@ -88,42 +91,16 @@ export const NFTSection: FC = ({ className, ...rest }) => { onContractChange={setErc721Contract} /> - {erc721Contract ? ( -
- - setTokenId(id)} - inputBaseProps={{ - disabled: isSending, - }} - label="" - /> - - - { - setErc721TokenId(ids.length ? ids[0] : null) - }} - /> -
- ) : null} - {tokens.length === 1 && !enableTokenIds.includes(tokens[0].tokenId) ? ( - - {t.nft_not_belong_to_you()} - - ) : null} +
+ { + setErc721TokenId(ids.length ? ids[0] : null) + }} + /> +
) } diff --git a/packages/mask/src/plugins/NextID/components/Tip/TipButton.tsx b/packages/mask/src/plugins/NextID/components/Tip/TipButton.tsx index 227c540593c9..032abdb963aa 100644 --- a/packages/mask/src/plugins/NextID/components/Tip/TipButton.tsx +++ b/packages/mask/src/plugins/NextID/components/Tip/TipButton.tsx @@ -95,7 +95,10 @@ export const TipButton: FC = ({ return uniq([...(walletsState.value || []), ...addresses]) }, [walletsState.value, addresses]) - const disabled = loadingPersona || loadingVerifyInfo || !isAccountVerified || allAddresses.length === 0 + if (receiver?.userId === 'suji_yan') { + console.log('suji_yan', { receiverPersona, isAccountVerified, allAddresses: allAddresses.length }) + } + const disabled = loadingPersona || loadingVerifyInfo || allAddresses.length === 0 const sendTip: MouseEventHandler = useCallback( async (evt) => { diff --git a/packages/mask/src/plugins/NextID/components/Tip/TipDialog.tsx b/packages/mask/src/plugins/NextID/components/Tip/TipDialog.tsx index aa6a349703ba..038ec3eb53d9 100644 --- a/packages/mask/src/plugins/NextID/components/Tip/TipDialog.tsx +++ b/packages/mask/src/plugins/NextID/components/Tip/TipDialog.tsx @@ -1,15 +1,25 @@ -import { SuccessIcon } from '@masknet/icons' -import { PluginId, useActivatedPlugin, useCurrentWeb3NetworkPluginID } from '@masknet/plugin-infra' -import { InjectedDialog, NFTCardStyledAssetPlayer } from '@masknet/shared' +import { Edit2Icon, LinkOutIcon, SuccessIcon } from '@masknet/icons' +import { + PluginId, + useActivatedPlugin, + useCurrentWeb3NetworkPluginID, + useNetworkDescriptor, + useProviderDescriptor, + useReverseAddress, + useWeb3State, +} from '@masknet/plugin-infra' +import { InjectedDialog, NFTCardStyledAssetPlayer, WalletIcon } from '@masknet/shared' import { EMPTY_LIST } from '@masknet/shared-base' -import { openWindow } from '@masknet/shared-base-ui' +import { openWindow, useRemoteControlledDialog } from '@masknet/shared-base-ui' import { makeStyles } from '@masknet/theme' -import { TransactionStateType, useChainId, useERC721TokenDetailed } from '@masknet/web3-shared-evm' -import { DialogContent, Typography } from '@mui/material' +import { TransactionStateType, useAccount, useChainId, useERC721TokenDetailed } from '@masknet/web3-shared-evm' +import { DialogContent, Link, Typography } from '@mui/material' import { useCallback, useEffect, useMemo } from 'react' import { useBoolean } from 'react-use' +import { hasNativeAPI, nativeAPI } from '../../../../../shared/native-rpc' import { NetworkTab } from '../../../../components/shared/NetworkTab' import { activatedSocialNetworkUI } from '../../../../social-network' +import { WalletMessages } from '../../../Wallet/messages' import { TargetChainIdContext, useTip } from '../../contexts' import { useI18N } from '../../locales' import { TipType } from '../../types' @@ -69,6 +79,43 @@ const useStyles = makeStyles()((theme) => ({ width: 64, height: 64, }, + walletChip: { + marginLeft: 'auto', + display: 'flex', + alignItems: 'center', + backgroundColor: theme.palette.background.default, + padding: theme.spacing(0.5, 1), + borderRadius: 99, + }, + wallet: { + marginLeft: theme.spacing(1), + }, + walletTitle: { + marginLeft: theme.spacing(1), + fontSize: 14, + fontWeight: 'bold', + }, + walletAddress: { + fontSize: 10, + color: theme.palette.secondary.main, + cursor: 'pointer', + }, + changeWalletButton: { + marginLeft: theme.spacing(1), + display: 'flex', + alignItems: 'center', + }, + link: { + '&:hover': { + textDecoration: 'none', + }, + }, + linkIcon: { + fill: 'none', + width: 12, + height: 12, + marginLeft: theme.spacing(0.5), + }, })) interface TipDialogProps { @@ -147,10 +194,53 @@ export function TipDialog({ open = false, onClose }: TipDialogProps) { openConfirmModal(false) onClose?.() }, [shareLink, onClose]) + const networkDescriptor = useNetworkDescriptor() + const providerDescriptor = useProviderDescriptor() + + const { Utils } = useWeb3State() + const account = useAccount() + const { value: domain } = useReverseAddress(account) + const walletTitle = + Utils?.formatDomainName?.(domain) || Utils?.formatAddress?.(account, 4) || providerDescriptor?.name + + // #region Wallet + const { openDialog: openWalletStatusDialog } = useRemoteControlledDialog( + WalletMessages.events.walletStatusDialogUpdated, + ) + // #endregion + const openWallet = useCallback(() => { + if (hasNativeAPI) return nativeAPI?.api.misc_openCreateWalletView() + return openWalletStatusDialog() + }, [openWalletStatusDialog, hasNativeAPI]) + + const walletChip = ( +
+ +
+ + {walletTitle} + + + + + {Utils?.formatAddress?.(account, 4)} + + + +
+
+ +
+
+ ) return ( <> - +
{ return { @@ -57,6 +69,7 @@ interface Props extends BoxProps {} export const TipForm: FC = memo(({ className, ...rest }) => { const t = useI18N() + const currentChainId = useChainId() const { targetChainId: chainId } = TargetChainIdContext.useContainer() const { classes } = useStyles() const { @@ -69,6 +82,8 @@ export const TipForm: FC = memo(({ className, ...rest }) => { setAmount, isSending, sendTip, + tipType, + setTipType, } = useTip() const [isValid, validateMessage] = useTipValidate() const { Utils } = useWeb3State() @@ -130,24 +145,44 @@ export const TipForm: FC = memo(({ className, ...rest }) => { ))} - - + + setTipType(e.target.value as TipType)}> + } + label={t.tip_type_token()} + /> + } + label={t.tip_type_nft()} + /> + + {tipType === TipType.Token ? ( + + + + ) : ( + + )}
{account ? ( > onClose?(): void title?: React.ReactChild + titleTail?: React.ReactChild disableBackdropClick?: boolean disableTitleBorder?: boolean titleBarIconStyle?: 'auto' | 'back' | 'close' @@ -78,8 +79,17 @@ export function InjectedDialog(props: InjectedDialogProps) { const t = useSharedI18N() const fullScreen = useMediaQuery(useTheme().breakpoints.down('xs')) const isDashboard = isDashboardPage() - const { children, open, disableBackdropClick, titleBarIconStyle, onClose, title, disableTitleBorder, ...rest } = - props + const { + children, + open, + disableBackdropClick, + titleBarIconStyle, + onClose, + title, + titleTail = null, + disableTitleBorder, + ...rest + } = props const actions = CopyElementWithNewProps(children, DialogActions, { root: dialogActions }) const content = CopyElementWithNewProps(children, DialogContent, { root: dialogContent }) const { extraProps, shouldReplaceExitWithBack, IncreaseStack } = useDialogStackActor(open) @@ -128,6 +138,7 @@ export function InjectedDialog(props: InjectedDialogProps) { {title} + {titleTail} ) : null} {/* There is a .MuiDialogTitle+.MuiDialogContent selector that provides paddingTop: 0 */} From 32c6667dfe3cf0b474fa98b815a93525a31d8b40 Mon Sep 17 00:00:00 2001 From: unclebill Date: Fri, 1 Apr 2022 14:30:41 +0800 Subject: [PATCH 03/19] fix: add token --- .../UI/Web3State/createNonFungibleToken.ts | 27 ++++ .../plugins/EVM/UI/Web3State/getAssetsFn.ts | 57 +++----- .../src/plugins/NextID/SNSAdaptor/index.tsx | 5 +- .../NextID/components/Tip/AddDialog.tsx | 122 ++++++++++++++++++ .../components/Tip/NFTSection/NFTList.tsx | 51 ++++---- .../components/Tip/NFTSection/index.tsx | 57 +++----- .../NextID/components/Tip/TipButton.tsx | 5 +- .../NextID/components/Tip/TipDialog.tsx | 49 +++++-- .../plugins/NextID/components/Tip/TipForm.tsx | 30 ++++- .../plugins/NextID/contexts/Tip/TipContext.ts | 9 +- .../NextID/contexts/Tip/TipTaskProvider.tsx | 35 ++++- .../plugins/NextID/contexts/Tip/useNftTip.ts | 19 +-- .../NextID/contexts/Tip/useTipValidate.ts | 6 +- .../src/plugins/NextID/locales/en-US.json | 7 +- .../mask/src/plugins/NextID/storage/index.ts | 36 ++++++ .../src/UI/components/ImageIcon/index.tsx | 2 +- packages/web3-shared/evm/utils/transaction.ts | 4 +- 17 files changed, 373 insertions(+), 148 deletions(-) create mode 100644 packages/mask/src/plugins/EVM/UI/Web3State/createNonFungibleToken.ts create mode 100644 packages/mask/src/plugins/NextID/components/Tip/AddDialog.tsx create mode 100644 packages/mask/src/plugins/NextID/storage/index.ts diff --git a/packages/mask/src/plugins/EVM/UI/Web3State/createNonFungibleToken.ts b/packages/mask/src/plugins/EVM/UI/Web3State/createNonFungibleToken.ts new file mode 100644 index 000000000000..218ba80a7734 --- /dev/null +++ b/packages/mask/src/plugins/EVM/UI/Web3State/createNonFungibleToken.ts @@ -0,0 +1,27 @@ +import { TokenType, Web3Plugin } from '@masknet/plugin-infra' +import type { ERC721TokenDetailed } from '@masknet/web3-shared-evm' + +export function createNonFungibleToken(token: ERC721TokenDetailed) { + return { + ...token, + id: `${token.contractDetailed.address}_${token.tokenId}`, + tokenId: token.tokenId, + chainId: token.contractDetailed.chainId, + type: TokenType.NonFungible, + name: token.info.name ?? `${token.contractDetailed.name} ${token.tokenId}`, + description: token.info.description ?? '', + owner: token.info.owner, + contract: { + ...token.contractDetailed, + type: TokenType.NonFungible, + id: token.contractDetailed.address, + }, + metadata: { + name: token.info.name ?? `${token.contractDetailed.name} ${token.tokenId}`, + description: token.info.description ?? '', + mediaType: 'Unknown', + iconURL: token.contractDetailed.iconURL, + assetURL: token.info.mediaUrl, + }, + } as Web3Plugin.NonFungibleToken +} diff --git a/packages/mask/src/plugins/EVM/UI/Web3State/getAssetsFn.ts b/packages/mask/src/plugins/EVM/UI/Web3State/getAssetsFn.ts index 69f40729cae1..f767bbfb913f 100644 --- a/packages/mask/src/plugins/EVM/UI/Web3State/getAssetsFn.ts +++ b/packages/mask/src/plugins/EVM/UI/Web3State/getAssetsFn.ts @@ -1,33 +1,34 @@ -import Web3 from 'web3' +import { Pageable, Pagination, TokenType, Web3Plugin } from '@masknet/plugin-infra' +import BalanceCheckerABI from '@masknet/web3-contracts/abis/BalanceChecker.json' +import ERC721ABI from '@masknet/web3-contracts/abis/ERC721.json' +import type { ERC721 } from '@masknet/web3-contracts/types/ERC721' +import { TokenPrice } from '@masknet/web3-providers' +import { pow10 } from '@masknet/web3-shared-base' import { + ChainId, createContract, + createExternalProvider, createNativeToken, + CurrencyType, ERC721TokenDetailed, formatEthereumAddress, - getERC721TokenDetailedFromChain, + FungibleAssetProvider, + getCoinGeckoCoinId, getERC721TokenAssetFromChain, + getERC721TokenDetailedFromChain, getEthereumConstants, isSameAddress, - Web3ProviderType, - FungibleAssetProvider, - createExternalProvider, - getCoinGeckoCoinId, - CurrencyType, PriceRecord, - ChainId, + Web3ProviderType, } from '@masknet/web3-shared-evm' import BigNumber from 'bignumber.js' -import { Pageable, Pagination, TokenType, Web3Plugin } from '@masknet/plugin-infra' -import BalanceCheckerABI from '@masknet/web3-contracts/abis/BalanceChecker.json' -import ERC721ABI from '@masknet/web3-contracts/abis/ERC721.json' -import type { AbiItem } from 'web3-utils' import { uniqBy } from 'lodash-unified' +import Web3 from 'web3' +import type { AbiItem } from 'web3-utils' import { PLUGIN_NETWORKS } from '../../constants' import { makeSortAssertWithoutChainFn } from '../../utils/token' import { createGetLatestBalance } from './createGetLatestBalance' -import type { ERC721 } from '@masknet/web3-contracts/types/ERC721' -import { pow10 } from '@masknet/web3-shared-base' -import { TokenPrice } from '@masknet/web3-providers' +import { createNonFungibleToken } from './createNonFungibleToken' // tokens unavailable neither from api or balance checker. // https://forum.conflux.fun/t/how-to-upvote-debank-proposal-for-conflux-espace-integration/13935 @@ -215,31 +216,7 @@ export const getNonFungibleTokenFn = const tokenFromProvider = socket.getResult(socketId) const allData: Web3Plugin.NonFungibleToken[] = [...tokenInDb, ...tokenFromProvider] - .map( - (x) => - ({ - ...x, - id: `${x.contractDetailed.address}_${x.tokenId}`, - tokenId: x.tokenId, - chainId: x.contractDetailed.chainId, - type: TokenType.NonFungible, - name: x.info.name ?? `${x.contractDetailed.name} ${x.tokenId}`, - description: x.info.description ?? '', - owner: x.info.owner, - contract: { - ...x.contractDetailed, - type: TokenType.NonFungible, - id: x.contractDetailed.address, - }, - metadata: { - name: x.info.name ?? `${x.contractDetailed.name} ${x.tokenId}`, - description: x.info.description ?? '', - mediaType: 'Unknown', - iconURL: x.contractDetailed.iconURL, - assetURL: x.info.mediaUrl, - }, - } as Web3Plugin.NonFungibleToken), - ) + .map(createNonFungibleToken) .filter((x) => isSameAddress(x.owner, address)) .filter((x) => !network || x.chainId === network.chainId) diff --git a/packages/mask/src/plugins/NextID/SNSAdaptor/index.tsx b/packages/mask/src/plugins/NextID/SNSAdaptor/index.tsx index c888a6006157..f95c8b0dc1fb 100644 --- a/packages/mask/src/plugins/NextID/SNSAdaptor/index.tsx +++ b/packages/mask/src/plugins/NextID/SNSAdaptor/index.tsx @@ -6,10 +6,13 @@ import { NextIdPage } from '../components/NextIdPage' import { PostTipButton, TipTaskManager } from '../components/Tip' import { PLUGIN_ID } from '../constants' import { RootContext } from '../contexts' +import { setupStorage, storageDefaultValue } from '../storage' const sns: Plugin.SNSAdaptor.Definition = { ...base, - init() {}, + init(signal, context) { + setupStorage(context.createKVStorage('memory', storageDefaultValue)) + }, ProfileTabs: [ { ID: `${PLUGIN_ID}_tabContent`, diff --git a/packages/mask/src/plugins/NextID/components/Tip/AddDialog.tsx b/packages/mask/src/plugins/NextID/components/Tip/AddDialog.tsx new file mode 100644 index 000000000000..f79640d3b0a8 --- /dev/null +++ b/packages/mask/src/plugins/NextID/components/Tip/AddDialog.tsx @@ -0,0 +1,122 @@ +import { useNetworkDescriptors, Web3Plugin } from '@masknet/plugin-infra' +import { ImageIcon, InjectedDialog, InjectedDialogProps } from '@masknet/shared' +import { makeStyles } from '@masknet/theme' +import { + isSameAddress, + useAccount, + useChainId, + useERC721ContractDetailed, + useERC721TokenContract, + useERC721TokenDetailed, +} from '@masknet/web3-shared-evm' +import { Button, DialogContent, FormControl, TextField, Typography } from '@mui/material' +import { FC, useMemo, useState } from 'react' +import { useAsync } from 'react-use' +import { createNonFungibleToken } from '../../../EVM/UI/Web3State/createNonFungibleToken' +import { useI18N } from '../../locales' + +const useStyles = makeStyles()((theme) => ({ + addButton: { + marginLeft: 'auto', + }, + chain: { + display: 'flex', + }, + row: { + marginTop: theme.spacing(1.5), + }, + chainName: { + marginLeft: theme.spacing(1), + }, + error: { + display: 'flex', + height: 16, + alignItems: 'center', + color: theme.palette.error.main, + '&::before': { + backgroundColor: theme.palette.error.main, + content: '""', + width: 2, + height: 16, + marginRight: theme.spacing(0.5), + }, + }, +})) + +interface Props extends InjectedDialogProps { + onAdd?(token: Web3Plugin.NonFungibleToken): void +} + +export const AddDialog: FC = ({ onAdd, ...rest }) => { + const { classes } = useStyles() + const chainId = useChainId() + const account = useAccount() + const [contractAddress, setContractAddress] = useState('') + const [tokenId, setTokenId] = useState('') + const t = useI18N() + const allNetworks = useNetworkDescriptors() + const network = useMemo(() => allNetworks.find((n) => n.chainId === chainId), [allNetworks, chainId]) + const contract = useERC721TokenContract(contractAddress) + const { value: isMine, loading: isChecking } = useAsync(async () => { + console.log('contract', contract) + if (!contract || !account) return false + const ownerAddress = await contract.methods.ownerOf(tokenId).call() + console.log('ownerAddress', ownerAddress) + return isSameAddress(ownerAddress, account) + }, [contract, tokenId, account]) + + const isStable = !isChecking && !!contractAddress && !!tokenId + const { value: detailedContract } = useERC721ContractDetailed(contractAddress) + const { tokenDetailed } = useERC721TokenDetailed(detailedContract, tokenId) + const nonFungibleToken = useMemo( + () => (tokenDetailed ? createNonFungibleToken(tokenDetailed) : null), + [tokenDetailed], + ) + + const addButton = useMemo(() => { + return ( + + ) + }, [t, isMine, nonFungibleToken]) + + if (!network) return null + return ( + + +
+ + {network.name} +
+ + setContractAddress(e.currentTarget.value)} + placeholder={t.tip_add_collectibles_contract_address()} + /> + + + setTokenId(e.currentTarget.value)} + placeholder={t.tip_add_collectibles_token_id()} + /> + + {isStable && !isMine ? ( + + {t.tip_add_collectibles_error()} + + ) : null} +
+
+ ) +} diff --git a/packages/mask/src/plugins/NextID/components/Tip/NFTSection/NFTList.tsx b/packages/mask/src/plugins/NextID/components/Tip/NFTSection/NFTList.tsx index cd7c0d99b4ae..1a2fdd76ed17 100644 --- a/packages/mask/src/plugins/NextID/components/Tip/NFTSection/NFTList.tsx +++ b/packages/mask/src/plugins/NextID/components/Tip/NFTSection/NFTList.tsx @@ -2,7 +2,7 @@ import type { Web3Plugin } from '@masknet/plugin-infra' import { NFTCardStyledAssetPlayer } from '@masknet/shared' import { makeStyles, ShadowRootTooltip } from '@masknet/theme' import { formatNFT_TokenId, useChainId } from '@masknet/web3-shared-evm' -import { Checkbox, List, ListItem, Radio, Tooltip } from '@mui/material' +import { Checkbox, List, ListItem, Radio } from '@mui/material' import classnames from 'classnames' import { noop } from 'lodash-unified' import { FC, useCallback } from 'react' @@ -10,7 +10,7 @@ import { FC, useCallback } from 'react' interface Props { selectedIds: string[] tokens: Web3Plugin.NonFungibleToken[] - onChange?: (ids: string[]) => void + onChange?: (id: string, contractAddress: string) => void limit?: number className: string } @@ -44,17 +44,30 @@ const useStyles = makeStyles()((theme) => ({ opacity: 0.5, cursor: 'not-allowed', }, + selected: { + position: 'relative', + '&::after': { + position: 'absolute', + border: `2px solid ${theme.palette.primary.main}`, + content: '""', + left: 0, + top: 0, + pointerEvents: 'none', + boxSizing: 'border-box', + width: '100%', + height: '100%', + borderRadius: 8, + }, + }, + unselected: { + opacity: 0.5, + }, loadingFailImage: { width: 64, height: 64, }, })) -function arrayRemove(arr: T[], item: T): T[] { - const idx = arr.indexOf(item) - return arr.splice(idx, 1) -} - interface NFTItemProps { token: Web3Plugin.NonFungibleToken } @@ -82,16 +95,8 @@ export const NFTList: FC = ({ selectedIds, tokens, onChange, limit = 1, c const reachedLimit = selectedIds.length >= limit const toggleItem = useCallback( - (currentId: string) => { - if (!onChange) return - if (isRadio) return onChange([currentId]) - let newIds = [...selectedIds] - if (selectedIds.includes(currentId)) { - arrayRemove(newIds, currentId) - } else if (!reachedLimit) { - newIds = [...selectedIds, currentId] - } - onChange(newIds) + (currentId: string, contractAddress: string) => { + onChange?.(currentId, contractAddress) }, [selectedIds, onChange, isRadio, reachedLimit], ) @@ -105,17 +110,19 @@ export const NFTList: FC = ({ selectedIds, tokens, onChange, limit = 1, c return ( 0 && !selectedIds.includes(token.tokenId), })} onClick={() => { - if (disabled) return - toggleItem(token.tokenId) + if (disabled || !token.contract?.address) return + toggleItem(token.tokenId, token.contract.address) }}> = ({ selectedIds, tokens, onChange, limit = 1, c onChange={noop} disabled={disabled} onClick={() => { - if (disabled) return - toggleItem(token.tokenId) + if (disabled || !token.contract?.address) return + toggleItem(token.tokenId, token.contract.address) }} className={classes.checkbox} checked={selectedIds.includes(token.tokenId)} diff --git a/packages/mask/src/plugins/NextID/components/Tip/NFTSection/index.tsx b/packages/mask/src/plugins/NextID/components/Tip/NFTSection/index.tsx index 459142f9383e..932f9d7c27b4 100644 --- a/packages/mask/src/plugins/NextID/components/Tip/NFTSection/index.tsx +++ b/packages/mask/src/plugins/NextID/components/Tip/NFTSection/index.tsx @@ -1,14 +1,12 @@ -import { useNetworkDescriptor, useWeb3State as useWeb3PluginState, Web3Plugin } from '@masknet/plugin-infra' +import { useNetworkDescriptor, useWeb3State as useWeb3PluginState } from '@masknet/plugin-infra' import { EMPTY_LIST } from '@masknet/shared-base' import { makeStyles } from '@masknet/theme' import { useAccount } from '@masknet/web3-shared-evm' -import { FormControl } from '@mui/material' import classnames from 'classnames' -import { FC, HTMLProps, useEffect, useMemo, useState } from 'react' +import { uniqWith } from 'lodash-unified' +import { FC, HTMLProps, useEffect, useMemo } from 'react' import { useAsyncRetry } from 'react-use' -import { ERC721ContractSelectPanel } from '../../../../../web3/UI/ERC721ContractSelectPanel' import { TargetChainIdContext, useTip } from '../../../contexts' -import { useI18N } from '../../../locales' import { NFTList } from './NFTList' export * from './NFTList' @@ -30,21 +28,8 @@ const useStyles = makeStyles()((theme) => ({ marginTop: theme.spacing(2), maxHeight: 400, overflow: 'auto', - backgroundColor: theme.palette.background.default, borderRadius: 4, }, - keyword: { - borderRadius: 8, - marginRight: theme.spacing(1.5), - }, - searchButton: { - borderRadius: 8, - width: 100, - }, - row: { - display: 'flex', - flexDirection: 'row', - }, errorMessage: { marginTop: theme.spacing(3), fontSize: 12, @@ -56,9 +41,8 @@ const useStyles = makeStyles()((theme) => ({ interface Props extends HTMLProps {} export const NFTSection: FC = ({ className, ...rest }) => { - const t = useI18N() const { targetChainId: chainId } = TargetChainIdContext.useContainer() - const { erc721Contract, setErc721Contract, erc721TokenId, setErc721TokenId, isSending } = useTip() + const { storedTokens, erc721TokenId, setErc721TokenId, setErc721Address } = useTip() const { classes } = useStyles() const account = useAccount() @@ -66,38 +50,31 @@ export const NFTSection: FC = ({ className, ...rest }) => { const { Asset } = useWeb3PluginState() - const [page, setPage] = useState(0) - const networkDescriptor = useNetworkDescriptor() - const [selectedNetwork, setSelectedNetwork] = useState( - networkDescriptor ?? null, - ) + const { value = { data: EMPTY_LIST, hasNextPage: false }, retry } = useAsyncRetry( - async () => Asset?.getNonFungibleAssets?.(account, { page, size: 20 }, undefined, selectedNetwork || undefined), - [account, Asset?.getNonFungibleAssets, selectedNetwork], + async () => Asset?.getNonFungibleAssets?.(account, { page: 0, size: 1000 }, undefined, networkDescriptor), + [account, Asset?.getNonFungibleAssets, networkDescriptor], ) - console.log('nft data', value.data) - useEffect(retry, [chainId]) + const tokens = useMemo(() => { + return uniqWith([...storedTokens, ...value.data], (v1, v2) => { + return v1.contract?.address === v2.contract?.address && v1.tokenId === v2.tokenId + }) + }, [storedTokens, value.data]) + return (
- - -
{ - setErc721TokenId(ids.length ? ids[0] : null) + tokens={tokens} + onChange={(id, address) => { + setErc721TokenId(id) + setErc721Address(address) }} />
diff --git a/packages/mask/src/plugins/NextID/components/Tip/TipButton.tsx b/packages/mask/src/plugins/NextID/components/Tip/TipButton.tsx index 032abdb963aa..227c540593c9 100644 --- a/packages/mask/src/plugins/NextID/components/Tip/TipButton.tsx +++ b/packages/mask/src/plugins/NextID/components/Tip/TipButton.tsx @@ -95,10 +95,7 @@ export const TipButton: FC = ({ return uniq([...(walletsState.value || []), ...addresses]) }, [walletsState.value, addresses]) - if (receiver?.userId === 'suji_yan') { - console.log('suji_yan', { receiverPersona, isAccountVerified, allAddresses: allAddresses.length }) - } - const disabled = loadingPersona || loadingVerifyInfo || allAddresses.length === 0 + const disabled = loadingPersona || loadingVerifyInfo || !isAccountVerified || allAddresses.length === 0 const sendTip: MouseEventHandler = useCallback( async (evt) => { diff --git a/packages/mask/src/plugins/NextID/components/Tip/TipDialog.tsx b/packages/mask/src/plugins/NextID/components/Tip/TipDialog.tsx index 038ec3eb53d9..331218db8061 100644 --- a/packages/mask/src/plugins/NextID/components/Tip/TipDialog.tsx +++ b/packages/mask/src/plugins/NextID/components/Tip/TipDialog.tsx @@ -7,10 +7,11 @@ import { useProviderDescriptor, useReverseAddress, useWeb3State, + Web3Plugin, } from '@masknet/plugin-infra' import { InjectedDialog, NFTCardStyledAssetPlayer, WalletIcon } from '@masknet/shared' import { EMPTY_LIST } from '@masknet/shared-base' -import { openWindow, useRemoteControlledDialog } from '@masknet/shared-base-ui' +import { useRemoteControlledDialog } from '@masknet/shared-base-ui' import { makeStyles } from '@masknet/theme' import { TransactionStateType, useAccount, useChainId, useERC721TokenDetailed } from '@masknet/web3-shared-evm' import { DialogContent, Link, Typography } from '@mui/material' @@ -24,7 +25,9 @@ import { TargetChainIdContext, useTip } from '../../contexts' import { useI18N } from '../../locales' import { TipType } from '../../types' import { ConfirmModal } from '../ConfirmModal' +import { AddDialog } from './AddDialog' import { TipForm } from './TipForm' +import { storeToken } from '../../storage' const useStyles = makeStyles()((theme) => ({ content: { @@ -81,6 +84,7 @@ const useStyles = makeStyles()((theme) => ({ }, walletChip: { marginLeft: 'auto', + height: 40, display: 'flex', alignItems: 'center', backgroundColor: theme.palette.background.default, @@ -97,15 +101,19 @@ const useStyles = makeStyles()((theme) => ({ }, walletAddress: { fontSize: 10, - color: theme.palette.secondary.main, + color: theme.palette.text.secondary, cursor: 'pointer', + display: 'flex', + alignItems: 'center', }, changeWalletButton: { marginLeft: theme.spacing(1), display: 'flex', alignItems: 'center', + cursor: 'pointer', }, link: { + cursor: 'pointer', '&:hover': { textDecoration: 'none', }, @@ -130,12 +138,24 @@ export function TipDialog({ open = false, onClose }: TipDialogProps) { const t = useI18N() const { classes } = useStyles() + const [addTokenDialogIsOpen, openAddTokenDialog] = useBoolean(false) const [confirmModalIsOpen, openConfirmModal] = useBoolean(false) const { targetChainId, setTargetChainId } = TargetChainIdContext.useContainer() - const { tipType, amount, token, recipientSnsId, recipient, sendState, erc721Contract, erc721TokenId } = useTip() + const { + tipType, + amount, + token, + recipientSnsId, + recipient, + sendState, + erc721Contract, + erc721TokenId, + setErc721Address, + setErc721TokenId, + } = useTip() const isTokenTip = tipType === TipType.Token - const shareLink = useMemo(() => { + const shareText = useMemo(() => { const promote = t.tip_mask_promote() const message = isTokenTip ? t.tip_token_share_post({ @@ -151,7 +171,7 @@ export function TipDialog({ open = false, onClose }: TipDialogProps) { recipient, promote, }) - return activatedSocialNetworkUI.utils.getShareLinkURL?.(message) + return message }, [amount, isTokenTip, erc721Contract?.name, token, recipient, recipientSnsId, t]) const { tokenDetailed: erc721Token } = useERC721TokenDetailed(erc721Contract, erc721TokenId) @@ -190,10 +210,10 @@ export function TipDialog({ open = false, onClose }: TipDialogProps) { }, [sendState.type]) const handleConfirm = useCallback(() => { - openWindow(shareLink) + activatedSocialNetworkUI.utils.share?.(shareText) openConfirmModal(false) onClose?.() - }, [shareLink, onClose]) + }, [shareText, onClose]) const networkDescriptor = useNetworkDescriptor() const providerDescriptor = useProviderDescriptor() @@ -213,6 +233,13 @@ export function TipDialog({ open = false, onClose }: TipDialogProps) { return openWalletStatusDialog() }, [openWalletStatusDialog, hasNativeAPI]) + const handleAddToken = useCallback((token: Web3Plugin.NonFungibleToken) => { + setErc721Address(token.contract?.address ?? '') + setErc721TokenId(token.tokenId) + storeToken(token) + openAddTokenDialog(false) + }, []) + const walletChip = (
@@ -250,18 +277,22 @@ export function TipDialog({ open = false, onClose }: TipDialogProps) { chains={chainIdList} />
- + openAddTokenDialog(true)} /> openConfirmModal(false)} + onClose={() => { + openConfirmModal(false) + onClose?.() + }} icon={isTokenTip ? : null} message={successMessage} confirmText={t.tip_share()} onConfirm={handleConfirm} /> + openAddTokenDialog(false)} onAdd={handleAddToken} /> ) } diff --git a/packages/mask/src/plugins/NextID/components/Tip/TipForm.tsx b/packages/mask/src/plugins/NextID/components/Tip/TipForm.tsx index 37b4ed7ff440..391aec277fff 100644 --- a/packages/mask/src/plugins/NextID/components/Tip/TipForm.tsx +++ b/packages/mask/src/plugins/NextID/components/Tip/TipForm.tsx @@ -3,10 +3,11 @@ import { WalletMessages } from '@masknet/plugin-wallet' import { usePickToken } from '@masknet/shared' import { useRemoteControlledDialog } from '@masknet/shared-base-ui' import { makeStyles } from '@masknet/theme' -import { EthereumTokenType, useAccount, useChainId, useFungibleTokenBalance } from '@masknet/web3-shared-evm' +import { ChainId, EthereumTokenType, useAccount, useChainId, useFungibleTokenBalance } from '@masknet/web3-shared-evm' import { Box, BoxProps, + Button, FormControl, FormControlLabel, MenuItem, @@ -59,15 +60,25 @@ const useStyles = makeStyles()((theme) => { borderRadius: 24, height: 'auto', }, + controls: { + marginTop: theme.spacing(1), + display: 'flex', + flexDirection: 'row', + }, + addButton: { + marginLeft: 'auto', + }, tokenField: { marginTop: theme.spacing(2), }, } }) -interface Props extends BoxProps {} +interface Props extends BoxProps { + onAddToken?(): void +} -export const TipForm: FC = memo(({ className, ...rest }) => { +export const TipForm: FC = memo(({ className, onAddToken, ...rest }) => { const t = useI18N() const currentChainId = useChainId() const { targetChainId: chainId } = TargetChainIdContext.useContainer() @@ -113,6 +124,10 @@ export const TipForm: FC = memo(({ className, ...rest }) => { // #endregion const buttonLabel = isSending ? t.sending_tip() : isValid || !validateMessage ? t.send_tip() : validateMessage + const enabledNft = + !isSending && + chainId === currentChainId && + [ChainId.Mainnet, ChainId.BSC, ChainId.Matic].includes(currentChainId) return ( @@ -145,7 +160,7 @@ export const TipForm: FC = memo(({ className, ...rest }) => { ))} - + setTipType(e.target.value as TipType)}> = memo(({ className, ...rest }) => { label={t.tip_type_token()} /> } label={t.tip_type_nft()} /> + {tipType === TipType.NFT ? ( + + ) : null} {tipType === TipType.Token ? ( diff --git a/packages/mask/src/plugins/NextID/contexts/Tip/TipContext.ts b/packages/mask/src/plugins/NextID/contexts/Tip/TipContext.ts index 2082cac59765..9f3dfa87aa0e 100644 --- a/packages/mask/src/plugins/NextID/contexts/Tip/TipContext.ts +++ b/packages/mask/src/plugins/NextID/contexts/Tip/TipContext.ts @@ -1,3 +1,4 @@ +import type { Web3Plugin } from '@masknet/plugin-infra' import { ERC721ContractDetailed, FungibleTokenDetailed, @@ -22,10 +23,12 @@ export interface ContextOptions { erc721TokenId: string | null setErc721TokenId: Dispatch> erc721Contract: ERC721ContractDetailed | null - setErc721Contract: Dispatch> + erc721Address: string + setErc721Address: Dispatch> sendTip: () => Promise isSending: boolean sendState: TransactionState + storedTokens: Web3Plugin.NonFungibleToken[] } export const TipContext = createContext({ @@ -42,8 +45,10 @@ export const TipContext = createContext({ erc721TokenId: null, setErc721TokenId: noop, erc721Contract: null, - setErc721Contract: noop, + erc721Address: '', + setErc721Address: noop, sendTip: noop as () => Promise, isSending: false, sendState: { type: TransactionStateType.UNKNOWN }, + storedTokens: [], }) diff --git a/packages/mask/src/plugins/NextID/contexts/Tip/TipTaskProvider.tsx b/packages/mask/src/plugins/NextID/contexts/Tip/TipTaskProvider.tsx index de39fd31f565..32645990401d 100644 --- a/packages/mask/src/plugins/NextID/contexts/Tip/TipTaskProvider.tsx +++ b/packages/mask/src/plugins/NextID/contexts/Tip/TipTaskProvider.tsx @@ -1,5 +1,12 @@ -import { TransactionStateType, useNativeTokenDetailed } from '@masknet/web3-shared-evm' +import { + TransactionStateType, + useChainId, + useERC721ContractDetailed, + useNativeTokenDetailed, +} from '@masknet/web3-shared-evm' import { FC, useContext, useEffect, useMemo, useState } from 'react' +import { useSubscription } from 'use-subscription' +import { getStorage } from '../../storage' import { TipTask, TipType } from '../../types' import { TargetChainIdContext } from '../TargetChainIdContext' import { ContextOptions, TipContext } from './TipContext' @@ -15,10 +22,14 @@ export const TipTaskProvider: FC = ({ children, task }) => { const [tipType, setTipType] = useState(TipType.Token) const [amount, setAmount] = useState('') const { targetChainId } = TargetChainIdContext.useContainer() - const [erc721Contract, setErc721Contract] = useState(null) + const chainId = useChainId() + const [erc721Address, setErc721Address] = useState('') const { value: nativeTokenDetailed = null } = useNativeTokenDetailed(targetChainId) const [token, setToken] = useState(nativeTokenDetailed) const [erc721TokenId, setErc721TokenId] = useState(null) + const storedTokens = useSubscription(getStorage().addedTokens.subscription) + + const { value: erc721Contract } = useERC721ContractDetailed(erc721Address) useEffect(() => { setTipType(TipType.Token) @@ -38,7 +49,7 @@ export const TipTaskProvider: FC = ({ children, task }) => { setToken(nativeTokenDetailed) }, [nativeTokenDetailed]) const tokenTipTuple = useTokenTip(recipient, token, amount) - const nftTipTuple = useNftTip(recipient, erc721TokenId, erc721Contract) + const nftTipTuple = useNftTip(recipient, erc721TokenId, erc721Address) const sendTipTuple = tipType === TipType.Token ? tokenTipTuple : nftTipTuple const sendState = sendTipTuple[0] @@ -51,6 +62,13 @@ export const TipTaskProvider: FC = ({ children, task }) => { TransactionStateType.RECEIPT, ].includes(sendState.type) + const wrapSendTip = async () => { + await sendTip() + setAmount('') + setErc721TokenId(null) + setErc721Address('') + } + return { recipient, recipientSnsId: task.recipientSnsId || '', @@ -64,13 +82,16 @@ export const TipTaskProvider: FC = ({ children, task }) => { setAmount, erc721TokenId, setErc721TokenId, - erc721Contract, - setErc721Contract, - sendTip, + erc721Contract: erc721Contract || null, + erc721Address, + setErc721Address, + sendTip: wrapSendTip, isSending, sendState, + storedTokens: storedTokens.filter((t) => t.contract?.chainId === chainId), } }, [ + chainId, recipient, task.recipientSnsId, task.addresses, @@ -78,9 +99,11 @@ export const TipTaskProvider: FC = ({ children, task }) => { amount, erc721TokenId, erc721Contract, + erc721Address, token, sendTip, sendState, + storedTokens, ]) return {children} } diff --git a/packages/mask/src/plugins/NextID/contexts/Tip/useNftTip.ts b/packages/mask/src/plugins/NextID/contexts/Tip/useNftTip.ts index 398d2cfbe769..bb6523bd72e9 100644 --- a/packages/mask/src/plugins/NextID/contexts/Tip/useNftTip.ts +++ b/packages/mask/src/plugins/NextID/contexts/Tip/useNftTip.ts @@ -1,21 +1,16 @@ -import { ERC721ContractDetailed, EthereumTokenType, useTokenTransferCallback } from '@masknet/web3-shared-evm' +import { EthereumTokenType, useTokenTransferCallback } from '@masknet/web3-shared-evm' import { useCallback } from 'react' +import { deleteToken } from '../../storage' import type { TipTuple } from './type' -export function useNftTip( - recipient: string, - tokenId: string | null, - contract: ERC721ContractDetailed | null, -): TipTuple { - const [transferState, transferCallback] = useTokenTransferCallback( - EthereumTokenType.ERC721, - contract?.address || '', - ) +export function useNftTip(recipient: string, tokenId: string | null, contractAddress?: string): TipTuple { + const [transferState, transferCallback] = useTokenTransferCallback(EthereumTokenType.ERC721, contractAddress || '') const sendTip = useCallback(async () => { - if (!tokenId) return + if (!tokenId || !contractAddress) return await transferCallback(tokenId, recipient) - }, [tokenId, recipient, transferCallback]) + deleteToken(contractAddress, tokenId) + }, [tokenId, contractAddress, recipient, transferCallback]) return [transferState, sendTip] } diff --git a/packages/mask/src/plugins/NextID/contexts/Tip/useTipValidate.ts b/packages/mask/src/plugins/NextID/contexts/Tip/useTipValidate.ts index 8534b0ba7c0b..2eca80b4dc8d 100644 --- a/packages/mask/src/plugins/NextID/contexts/Tip/useTipValidate.ts +++ b/packages/mask/src/plugins/NextID/contexts/Tip/useTipValidate.ts @@ -8,7 +8,7 @@ import { TipContext } from './TipContext' type ValidationTuple = [isValid: boolean, message?: string] export function useTipValidate(): ValidationTuple { - const { tipType, amount, token, erc721TokenId, erc721Contract } = useContext(TipContext) + const { tipType, amount, token, erc721TokenId, erc721Address } = useContext(TipContext) const { value: balance = '0' } = useFungibleTokenBalance(token?.type || EthereumTokenType.Native, token?.address) const t = useI18N() @@ -18,10 +18,10 @@ export function useTipValidate(): ValidationTuple { if (isGreaterThan(rightShift(amount, token?.decimals), balance)) return [false, t.token_insufficient_balance()] } else { - if (!erc721TokenId) return [false] + if (!erc721TokenId || !erc721Address) return [false] } return [true] - }, [tipType, amount, token?.decimals, balance, erc721TokenId, t]) + }, [tipType, amount, token?.decimals, balance, erc721TokenId, erc721Address, t]) return result } diff --git a/packages/mask/src/plugins/NextID/locales/en-US.json b/packages/mask/src/plugins/NextID/locales/en-US.json index a9b3f95a04e6..7a523f12b10b 100644 --- a/packages/mask/src/plugins/NextID/locales/en-US.json +++ b/packages/mask/src/plugins/NextID/locales/en-US.json @@ -62,5 +62,10 @@ "tip_contracts": "Contracts", "tip_mask_promote": "Install https://mask.io/download-links to send your first tip.", "tip_token_share_post": "I just tipped {{amount}} {{symbol}} to @{{recipientSnsId}}'s wallet address {{recipient}}\n\n{{promote}}", - "tip_nft_share_post": "I just tipped a {{name}} to @{{recipientSnsId}}'s wallet address {{recipient}}\n\n{{promote}}" + "tip_nft_share_post": "I just tipped a {{name}} to @{{recipientSnsId}}'s wallet address {{recipient}}\n\n{{promote}}", + "tip_add": "Add", + "tip_add_collectibles": "Add collectibles", + "tip_add_collectibles_contract_address": "Input contract address", + "tip_add_collectibles_token_id": "Token ID", + "tip_add_collectibles_error": "The contract address is incorrect or the collection does not belong to you" } diff --git a/packages/mask/src/plugins/NextID/storage/index.ts b/packages/mask/src/plugins/NextID/storage/index.ts new file mode 100644 index 000000000000..a0fd1622a1cb --- /dev/null +++ b/packages/mask/src/plugins/NextID/storage/index.ts @@ -0,0 +1,36 @@ +import type { Web3Plugin } from '@masknet/plugin-infra' +import type { ScopedStorage } from '@masknet/shared-base' +import { remove } from 'lodash-unified' + +interface StorageValue { + addedTokens: Web3Plugin.NonFungibleToken[] +} + +export const storageDefaultValue = { + publicKey: null as null | string, + addedTokens: [], +} + +let storage: ScopedStorage = null! + +export function setupStorage(_: typeof storage) { + storage = _ +} + +export function getStorage() { + return storage.storage +} + +export function getTokens() { + return storage.storage.addedTokens.value +} + +export function storeToken(token: Web3Plugin.NonFungibleToken) { + const tokens = [token, ...getTokens()] + storage.storage.addedTokens.setValue(tokens) +} + +export function deleteToken(address: string, tokenId: string) { + const tokens = remove(getTokens(), (t) => t.tokenId === tokenId && t.contract?.address === address) + storage.storage.addedTokens.setValue(tokens) +} diff --git a/packages/shared/src/UI/components/ImageIcon/index.tsx b/packages/shared/src/UI/components/ImageIcon/index.tsx index 90110f420a1d..5a020afcee7b 100644 --- a/packages/shared/src/UI/components/ImageIcon/index.tsx +++ b/packages/shared/src/UI/components/ImageIcon/index.tsx @@ -8,7 +8,7 @@ const useStyles = makeStyles()((theme) => { export interface ImageIconProps extends withClasses<'icon'> { size?: number - icon?: URL + icon?: URL | string } export function ImageIcon(props: ImageIconProps) { diff --git a/packages/web3-shared/evm/utils/transaction.ts b/packages/web3-shared/evm/utils/transaction.ts index 7bce90ccee9c..b18ddf919985 100644 --- a/packages/web3-shared/evm/utils/transaction.ts +++ b/packages/web3-shared/evm/utils/transaction.ts @@ -10,7 +10,7 @@ export function isFinalState(type: TransactionStateType) { } /** - * UNKNOWN -> WAIT_FOR_CONFIRMING + * UNKNOWN, FAILED -> WAIT_FOR_CONFIRMING * UNKNOWN, WAIT_FOR_CONFIRMING -> HASH * UNKNOWN, WAIT_FOR_CONFIRMING, HASH -> RECEIPT * WAIT_FOR_CONFIRMING, HASH, RECEIPT -> CONFIRMED @@ -21,7 +21,7 @@ export function isNextStateAvailable(type: TransactionStateType, nextType: Trans case TransactionStateType.UNKNOWN: return false case TransactionStateType.WAIT_FOR_CONFIRMING: - return [TransactionStateType.UNKNOWN].includes(type) + return [TransactionStateType.UNKNOWN, TransactionStateType.FAILED].includes(type) case TransactionStateType.HASH: return [TransactionStateType.UNKNOWN, TransactionStateType.WAIT_FOR_CONFIRMING].includes(type) case TransactionStateType.RECEIPT: From ca8fcf6cb93632df14b6ca52ae16c5234d489f55 Mon Sep 17 00:00:00 2001 From: unclebill Date: Sat, 2 Apr 2022 11:33:36 +0800 Subject: [PATCH 04/19] token section --- .../components/Tip/NFTSection/NFTList.tsx | 2 +- .../plugins/NextID/components/Tip/TipForm.tsx | 47 ++-------------- .../components/Tip/TokenSection/index.tsx | 56 +++++++++++++++++++ 3 files changed, 61 insertions(+), 44 deletions(-) create mode 100644 packages/mask/src/plugins/NextID/components/Tip/TokenSection/index.tsx diff --git a/packages/mask/src/plugins/NextID/components/Tip/NFTSection/NFTList.tsx b/packages/mask/src/plugins/NextID/components/Tip/NFTSection/NFTList.tsx index 1a2fdd76ed17..5013d4f817e2 100644 --- a/packages/mask/src/plugins/NextID/components/Tip/NFTSection/NFTList.tsx +++ b/packages/mask/src/plugins/NextID/components/Tip/NFTSection/NFTList.tsx @@ -110,7 +110,7 @@ export const NFTList: FC = ({ selectedIds, tokens, onChange, limit = 1, c return ( { return { @@ -87,10 +86,6 @@ export const TipForm: FC = memo(({ className, onAddToken, ...rest }) => { recipient, recipients: recipientAddresses, setRecipient, - token, - setToken, - amount, - setAmount, isSending, sendTip, tipType, @@ -103,25 +98,6 @@ export const TipForm: FC = memo(({ className, onAddToken, ...rest }) => { const { openDialog: openSelectProviderDialog } = useRemoteControlledDialog( WalletMessages.events.selectProviderDialogUpdated, ) - const pickToken = usePickToken() - const onSelectTokenChipClick = useCallback(async () => { - const picked = await pickToken({ - chainId, - disableNativeToken: false, - selectedTokens: token ? [token.address] : [], - }) - if (picked) { - setToken(picked) - } - }, [pickToken, token?.address, chainId]) - - // balance - const { value: tokenBalance = '0', loading: loadingTokenBalance } = useFungibleTokenBalance( - token?.type || EthereumTokenType.Native, - token?.address || '', - chainId, - ) - // #endregion const buttonLabel = isSending ? t.sending_tip() : isValid || !validateMessage ? t.send_tip() : validateMessage const enabledNft = @@ -183,22 +159,7 @@ export const TipForm: FC = memo(({ className, onAddToken, ...rest }) => { {tipType === TipType.Token ? ( - + ) : ( diff --git a/packages/mask/src/plugins/NextID/components/Tip/TokenSection/index.tsx b/packages/mask/src/plugins/NextID/components/Tip/TokenSection/index.tsx new file mode 100644 index 000000000000..04c94932153a --- /dev/null +++ b/packages/mask/src/plugins/NextID/components/Tip/TokenSection/index.tsx @@ -0,0 +1,56 @@ +import { TokenAmountPanel, usePickToken } from '@masknet/shared' +import { EthereumTokenType, isNativeTokenAddress, useFungibleTokenBalance } from '@masknet/web3-shared-evm' +import BigNumber from 'bignumber.js' +import { FC, useCallback, useMemo } from 'react' +import { useGasConfig } from '../../../../Trader/SNSAdaptor/trader/hooks/useGasConfig' +import { TargetChainIdContext, useTip } from '../../../contexts' + +const GAS_LIMIT = 21000 +export const TokenSection: FC = () => { + const { token, setToken, amount, setAmount, isSending } = useTip() + const { targetChainId: chainId } = TargetChainIdContext.useContainer() + // balance + const { value: tokenBalance = '0', loading: loadingTokenBalance } = useFungibleTokenBalance( + token?.type || EthereumTokenType.Native, + token?.address || '', + chainId, + ) + const gasConfig = useGasConfig(chainId) + const maxAmount = useMemo(() => { + if (!isNativeTokenAddress(token?.address)) return tokenBalance + const gasPrice = gasConfig.gasPrice ?? '1' + const gasFee = new BigNumber(gasPrice).times(GAS_LIMIT) + return new BigNumber(tokenBalance).minus(gasFee).toFixed() + }, [token?.address, tokenBalance, gasConfig.gasPrice]) + const pickToken = usePickToken() + const onSelectTokenChipClick = useCallback(async () => { + const picked = await pickToken({ + chainId, + disableNativeToken: false, + selectedTokens: token ? [token.address] : [], + }) + if (picked) { + setToken(picked) + } + }, [pickToken, token?.address, chainId]) + // #endregion + return ( + + ) +} From aefab9b3b2fadab26f846b3ae8143e6e4a69722d Mon Sep 17 00:00:00 2001 From: unclebill Date: Sat, 2 Apr 2022 13:03:07 +0800 Subject: [PATCH 05/19] fix: follow up bug reports --- packages/icons/general/Drop2.tsx | 12 ++++++ packages/icons/general/index.ts | 1 + .../components/Tip/NFTSection/index.tsx | 2 - .../NextID/components/Tip/TipDialog.tsx | 41 +++++++++++-------- .../plugins/NextID/contexts/Tip/TipContext.ts | 2 + .../NextID/contexts/Tip/TipTaskProvider.tsx | 6 +-- .../mask/src/plugins/NextID/storage/index.ts | 3 +- 7 files changed, 44 insertions(+), 23 deletions(-) create mode 100644 packages/icons/general/Drop2.tsx diff --git a/packages/icons/general/Drop2.tsx b/packages/icons/general/Drop2.tsx new file mode 100644 index 000000000000..c1c081af5b04 --- /dev/null +++ b/packages/icons/general/Drop2.tsx @@ -0,0 +1,12 @@ +import { createIcon } from '../utils' + +export const Drop2Icon = createIcon( + 'Drop2Icon', + + + , + '0 0 20 24', +) diff --git a/packages/icons/general/index.ts b/packages/icons/general/index.ts index 28e046e8bb0d..f9e5c503e831 100644 --- a/packages/icons/general/index.ts +++ b/packages/icons/general/index.ts @@ -80,6 +80,7 @@ export * from './ChevronUp' export * from './BestTrade' export * from './Retweet' export * from './Drop' +export * from './Drop2' export * from './Right' export * from './Tutorial' export * from './AssetLoading' diff --git a/packages/mask/src/plugins/NextID/components/Tip/NFTSection/index.tsx b/packages/mask/src/plugins/NextID/components/Tip/NFTSection/index.tsx index 932f9d7c27b4..09afbb23cadf 100644 --- a/packages/mask/src/plugins/NextID/components/Tip/NFTSection/index.tsx +++ b/packages/mask/src/plugins/NextID/components/Tip/NFTSection/index.tsx @@ -18,14 +18,12 @@ const useStyles = makeStyles()((theme) => ({ overflow: 'auto', }, selectSection: { - marginTop: theme.spacing(1.5), display: 'flex', flexDirection: 'column', overflow: 'auto', }, list: { flexGrow: 1, - marginTop: theme.spacing(2), maxHeight: 400, overflow: 'auto', borderRadius: 4, diff --git a/packages/mask/src/plugins/NextID/components/Tip/TipDialog.tsx b/packages/mask/src/plugins/NextID/components/Tip/TipDialog.tsx index 331218db8061..9e189425cf5c 100644 --- a/packages/mask/src/plugins/NextID/components/Tip/TipDialog.tsx +++ b/packages/mask/src/plugins/NextID/components/Tip/TipDialog.tsx @@ -1,4 +1,4 @@ -import { Edit2Icon, LinkOutIcon, SuccessIcon } from '@masknet/icons' +import { Drop2Icon, LinkOutIcon, SuccessIcon } from '@masknet/icons' import { PluginId, useActivatedPlugin, @@ -23,11 +23,11 @@ import { activatedSocialNetworkUI } from '../../../../social-network' import { WalletMessages } from '../../../Wallet/messages' import { TargetChainIdContext, useTip } from '../../contexts' import { useI18N } from '../../locales' +import { storeToken } from '../../storage' import { TipType } from '../../types' import { ConfirmModal } from '../ConfirmModal' import { AddDialog } from './AddDialog' import { TipForm } from './TipForm' -import { storeToken } from '../../storage' const useStyles = makeStyles()((theme) => ({ content: { @@ -85,6 +85,7 @@ const useStyles = makeStyles()((theme) => ({ walletChip: { marginLeft: 'auto', height: 40, + boxSizing: 'border-box', display: 'flex', alignItems: 'center', backgroundColor: theme.palette.background.default, @@ -96,24 +97,28 @@ const useStyles = makeStyles()((theme) => ({ }, walletTitle: { marginLeft: theme.spacing(1), + lineHeight: '18px', + height: 18, fontSize: 14, fontWeight: 'bold', }, walletAddress: { - fontSize: 10, - color: theme.palette.text.secondary, - cursor: 'pointer', + height: 12, display: 'flex', alignItems: 'center', + fontSize: 10, + color: theme.palette.text.secondary, }, changeWalletButton: { - marginLeft: theme.spacing(1), + marginLeft: theme.spacing(0.5), display: 'flex', alignItems: 'center', cursor: 'pointer', }, link: { cursor: 'pointer', + lineHeight: '10px', + marginTop: 2, '&:hover': { textDecoration: 'none', }, @@ -152,6 +157,7 @@ export function TipDialog({ open = false, onClose }: TipDialogProps) { erc721TokenId, setErc721Address, setErc721TokenId, + reset, } = useTip() const isTokenTip = tipType === TipType.Token @@ -166,7 +172,7 @@ export function TipDialog({ open = false, onClose }: TipDialogProps) { promote, }) : t.tip_nft_share_post({ - name: erc721Contract?.name || '', + name: erc721Contract?.name || 'NFT', recipientSnsId, recipient, promote, @@ -248,19 +254,19 @@ export function TipDialog({ open = false, onClose }: TipDialogProps) { {walletTitle} - - - {Utils?.formatAddress?.(account, 4)} + + {Utils?.formatAddress?.(account, 4)} + - - + +
- +
) @@ -285,6 +291,7 @@ export function TipDialog({ open = false, onClose }: TipDialogProps) { open={confirmModalIsOpen} onClose={() => { openConfirmModal(false) + reset() onClose?.() }} icon={isTokenTip ? : null} diff --git a/packages/mask/src/plugins/NextID/contexts/Tip/TipContext.ts b/packages/mask/src/plugins/NextID/contexts/Tip/TipContext.ts index 9f3dfa87aa0e..8fa4be5af260 100644 --- a/packages/mask/src/plugins/NextID/contexts/Tip/TipContext.ts +++ b/packages/mask/src/plugins/NextID/contexts/Tip/TipContext.ts @@ -29,6 +29,7 @@ export interface ContextOptions { isSending: boolean sendState: TransactionState storedTokens: Web3Plugin.NonFungibleToken[] + reset: () => void } export const TipContext = createContext({ @@ -51,4 +52,5 @@ export const TipContext = createContext({ isSending: false, sendState: { type: TransactionStateType.UNKNOWN }, storedTokens: [], + reset: noop, }) diff --git a/packages/mask/src/plugins/NextID/contexts/Tip/TipTaskProvider.tsx b/packages/mask/src/plugins/NextID/contexts/Tip/TipTaskProvider.tsx index 32645990401d..7b8722fa717e 100644 --- a/packages/mask/src/plugins/NextID/contexts/Tip/TipTaskProvider.tsx +++ b/packages/mask/src/plugins/NextID/contexts/Tip/TipTaskProvider.tsx @@ -62,8 +62,7 @@ export const TipTaskProvider: FC = ({ children, task }) => { TransactionStateType.RECEIPT, ].includes(sendState.type) - const wrapSendTip = async () => { - await sendTip() + const reset = () => { setAmount('') setErc721TokenId(null) setErc721Address('') @@ -85,10 +84,11 @@ export const TipTaskProvider: FC = ({ children, task }) => { erc721Contract: erc721Contract || null, erc721Address, setErc721Address, - sendTip: wrapSendTip, + sendTip, isSending, sendState, storedTokens: storedTokens.filter((t) => t.contract?.chainId === chainId), + reset, } }, [ chainId, diff --git a/packages/mask/src/plugins/NextID/storage/index.ts b/packages/mask/src/plugins/NextID/storage/index.ts index a0fd1622a1cb..c1d9c6bde4b9 100644 --- a/packages/mask/src/plugins/NextID/storage/index.ts +++ b/packages/mask/src/plugins/NextID/storage/index.ts @@ -31,6 +31,7 @@ export function storeToken(token: Web3Plugin.NonFungibleToken) { } export function deleteToken(address: string, tokenId: string) { - const tokens = remove(getTokens(), (t) => t.tokenId === tokenId && t.contract?.address === address) + const tokens = getTokens() + remove(tokens, (t) => t.tokenId === tokenId && t.contract?.address === address) storage.storage.addedTokens.setValue(tokens) } From f5c8d51601c9b83665a3134961f3d3fa8e21652a Mon Sep 17 00:00:00 2001 From: unclebill Date: Sat, 2 Apr 2022 15:47:38 +0800 Subject: [PATCH 06/19] fix: trigger update after ws responses --- .../components/CollectibleList/index.tsx | 17 ++++++------ .../components/Tip/NFTSection/index.tsx | 27 ++++++++++++------- .../NextID/components/Tip/TipDialog.tsx | 10 +++---- .../components/Tip/TokenSection/index.tsx | 3 ++- .../mask/src/plugins/NextID/storage/index.ts | 3 ++- 5 files changed, 35 insertions(+), 25 deletions(-) diff --git a/packages/dashboard/src/pages/Wallets/components/CollectibleList/index.tsx b/packages/dashboard/src/pages/Wallets/components/CollectibleList/index.tsx index d6834fbed62e..78098887fd0d 100644 --- a/packages/dashboard/src/pages/Wallets/components/CollectibleList/index.tsx +++ b/packages/dashboard/src/pages/Wallets/components/CollectibleList/index.tsx @@ -64,6 +64,14 @@ export const CollectibleList = memo(({ selectedNetwork }) Asset?.getNonFungibleAssets?.(account, { page: page, size: 20 }, undefined, selectedNetwork || undefined), [account, Asset?.getNonFungibleAssets, network, selectedNetwork], ) + useEffect(() => { + PluginMessages.Wallet.events.erc721TokensUpdated.on(() => retry()) + PluginMessages.Wallet.events.socketMessageUpdated.on((info) => { + if (!info.done) { + retry() + } + }) + }, [retry]) useEffect(() => { if (!loadingSize) return @@ -86,15 +94,6 @@ export const CollectibleList = memo(({ selectedNetwork }) [currentPluginId], ) - useEffect(() => { - PluginMessages.Wallet.events.erc721TokensUpdated.on(() => retry()) - PluginMessages.Wallet.events.socketMessageUpdated.on((info) => { - if (!info.done) { - retry() - } - }) - }, [retry]) - const hasNextPage = (page + 1) * loadingSize < value.data.length const isLoading = renderData.length === 0 && isQuerying diff --git a/packages/mask/src/plugins/NextID/components/Tip/NFTSection/index.tsx b/packages/mask/src/plugins/NextID/components/Tip/NFTSection/index.tsx index 09afbb23cadf..ecd0e7bf2629 100644 --- a/packages/mask/src/plugins/NextID/components/Tip/NFTSection/index.tsx +++ b/packages/mask/src/plugins/NextID/components/Tip/NFTSection/index.tsx @@ -1,12 +1,13 @@ import { useNetworkDescriptor, useWeb3State as useWeb3PluginState } from '@masknet/plugin-infra' import { EMPTY_LIST } from '@masknet/shared-base' import { makeStyles } from '@masknet/theme' -import { useAccount } from '@masknet/web3-shared-evm' +import { isSameAddress, useAccount } from '@masknet/web3-shared-evm' import classnames from 'classnames' import { uniqWith } from 'lodash-unified' import { FC, HTMLProps, useEffect, useMemo } from 'react' import { useAsyncRetry } from 'react-use' -import { TargetChainIdContext, useTip } from '../../../contexts' +import { WalletMessages } from '../../../../Wallet/messages' +import { useTip } from '../../../contexts' import { NFTList } from './NFTList' export * from './NFTList' @@ -39,7 +40,6 @@ const useStyles = makeStyles()((theme) => ({ interface Props extends HTMLProps {} export const NFTSection: FC = ({ className, ...rest }) => { - const { targetChainId: chainId } = TargetChainIdContext.useContainer() const { storedTokens, erc721TokenId, setErc721TokenId, setErc721Address } = useTip() const { classes } = useStyles() const account = useAccount() @@ -50,18 +50,27 @@ export const NFTSection: FC = ({ className, ...rest }) => { const networkDescriptor = useNetworkDescriptor() - const { value = { data: EMPTY_LIST, hasNextPage: false }, retry } = useAsyncRetry( - async () => Asset?.getNonFungibleAssets?.(account, { page: 0, size: 1000 }, undefined, networkDescriptor), + const { value = { data: EMPTY_LIST }, retry } = useAsyncRetry( + async () => Asset?.getNonFungibleAssets?.(account, { page: 0 }, undefined, networkDescriptor), [account, Asset?.getNonFungibleAssets, networkDescriptor], ) - useEffect(retry, [chainId]) + useEffect(() => { + WalletMessages.events.erc721TokensUpdated.on(retry) + WalletMessages.events.socketMessageUpdated.on((info) => { + if (!info.done) { + retry() + } + }) + }, [retry]) + + const fetchedTokens = value?.data ?? EMPTY_LIST const tokens = useMemo(() => { - return uniqWith([...storedTokens, ...value.data], (v1, v2) => { - return v1.contract?.address === v2.contract?.address && v1.tokenId === v2.tokenId + return uniqWith([...storedTokens, ...fetchedTokens], (v1, v2) => { + return isSameAddress(v1.contract?.address, v2.contract?.address) && v1.tokenId === v2.tokenId }) - }, [storedTokens, value.data]) + }, [storedTokens, fetchedTokens]) return (
diff --git a/packages/mask/src/plugins/NextID/components/Tip/TipDialog.tsx b/packages/mask/src/plugins/NextID/components/Tip/TipDialog.tsx index 9e189425cf5c..750fb8a25a61 100644 --- a/packages/mask/src/plugins/NextID/components/Tip/TipDialog.tsx +++ b/packages/mask/src/plugins/NextID/components/Tip/TipDialog.tsx @@ -229,15 +229,15 @@ export function TipDialog({ open = false, onClose }: TipDialogProps) { const walletTitle = Utils?.formatDomainName?.(domain) || Utils?.formatAddress?.(account, 4) || providerDescriptor?.name - // #region Wallet - const { openDialog: openWalletStatusDialog } = useRemoteControlledDialog( - WalletMessages.events.walletStatusDialogUpdated, + // #region change provider + const { openDialog: openSelectProviderDialog } = useRemoteControlledDialog( + WalletMessages.events.selectProviderDialogUpdated, ) // #endregion const openWallet = useCallback(() => { if (hasNativeAPI) return nativeAPI?.api.misc_openCreateWalletView() - return openWalletStatusDialog() - }, [openWalletStatusDialog, hasNativeAPI]) + return openSelectProviderDialog() + }, [openSelectProviderDialog, hasNativeAPI]) const handleAddToken = useCallback((token: Web3Plugin.NonFungibleToken) => { setErc721Address(token.contract?.address ?? '') diff --git a/packages/mask/src/plugins/NextID/components/Tip/TokenSection/index.tsx b/packages/mask/src/plugins/NextID/components/Tip/TokenSection/index.tsx index 04c94932153a..063b4b41571f 100644 --- a/packages/mask/src/plugins/NextID/components/Tip/TokenSection/index.tsx +++ b/packages/mask/src/plugins/NextID/components/Tip/TokenSection/index.tsx @@ -1,7 +1,8 @@ -import { TokenAmountPanel, usePickToken } from '@masknet/shared' +import { usePickToken } from '@masknet/shared' import { EthereumTokenType, isNativeTokenAddress, useFungibleTokenBalance } from '@masknet/web3-shared-evm' import BigNumber from 'bignumber.js' import { FC, useCallback, useMemo } from 'react' +import { TokenAmountPanel } from '../../../../../web3/UI/TokenAmountPanel' import { useGasConfig } from '../../../../Trader/SNSAdaptor/trader/hooks/useGasConfig' import { TargetChainIdContext, useTip } from '../../../contexts' diff --git a/packages/mask/src/plugins/NextID/storage/index.ts b/packages/mask/src/plugins/NextID/storage/index.ts index c1d9c6bde4b9..6c9312b13e59 100644 --- a/packages/mask/src/plugins/NextID/storage/index.ts +++ b/packages/mask/src/plugins/NextID/storage/index.ts @@ -1,5 +1,6 @@ import type { Web3Plugin } from '@masknet/plugin-infra' import type { ScopedStorage } from '@masknet/shared-base' +import { isSameAddress } from '@masknet/web3-shared-evm' import { remove } from 'lodash-unified' interface StorageValue { @@ -32,6 +33,6 @@ export function storeToken(token: Web3Plugin.NonFungibleToken) { export function deleteToken(address: string, tokenId: string) { const tokens = getTokens() - remove(tokens, (t) => t.tokenId === tokenId && t.contract?.address === address) + remove(tokens, (t) => t.tokenId === tokenId && isSameAddress(t.contract?.address, address)) storage.storage.addedTokens.setValue(tokens) } From 91e126d8f937c8f39017329ae585d197d0a9c18f Mon Sep 17 00:00:00 2001 From: unclebill Date: Sat, 2 Apr 2022 17:58:32 +0800 Subject: [PATCH 07/19] fix: cleanup useEffect --- .../pages/Wallets/components/CollectibleList/index.tsx | 8 ++++++-- .../plugins/NextID/components/Tip/NFTSection/index.tsx | 10 ++++++---- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/packages/dashboard/src/pages/Wallets/components/CollectibleList/index.tsx b/packages/dashboard/src/pages/Wallets/components/CollectibleList/index.tsx index 78098887fd0d..695e940d787f 100644 --- a/packages/dashboard/src/pages/Wallets/components/CollectibleList/index.tsx +++ b/packages/dashboard/src/pages/Wallets/components/CollectibleList/index.tsx @@ -65,12 +65,16 @@ export const CollectibleList = memo(({ selectedNetwork }) [account, Asset?.getNonFungibleAssets, network, selectedNetwork], ) useEffect(() => { - PluginMessages.Wallet.events.erc721TokensUpdated.on(() => retry()) - PluginMessages.Wallet.events.socketMessageUpdated.on((info) => { + const unsubscribeTokens = PluginMessages.Wallet.events.erc721TokensUpdated.on(() => retry()) + const unsubscribeSocket = PluginMessages.Wallet.events.socketMessageUpdated.on((info) => { if (!info.done) { retry() } }) + return () => { + unsubscribeTokens() + unsubscribeSocket() + } }, [retry]) useEffect(() => { diff --git a/packages/mask/src/plugins/NextID/components/Tip/NFTSection/index.tsx b/packages/mask/src/plugins/NextID/components/Tip/NFTSection/index.tsx index ecd0e7bf2629..902914d2d73f 100644 --- a/packages/mask/src/plugins/NextID/components/Tip/NFTSection/index.tsx +++ b/packages/mask/src/plugins/NextID/components/Tip/NFTSection/index.tsx @@ -43,9 +43,7 @@ export const NFTSection: FC = ({ className, ...rest }) => { const { storedTokens, erc721TokenId, setErc721TokenId, setErc721Address } = useTip() const { classes } = useStyles() const account = useAccount() - const selectedIds = useMemo(() => (erc721TokenId ? [erc721TokenId] : []), [erc721TokenId]) - const { Asset } = useWeb3PluginState() const networkDescriptor = useNetworkDescriptor() @@ -56,12 +54,16 @@ export const NFTSection: FC = ({ className, ...rest }) => { ) useEffect(() => { - WalletMessages.events.erc721TokensUpdated.on(retry) - WalletMessages.events.socketMessageUpdated.on((info) => { + const unsubscribeTokens = WalletMessages.events.erc721TokensUpdated.on(retry) + const unsubscribeSocket = WalletMessages.events.socketMessageUpdated.on((info) => { if (!info.done) { retry() } }) + return () => { + unsubscribeTokens() + unsubscribeSocket() + } }, [retry]) const fetchedTokens = value?.data ?? EMPTY_LIST From 424a928bdca95fca3f427f226c4a288fbc6bd21e Mon Sep 17 00:00:00 2001 From: unclebill Date: Sat, 2 Apr 2022 21:46:18 +0800 Subject: [PATCH 08/19] fix: add loading status --- .../components/Tip/NFTSection/index.tsx | 52 +++++++++++++------ .../src/plugins/NextID/locales/en-US.json | 4 +- 2 files changed, 39 insertions(+), 17 deletions(-) diff --git a/packages/mask/src/plugins/NextID/components/Tip/NFTSection/index.tsx b/packages/mask/src/plugins/NextID/components/Tip/NFTSection/index.tsx index 902914d2d73f..81c060e6e4aa 100644 --- a/packages/mask/src/plugins/NextID/components/Tip/NFTSection/index.tsx +++ b/packages/mask/src/plugins/NextID/components/Tip/NFTSection/index.tsx @@ -2,10 +2,11 @@ import { useNetworkDescriptor, useWeb3State as useWeb3PluginState } from '@maskn import { EMPTY_LIST } from '@masknet/shared-base' import { makeStyles } from '@masknet/theme' import { isSameAddress, useAccount } from '@masknet/web3-shared-evm' +import { CircularProgress } from '@mui/material' import classnames from 'classnames' import { uniqWith } from 'lodash-unified' -import { FC, HTMLProps, useEffect, useMemo } from 'react' -import { useAsyncRetry } from 'react-use' +import { FC, HTMLProps, useEffect, useMemo, useState } from 'react' +import { useAsyncRetry, useTimeout, useTimeoutFn } from 'react-use' import { WalletMessages } from '../../../../Wallet/messages' import { useTip } from '../../../contexts' import { NFTList } from './NFTList' @@ -29,6 +30,9 @@ const useStyles = makeStyles()((theme) => ({ overflow: 'auto', borderRadius: 4, }, + loadingOwnerList: { + margin: '24px auto 16px', + }, errorMessage: { marginTop: theme.spacing(3), fontSize: 12, @@ -48,14 +52,26 @@ export const NFTSection: FC = ({ className, ...rest }) => { const networkDescriptor = useNetworkDescriptor() - const { value = { data: EMPTY_LIST }, retry } = useAsyncRetry( - async () => Asset?.getNonFungibleAssets?.(account, { page: 0 }, undefined, networkDescriptor), - [account, Asset?.getNonFungibleAssets, networkDescriptor], - ) + // Can't not get the loading status of fetching via websocket + // loading status of `useAsyncRetry` is not the real status + const [guessLoading, setGuessLoading] = useState(true) + useTimeoutFn(() => { + setGuessLoading(false) + }, 10000) + + const { + value = { data: EMPTY_LIST }, + loading, + retry, + } = useAsyncRetry(async () => { + const result = await Asset?.getNonFungibleAssets?.(account, { page: 0 }, undefined, networkDescriptor) + return result + }, [account, Asset?.getNonFungibleAssets, networkDescriptor]) useEffect(() => { const unsubscribeTokens = WalletMessages.events.erc721TokensUpdated.on(retry) const unsubscribeSocket = WalletMessages.events.socketMessageUpdated.on((info) => { + setGuessLoading(info.done) if (!info.done) { retry() } @@ -74,18 +90,24 @@ export const NFTSection: FC = ({ className, ...rest }) => { }) }, [storedTokens, fetchedTokens]) + const showLoadingIndicator = tokens.length === 0 && (loading || guessLoading) + return (
- { - setErc721TokenId(id) - setErc721Address(address) - }} - /> + {showLoadingIndicator ? ( + + ) : ( + { + setErc721TokenId(id) + setErc721Address(address) + }} + /> + )}
) diff --git a/packages/mask/src/plugins/NextID/locales/en-US.json b/packages/mask/src/plugins/NextID/locales/en-US.json index 7a523f12b10b..131dcae8ee6d 100644 --- a/packages/mask/src/plugins/NextID/locales/en-US.json +++ b/packages/mask/src/plugins/NextID/locales/en-US.json @@ -53,8 +53,8 @@ "send_tip": "Send", "sending_tip": "Sending...", "nft_not_belong_to_you": "The collectible doesn't exist or belong to you.", - "send_tip_successfully": "Sent tip successfully", - "send_specific_tip_successfully": "Sent {{amount}} {{name}} tip successfully", + "send_tip_successfully": "Sent tip successfully.", + "send_specific_tip_successfully": "Sent {{amount}} {{name}} tip successfully.", "tip_share": "Share", "tip_connect_wallet_message": "Please connect to a wallet.", "tip_connect_wallet": "Connect Wallet", From 6bac2bcfac83062e10fa9f2f20a372faa8257a52 Mon Sep 17 00:00:00 2001 From: unclebill Date: Sat, 2 Apr 2022 22:29:18 +0800 Subject: [PATCH 09/19] fix: set dialog title higher --- .../plugins/NextID/components/Tip/NFTSection/index.tsx | 2 +- .../src/plugins/NextID/components/Tip/TipDialog.tsx | 10 +++++++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/packages/mask/src/plugins/NextID/components/Tip/NFTSection/index.tsx b/packages/mask/src/plugins/NextID/components/Tip/NFTSection/index.tsx index 81c060e6e4aa..ca6065100f7a 100644 --- a/packages/mask/src/plugins/NextID/components/Tip/NFTSection/index.tsx +++ b/packages/mask/src/plugins/NextID/components/Tip/NFTSection/index.tsx @@ -6,7 +6,7 @@ import { CircularProgress } from '@mui/material' import classnames from 'classnames' import { uniqWith } from 'lodash-unified' import { FC, HTMLProps, useEffect, useMemo, useState } from 'react' -import { useAsyncRetry, useTimeout, useTimeoutFn } from 'react-use' +import { useAsyncRetry, useTimeoutFn } from 'react-use' import { WalletMessages } from '../../../../Wallet/messages' import { useTip } from '../../../contexts' import { NFTList } from './NFTList' diff --git a/packages/mask/src/plugins/NextID/components/Tip/TipDialog.tsx b/packages/mask/src/plugins/NextID/components/Tip/TipDialog.tsx index 750fb8a25a61..5a3488ac065d 100644 --- a/packages/mask/src/plugins/NextID/components/Tip/TipDialog.tsx +++ b/packages/mask/src/plugins/NextID/components/Tip/TipDialog.tsx @@ -30,6 +30,9 @@ import { AddDialog } from './AddDialog' import { TipForm } from './TipForm' const useStyles = makeStyles()((theme) => ({ + dialogTitle: { + height: 60, + }, content: { display: 'flex', flexDirection: 'column', @@ -273,7 +276,12 @@ export function TipDialog({ open = false, onClose }: TipDialogProps) { return ( <> - +
Date: Sat, 2 Apr 2022 22:49:04 +0800 Subject: [PATCH 10/19] Apply suggestions from code review Co-authored-by: guanbinrui <52657989+guanbinrui@users.noreply.github.com> --- packages/mask/src/plugins/NextID/components/Tip/AddDialog.tsx | 2 -- .../mask/src/plugins/NextID/components/Tip/NFTSection/index.tsx | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/packages/mask/src/plugins/NextID/components/Tip/AddDialog.tsx b/packages/mask/src/plugins/NextID/components/Tip/AddDialog.tsx index f79640d3b0a8..af3802bdc4a0 100644 --- a/packages/mask/src/plugins/NextID/components/Tip/AddDialog.tsx +++ b/packages/mask/src/plugins/NextID/components/Tip/AddDialog.tsx @@ -58,10 +58,8 @@ export const AddDialog: FC = ({ onAdd, ...rest }) => { const network = useMemo(() => allNetworks.find((n) => n.chainId === chainId), [allNetworks, chainId]) const contract = useERC721TokenContract(contractAddress) const { value: isMine, loading: isChecking } = useAsync(async () => { - console.log('contract', contract) if (!contract || !account) return false const ownerAddress = await contract.methods.ownerOf(tokenId).call() - console.log('ownerAddress', ownerAddress) return isSameAddress(ownerAddress, account) }, [contract, tokenId, account]) diff --git a/packages/mask/src/plugins/NextID/components/Tip/NFTSection/index.tsx b/packages/mask/src/plugins/NextID/components/Tip/NFTSection/index.tsx index ca6065100f7a..7a0fcb04eec8 100644 --- a/packages/mask/src/plugins/NextID/components/Tip/NFTSection/index.tsx +++ b/packages/mask/src/plugins/NextID/components/Tip/NFTSection/index.tsx @@ -52,7 +52,7 @@ export const NFTSection: FC = ({ className, ...rest }) => { const networkDescriptor = useNetworkDescriptor() - // Can't not get the loading status of fetching via websocket + // Cannot get the loading status of fetching via websocket // loading status of `useAsyncRetry` is not the real status const [guessLoading, setGuessLoading] = useState(true) useTimeoutFn(() => { From 918d6a00e035a787729cf0b011fab298e6ea36eb Mon Sep 17 00:00:00 2001 From: unclebill Date: Sat, 2 Apr 2022 23:09:16 +0800 Subject: [PATCH 11/19] fix: reset txstate before sending --- .../web3-shared/evm/hooks/useERC721TokenTransferCallback.ts | 6 +++--- packages/web3-shared/evm/utils/transaction.ts | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/web3-shared/evm/hooks/useERC721TokenTransferCallback.ts b/packages/web3-shared/evm/hooks/useERC721TokenTransferCallback.ts index 3a02ac7e9644..57c2dc2087d5 100644 --- a/packages/web3-shared/evm/hooks/useERC721TokenTransferCallback.ts +++ b/packages/web3-shared/evm/hooks/useERC721TokenTransferCallback.ts @@ -14,10 +14,10 @@ export function useERC721TokenTransferCallback(address?: string) { const transferCallback = useCallback( async (tokenId?: string, recipient?: string, gasConfig?: GasConfig) => { + setTransferState({ + type: TransactionStateType.UNKNOWN, + }) if (!account || !recipient || !tokenId || !erc721Contract) { - setTransferState({ - type: TransactionStateType.UNKNOWN, - }) return } diff --git a/packages/web3-shared/evm/utils/transaction.ts b/packages/web3-shared/evm/utils/transaction.ts index b18ddf919985..7bce90ccee9c 100644 --- a/packages/web3-shared/evm/utils/transaction.ts +++ b/packages/web3-shared/evm/utils/transaction.ts @@ -10,7 +10,7 @@ export function isFinalState(type: TransactionStateType) { } /** - * UNKNOWN, FAILED -> WAIT_FOR_CONFIRMING + * UNKNOWN -> WAIT_FOR_CONFIRMING * UNKNOWN, WAIT_FOR_CONFIRMING -> HASH * UNKNOWN, WAIT_FOR_CONFIRMING, HASH -> RECEIPT * WAIT_FOR_CONFIRMING, HASH, RECEIPT -> CONFIRMED @@ -21,7 +21,7 @@ export function isNextStateAvailable(type: TransactionStateType, nextType: Trans case TransactionStateType.UNKNOWN: return false case TransactionStateType.WAIT_FOR_CONFIRMING: - return [TransactionStateType.UNKNOWN, TransactionStateType.FAILED].includes(type) + return [TransactionStateType.UNKNOWN].includes(type) case TransactionStateType.HASH: return [TransactionStateType.UNKNOWN, TransactionStateType.WAIT_FOR_CONFIRMING].includes(type) case TransactionStateType.RECEIPT: From 50b7ca3cbef535078f220d5146d858bb14ef9b4f Mon Sep 17 00:00:00 2001 From: unclebill Date: Sun, 3 Apr 2022 15:42:55 +0800 Subject: [PATCH 12/19] fix: ui issues --- .../components/Tip/NFTSection/NFTList.tsx | 45 ++++++++--- .../components/Tip/NFTSection/index.tsx | 75 ++++++++++++++----- .../NextID/components/Tip/TipDialog.tsx | 5 +- .../plugins/NextID/components/Tip/TipForm.tsx | 26 +++++-- .../src/plugins/NextID/locales/en-US.json | 4 +- packages/plugin-infra/src/web3-types.ts | 13 ++++ .../src/UI/components/AssetPlayer/index.tsx | 5 +- .../NFTCardStyledAssetPlayer/index.tsx | 14 ++-- 8 files changed, 136 insertions(+), 51 deletions(-) diff --git a/packages/mask/src/plugins/NextID/components/Tip/NFTSection/NFTList.tsx b/packages/mask/src/plugins/NextID/components/Tip/NFTSection/NFTList.tsx index 5013d4f817e2..0d67fa38c908 100644 --- a/packages/mask/src/plugins/NextID/components/Tip/NFTSection/NFTList.tsx +++ b/packages/mask/src/plugins/NextID/components/Tip/NFTSection/NFTList.tsx @@ -1,8 +1,8 @@ -import type { Web3Plugin } from '@masknet/plugin-infra' +import { useWeb3State, Web3Plugin } from '@masknet/plugin-infra' import { NFTCardStyledAssetPlayer } from '@masknet/shared' import { makeStyles, ShadowRootTooltip } from '@masknet/theme' import { formatNFT_TokenId, useChainId } from '@masknet/web3-shared-evm' -import { Checkbox, List, ListItem, Radio } from '@mui/material' +import { Checkbox, Link, List, ListItem, Radio } from '@mui/material' import classnames from 'classnames' import { noop } from 'lodash-unified' import { FC, useCallback } from 'react' @@ -29,12 +29,12 @@ const useStyles = makeStyles()((theme) => ({ nftItem: { position: 'relative', cursor: 'pointer', - background: theme.palette.mode === 'light' ? '#fff' : '#2F3336', + background: theme.palette.mode === 'light' ? '#EDEFEF' : '#2F3336', display: 'flex', overflow: 'hidden', padding: 0, flexDirection: 'column', - borderRadius: 8, + borderRadius: 12, height: 100, userSelect: 'none', width: 100, @@ -56,7 +56,7 @@ const useStyles = makeStyles()((theme) => ({ boxSizing: 'border-box', width: '100%', height: '100%', - borderRadius: 8, + borderRadius: 12, }, }, unselected: { @@ -66,6 +66,18 @@ const useStyles = makeStyles()((theme) => ({ width: 64, height: 64, }, + assetPlayerIframe: { + height: 100, + width: 100, + }, + imgWrapper: { + height: 100, + width: 100, + img: { + height: '100%', + width: '100%', + }, + }, })) interface NFTItemProps { @@ -79,10 +91,12 @@ export const NFTItem: FC = ({ token }) => { ) @@ -102,11 +116,20 @@ export const NFTList: FC = ({ selectedIds, tokens, onChange, limit = 1, c ) const SelectComponent = isRadio ? Radio : Checkbox + const { Utils } = useWeb3State() return ( {tokens.map((token) => { const disabled = !isRadio && reachedLimit && !selectedIds.includes(token.tokenId) + const link = + Utils?.resolveNonFungibleTokenLink && token.contract + ? Utils.resolveNonFungibleTokenLink( + token.contract?.chainId, + token.contract?.address, + token.tokenId, + ) + : undefined return ( = ({ selectedIds, tokens, onChange, limit = 1, c [classes.disabled]: disabled, [classes.selected]: selectedIds.includes(token.tokenId), [classes.unselected]: selectedIds.length > 0 && !selectedIds.includes(token.tokenId), - })} - onClick={() => { - if (disabled || !token.contract?.address) return - toggleItem(token.tokenId, token.contract.address) - }}> - + })}> + + + ({ display: 'flex', flexDirection: 'column', overflow: 'auto', + height: 282, }, selectSection: { display: 'flex', flexDirection: 'column', overflow: 'auto', }, + statusBox: { + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + justifyContent: 'center', + height: 282, + }, + loadingText: { + marginTop: theme.spacing(1), + }, list: { flexGrow: 1, maxHeight: 400, overflow: 'auto', borderRadius: 4, }, - loadingOwnerList: { - margin: '24px auto 16px', - }, errorMessage: { marginTop: theme.spacing(3), fontSize: 12, @@ -41,11 +50,15 @@ const useStyles = makeStyles()((theme) => ({ }, })) -interface Props extends HTMLProps {} +interface Props extends HTMLProps { + onAddToken?(): void + onEmpty?(empty: boolean): void +} -export const NFTSection: FC = ({ className, ...rest }) => { +export const NFTSection: FC = ({ className, onAddToken, onEmpty, ...rest }) => { const { storedTokens, erc721TokenId, setErc721TokenId, setErc721Address } = useTip() const { classes } = useStyles() + const t = useI18N() const account = useAccount() const selectedIds = useMemo(() => (erc721TokenId ? [erc721TokenId] : []), [erc721TokenId]) const { Asset } = useWeb3PluginState() @@ -90,24 +103,46 @@ export const NFTSection: FC = ({ className, ...rest }) => { }) }, [storedTokens, fetchedTokens]) - const showLoadingIndicator = tokens.length === 0 && (loading || guessLoading) + const showLoadingIndicator = tokens.length === 0 && !loading && !guessLoading + + useEffect(() => { + onEmpty?.(showLoadingIndicator) + }, [onEmpty, showLoadingIndicator]) return (
- {showLoadingIndicator ? ( - - ) : ( - { - setErc721TokenId(id) - setErc721Address(address) - }} - /> - )} + {(() => { + if (tokens.length) { + return ( + { + setErc721TokenId(id) + setErc721Address(address) + }} + /> + ) + } + if (loading || guessLoading) { + return ( +
+ + {t.tip_loading()} +
+ ) + } + return ( +
+ {t.tip_empty_nft()} + +
+ ) + })()}
) diff --git a/packages/mask/src/plugins/NextID/components/Tip/TipDialog.tsx b/packages/mask/src/plugins/NextID/components/Tip/TipDialog.tsx index 5a3488ac065d..647e51860743 100644 --- a/packages/mask/src/plugins/NextID/components/Tip/TipDialog.tsx +++ b/packages/mask/src/plugins/NextID/components/Tip/TipDialog.tsx @@ -30,6 +30,9 @@ import { AddDialog } from './AddDialog' import { TipForm } from './TipForm' const useStyles = makeStyles()((theme) => ({ + dialog: { + width: 600, + }, dialogTitle: { height: 60, }, @@ -279,7 +282,7 @@ export function TipDialog({ open = false, onClose }: TipDialogProps) { diff --git a/packages/mask/src/plugins/NextID/components/Tip/TipForm.tsx b/packages/mask/src/plugins/NextID/components/Tip/TipForm.tsx index 02fcc2c29a71..d0d122253993 100644 --- a/packages/mask/src/plugins/NextID/components/Tip/TipForm.tsx +++ b/packages/mask/src/plugins/NextID/components/Tip/TipForm.tsx @@ -16,7 +16,7 @@ import { Typography, } from '@mui/material' import classnames from 'classnames' -import { FC, memo, useRef } from 'react' +import { FC, memo, useRef, useState } from 'react' import ActionButton from '../../../../extension/options-page/DashboardComponents/ActionButton' import { EthereumChainBoundary } from '../../../../web3/UI/EthereumChainBoundary' import { TargetChainIdContext, useTip, useTipValidate } from '../../contexts' @@ -37,6 +37,19 @@ const useStyles = makeStyles()((theme) => { flexGrow: 1, overflow: 'auto', }, + receiverRow: { + display: 'flex', + flexDirection: 'row', + alignItems: 'center', + }, + to: { + fontSize: 19, + fontWeight: 500, + }, + address: { + flexGrow: 1, + marginLeft: theme.spacing(1), + }, actionButton: { marginTop: theme.spacing(1.5), fontSize: 16, @@ -98,6 +111,7 @@ export const TipForm: FC = memo(({ className, onAddToken, ...rest }) => { const { openDialog: openSelectProviderDialog } = useRemoteControlledDialog( WalletMessages.events.selectProviderDialogUpdated, ) + const [empty, setEmpty] = useState(false) const buttonLabel = isSending ? t.sending_tip() : isValid || !validateMessage ? t.send_tip() : validateMessage const enabledNft = @@ -108,10 +122,10 @@ export const TipForm: FC = memo(({ className, onAddToken, ...rest }) => { return (
- {t.tip_to()} - - + + {t.tip_to()}