Skip to content

Commit 985e74a

Browse files
guanbinruiswkatmaskJack-Works
authored
[Release] New release 2.28.0 (minor) (#11909)
* chore: bump version to v2.28.0 * fix: sort tokens in token picker (#11911) * fix: sort tokens in token picker * fix: mf-6477 do not show swap button for unsupported chain * fixup! fix: sort tokens in token picker * fix: mf-6482 merge all lens and fids (#11912) * fix: mf-6481 some feeds might render empty * fix: mf-6482 merge all lens and fids * fix: mf-6483 avatar from API could be outdate (#11913) * fix(Web3Profile): mf-6484 initial pending config (#11914) * fix: mf-6486 mf-6487 trader ui issues (#11915) * fix: NaN close MF-6478 * fix: follow up owner reviews (#11916) * fix: specific token picker title for trader * fix: do not pass toToken for native token * fix: network fee * fix: network fee * fix: run codegen --------- Co-authored-by: swkatmask <[email protected]> * feat: add add search box for token picker (#11919) * feat: add add search box for token picker * fix: add metis name * fixup! feat: add add search box for token picker * fix: guarantee persona proofs (#11921) closes #11920 --------- Co-authored-by: Wukong Sun <[email protected]> Co-authored-by: Jack Works <[email protected]> Co-authored-by: swkatmask <[email protected]>
1 parent 9481c1b commit 985e74a

File tree

45 files changed

+423
-122
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

45 files changed

+423
-122
lines changed

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
"yarn": ">=999.0.0",
99
"npm": ">=999.0.0"
1010
},
11-
"version": "2.27.1",
11+
"version": "2.28.0",
1212
"private": true,
1313
"license": "AGPL-3.0-or-later",
1414
"scripts": {

packages/mask/content-script/site-adaptors/twitter.com/collecting/identity.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,9 @@ function resolveCurrentVisitingIdentityInner(
116116
const handle = legacy.screen_name
117117
const ownerHandle = ownerRef.value.identifier?.userId
118118
const isOwner = !!ownerHandle && handle.toLowerCase() === ownerHandle.toLowerCase()
119-
const avatar = legacy.profile_image_url_https
119+
const domAvatar = document.querySelector(`a[href="/${handle}/photo"] img`)
120+
// DOM avatar is more accurate, avatar from api could be outdate
121+
const avatar = domAvatar?.getAttribute('src') || legacy.profile_image_url_https
120122
const bio = legacy.profile_image_url_https
121123
const homepage = legacy.entities.url?.urls?.[0]?.expanded_url
122124

packages/mask/popups/components/SocialAccounts/index.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { Trans } from '@lingui/macro'
12
import { Icons } from '@masknet/icons'
23
import { PopupModalRoutes, type EnhanceableSite, type ProfileAccount } from '@masknet/shared-base'
34
import { makeStyles } from '@masknet/theme'
@@ -6,7 +7,6 @@ import { memo } from 'react'
67
import { AccountAvatar } from '../../pages/Personas/components/AccountAvatar/index.js'
78
import { useModalNavigate } from '../ActionModal/index.js'
89
import { ConnectSocialAccounts } from '../ConnectSocialAccounts/index.js'
9-
import { Trans } from '@lingui/macro'
1010

1111
const useStyles = makeStyles()((theme) => ({
1212
tips: {

packages/mask/popups/components/TokenPicker/TokenItem.tsx

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { Trans } from '@lingui/macro'
12
import { Icons } from '@masknet/icons'
23
import { NetworkIcon, ProgressiveText, TokenIcon } from '@masknet/shared'
34
import { NetworkPluginID } from '@masknet/shared-base'
@@ -18,9 +19,8 @@ import {
1819
useForkRef,
1920
type ListItemProps,
2021
} from '@mui/material'
21-
import { memo, useEffect, useMemo, useRef } from 'react'
22+
import { memo, useMemo, useRef } from 'react'
2223
import { formatTokenBalance } from '../../../shared/index.js'
23-
import { Trans } from '@lingui/macro'
2424

2525
const useStyles = makeStyles()((theme) => {
2626
return {
@@ -99,10 +99,6 @@ export const TokenItem = memo(function TokenItem({
9999
}, [asset.address, asset.chainId, Utils.explorerResolver.fungibleTokenLink])
100100

101101
const liRef = useRef<HTMLLIElement>(null)
102-
useEffect(() => {
103-
if (!selected) return
104-
liRef.current?.scrollIntoView()
105-
}, [selected])
106102

107103
// #region Try getting balance through RPC.
108104
const providerURL = network?.isCustomized ? network.rpcUrl : undefined

packages/mask/popups/components/TokenPicker/index.tsx

Lines changed: 134 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,22 @@
1-
import { SelectNetworkSidebar } from '@masknet/shared'
1+
import { t, Trans } from '@lingui/macro'
2+
import { Icons } from '@masknet/icons'
3+
import { EmptyStatus, SelectNetworkSidebar } from '@masknet/shared'
24
import { EMPTY_LIST, NetworkPluginID } from '@masknet/shared-base'
3-
import { makeStyles } from '@masknet/theme'
5+
import { makeStyles, MaskTextField } from '@masknet/theme'
46
import type { Web3Helper } from '@masknet/web3-helpers'
5-
import { useFungibleAssets, useNetworks, useWallet } from '@masknet/web3-hooks-base'
7+
import { useAccount, useFungibleAssets, useNetworks, useUserTokenBalances, useWallet } from '@masknet/web3-hooks-base'
68
import { useOKXTokenList } from '@masknet/web3-hooks-evm'
7-
import { isSameAddress, type ReasonableNetwork } from '@masknet/web3-shared-base'
8-
import { ChainId } from '@masknet/web3-shared-evm'
9+
import {
10+
isEqual,
11+
isGreaterThan,
12+
isSameAddress,
13+
multipliedBy,
14+
rightShift,
15+
type ReasonableNetwork,
16+
} from '@masknet/web3-shared-base'
17+
import { ChainId, getMaskTokenAddress, getNativeTokenAddress } from '@masknet/web3-shared-evm'
918
import { Box, type BoxProps } from '@mui/material'
19+
import Fuse from 'fuse.js'
1020
import { memo, useCallback, useMemo, useState } from 'react'
1121
import { FixedSizeList, type ListChildComponentProps } from 'react-window'
1222
import { TokenItem, type TokenItemProps } from './TokenItem.js'
@@ -52,6 +62,13 @@ const useStyles = makeStyles()((theme) => {
5262
sidebar: {
5363
paddingRight: theme.spacing(1),
5464
},
65+
content: {
66+
flexGrow: 1,
67+
display: 'flex',
68+
flexDirection: 'column',
69+
gap: theme.spacing(1),
70+
boxSizing: 'border-box',
71+
},
5572
}
5673
})
5774

@@ -90,16 +107,56 @@ export const TokenPicker = memo(function TokenPicker({
90107
const [standardAssets] = useFungibleAssets(NetworkPluginID.PLUGIN_EVM, undefined, {
91108
chainId,
92109
})
93-
const { data: okxTokens } = useOKXTokenList(chainId, assetSource === AssetSource.Okx)
110+
const isFromOkx = assetSource === AssetSource.Okx
111+
const { data: okxTokens } = useOKXTokenList(chainId, isFromOkx)
112+
const account = useAccount(NetworkPluginID.PLUGIN_EVM)
113+
const { data: balances } = useUserTokenBalances(chainId, account, isFromOkx)
94114
const okxAssets = useMemo(() => {
95115
if (!okxTokens?.length) return EMPTY_LIST
96-
const balanceMap = new Map(standardAssets.map((x) => [x.address.toLowerCase(), x.balance]))
97-
// To reduce queries, get balance from standardAssets and patch okxTokens with it
98-
return okxTokens.map((x) => {
99-
const balance = balanceMap.get(x.address.toLowerCase())
100-
return !balance || balance === '0' ? x : { ...x, balance }
101-
}) as typeof okxTokens
102-
}, [okxTokens, standardAssets])
116+
if (!balances) {
117+
const balanceMap = new Map(standardAssets.map((x) => [x.address.toLowerCase(), x.balance]))
118+
// To reduce queries, get balance from standardAssets and patch okxTokens with it
119+
return okxTokens.map((x) => {
120+
const balance = balanceMap.get(x.address.toLowerCase())
121+
return !balance || balance === '0' ? x : { ...x, balance }
122+
}) as typeof okxTokens
123+
} else {
124+
const assets = okxTokens.map((x) => {
125+
const balance = balances.get(x.address.toLowerCase())
126+
return !balance ? x : { ...x, balance: rightShift(balance.balance, x.decimals).toFixed(0) }
127+
}) as Array<Web3Helper.FungibleAssetScope<void, NetworkPluginID.PLUGIN_EVM>> // typeof okxTokens
128+
return assets.sort((a, z) => {
129+
// native token
130+
const isNativeTokenA = isSameAddress(a.address, getNativeTokenAddress(a.chainId))
131+
if (isNativeTokenA) return -1
132+
const isNativeTokenZ = isSameAddress(z.address, getNativeTokenAddress(z.chainId))
133+
if (isNativeTokenZ) return 1
134+
135+
const aBalance = balances.get(a.address.toLowerCase())
136+
const zBalance = balances.get(z.address.toLowerCase())
137+
const isMaskTokenA = isSameAddress(a.address, getMaskTokenAddress(a.chainId))
138+
const isMaskTokenZ = isSameAddress(z.address, getMaskTokenAddress(z.chainId))
139+
// mask token with position value
140+
const aUSD = multipliedBy(aBalance?.balance ?? 0, aBalance?.tokenPrice ?? 0)
141+
if (aUSD.isPositive() && isMaskTokenA) return -1
142+
const zUSD = multipliedBy(zBalance?.balance ?? 0, zBalance?.tokenPrice ?? 0)
143+
if (zUSD.isPositive() && isMaskTokenZ) return 1
144+
145+
// token value
146+
if (!aUSD.isEqualTo(zUSD)) return zUSD.gt(aUSD) ? 1 : -1
147+
148+
// token balance
149+
if (!isEqual(aBalance?.balance || 0, zBalance?.balance || 0))
150+
return isGreaterThan(zBalance?.balance || 0, aBalance?.balance || 0) ? 1 : -1
151+
152+
// mask token with position value
153+
if (isMaskTokenA) return -1
154+
if (isMaskTokenZ) return 1
155+
156+
return 0
157+
})
158+
}
159+
}, [okxTokens, standardAssets, balances])
103160
const assets = assetSource === AssetSource.Okx ? okxAssets : standardAssets
104161
const handleChainChange = useCallback(
105162
(chainId: Web3Helper.ChainIdAll | undefined) => {
@@ -108,17 +165,32 @@ export const TokenPicker = memo(function TokenPicker({
108165
},
109166
[onChainChange],
110167
)
168+
const [keyword, setKeyword] = useState('')
111169
const availableAssets = useMemo(() => {
112170
if (!sidebarChainId) return assets
113171
return assets.filter((x) => x.chainId === sidebarChainId)
114172
}, [assets, sidebarChainId])
173+
const fuse = useMemo(() => {
174+
return new Fuse(availableAssets, {
175+
shouldSort: true,
176+
isCaseSensitive: false,
177+
threshold: 0.45,
178+
minMatchCharLength: 1,
179+
keys: ['address', 'symbol', 'name'],
180+
})
181+
}, [availableAssets])
182+
const filteredAssets = useMemo(() => {
183+
if (!keyword) return availableAssets
184+
return fuse.search(keyword).map((x) => x.item)
185+
}, [fuse, keyword])
115186

116187
const isSmartPay = !!useWallet()?.owner
117188
const networks = useNetworks(NetworkPluginID.PLUGIN_EVM, true)
118189
const filteredNetworks = useMemo(() => {
119190
const list = isSmartPay ? networks.filter((x) => x.chainId === ChainId.Polygon && !x.isCustomized) : networks
120191
return chains ? list.filter((x) => chains.includes(x.chainId)) : list
121192
}, [chains, networks, isSmartPay])
193+
const selectedIndex = filteredAssets.findIndex((x) => x.chainId === chainId && isSameAddress(x.address, address))
122194

123195
return (
124196
<Box className={cx(classes.picker, className)} {...rest}>
@@ -132,28 +204,55 @@ export const TokenPicker = memo(function TokenPicker({
132204
onChainChange={handleChainChange}
133205
/>
134206
: null}
135-
<FixedSizeList
136-
itemCount={availableAssets.length}
137-
itemSize={71}
138-
height={455}
139-
overscanCount={20}
140-
itemData={{
141-
tokens: availableAssets,
142-
networks: filteredNetworks,
143-
chainId,
144-
address,
145-
onSelect,
146-
}}
147-
itemKey={(index, data) => {
148-
const asset = data.tokens[index]
149-
return `${asset.chainId}.${asset.address}`
150-
}}
151-
style={{
152-
scrollbarWidth: 'none',
153-
}}
154-
width="100%">
155-
{Row}
156-
</FixedSizeList>
207+
<div className={classes.content}>
208+
<MaskTextField
209+
value={keyword}
210+
placeholder={t`Name or Contract address e.g. USDC or 0x234...`}
211+
autoFocus
212+
fullWidth
213+
wrapperProps={{
214+
padding: '2px',
215+
}}
216+
InputProps={{
217+
style: { height: 40 },
218+
inputProps: { style: { paddingLeft: 4 } },
219+
startAdornment: <Icons.Search size={18} />,
220+
endAdornment: keyword ? <Icons.Close size={18} onClick={() => setKeyword('')} /> : null,
221+
}}
222+
onChange={(e) => {
223+
setKeyword(e.target.value)
224+
}}
225+
/>
226+
{keyword && !filteredAssets.length ?
227+
<EmptyStatus flexGrow={1} alignItems="center">
228+
<Trans>No matched tokens</Trans>
229+
</EmptyStatus>
230+
: <FixedSizeList
231+
itemCount={filteredAssets.length}
232+
itemSize={71}
233+
height={403}
234+
overscanCount={20}
235+
// show half of previous token
236+
initialScrollOffset={Math.max(0, selectedIndex - 0.5) * 71}
237+
itemData={{
238+
tokens: filteredAssets,
239+
networks: filteredNetworks,
240+
chainId,
241+
address,
242+
onSelect,
243+
}}
244+
itemKey={(index, data) => {
245+
const asset = data.tokens[index]
246+
return `${asset.chainId}.${asset.address}`
247+
}}
248+
style={{
249+
scrollbarWidth: 'none',
250+
}}
251+
width="100%">
252+
{Row}
253+
</FixedSizeList>
254+
}
255+
</div>
157256
</Box>
158257
)
159258
})

packages/mask/popups/modals/ChooseToken/index.tsx

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
import type { SingletonModalProps } from '@masknet/shared-base'
22
import { useSingletonModal } from '@masknet/shared-base-ui'
33
import type { Web3Helper } from '@masknet/web3-helpers'
4-
import { memo, useState } from 'react'
4+
import { memo, useState, type ReactNode } from 'react'
55
import { BottomDrawer, TokenPicker, type BottomDrawerProps, type TokenPickerProps } from '../../components/index.js'
66
import { Trans } from '@lingui/macro'
77

8-
interface ChooseTokenModalProps extends BottomDrawerProps, Omit<TokenPickerProps, 'title' | 'classes'> {}
8+
interface ChooseTokenModalProps extends Omit<BottomDrawerProps, 'title'>, Omit<TokenPickerProps, 'title' | 'classes'> {
9+
title?: ReactNode
10+
}
911
const ChooseTokenDrawer = memo(function ChooseTokenDrawer({ title, open, onClose, ...others }: ChooseTokenModalProps) {
1012
return (
1113
<BottomDrawer title={title} open={open} onClose={onClose}>
@@ -20,7 +22,7 @@ const ChooseTokenDrawer = memo(function ChooseTokenDrawer({ title, open, onClose
2022
)
2123
})
2224

23-
export type ChooseTokenModalOpenProps = Omit<ChooseTokenModalProps, 'title' | 'open'>
25+
export type ChooseTokenModalOpenProps = Omit<ChooseTokenModalProps, 'open'>
2426
export type ChooseTokenModalCloseProps = Web3Helper.FungibleAssetAll | Web3Helper.FungibleTokenAll | void
2527
export function ChooseTokenModal({ ref }: SingletonModalProps<ChooseTokenModalOpenProps, ChooseTokenModalCloseProps>) {
2628
const [props, setProps] = useState<ChooseTokenModalOpenProps>({})
@@ -33,9 +35,9 @@ export function ChooseTokenModal({ ref }: SingletonModalProps<ChooseTokenModalOp
3335

3436
return (
3537
<ChooseTokenDrawer
38+
title={<Trans>Choose Token</Trans>}
3639
{...props}
3740
open={open}
38-
title={<Trans>Choose Token</Trans>}
3941
onClose={() => dispatch?.close()}
4042
onSelect={(asset) => dispatch?.close(asset)}
4143
/>

packages/mask/popups/pages/Friends/common.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ export const PlatformUrlMap: Record<SupportedPlatforms, string> = {
2222
[NextIDPlatform.Unstoppable]: 'https://ud.me/',
2323
[NextIDPlatform.GitHub]: 'https://github.com/',
2424
[NextIDPlatform.SpaceId]: 'https://bscscan.com/address/',
25-
[NextIDPlatform.Farcaster]: 'https://warpcast.com/',
26-
[NextIDPlatform.LENS]: 'https://lenster.xyz/u/',
25+
[NextIDPlatform.Farcaster]: 'https://firefly.mask.social/profile/farcaster/',
26+
[NextIDPlatform.LENS]: 'https://firefly.mask.social/profile/lens/',
2727
[NextIDPlatform.Ethereum]: 'https://etherscan.io/address/',
2828
[NextIDPlatform.Keybase]: 'https://keybase.io/',
2929
}

packages/mask/popups/pages/Trader/useImplementRuntime.ts renamed to packages/mask/popups/pages/Trader/useImplementRuntime.tsx

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
1+
import { Trans } from '@lingui/macro'
12
import { useSupportedChains, useTrade, type ShowTooltipOptions } from '@masknet/plugin-trader'
3+
import { usePopupCustomSnackbar } from '@masknet/theme'
24
import type { Web3Helper } from '@masknet/web3-helpers'
35
import { TokenType } from '@masknet/web3-shared-base'
46
import { isNativeTokenAddress, SchemaType, type ChainId } from '@masknet/web3-shared-evm'
57
import { useCallback, useMemo } from 'react'
6-
import { ChooseTokenModal, ConfirmModal } from '../../modals/modal-controls.js'
7-
import { usePopupCustomSnackbar } from '@masknet/theme'
8-
import { usePopupTheme } from '../../hooks/usePopupTheme.js'
98
import { AssetSource } from '../../components/index.js'
9+
import { usePopupTheme } from '../../hooks/usePopupTheme.js'
10+
import { ChooseTokenModal, ConfirmModal } from '../../modals/modal-controls.js'
1011

1112
export function useImplementRuntime() {
1213
const chainQuery = useSupportedChains()
@@ -23,6 +24,7 @@ export function useImplementRuntime() {
2324
const supportedChains = chainQuery.data ?? (await chainQuery.refetch()).data
2425

2526
const picked = await ChooseTokenModal.openAndWaitForClose({
27+
title: <Trans>Select</Trans>,
2628
// Only from token can decide the chain
2729
chainId: ((isSwap ? fromChainId : currentToken?.chainId) || chainId) as ChainId,
2830
address: currentToken?.address,

0 commit comments

Comments
 (0)