Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions packages/neuron-ui/src/components/History/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,6 @@ import styles from './history.module.scss'

const History = () => {
const {
app: {
loadings: { transactionList: isLoading },
},
wallet: { id, name: walletName },
chain: {
networkID,
Expand Down Expand Up @@ -111,7 +108,6 @@ const History = () => {
keywords,
onKeywordsChange,
onSearch,
isLoading,
id,
walletName,
items,
Expand Down
96 changes: 59 additions & 37 deletions packages/neuron-ui/src/components/MultisigAddress/hooks.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
import React, { useCallback, useState, useEffect } from 'react'
import { useDialogWrapper, isSuccessResponse } from 'utils'
import React, { useCallback, useState, useEffect, useMemo } from 'react'
import { useDialogWrapper, isSuccessResponse, getMultisigAddress, DefaultLockInfo } from 'utils'
import { MultisigOutputUpdate } from 'services/subjects'
import {
MultisigConfig,
MultisigEntity,
saveMultisigConfig,
getMultisigConfig,
importMultisigConfig,
Expand All @@ -13,10 +14,10 @@ import {
loadMultisigTxJson,
OfflineSignJSON,
} from 'services/remote'
import { addressToScript, scriptToAddress } from '@nervosnetwork/ckb-sdk-utils'

export const useSearch = (clearSelected: () => void) => {
export const useSearch = (clearSelected: () => void, onFilterConfig: (searchKey: string) => void) => {
const [keywords, setKeywords] = useState('')
const [searchKeywords, setSearchKeywords] = useState('')

const onKeywordsChange = (_e?: React.FormEvent<HTMLInputElement | HTMLTextAreaElement>, newValue?: string) => {
if (undefined !== newValue) {
Expand All @@ -26,90 +27,111 @@ export const useSearch = (clearSelected: () => void) => {

const onSearch = useCallback(
value => {
setSearchKeywords(value)
onFilterConfig(value)
clearSelected()
},
[setSearchKeywords, clearSelected]
[onFilterConfig, clearSelected]
)

const onClear = useCallback(() => {
onSearch('')
}, [onSearch])
return { keywords, onKeywordsChange, setKeywords, onSearch, searchKeywords, onClear }
return { keywords, onKeywordsChange, setKeywords, onSearch, onClear }
}

export const useConfigManage = ({ walletId, isMainnet }: { walletId: string; isMainnet: boolean }) => {
const [configs, setConfigs] = useState<MultisigConfig[]>([])
const [entities, setEntities] = useState<MultisigEntity[]>([])
const saveConfig = useCallback(
({ m, n, r, addresses, fullPayload }) => {
({ m, n, r, addresses }: { m: number; n: number; r: number; addresses: string[] }) => {
return saveMultisigConfig({
m,
n,
r,
addresses,
fullPayload,
blake160s: addresses.map(v => addressToScript(v).args),
Comment thread
Keith-CY marked this conversation as resolved.
walletId,
}).then(res => {
if (isSuccessResponse(res)) {
if (res.result) {
setConfigs(v => [res.result!, ...v])
}
setEntities(v => (res.result ? [res.result, ...v] : v))
} else {
throw new Error(typeof res.message === 'string' ? res.message : res.message.content)
}
})
},
[walletId, setConfigs]
[walletId, setEntities]
)
useEffect(() => {
getMultisigConfig({
walletId,
}).then(res => {
if (isSuccessResponse(res)) {
setConfigs(res.result)
getMultisigConfig(walletId).then(res => {
if (isSuccessResponse(res) && res.result) {
setEntities(res.result)
}
})
}, [setConfigs, walletId])
}, [setEntities, walletId])
const updateConfig = useCallback(
(id: number) => (alias: string | undefined) => {
updateMultisigConfig({ id, alias: alias || '' }).then(res => {
if (isSuccessResponse(res)) {
setConfigs(v => v.map(config => (config.id === res.result?.id ? res.result : config)))
setEntities(v => v.map(config => (res.result && config.id === res.result?.id ? res.result : config)))
}
})
},
[setConfigs]
[setEntities]
)
const filterConfig = useCallback((key: string) => {
setConfigs(v =>
v.filter(config => {
return config.alias?.includes(key) || config.fullPayload === key
})
)
}, [])
const deleteConfigById = useCallback(
(id: number) => {
setConfigs(v => v.filter(config => config.id !== id))
setEntities(v => v.filter(config => config.id !== id))
},
[setConfigs]
[setEntities]
)
const onImportConfig = useCallback(() => {
importMultisigConfig({ isMainnet, walletId }).then(res => {
importMultisigConfig(walletId).then(res => {
if (isSuccessResponse(res) && res.result) {
const { result } = res
if (result) {
setConfigs(v => [...result, ...v])
setEntities(v => [...result, ...v])
}
}
})
}, [walletId, isMainnet])
}, [walletId])
const [searchKeywords, setSearchKeywords] = useState('')
const onFilterConfig = useCallback(
(v: string) => {
setSearchKeywords(v)
},
[setSearchKeywords]
)
const allConfigs = useMemo<MultisigConfig[]>(
() =>
entities.map(entity => ({
...entity,
addresses: entity.blake160s.map(args =>
scriptToAddress(
{
args,
codeHash: DefaultLockInfo.CodeHash,
hashType: DefaultLockInfo.HashType,
},
Comment thread
Keith-CY marked this conversation as resolved.
isMainnet
)
),
fullPayload: getMultisigAddress(entity.blake160s, entity.r, entity.m, entity.n, isMainnet),
})),
[entities, isMainnet]
)
const configs = useMemo<MultisigConfig[]>(
() =>
searchKeywords
? allConfigs.filter(v => v.alias?.includes(searchKeywords) || v.fullPayload === searchKeywords)
: allConfigs,
[allConfigs, searchKeywords]
)
return {
saveConfig,
allConfigs: configs,
allConfigs,
updateConfig,
deleteConfigById,
filterConfig,
onImportConfig,
configs,
onFilterConfig,
}
}

Expand Down
24 changes: 11 additions & 13 deletions packages/neuron-ui/src/components/MultisigAddress/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,15 @@ const MultisigAddress = () => {
}, [i18n.language])
const isMainnet = isMainnetUtil(networks, networkID)
const { openDialog, closeDialog, dialogRef, isDialogOpen } = useDialogWrapper()
const { allConfigs, saveConfig, updateConfig, deleteConfigById, onImportConfig } = useConfigManage({
const {
allConfigs,
saveConfig,
updateConfig,
deleteConfigById,
onImportConfig,
configs,
onFilterConfig,
} = useConfigManage({
walletId,
isMainnet,
})
Expand Down Expand Up @@ -96,17 +104,8 @@ const MultisigAddress = () => {
onChangeCheckedAll,
exportConfig,
clearSelected,
} = useExportConfig(allConfigs)
const { keywords, onKeywordsChange, onSearch, searchKeywords, onClear } = useSearch(clearSelected)
const configs = useMemo(
() =>
searchKeywords
? allConfigs.filter(v => {
return v.alias?.includes(searchKeywords) || v.fullPayload === searchKeywords
})
: allConfigs,
[allConfigs, searchKeywords]
)
} = useExportConfig(configs)
const { keywords, onKeywordsChange, onSearch, onClear } = useSearch(clearSelected, onFilterConfig)
const sendTotalBalance = useMemo(() => {
if (sendAction.sendFromMultisig?.fullPayload) {
return multisigBanlances[sendAction.sendFromMultisig.fullPayload]
Expand All @@ -117,7 +116,6 @@ const MultisigAddress = () => {
<div>
<div className={styles.head}>
<SearchBox
data-
value={keywords}
className={styles.searchBox}
styles={searchBoxStyles}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, { useCallback, useMemo, useState, useEffect } from 'react'
import { createMultisigAddress } from 'services/remote'
import { isSuccessResponse, validateAddress, isSecp256k1Address } from 'utils'
import { validateAddress, isSecp256k1Address, getMultisigAddress } from 'utils'
import { useTranslation } from 'react-i18next'
import { addressToScript } from '@nervosnetwork/ckb-sdk-utils'

export enum Step {
setMN = 0,
Expand All @@ -28,15 +28,26 @@ export const useMAndN = () => {
},
[setN]
)
const isError = useMemo(() => {
return !m || !n || Number(m) > Number(n)
const errorI18nKey: string | undefined = useMemo(() => {
if (!m || !n) {
return 'm-n-required'
}
const numM = Number(m)
const numN = Number(n)
if (numM > numN) {
return 'm-less-equal-n'
}
if (numM < 1 || numM > 255 || numN < 1 || numN > 255) {
return 'm-n-between-0-255'
}
return undefined
}, [m, n])
return {
m,
n,
setMBySelect,
setNBySelect,
isError,
errorI18nKey,
}
}

Expand Down Expand Up @@ -117,17 +128,18 @@ export const useViewMultisigAddress = ({
const [multisigAddress, changeMultisigAddress] = useState('')
useEffect(() => {
if (step === Step.viewMultiAddress) {
createMultisigAddress({
r,
m,
n,
addresses,
isMainnet,
}).then(res => {
if (isSuccessResponse(res) && res.result) {
changeMultisigAddress(res.result)
}
})
try {
const address = getMultisigAddress(
addresses.map(v => addressToScript(v).args),
Comment thread
Keith-CY marked this conversation as resolved.
r,
m,
n,
isMainnet
)
changeMultisigAddress(address)
} catch (error) {
// ignore error. The ui ensures the correctness of the parameters
}
}
}, [step, changeMultisigAddress, m, n, r, addresses, isMainnet])
return multisigAddress
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,13 @@ const SetMN = ({
n,
changeM,
changeN,
errorI18nKey,
}: {
m: string
n: string
changeM: (v: string) => void
changeN: (v: string) => void
errorI18nKey?: string
}) => {
const [t] = useTranslation()
return (
Expand All @@ -34,9 +36,7 @@ const SetMN = ({
&nbsp;&nbsp;&nbsp;&nbsp;of&nbsp;&nbsp;&nbsp;&nbsp;
<InputSelect options={keysCountArr} value={n} onChange={changeN} />
</div>
{m && n && Number(m) > Number(n) && (
<span className={styles.error}>{t('multisig-address.create-dialog.m-n.error')}</span>
)}
{errorI18nKey && <span className={styles.error}>{t(`multisig-address.create-dialog.m-n.${errorI18nKey}`)}</span>}
</div>
)
}
Expand All @@ -46,11 +46,11 @@ const MultisigAddressCreateDialog = ({
confirm: saveConfig,
}: {
closeDialog: () => void
confirm: (v: Omit<MultisigConfig, 'id' | 'walletId'>) => Promise<void>
confirm: (v: Omit<MultisigConfig, 'id' | 'walletId' | 'fullPayload' | 'blake160s'>) => Promise<void>
}) => {
const [step, changeStep] = useState(Step.setMN)
const [t] = useTranslation()
const { m, n, setMBySelect, setNBySelect, isError: mnErr } = useMAndN()
const { m, n, setMBySelect, setNBySelect, errorI18nKey: mnErr } = useMAndN()
const next = useCallback(() => {
changeStep(step + 1)
}, [changeStep, step])
Expand Down Expand Up @@ -87,18 +87,17 @@ const MultisigAddressCreateDialog = ({
saveConfig({
m: Number(m),
n: Number(n),
r,
r: Number(r),
addresses,
fullPayload: multisigAddress,
}).then(() => {
closeDialog()
})
}, [m, n, r, addresses, multisigAddress, saveConfig, closeDialog])
}, [m, n, r, addresses, saveConfig, closeDialog])

return (
<>
<p>{t('multisig-address.create-dialog.title')}</p>
{step === Step.setMN && <SetMN m={m} n={n} changeM={setMBySelect} changeN={setNBySelect} />}
{step === Step.setMN && <SetMN m={m} n={n} changeM={setMBySelect} changeN={setNBySelect} errorI18nKey={mnErr} />}
{step === Step.setMultiAddress && (
<>
<p>{t('multisig-address.create-dialog.multi-address-info.title', { m, n })}</p>
Expand Down Expand Up @@ -126,7 +125,7 @@ const MultisigAddressCreateDialog = ({
<Button
label={t(`multisig-address.create-dialog.actions.${step === Step.viewMultiAddress ? 'confirm' : 'next'}`)}
type="primary"
disabled={(step === Step.setMN && mnErr) || (step === Step.setMultiAddress && addressErr)}
disabled={(step === Step.setMN && !!mnErr) || (step === Step.setMultiAddress && addressErr)}
onClick={step === Step.viewMultiAddress ? confirm : next}
/>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@ import { useDispatch } from 'states'
import { AppActions, StateDispatch } from 'states/stateProvider/reducer'
import {
generateMultisigTx,
MultisigConfig,
generateMultisigSendAllTx,
exportTransactionAsJSON,
OfflineSignStatus,
OfflineSignType,
MultisigConfig,
} from 'services/remote'
import { TFunction } from 'i18next'

Expand Down Expand Up @@ -95,8 +95,8 @@ export const useSendInfo = ({
t: TFunction
}) => {
const [sendInfoList, setSendInfoList] = useState<
{ address: string | undefined; amount: string | undefined; unit: CapacityUnit; disabled: boolean }[]
>([{ address: undefined, amount: undefined, unit: CapacityUnit.CKB, disabled: false }])
{ address: string | undefined; amount: string | undefined; unit: CapacityUnit; disabled?: boolean }[]
>([{ address: undefined, amount: undefined, unit: CapacityUnit.CKB }])
const addSendInfo = useCallback(() => {
setSendInfoList(v => [...v, { address: undefined, amount: undefined, unit: CapacityUnit.CKB, disabled: false }])
}, [setSendInfoList])
Expand Down
Loading