@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.
- package/LICENSE +0 -5
- package/lib/index.d.mts +2012 -200
- package/lib/index.d.ts +2012 -200
- package/lib/index.js +6951 -570
- package/lib/index.js.map +1 -1
- package/lib/index.mjs +6754 -517
- package/lib/index.mjs.map +1 -1
- package/lib/types/index.d.mts +1 -1
- package/lib/types/index.d.ts +1 -1
- package/package.json +19 -15
- package/src/.DS_Store +0 -0
- package/src/context/ApprovalContext.tsx +27 -0
- package/src/context/DeviceContext.tsx +36 -0
- package/src/context/I18nContext.tsx +14 -172
- package/src/context/NavigationContext.tsx +305 -0
- package/src/context/PriceContext.tsx +2 -2
- package/src/context/StorageContext.tsx +393 -0
- package/src/context/ToolsContext.tsx +50 -0
- package/src/context/WalletContext.tsx +119 -108
- package/src/context/index.ts +17 -2
- package/src/hooks/accounts.ts +11 -5
- package/src/hooks/browser.ts +11 -0
- package/src/hooks/global.ts +170 -7
- package/src/hooks/index.ts +1 -2
- package/src/hooks/settings.ts +28 -37
- package/src/hooks/transactions.ts +28 -155
- package/src/hooks/ui.ts +232 -36
- package/src/index.ts +33 -24
- package/src/reducers/accounts.ts +19 -2
- package/src/reducers/browser.ts +223 -0
- package/src/reducers/global.ts +67 -1
- package/src/reducers/index.ts +1 -0
- package/src/reducers/transactions.ts +0 -9
- package/src/reducers/ui.ts +127 -8
- package/src/types/index.ts +1 -1
- package/src/ui-hooks/index.ts +107 -0
- package/src/ui-hooks/useActionOverviewSectionLogic.ts +150 -0
- package/src/ui-hooks/useAddressTypeScreenLogic.ts +160 -0
- package/src/ui-hooks/useAlkanesBalanceCardLogic.ts +41 -0
- package/src/ui-hooks/useAlkanesCollectionListLogic.ts +68 -0
- package/src/ui-hooks/useAlkanesListLogic.ts +69 -0
- package/src/ui-hooks/useAlkanesNFTListLogic.ts +42 -0
- package/src/ui-hooks/useAlkanesNFTScreenLogic.ts +45 -0
- package/src/ui-hooks/useAlkanesTokenScreenLogic.ts +130 -0
- package/src/ui-hooks/useAmountInputLogic.ts +80 -0
- package/src/ui-hooks/useAnnouncementCardLogic.ts +91 -0
- package/src/ui-hooks/useBRC20BalanceCardLogic.ts +115 -0
- package/src/ui-hooks/useBRC20InscribeTransferLogic.ts +398 -0
- package/src/ui-hooks/useBRC20ListLogic.ts +75 -0
- package/src/ui-hooks/useBRC20ProgListLogic.ts +77 -0
- package/src/ui-hooks/useBRC20SendScreenLogic.ts +411 -0
- package/src/ui-hooks/useBRC20SingleStepScreenLogic.ts +208 -0
- package/src/ui-hooks/useBRC20TokenScreenLogic.ts +469 -0
- package/src/ui-hooks/useBalanceCardLogic.ts +164 -0
- package/src/ui-hooks/useBtcDisplayLogic.ts +16 -0
- package/src/ui-hooks/useCAT20BalanceCardLogic.ts +35 -0
- package/src/ui-hooks/useCAT20ListLogic.ts +83 -0
- package/src/ui-hooks/useCAT20TokenScreenLogic.ts +122 -0
- package/src/ui-hooks/useCAT721ListLogic.ts +68 -0
- package/src/ui-hooks/useCAT721NFTScreenLogic.ts +37 -0
- package/src/ui-hooks/useCreatePasswordScreenLogic.ts +92 -0
- package/src/ui-hooks/useCreateWalletLogicImportWordsStep.ts +299 -0
- package/src/ui-hooks/useEditAccountNameScreenLogic.ts +71 -0
- package/src/ui-hooks/useEditContactScreenLogic.ts +162 -0
- package/src/ui-hooks/useEditWalletNameScreenLogic.ts +58 -0
- package/src/ui-hooks/useExportMnemonicsScreenLogic.ts +75 -0
- package/src/ui-hooks/useExportPrivateKeyScreenLogic.ts +64 -0
- package/src/ui-hooks/useFeeRateBarLogic.ts +303 -0
- package/src/ui-hooks/useInfiniteList.ts +85 -0
- package/src/ui-hooks/useInscriptionListLogic.ts +68 -0
- package/src/ui-hooks/useLockTimePageLogic.ts +43 -0
- package/src/ui-hooks/useNotificationsLogic.ts +115 -0
- package/src/ui-hooks/useOrdinalsInscriptionScreenLogic.ts +130 -0
- package/src/ui-hooks/useRunesBalanceCardLogic.ts +44 -0
- package/src/ui-hooks/useRunesListLogic.ts +74 -0
- package/src/ui-hooks/useRunesTokenScreenLogic.ts +149 -0
- package/src/ui-hooks/useSecurityCardLogic.ts +0 -0
- package/src/ui-hooks/useSendAlkanesNFTScreenLogic.ts +138 -0
- package/src/ui-hooks/useSendAlkanesScreenLogic.ts +192 -0
- package/src/ui-hooks/useSendCAT20ScreenLogic.ts +297 -0
- package/src/ui-hooks/useSendCAT721ScreenLogic.ts +205 -0
- package/src/ui-hooks/useSendOrdinalsInscriptionScreenLogic.ts +137 -0
- package/src/ui-hooks/useSendRunesScreenLogic.ts +172 -0
- package/src/ui-hooks/useSettingsTabScreenLogic.ts +211 -0
- package/src/ui-hooks/useSignMessageLogic.ts +302 -0
- package/src/ui-hooks/useSignPsbtLogic.ts +517 -0
- package/src/ui-hooks/useSplitOrdinalsInscriptionScreenLogic.ts +95 -0
- package/src/ui-hooks/useTxConfirmScreenLogic.ts +47 -0
- package/src/ui-hooks/useTxCreateScreenLogic.ts +161 -0
- package/src/ui-hooks/useTxFailScreenLogic.ts +26 -0
- package/src/ui-hooks/useTxSuccessScreenLogic.ts +33 -0
- package/src/updater/accounts.ts +11 -11
- package/src/utils/bitcoin-utils.ts +17 -8
- package/src/utils/eventBus.ts +2 -1
- package/src/utils/password-utils.ts +78 -0
- package/src/utils/ui-utils.ts +28 -0
- package/src/hooks/approval.ts +0 -72
- package/src/hooks/i18n.ts +0 -53
- package/src/utils/i18n.ts +0 -41
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import { useEffect, useMemo, useState } from 'react'
|
|
2
|
+
|
|
3
|
+
import { numUtils } from '@unisat/base-utils'
|
|
4
|
+
import { COIN_DUST } from '@unisat/wallet-shared'
|
|
5
|
+
import {
|
|
6
|
+
useAccountBalance,
|
|
7
|
+
useBTCUnit,
|
|
8
|
+
useChain,
|
|
9
|
+
useFeeRateBar,
|
|
10
|
+
useFetchUtxosCallback,
|
|
11
|
+
useI18n,
|
|
12
|
+
useNavigation,
|
|
13
|
+
usePrepareSendBTCCallback,
|
|
14
|
+
useTools,
|
|
15
|
+
useUiTxCreateScreen,
|
|
16
|
+
useUpdateUiTxCreateScreen,
|
|
17
|
+
useWalletConfig,
|
|
18
|
+
} from '..'
|
|
19
|
+
import { isValidAddress } from '../utils/bitcoin-utils'
|
|
20
|
+
|
|
21
|
+
export function useTxCreateScreenLogic() {
|
|
22
|
+
const { t, isSpecialLocale } = useI18n()
|
|
23
|
+
const accountBalance = useAccountBalance()
|
|
24
|
+
const nav = useNavigation()
|
|
25
|
+
const btcUnit = useBTCUnit()
|
|
26
|
+
|
|
27
|
+
const [disabled, setDisabled] = useState(true)
|
|
28
|
+
|
|
29
|
+
const setUiState = useUpdateUiTxCreateScreen()
|
|
30
|
+
const uiState = useUiTxCreateScreen()
|
|
31
|
+
const feeRateBarState = useFeeRateBar()
|
|
32
|
+
|
|
33
|
+
const toInfo = uiState.toInfo
|
|
34
|
+
const inputAmount = uiState.inputAmount
|
|
35
|
+
const feeRate = feeRateBarState.feeRate
|
|
36
|
+
|
|
37
|
+
const [error, setError] = useState('')
|
|
38
|
+
|
|
39
|
+
const [autoAdjust, setAutoAdjust] = useState(false)
|
|
40
|
+
const fetchUtxos = useFetchUtxosCallback()
|
|
41
|
+
|
|
42
|
+
const tools = useTools()
|
|
43
|
+
useEffect(() => {
|
|
44
|
+
tools.showLoading(true)
|
|
45
|
+
fetchUtxos().finally(() => {
|
|
46
|
+
tools.showLoading(false)
|
|
47
|
+
})
|
|
48
|
+
}, [])
|
|
49
|
+
|
|
50
|
+
const prepareSendBTC = usePrepareSendBTCCallback()
|
|
51
|
+
|
|
52
|
+
const toSatoshis = useMemo(() => {
|
|
53
|
+
if (!inputAmount) return 0
|
|
54
|
+
return numUtils.amountToSatoshis(inputAmount)
|
|
55
|
+
}, [inputAmount])
|
|
56
|
+
|
|
57
|
+
const dustAmount = useMemo(() => numUtils.satoshisToAmount(COIN_DUST), [COIN_DUST])
|
|
58
|
+
|
|
59
|
+
const availableAmount = numUtils.satoshisToAmount(accountBalance.availableBalance)
|
|
60
|
+
const unavailableAmount = numUtils.satoshisToAmount(accountBalance.unavailableBalance)
|
|
61
|
+
|
|
62
|
+
const showUnavailable = accountBalance.unavailableBalance > 0
|
|
63
|
+
|
|
64
|
+
const chain = useChain()
|
|
65
|
+
useEffect(() => {
|
|
66
|
+
setError('')
|
|
67
|
+
setDisabled(true)
|
|
68
|
+
if (!isValidAddress(toInfo.address)) {
|
|
69
|
+
return
|
|
70
|
+
}
|
|
71
|
+
if (!toSatoshis) {
|
|
72
|
+
return
|
|
73
|
+
}
|
|
74
|
+
if (toSatoshis < COIN_DUST) {
|
|
75
|
+
setError(`${t('amount_must_be_at_least')} ${dustAmount} ${btcUnit}`)
|
|
76
|
+
return
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (toSatoshis > accountBalance.availableBalance) {
|
|
80
|
+
setError(t('amount_exceeds_your_available_balance'))
|
|
81
|
+
return
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (feeRate <= 0) {
|
|
85
|
+
return
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
setDisabled(false)
|
|
89
|
+
}, [toInfo, inputAmount, feeRate])
|
|
90
|
+
|
|
91
|
+
const walletConfig = useWalletConfig()
|
|
92
|
+
|
|
93
|
+
const unavailableTipText = useMemo(() => {
|
|
94
|
+
let tipText = ''
|
|
95
|
+
tipText += t('unavailable_tooltip')
|
|
96
|
+
|
|
97
|
+
if (walletConfig.disableUtxoTools) {
|
|
98
|
+
tipText += t('future_versions_will_support_spending_these_assets')
|
|
99
|
+
} else {
|
|
100
|
+
tipText += t('you_can_unlock_these_assets_by_using_the_utxos_tools')
|
|
101
|
+
}
|
|
102
|
+
return tipText
|
|
103
|
+
}, [chain.enum])
|
|
104
|
+
|
|
105
|
+
const headerTitle = `${t('send')} ${btcUnit}`
|
|
106
|
+
|
|
107
|
+
const onAddressInputChange = val => setUiState({ toInfo: val })
|
|
108
|
+
|
|
109
|
+
const onAmountInputChange = amount => {
|
|
110
|
+
if (autoAdjust == true) {
|
|
111
|
+
setAutoAdjust(false)
|
|
112
|
+
}
|
|
113
|
+
setUiState({ inputAmount: amount })
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const onAmountMaxClick = () => {
|
|
117
|
+
setAutoAdjust(true)
|
|
118
|
+
setUiState({ inputAmount: availableAmount.toString() })
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const onClickNext = () => {
|
|
122
|
+
prepareSendBTC({ toAddressInfo: toInfo, toAmount: toSatoshis, feeRate })
|
|
123
|
+
.then(toSignData => {
|
|
124
|
+
nav.navigate('TxConfirmScreen', {
|
|
125
|
+
toSignData,
|
|
126
|
+
})
|
|
127
|
+
})
|
|
128
|
+
.catch(e => {
|
|
129
|
+
console.log(e)
|
|
130
|
+
setError(e.message)
|
|
131
|
+
})
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return {
|
|
135
|
+
headerTitle,
|
|
136
|
+
chain,
|
|
137
|
+
|
|
138
|
+
toInfo,
|
|
139
|
+
onAddressInputChange,
|
|
140
|
+
|
|
141
|
+
toSatoshis,
|
|
142
|
+
inputAmount,
|
|
143
|
+
onAmountInputChange,
|
|
144
|
+
onAmountMaxClick,
|
|
145
|
+
|
|
146
|
+
showUnavailable,
|
|
147
|
+
availableAmount,
|
|
148
|
+
unavailableAmount,
|
|
149
|
+
unavailableTipText,
|
|
150
|
+
btcUnit,
|
|
151
|
+
t,
|
|
152
|
+
|
|
153
|
+
walletConfig,
|
|
154
|
+
isSpecialLocale,
|
|
155
|
+
|
|
156
|
+
error,
|
|
157
|
+
disabled,
|
|
158
|
+
|
|
159
|
+
onClickNext,
|
|
160
|
+
}
|
|
161
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { useI18n, useNavigation } from 'src/context'
|
|
2
|
+
|
|
3
|
+
export function useTxFailScreenLogic() {
|
|
4
|
+
const nav = useNavigation()
|
|
5
|
+
const { error } = nav.getRouteState<'TxFailScreen'>()
|
|
6
|
+
const { t } = useI18n()
|
|
7
|
+
|
|
8
|
+
const onClickBack = () => {
|
|
9
|
+
nav.goBack()
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const onClickDone = () => {
|
|
13
|
+
nav.navigate('TabMainScreen')
|
|
14
|
+
}
|
|
15
|
+
return {
|
|
16
|
+
// info
|
|
17
|
+
error,
|
|
18
|
+
|
|
19
|
+
// actions
|
|
20
|
+
onClickBack,
|
|
21
|
+
onClickDone,
|
|
22
|
+
|
|
23
|
+
// tools
|
|
24
|
+
t,
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { useI18n, useNavigation, useTools } from 'src/context'
|
|
2
|
+
|
|
3
|
+
export function useTxSuccessScreenLogic() {
|
|
4
|
+
const nav = useNavigation()
|
|
5
|
+
const { txid } = nav.getRouteState<'TxSuccessScreen'>()
|
|
6
|
+
const { t } = useI18n()
|
|
7
|
+
const tools = useTools()
|
|
8
|
+
|
|
9
|
+
const onClickExploreTx = () => {
|
|
10
|
+
nav.navToExplorerTx(txid)
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const onClickDone = () => {
|
|
14
|
+
nav.navigate('MainScreen')
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const onClickCopy = () => {
|
|
18
|
+
tools.copyToClipboard(txid)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return {
|
|
22
|
+
// data
|
|
23
|
+
txid,
|
|
24
|
+
|
|
25
|
+
// actions
|
|
26
|
+
onClickExploreTx,
|
|
27
|
+
onClickDone,
|
|
28
|
+
onClickCopy,
|
|
29
|
+
|
|
30
|
+
// tools
|
|
31
|
+
t,
|
|
32
|
+
}
|
|
33
|
+
}
|
package/src/updater/accounts.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useCallback, useEffect, useRef } from 'react'
|
|
2
2
|
|
|
3
|
-
import { Account } from '@unisat/wallet-shared'
|
|
4
|
-
import
|
|
3
|
+
import { Account, BUS_METHODS } from '@unisat/wallet-shared'
|
|
4
|
+
import { uiEventBus } from '../utils/eventBus'
|
|
5
5
|
|
|
6
6
|
import { useIsUnlocked } from '../hooks/global'
|
|
7
7
|
import { globalActions } from '../reducers/global'
|
|
@@ -18,7 +18,7 @@ export function AccountUpdater() {
|
|
|
18
18
|
const currentAccount = useCurrentAccount()
|
|
19
19
|
const isUnlocked = useIsUnlocked()
|
|
20
20
|
const selfRef = useRef({
|
|
21
|
-
preAccountKey: '',
|
|
21
|
+
preAccountKey: '_',
|
|
22
22
|
loadingBalance: false,
|
|
23
23
|
loadingHistory: false,
|
|
24
24
|
})
|
|
@@ -61,9 +61,9 @@ export function AccountUpdater() {
|
|
|
61
61
|
dispatch((accountActions as any).setCurrent(account))
|
|
62
62
|
}
|
|
63
63
|
}
|
|
64
|
-
|
|
64
|
+
uiEventBus.addEventListener(BUS_METHODS.ACCOUNTS_CHANGED, accountChangeHandler)
|
|
65
65
|
return () => {
|
|
66
|
-
|
|
66
|
+
uiEventBus.removeEventListener(BUS_METHODS.ACCOUNTS_CHANGED, accountChangeHandler)
|
|
67
67
|
}
|
|
68
68
|
}, [dispatch])
|
|
69
69
|
|
|
@@ -77,9 +77,9 @@ export function AccountUpdater() {
|
|
|
77
77
|
|
|
78
78
|
reloadAccounts()
|
|
79
79
|
}
|
|
80
|
-
|
|
80
|
+
uiEventBus.addEventListener(BUS_METHODS.CHAIN_CHANGED, chaintChangeHandler)
|
|
81
81
|
return () => {
|
|
82
|
-
|
|
82
|
+
uiEventBus.removeEventListener(BUS_METHODS.CHAIN_CHANGED, chaintChangeHandler)
|
|
83
83
|
}
|
|
84
84
|
}, [dispatch])
|
|
85
85
|
|
|
@@ -87,9 +87,9 @@ export function AccountUpdater() {
|
|
|
87
87
|
const lockHandler = () => {
|
|
88
88
|
dispatch(globalActions.update({ isUnlocked: false }))
|
|
89
89
|
}
|
|
90
|
-
|
|
90
|
+
uiEventBus.addEventListener(BUS_METHODS.LOCKED, lockHandler)
|
|
91
91
|
return () => {
|
|
92
|
-
|
|
92
|
+
uiEventBus.removeEventListener(BUS_METHODS.LOCKED, lockHandler)
|
|
93
93
|
}
|
|
94
94
|
}, [dispatch])
|
|
95
95
|
|
|
@@ -97,9 +97,9 @@ export function AccountUpdater() {
|
|
|
97
97
|
const unlockHandler = () => {
|
|
98
98
|
dispatch(globalActions.update({ isUnlocked: true }))
|
|
99
99
|
}
|
|
100
|
-
|
|
100
|
+
uiEventBus.addEventListener(BUS_METHODS.UNLOCKED, unlockHandler)
|
|
101
101
|
return () => {
|
|
102
|
-
|
|
102
|
+
uiEventBus.removeEventListener(BUS_METHODS.UNLOCKED, unlockHandler)
|
|
103
103
|
}
|
|
104
104
|
}, [dispatch])
|
|
105
105
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import { addressUtils } from '@unisat/base-utils'
|
|
1
2
|
import { AddressType, NetworkType } from '@unisat/wallet-types'
|
|
2
|
-
|
|
3
|
+
import * as bip39 from 'bip39'
|
|
3
4
|
export function getAddressType(address: string, networkType?: NetworkType) {
|
|
4
5
|
if (address.startsWith('bc1q') || address.startsWith('tb1q')) {
|
|
5
6
|
return AddressType.P2WPKH
|
|
@@ -15,18 +16,17 @@ export function getAddressType(address: string, networkType?: NetworkType) {
|
|
|
15
16
|
}
|
|
16
17
|
|
|
17
18
|
export function isValidAddress(address: string, networkType?: NetworkType) {
|
|
18
|
-
|
|
19
|
-
if (addressType === AddressType.UNKNOWN) {
|
|
20
|
-
return false
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
return true
|
|
19
|
+
return addressUtils.isAddressLikelyValid(address)
|
|
24
20
|
}
|
|
25
21
|
|
|
26
22
|
export function getAddressUtxoDust(address: string) {
|
|
27
23
|
const addressType = getAddressType(address)
|
|
28
24
|
if (addressType === AddressType.P2WPKH) {
|
|
29
|
-
|
|
25
|
+
if (address.length === 42) {
|
|
26
|
+
return 294
|
|
27
|
+
} else {
|
|
28
|
+
return 330
|
|
29
|
+
}
|
|
30
30
|
} else if (addressType === AddressType.P2TR) {
|
|
31
31
|
return 330
|
|
32
32
|
} else {
|
|
@@ -79,3 +79,12 @@ export function isValidHdPath(path: string): boolean {
|
|
|
79
79
|
|
|
80
80
|
return true
|
|
81
81
|
}
|
|
82
|
+
|
|
83
|
+
export function validateMnemonic(mnemonic: string): boolean {
|
|
84
|
+
// do not use bip39.validateMnemonic here to reduce bundle size
|
|
85
|
+
// const words = mnemonic.trim().split(/\s+/);
|
|
86
|
+
// const wordCount = words.length;
|
|
87
|
+
// return [12, 15, 18, 21, 24].includes(wordCount);
|
|
88
|
+
|
|
89
|
+
return bip39.validateMnemonic(mnemonic)
|
|
90
|
+
}
|
package/src/utils/eventBus.ts
CHANGED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
export const MIN_PASSWORD_LENGTH = 8
|
|
2
|
+
export const UNRECOGNIZED_PASSWORD_STRENGTH = 'Unrecognized password strength.'
|
|
3
|
+
|
|
4
|
+
const calculatePasswordStrength = (password: string): number => {
|
|
5
|
+
let score = 0
|
|
6
|
+
|
|
7
|
+
// Length check
|
|
8
|
+
if (password.length >= 8) score += 1
|
|
9
|
+
if (password.length >= 12) score += 1
|
|
10
|
+
if (password.length >= 16) score += 1
|
|
11
|
+
|
|
12
|
+
// Character variety checks
|
|
13
|
+
if (/[a-z]/.test(password)) score += 1 // lowercase
|
|
14
|
+
if (/[A-Z]/.test(password)) score += 1 // uppercase
|
|
15
|
+
if (/[0-9]/.test(password)) score += 1 // numbers
|
|
16
|
+
if (/[^A-Za-z0-9]/.test(password)) score += 1 // special characters
|
|
17
|
+
|
|
18
|
+
// Bonus for good mix
|
|
19
|
+
if (
|
|
20
|
+
password.length >= 10 &&
|
|
21
|
+
/[a-z]/.test(password) &&
|
|
22
|
+
/[A-Z]/.test(password) &&
|
|
23
|
+
/[0-9]/.test(password) &&
|
|
24
|
+
/[^A-Za-z0-9]/.test(password)
|
|
25
|
+
) {
|
|
26
|
+
score += 1
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Penalty for common patterns
|
|
30
|
+
if (/(.)\1{2,}/.test(password)) score -= 1 // repeated characters (aaa, 111)
|
|
31
|
+
if (/123|abc|qwe|asd|zxc/i.test(password)) score -= 1 // common sequences
|
|
32
|
+
if (/password|123456|qwerty/i.test(password)) score -= 2 // common passwords
|
|
33
|
+
|
|
34
|
+
return Math.max(0, Math.min(4, score))
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const colors = {
|
|
38
|
+
red: '#ED334B',
|
|
39
|
+
green: '#41B530',
|
|
40
|
+
orange: '#FF7B21',
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export const getPasswordStrengthWord = (password: string, t) => {
|
|
44
|
+
if (password.length < MIN_PASSWORD_LENGTH) {
|
|
45
|
+
return {
|
|
46
|
+
text: t('not_long_enough'),
|
|
47
|
+
color: colors.red,
|
|
48
|
+
tip: t('password_must_be_at_least_8_characters'),
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const strength = calculatePasswordStrength(password)
|
|
53
|
+
|
|
54
|
+
if (strength <= 1) {
|
|
55
|
+
return {
|
|
56
|
+
text: t('weak'),
|
|
57
|
+
color: colors.red,
|
|
58
|
+
tip: t('strong_password_tip'),
|
|
59
|
+
}
|
|
60
|
+
} else if (strength === 2) {
|
|
61
|
+
return {
|
|
62
|
+
text: t('weak'),
|
|
63
|
+
color: colors.red,
|
|
64
|
+
tip: t('strong_password_tip'),
|
|
65
|
+
}
|
|
66
|
+
} else if (strength === 3) {
|
|
67
|
+
return {
|
|
68
|
+
text: t('average'),
|
|
69
|
+
color: colors.orange,
|
|
70
|
+
tip: t('strong_password_tip'),
|
|
71
|
+
}
|
|
72
|
+
} else {
|
|
73
|
+
return {
|
|
74
|
+
text: t('strong'),
|
|
75
|
+
color: colors.green,
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { useEffect } from 'react'
|
|
2
|
+
|
|
3
|
+
export function shortAddress(address?: string, len = 8) {
|
|
4
|
+
if (!address) return ''
|
|
5
|
+
if (address.length <= len * 2) return address
|
|
6
|
+
return address.slice(0, len) + '...' + address.slice(address.length - len)
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function useAsyncEffect(
|
|
10
|
+
effect: () => Promise<void | (() => void)>,
|
|
11
|
+
deps: React.DependencyList = []
|
|
12
|
+
) {
|
|
13
|
+
useEffect(() => {
|
|
14
|
+
let isMounted = true
|
|
15
|
+
let cleanup: void | (() => void)
|
|
16
|
+
;(async () => {
|
|
17
|
+
cleanup = await effect()
|
|
18
|
+
})()
|
|
19
|
+
|
|
20
|
+
return () => {
|
|
21
|
+
isMounted = false
|
|
22
|
+
if (cleanup) {
|
|
23
|
+
cleanup()
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
27
|
+
}, deps)
|
|
28
|
+
}
|
package/src/hooks/approval.ts
DELETED
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
import { useEffect, useRef, useState } from 'react'
|
|
2
|
-
import { useNavigate } from 'react-router-dom'
|
|
3
|
-
|
|
4
|
-
import { useWallet } from '../context/WalletContext'
|
|
5
|
-
|
|
6
|
-
const UI_TYPE = {
|
|
7
|
-
Tab: 'index',
|
|
8
|
-
Pop: 'popup',
|
|
9
|
-
Notification: 'notification',
|
|
10
|
-
SidePanel: 'sidepanel',
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
type UiTypeCheck = {
|
|
14
|
-
isTab: boolean
|
|
15
|
-
isNotification: boolean
|
|
16
|
-
isPop: boolean
|
|
17
|
-
isSidePanel: boolean
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export const getUiType = (): UiTypeCheck => {
|
|
21
|
-
// @ts-ignore
|
|
22
|
-
const { pathname } = window.location
|
|
23
|
-
return Object.entries(UI_TYPE).reduce((m, [key, value]) => {
|
|
24
|
-
// @ts-ignore
|
|
25
|
-
m[`is${key}`] = pathname === `/${value}.html`
|
|
26
|
-
|
|
27
|
-
return m
|
|
28
|
-
}, {} as UiTypeCheck)
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export const useApproval = () => {
|
|
32
|
-
const wallet = useWallet()
|
|
33
|
-
const navigate = useNavigate()
|
|
34
|
-
const getApproval = wallet.getApproval
|
|
35
|
-
|
|
36
|
-
const resolveApproval = async (data?: any, stay = false, forceReject = false) => {
|
|
37
|
-
const approval = await getApproval()
|
|
38
|
-
|
|
39
|
-
if (approval) {
|
|
40
|
-
wallet.resolveApproval(data, forceReject)
|
|
41
|
-
}
|
|
42
|
-
if (stay) {
|
|
43
|
-
return
|
|
44
|
-
}
|
|
45
|
-
setTimeout(() => {
|
|
46
|
-
navigate('/')
|
|
47
|
-
})
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
const rejectApproval = async (err: any, stay = false, isInternal = false) => {
|
|
51
|
-
const approval = await getApproval()
|
|
52
|
-
if (approval) {
|
|
53
|
-
await wallet.rejectApproval(err, stay, isInternal)
|
|
54
|
-
}
|
|
55
|
-
if (!stay) {
|
|
56
|
-
navigate('/')
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
useEffect(() => {
|
|
61
|
-
if (!getUiType().isNotification) {
|
|
62
|
-
return () => {}
|
|
63
|
-
}
|
|
64
|
-
// @ts-ignore
|
|
65
|
-
window.addEventListener('beforeunload', rejectApproval)
|
|
66
|
-
|
|
67
|
-
// @ts-ignore
|
|
68
|
-
return () => window.removeEventListener('beforeunload', rejectApproval)
|
|
69
|
-
}, [])
|
|
70
|
-
|
|
71
|
-
return [getApproval, resolveApproval, rejectApproval] as const
|
|
72
|
-
}
|
package/src/hooks/i18n.ts
DELETED
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
import { useContext } from 'react'
|
|
2
|
-
|
|
3
|
-
import { I18nContext } from '../context/I18nContext'
|
|
4
|
-
import { FALLBACK_LOCALE, getCurrentLocaleAsync, LOCALE_NAMES } from '@unisat/i18n'
|
|
5
|
-
|
|
6
|
-
const defaultI18nContext = {
|
|
7
|
-
t: (key: string) => key,
|
|
8
|
-
locale: FALLBACK_LOCALE,
|
|
9
|
-
supportedLocales: [FALLBACK_LOCALE],
|
|
10
|
-
localeNames: LOCALE_NAMES,
|
|
11
|
-
changeLocale: async () => {
|
|
12
|
-
/* empty implementation */
|
|
13
|
-
},
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Use i18n Hook
|
|
18
|
-
* @returns i18n context
|
|
19
|
-
*/
|
|
20
|
-
export const useI18n = () => {
|
|
21
|
-
try {
|
|
22
|
-
const context = useContext(I18nContext)
|
|
23
|
-
|
|
24
|
-
if (!context) {
|
|
25
|
-
console.warn('useI18n must be used within an I18nProvider, using default context instead')
|
|
26
|
-
return defaultI18nContext
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
return context as any
|
|
30
|
-
} catch (error) {
|
|
31
|
-
console.error('Error in useI18n:', error)
|
|
32
|
-
return defaultI18nContext
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Get current language
|
|
38
|
-
* @returns current language code
|
|
39
|
-
*/
|
|
40
|
-
export const getCurrentLocale = async (): Promise<string> => {
|
|
41
|
-
return await getCurrentLocaleAsync()
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Select special languages for style adaptation
|
|
46
|
-
* @returns { currentLocale: string, isSpecialLocale: boolean }
|
|
47
|
-
*/
|
|
48
|
-
export const getSpecialLocale = async () => {
|
|
49
|
-
const currentLocale = await getCurrentLocale()
|
|
50
|
-
const specialLocales = ['es', 'ru', 'fr', 'ja']
|
|
51
|
-
const isSpecialLocale = specialLocales.includes(currentLocale)
|
|
52
|
-
return { currentLocale, isSpecialLocale }
|
|
53
|
-
}
|
package/src/utils/i18n.ts
DELETED
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import i18n from 'i18next'
|
|
2
|
-
import { initReactI18next } from 'react-i18next'
|
|
3
|
-
|
|
4
|
-
export const fetchLocale = async locale => {
|
|
5
|
-
// @ts-ignore
|
|
6
|
-
const res = await fetch(`./_locales/${locale}/messages.json`)
|
|
7
|
-
const data: Record<string, { message: string; description: string }> = await res.json()
|
|
8
|
-
return Object.keys(data).reduce((res, key) => {
|
|
9
|
-
return {
|
|
10
|
-
...res,
|
|
11
|
-
[key.replace(/__/g, ' ')]: data[key].message,
|
|
12
|
-
}
|
|
13
|
-
}, {})
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
i18n
|
|
17
|
-
.use(initReactI18next) // passes i18n down to react-i18next
|
|
18
|
-
.init({
|
|
19
|
-
fallbackLng: 'en',
|
|
20
|
-
defaultNS: 'translations',
|
|
21
|
-
interpolation: {
|
|
22
|
-
escapeValue: false, // react already safes from xss
|
|
23
|
-
},
|
|
24
|
-
})
|
|
25
|
-
|
|
26
|
-
export const I18N_NS = 'translations'
|
|
27
|
-
|
|
28
|
-
export const addResourceBundle = async (locale: string) => {
|
|
29
|
-
if (i18n.hasResourceBundle(locale, I18N_NS)) return
|
|
30
|
-
const bundle = await fetchLocale(locale)
|
|
31
|
-
|
|
32
|
-
i18n.addResourceBundle(locale, 'translations', bundle)
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
addResourceBundle('en')
|
|
36
|
-
|
|
37
|
-
i18n.on('languageChanged', function (lng: string) {
|
|
38
|
-
addResourceBundle(lng)
|
|
39
|
-
})
|
|
40
|
-
|
|
41
|
-
export default i18n
|