@unisat/wallet-state 1.0.4 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (99) hide show
  1. package/LICENSE +0 -5
  2. package/lib/index.d.mts +2012 -200
  3. package/lib/index.d.ts +2012 -200
  4. package/lib/index.js +6951 -570
  5. package/lib/index.js.map +1 -1
  6. package/lib/index.mjs +6754 -517
  7. package/lib/index.mjs.map +1 -1
  8. package/lib/types/index.d.mts +1 -1
  9. package/lib/types/index.d.ts +1 -1
  10. package/package.json +19 -15
  11. package/src/.DS_Store +0 -0
  12. package/src/context/ApprovalContext.tsx +27 -0
  13. package/src/context/DeviceContext.tsx +36 -0
  14. package/src/context/I18nContext.tsx +14 -172
  15. package/src/context/NavigationContext.tsx +305 -0
  16. package/src/context/PriceContext.tsx +2 -2
  17. package/src/context/StorageContext.tsx +393 -0
  18. package/src/context/ToolsContext.tsx +50 -0
  19. package/src/context/WalletContext.tsx +119 -108
  20. package/src/context/index.ts +17 -2
  21. package/src/hooks/accounts.ts +11 -5
  22. package/src/hooks/browser.ts +11 -0
  23. package/src/hooks/global.ts +170 -7
  24. package/src/hooks/index.ts +1 -2
  25. package/src/hooks/settings.ts +28 -37
  26. package/src/hooks/transactions.ts +28 -155
  27. package/src/hooks/ui.ts +232 -36
  28. package/src/index.ts +33 -24
  29. package/src/reducers/accounts.ts +19 -2
  30. package/src/reducers/browser.ts +223 -0
  31. package/src/reducers/global.ts +67 -1
  32. package/src/reducers/index.ts +1 -0
  33. package/src/reducers/transactions.ts +0 -9
  34. package/src/reducers/ui.ts +127 -8
  35. package/src/types/index.ts +1 -1
  36. package/src/ui-hooks/index.ts +107 -0
  37. package/src/ui-hooks/useActionOverviewSectionLogic.ts +150 -0
  38. package/src/ui-hooks/useAddressTypeScreenLogic.ts +160 -0
  39. package/src/ui-hooks/useAlkanesBalanceCardLogic.ts +41 -0
  40. package/src/ui-hooks/useAlkanesCollectionListLogic.ts +68 -0
  41. package/src/ui-hooks/useAlkanesListLogic.ts +69 -0
  42. package/src/ui-hooks/useAlkanesNFTListLogic.ts +42 -0
  43. package/src/ui-hooks/useAlkanesNFTScreenLogic.ts +45 -0
  44. package/src/ui-hooks/useAlkanesTokenScreenLogic.ts +130 -0
  45. package/src/ui-hooks/useAmountInputLogic.ts +80 -0
  46. package/src/ui-hooks/useAnnouncementCardLogic.ts +91 -0
  47. package/src/ui-hooks/useBRC20BalanceCardLogic.ts +115 -0
  48. package/src/ui-hooks/useBRC20InscribeTransferLogic.ts +398 -0
  49. package/src/ui-hooks/useBRC20ListLogic.ts +75 -0
  50. package/src/ui-hooks/useBRC20ProgListLogic.ts +77 -0
  51. package/src/ui-hooks/useBRC20SendScreenLogic.ts +411 -0
  52. package/src/ui-hooks/useBRC20SingleStepScreenLogic.ts +208 -0
  53. package/src/ui-hooks/useBRC20TokenScreenLogic.ts +469 -0
  54. package/src/ui-hooks/useBalanceCardLogic.ts +164 -0
  55. package/src/ui-hooks/useBtcDisplayLogic.ts +16 -0
  56. package/src/ui-hooks/useCAT20BalanceCardLogic.ts +35 -0
  57. package/src/ui-hooks/useCAT20ListLogic.ts +83 -0
  58. package/src/ui-hooks/useCAT20TokenScreenLogic.ts +122 -0
  59. package/src/ui-hooks/useCAT721ListLogic.ts +68 -0
  60. package/src/ui-hooks/useCAT721NFTScreenLogic.ts +37 -0
  61. package/src/ui-hooks/useCreatePasswordScreenLogic.ts +92 -0
  62. package/src/ui-hooks/useCreateWalletLogicImportWordsStep.ts +299 -0
  63. package/src/ui-hooks/useEditAccountNameScreenLogic.ts +71 -0
  64. package/src/ui-hooks/useEditContactScreenLogic.ts +162 -0
  65. package/src/ui-hooks/useEditWalletNameScreenLogic.ts +58 -0
  66. package/src/ui-hooks/useExportMnemonicsScreenLogic.ts +75 -0
  67. package/src/ui-hooks/useExportPrivateKeyScreenLogic.ts +64 -0
  68. package/src/ui-hooks/useFeeRateBarLogic.ts +303 -0
  69. package/src/ui-hooks/useInfiniteList.ts +85 -0
  70. package/src/ui-hooks/useInscriptionListLogic.ts +68 -0
  71. package/src/ui-hooks/useLockTimePageLogic.ts +43 -0
  72. package/src/ui-hooks/useNotificationsLogic.ts +115 -0
  73. package/src/ui-hooks/useOrdinalsInscriptionScreenLogic.ts +130 -0
  74. package/src/ui-hooks/useRunesBalanceCardLogic.ts +44 -0
  75. package/src/ui-hooks/useRunesListLogic.ts +74 -0
  76. package/src/ui-hooks/useRunesTokenScreenLogic.ts +149 -0
  77. package/src/ui-hooks/useSecurityCardLogic.ts +0 -0
  78. package/src/ui-hooks/useSendAlkanesNFTScreenLogic.ts +138 -0
  79. package/src/ui-hooks/useSendAlkanesScreenLogic.ts +192 -0
  80. package/src/ui-hooks/useSendCAT20ScreenLogic.ts +297 -0
  81. package/src/ui-hooks/useSendCAT721ScreenLogic.ts +205 -0
  82. package/src/ui-hooks/useSendOrdinalsInscriptionScreenLogic.ts +137 -0
  83. package/src/ui-hooks/useSendRunesScreenLogic.ts +172 -0
  84. package/src/ui-hooks/useSettingsTabScreenLogic.ts +211 -0
  85. package/src/ui-hooks/useSignMessageLogic.ts +302 -0
  86. package/src/ui-hooks/useSignPsbtLogic.ts +517 -0
  87. package/src/ui-hooks/useSplitOrdinalsInscriptionScreenLogic.ts +95 -0
  88. package/src/ui-hooks/useTxConfirmScreenLogic.ts +47 -0
  89. package/src/ui-hooks/useTxCreateScreenLogic.ts +161 -0
  90. package/src/ui-hooks/useTxFailScreenLogic.ts +26 -0
  91. package/src/ui-hooks/useTxSuccessScreenLogic.ts +33 -0
  92. package/src/updater/accounts.ts +11 -11
  93. package/src/utils/bitcoin-utils.ts +17 -8
  94. package/src/utils/eventBus.ts +2 -1
  95. package/src/utils/password-utils.ts +78 -0
  96. package/src/utils/ui-utils.ts +28 -0
  97. package/src/hooks/approval.ts +0 -72
  98. package/src/hooks/i18n.ts +0 -53
  99. package/src/utils/i18n.ts +0 -41
@@ -0,0 +1,398 @@
1
+ import { numUtils } from '@unisat/base-utils'
2
+ import {
3
+ InscribeOrder,
4
+ SignedData,
5
+ SignPsbtParams,
6
+ TokenBalance,
7
+ TokenInfo,
8
+ ToSignData,
9
+ } from '@unisat/wallet-shared'
10
+ import BigNumber from 'bignumber.js'
11
+ import { useCallback, useEffect, useMemo, useState } from 'react'
12
+ import { useApproval, useI18n, useNavigation, useTools, useWallet } from 'src/context'
13
+ import {
14
+ useBTCUnit,
15
+ useCurrentAccount,
16
+ useFeeRateBar,
17
+ useFetchUtxosCallback,
18
+ usePrepareSendBypassHeadOffsetsCallback,
19
+ usePushBitcoinTxCallback,
20
+ useUpdateFeeRateBar,
21
+ } from 'src/hooks'
22
+ import { getAddressUtxoDust } from 'src/utils/bitcoin-utils'
23
+
24
+ enum Step {
25
+ STEP1,
26
+ STEP2,
27
+ STEP3,
28
+ STEP4,
29
+ }
30
+
31
+ interface ContextData {
32
+ step: Step
33
+ ticker: string
34
+ session?: any
35
+ tokenBalance?: TokenBalance
36
+ order?: InscribeOrder
37
+ toSignData?: ToSignData
38
+ amount?: string
39
+ isApproval: boolean
40
+ tokenInfo?: TokenInfo
41
+ amountEditable?: boolean
42
+ }
43
+
44
+ interface UpdateContextDataParams {
45
+ step?: Step
46
+ ticket?: string
47
+ session?: any
48
+ tokenBalance?: TokenBalance
49
+ order?: InscribeOrder
50
+ toSignData?: ToSignData
51
+ amount?: string
52
+ tokenInfo?: TokenInfo
53
+ amountEditable?: boolean
54
+ }
55
+
56
+ export interface BRC20InscribeTransferParams {
57
+ contextData: ContextData
58
+ updateContextData: (params: UpdateContextDataParams) => void
59
+ }
60
+
61
+ export function useBRC20InscribeTransferLogic() {
62
+ const nav = useNavigation()
63
+ const { ticker } = nav.getRouteState<'BRC20InscribeTransfer'>()
64
+
65
+ const [contextData, setContextData] = useState<ContextData>({
66
+ step: Step.STEP1,
67
+ ticker: ticker,
68
+ isApproval: false,
69
+ })
70
+ const updateContextData = useCallback(
71
+ (params: UpdateContextDataParams) => {
72
+ setContextData(Object.assign({}, contextData, params))
73
+ },
74
+ [contextData, setContextData]
75
+ )
76
+ return {
77
+ contextData,
78
+ updateContextData,
79
+ }
80
+ }
81
+
82
+ export function useBRC20InscribeTransferLogicStep1(params: BRC20InscribeTransferParams) {
83
+ const { contextData, updateContextData } = params
84
+ const { t } = useI18n()
85
+ const nav = useNavigation()
86
+
87
+ const { rejectApproval } = useApproval()
88
+
89
+ const handleCancel = () => {
90
+ rejectApproval(t('user_rejected_the_request'))
91
+ }
92
+
93
+ const wallet = useWallet()
94
+ const account = useCurrentAccount()
95
+ const feeRateBarState = useFeeRateBar()
96
+ const updateFeeRateBar = useUpdateFeeRateBar()
97
+ const [inputAmount, setInputAmount] = useState('')
98
+
99
+ const tools = useTools()
100
+ const prepareSendBypassHeadOffsets = usePrepareSendBypassHeadOffsetsCallback()
101
+
102
+ const fetchUtxos = useFetchUtxosCallback()
103
+
104
+ const [loading, setLoading] = useState(true)
105
+ const [loadingOnly, setLoadingOnly] = useState(false)
106
+ const [inputError, setInputError] = useState('')
107
+ const [inputErrorAvailable, setInputErrorAvailable] = useState('')
108
+
109
+ const [disabled, setDisabled] = useState(true)
110
+
111
+ const [inputDisabled, setInputDisabled] = useState(false)
112
+
113
+ const defaultOutputValue = 546
114
+ // const defaultOutputValue = getAddressUtxoDust(account.address);
115
+
116
+ const [outputValue, setOutputValue] = useState<number>(defaultOutputValue)
117
+
118
+ useEffect(() => {
119
+ if (contextData.amount) {
120
+ setInputAmount(contextData.amount.toString())
121
+ setInputDisabled(true)
122
+ }
123
+ }, [])
124
+
125
+ useEffect(() => {
126
+ setInputError('')
127
+ setInputErrorAvailable('')
128
+ setDisabled(true)
129
+ if (!inputAmount) {
130
+ return
131
+ }
132
+
133
+ if (inputAmount.split('.').length > 1) {
134
+ const decimal = inputAmount.split('.')[1].length
135
+ const token_decimal = contextData.tokenInfo?.decimal || 0
136
+ if (decimal > token_decimal) {
137
+ setInputError(
138
+ `${t('this_token_only_supports_up_to')} ${token_decimal} ${t('decimal_places')}`
139
+ )
140
+ return
141
+ }
142
+ }
143
+
144
+ const amount = new BigNumber(inputAmount)
145
+ if (!amount) {
146
+ return
147
+ }
148
+
149
+ if (!contextData.tokenBalance) {
150
+ return
151
+ }
152
+
153
+ if (amount.lte(0)) {
154
+ return
155
+ }
156
+
157
+ if (amount.gt(contextData.tokenBalance.availableBalanceSafe)) {
158
+ setInputErrorAvailable(t('insufficient_balance'))
159
+ return
160
+ }
161
+
162
+ if (feeRateBarState.feeRate <= 0) {
163
+ return
164
+ }
165
+
166
+ const dust = getAddressUtxoDust(account.address)
167
+ if (outputValue < dust) {
168
+ setInputError(`${t('output_value_must_be_at_least')} ${dust}`)
169
+ return
170
+ }
171
+
172
+ if (!outputValue) {
173
+ return
174
+ }
175
+
176
+ setDisabled(false)
177
+ }, [inputAmount, feeRateBarState.feeRate, outputValue, contextData.tokenBalance])
178
+
179
+ useEffect(() => {
180
+ fetchUtxos()
181
+
182
+ wallet
183
+ .getBRC20Summary(account.address, contextData.ticker)
184
+ .then(v => {
185
+ updateContextData({ tokenBalance: v.tokenBalance, tokenInfo: v.tokenInfo })
186
+ setTimeout(() => {
187
+ setLoading(false)
188
+ }, 100)
189
+ })
190
+ .catch(e => {
191
+ tools.toastError(e.message)
192
+ })
193
+ }, [])
194
+
195
+ const onClickInscribe = async () => {
196
+ try {
197
+ tools.showLoading(true)
198
+ const amount = inputAmount
199
+ const order = await wallet.inscribeBRC20Transfer(
200
+ account.address,
201
+ contextData.ticker,
202
+ amount,
203
+ feeRateBarState.feeRate,
204
+ outputValue
205
+ )
206
+
207
+ const toSignData = await prepareSendBypassHeadOffsets({
208
+ toAddressInfo: { address: order.payAddress, domain: '' },
209
+ toAmount: order.totalFee,
210
+ feeRate: feeRateBarState.feeRate,
211
+ })
212
+ updateContextData({ order, amount, toSignData, step: Step.STEP2 })
213
+ } catch (e) {
214
+ tools.toastError((e as Error).message.replace('Error:', ''))
215
+ } finally {
216
+ tools.showLoading(false)
217
+ }
218
+ }
219
+
220
+ return {
221
+ onClickInscribe,
222
+ loading,
223
+ t,
224
+ nav,
225
+ inputAmount,
226
+ inputError,
227
+ setInputAmount,
228
+ inputDisabled,
229
+ inputErrorAvailable,
230
+ defaultOutputValue,
231
+ setOutputValue,
232
+ disabled,
233
+ loadingOnly,
234
+ handleCancel,
235
+ }
236
+ }
237
+
238
+ export function useBRC20InscribeTransferLogicStep2(params: BRC20InscribeTransferParams) {
239
+ const { contextData } = params
240
+ const { order, tokenBalance, amount, toSignData, session } = contextData
241
+ const btcUnit = useBTCUnit()
242
+ const { t } = useI18n()
243
+
244
+ if (!order || !tokenBalance || !toSignData) {
245
+ return {
246
+ isEmpty: true,
247
+ networkFee: '0',
248
+ outputValue: '0',
249
+ minerFee: '0',
250
+ originServiceFee: '0',
251
+ serviceFee: '0',
252
+ totalFee: '0',
253
+ btcUnit,
254
+ t,
255
+ session,
256
+ amount,
257
+ tokenBalance,
258
+ }
259
+ }
260
+
261
+ const fee = toSignData.estimatedFee || 0
262
+ const networkFee = useMemo(() => numUtils.satoshisToAmount(fee), [fee])
263
+ const outputValue = useMemo(
264
+ () => numUtils.satoshisToAmount(order.outputValue),
265
+ [order.outputValue]
266
+ )
267
+ const minerFee = useMemo(() => numUtils.satoshisToAmount(order.minerFee + fee), [order.minerFee])
268
+ const originServiceFee = useMemo(
269
+ () => numUtils.satoshisToAmount(order.originServiceFee),
270
+ [order.originServiceFee]
271
+ )
272
+ const serviceFee = useMemo(() => numUtils.satoshisToAmount(order.serviceFee), [order.serviceFee])
273
+ const totalFee = useMemo(() => numUtils.satoshisToAmount(order.totalFee + fee), [order.totalFee])
274
+
275
+ return {
276
+ networkFee,
277
+ outputValue,
278
+ minerFee,
279
+ originServiceFee,
280
+ serviceFee,
281
+ totalFee,
282
+ btcUnit,
283
+ t,
284
+ session,
285
+ amount,
286
+ tokenBalance,
287
+ order,
288
+ toSignData,
289
+ }
290
+ }
291
+
292
+ export function useBRC20InscribeTransferLogicStep3(params: BRC20InscribeTransferParams) {
293
+ const { contextData, updateContextData } = params
294
+
295
+ const pushBitcoinTx = usePushBitcoinTxCallback()
296
+ const nav = useNavigation()
297
+
298
+ const onHeaderBack = () => {
299
+ updateContextData({
300
+ step: Step.STEP2,
301
+ })
302
+ }
303
+
304
+ const onSignPsbtHandleCancel = () => {
305
+ updateContextData({
306
+ step: Step.STEP2,
307
+ })
308
+ }
309
+ const signPsbtParams: SignPsbtParams = {
310
+ data: {
311
+ toSignDatas: [contextData.toSignData!],
312
+ },
313
+ }
314
+
315
+ const tools = useTools()
316
+ const onSignPsbtHandleConfirm = async (signedDatas: SignedData[]) => {
317
+ tools.showLoading(true)
318
+ try {
319
+ const { success, txid, error } = await pushBitcoinTx(signedDatas[0].psbtHex)
320
+ if (success) {
321
+ nav.navigate('TxSuccessScreen', { txid })
322
+ } else {
323
+ throw new Error(error)
324
+ }
325
+ return
326
+ } catch (e) {
327
+ nav.navigate('TxFailScreen', { error: (e as any).message })
328
+ } finally {
329
+ tools.showLoading(false)
330
+ }
331
+ }
332
+
333
+ return {
334
+ signPsbtParams,
335
+ onSignPsbtHandleConfirm,
336
+ onSignPsbtHandleCancel,
337
+ onHeaderBack,
338
+ }
339
+ }
340
+
341
+ export function useBRC20InscribeTransferLogicStep4(params: BRC20InscribeTransferParams) {
342
+ const { contextData } = params
343
+ const { tokenBalance, order } = contextData
344
+ const tools = useTools()
345
+ const wallet = useWallet()
346
+ const currentAccount = useCurrentAccount()
347
+ const { resolveApproval, rejectApproval } = useApproval()
348
+ const nav = useNavigation()
349
+ const [result, setResult] = useState<any>()
350
+ const { t } = useI18n()
351
+ const checkResult = async () => {
352
+ const result = await wallet.getInscribeResult(order.orderId)
353
+ if (!result) {
354
+ setTimeout(() => {
355
+ checkResult()
356
+ }, 2000)
357
+ return
358
+ }
359
+ tools.showLoading(false)
360
+ setResult(result)
361
+ }
362
+
363
+ useEffect(() => {
364
+ checkResult()
365
+ }, [])
366
+
367
+ const onClickConfirm = () => {
368
+ tools.showLoading(true)
369
+ wallet
370
+ .getBRC20Summary(currentAccount.address, tokenBalance.ticker)
371
+ .then(v => {
372
+ if (contextData.isApproval) {
373
+ resolveApproval({
374
+ inscriptionId: result.inscriptionId,
375
+ inscriptionNumber: result.inscriptionNumber,
376
+ ticker: tokenBalance.ticker,
377
+ amount: result.amount,
378
+ })
379
+ } else {
380
+ nav.navigate('BRC20SendScreen', {
381
+ tokenBalance: v.tokenBalance,
382
+ selectedInscriptionIds: [result.inscriptionId],
383
+ selectedAmount: result.amount,
384
+ tokenInfo: v.tokenInfo,
385
+ })
386
+ }
387
+ })
388
+ .finally(() => {
389
+ tools.showLoading(false)
390
+ })
391
+ }
392
+
393
+ return {
394
+ t,
395
+ result,
396
+ onClickConfirm,
397
+ }
398
+ }
@@ -0,0 +1,75 @@
1
+ import { TickPriceItem, TokenBalance } from '@unisat/wallet-shared'
2
+ import { useEffect, useRef, useState } from 'react'
3
+ import {
4
+ OrdinalsAssetTabKey,
5
+ useChainType,
6
+ useCurrentAccount,
7
+ useNavigation,
8
+ useOrdinalsAssetTabKey,
9
+ useWallet,
10
+ useWallTabFocusRefresh,
11
+ } from '..'
12
+ import { useInfiniteList } from './useInfiniteList'
13
+
14
+ export function useBRC20ListLogic() {
15
+ const nav = useNavigation()
16
+ const wallet = useWallet()
17
+ const currentAccount = useCurrentAccount()
18
+ const chainType = useChainType()
19
+
20
+ const [priceMap, setPriceMap] = useState<{ [key: string]: TickPriceItem }>({})
21
+
22
+ const priceMapRef = useRef(priceMap)
23
+ const updatePrices = (res: { [tick: string]: TickPriceItem }) => {
24
+ const newPriceMap = { ...priceMapRef.current }
25
+ Object.keys(res).forEach(tick => {
26
+ newPriceMap[tick] = res[tick]
27
+ })
28
+ priceMapRef.current = newPriceMap
29
+ setPriceMap(newPriceMap)
30
+ }
31
+
32
+ const {
33
+ data: items,
34
+ total,
35
+ loading,
36
+ hasMore,
37
+ onRefresh,
38
+ onLoadMore,
39
+ } = useInfiniteList<TokenBalance>({
40
+ fetcher: async (page, pageSize) => {
41
+ if (currentAccount.address === '') {
42
+ return { list: [], total: 0 }
43
+ }
44
+ const { list, total } = await wallet.getBRC20List(currentAccount.address, page, pageSize)
45
+ if (list.length > 0) {
46
+ wallet.getBrc20sPrice(list.map(item => item.ticker)).then(updatePrices)
47
+ }
48
+ return { list, total }
49
+ },
50
+ dependencies: [currentAccount.address, chainType],
51
+ })
52
+
53
+ const tabKey = useOrdinalsAssetTabKey()
54
+ const isFocus = tabKey === OrdinalsAssetTabKey.BRC20
55
+ const lastRefreshTimeRef = useRef<number>(0)
56
+ const walletTabFocusRefresh = useWallTabFocusRefresh()
57
+ useEffect(() => {
58
+ if (!isFocus) return
59
+
60
+ // already refreshed → do nothing
61
+ const alreadyRefreshed = lastRefreshTimeRef.current === walletTabFocusRefresh
62
+ if (alreadyRefreshed) return
63
+
64
+ onRefresh()
65
+
66
+ // mark refreshed
67
+ lastRefreshTimeRef.current = walletTabFocusRefresh
68
+ }, [walletTabFocusRefresh, isFocus])
69
+
70
+ const onClickItem = (item: TokenBalance) => {
71
+ nav.navigate('BRC20TokenScreen', { tokenBalance: item, ticker: item.ticker })
72
+ }
73
+
74
+ return { items, total, loading, hasMore, onRefresh, onLoadMore, onClickItem, priceMap }
75
+ }
@@ -0,0 +1,77 @@
1
+ import { TickPriceItem, TokenBalance } from '@unisat/wallet-shared'
2
+ import { useEffect, useRef, useState } from 'react'
3
+ import {
4
+ getSupportedAssets,
5
+ OrdinalsAssetTabKey,
6
+ useChainType,
7
+ useCurrentAccount,
8
+ useNavigation,
9
+ useOrdinalsAssetTabKey,
10
+ useWallet,
11
+ useWallTabFocusRefresh,
12
+ } from '..'
13
+ import { useInfiniteList } from './useInfiniteList'
14
+
15
+ export function useBRC20ProgListLogic() {
16
+ const nav = useNavigation()
17
+ const wallet = useWallet()
18
+ const currentAccount = useCurrentAccount()
19
+ const chainType = useChainType()
20
+ const [priceMap, setPriceMap] = useState<{ [key: string]: TickPriceItem }>({})
21
+
22
+ const priceMapRef = useRef(priceMap)
23
+ const updatePrices = (res: { [tick: string]: TickPriceItem }) => {
24
+ const newPriceMap = { ...priceMapRef.current }
25
+ Object.keys(res).forEach(tick => {
26
+ newPriceMap[tick] = res[tick]
27
+ })
28
+ priceMapRef.current = newPriceMap
29
+ setPriceMap(newPriceMap)
30
+ }
31
+
32
+ const {
33
+ data: items,
34
+ total,
35
+ loading,
36
+ hasMore,
37
+ onRefresh,
38
+ onLoadMore,
39
+ } = useInfiniteList<TokenBalance>({
40
+ fetcher: async (page, pageSize) => {
41
+ const supportedAssets = getSupportedAssets(chainType, currentAccount.address)
42
+ if (!supportedAssets.assets.brc20Prog || currentAccount.address === '') {
43
+ return { list: [], total: 0 }
44
+ }
45
+
46
+ const { list, total } = await wallet.getBRC20ProgList(currentAccount.address, page, pageSize)
47
+ if (list.length > 0) {
48
+ wallet.getBrc20sPrice(list.map(item => item.ticker)).then(updatePrices)
49
+ }
50
+ return { list, total }
51
+ },
52
+ dependencies: [currentAccount.address, chainType],
53
+ })
54
+
55
+ const tabKey = useOrdinalsAssetTabKey()
56
+ const isFocus = tabKey === OrdinalsAssetTabKey.BRC20_6BYTE
57
+ const lastRefreshTimeRef = useRef<number>(0)
58
+ const walletTabFocusRefresh = useWallTabFocusRefresh()
59
+ useEffect(() => {
60
+ if (!isFocus) return
61
+
62
+ // already refreshed → do nothing
63
+ const alreadyRefreshed = lastRefreshTimeRef.current === walletTabFocusRefresh
64
+ if (alreadyRefreshed) return
65
+
66
+ onRefresh()
67
+
68
+ // mark refreshed
69
+ lastRefreshTimeRef.current = walletTabFocusRefresh
70
+ }, [walletTabFocusRefresh, isFocus])
71
+
72
+ const onClickItem = (item: TokenBalance) => {
73
+ nav.navigate('BRC20TokenScreen', { tokenBalance: item, ticker: item.ticker })
74
+ }
75
+
76
+ return { items, total, loading, hasMore, onRefresh, onLoadMore, onClickItem, priceMap }
77
+ }