From f8d4e95a4a1da7d1e1c22db5f9e852062055c2ff Mon Sep 17 00:00:00 2001 From: yanguoyu <841185308@qq.com> Date: Sat, 4 Nov 2023 17:49:10 +0800 Subject: [PATCH 01/22] feat: Adapt switch internal network type, and notify the user network type is not running external. --- .../src/components/DataSetting/hooks.ts | 10 +- .../src/components/DataSetting/index.tsx | 39 ++-- .../components/NetworkEditorDialog/index.tsx | 5 +- .../src/components/NetworkSetting/index.tsx | 51 ++++- .../NetworkSetting/networkSetting.module.scss | 11 ++ .../neuron-ui/src/containers/Main/hooks.ts | 75 +++++++- .../neuron-ui/src/containers/Main/index.tsx | 71 ++++++- .../src/containers/Main/main.module.scss | 27 +++ .../neuron-ui/src/containers/Navbar/index.tsx | 20 +- packages/neuron-ui/src/locales/en.json | 12 +- packages/neuron-ui/src/locales/zh-tw.json | 12 +- packages/neuron-ui/src/locales/zh.json | 12 +- packages/neuron-ui/src/services/remote/app.ts | 3 +- .../neuron-ui/src/services/remote/networks.ts | 1 + .../src/services/remote/remoteApiWrapper.ts | 4 +- packages/neuron-ui/src/utils/const.ts | 1 + .../neuron-ui/src/utils/getNetworkLabel.ts | 7 +- .../neuron-ui/src/widgets/Icons/Switch.svg | 3 + packages/neuron-ui/src/widgets/Icons/icon.tsx | 4 + .../src/widgets/RadioGroup/index.tsx | 30 ++- packages/neuron-wallet/electron-builder.yml | 18 +- .../src/block-sync-renderer/index.ts | 5 +- .../sync/indexer-connector.ts | 5 +- .../sync/light-connector.ts | 6 +- .../src/block-sync-renderer/sync/queue.ts | 11 +- .../src/block-sync-renderer/task.ts | 3 +- .../block-sync-renderer/tx-status-listener.ts | 8 +- packages/neuron-wallet/src/controllers/api.ts | 19 +- .../src/controllers/export-debug.ts | 8 +- .../neuron-wallet/src/controllers/mercury.ts | 2 +- .../src/controllers/networks/chain-info.ts | 2 +- .../src/controllers/networks/index.ts | 21 ++- .../src/controllers/offline-sign.ts | 5 +- .../neuron-wallet/src/controllers/sync-api.ts | 24 +-- .../neuron-wallet/src/controllers/wallets.ts | 4 +- packages/neuron-wallet/src/models/network.ts | 2 +- .../src/models/system-script-info.ts | 17 +- .../neuron-wallet/src/services/ckb-runner.ts | 32 +++- .../src/services/hardware/ledger.ts | 4 +- .../neuron-wallet/src/services/indexer.ts | 6 +- .../src/services/light-runner.ts | 61 +++--- .../neuron-wallet/src/services/networks.ts | 108 +++++++++-- packages/neuron-wallet/src/services/node.ts | 55 +++--- .../neuron-wallet/src/services/rpc-service.ts | 5 +- .../neuron-wallet/src/services/settings.ts | 50 +++-- .../src/services/transaction-sender.ts | 19 +- .../src/services/tx/transaction-generator.ts | 5 +- .../src/services/tx/transaction-service.ts | 4 +- packages/neuron-wallet/src/utils/ckb-rpc.ts | 11 +- packages/neuron-wallet/src/utils/const.ts | 4 + .../src/utils/get-usable-port.ts | 41 ++++ packages/neuron-wallet/src/utils/toml.ts | 33 ++++ .../tests/block-sync-renderer/task.test.ts | 4 +- .../tests/controllers/offline-sign.test.ts | 12 ++ .../tests/controllers/sync-api.test.ts | 29 +-- .../models/keys/hd-public-key-info.test.ts | 2 +- .../models/synced-block-number.intg.test.ts | 14 +- .../tests/services/ckb-runner.test.ts | 53 +++++- .../tests/services/indexer.test.ts | 24 +-- .../tests/services/light-runner.test.ts | 89 +++++++-- .../tests/services/networks.test.ts | 177 +++++++++++++++++- .../neuron-wallet/tests/services/node.test.ts | 71 +++---- .../tests/services/setting.test.ts | 32 +++- .../services/tx/transaction-generator.test.ts | 6 +- .../services/tx/transaction-sender.test.ts | 13 ++ .../tests/services/wallets.test.ts | 15 +- .../neuron-wallet/tests/utils/ckb-rpc.test.ts | 7 +- .../tests/utils/get-usable-port.test.ts | 58 ++++++ .../neuron-wallet/tests/utils/toml/test.toml | 28 +++ .../tests/utils/toml/toml.test.ts | 68 +++++++ scripts/download-ckb.sh | 3 +- 71 files changed, 1311 insertions(+), 390 deletions(-) create mode 100644 packages/neuron-ui/src/containers/Main/main.module.scss create mode 100644 packages/neuron-ui/src/widgets/Icons/Switch.svg create mode 100644 packages/neuron-wallet/src/utils/get-usable-port.ts create mode 100644 packages/neuron-wallet/src/utils/toml.ts create mode 100644 packages/neuron-wallet/tests/utils/get-usable-port.test.ts create mode 100644 packages/neuron-wallet/tests/utils/toml/test.toml create mode 100644 packages/neuron-wallet/tests/utils/toml/toml.test.ts diff --git a/packages/neuron-ui/src/components/DataSetting/hooks.ts b/packages/neuron-ui/src/components/DataSetting/hooks.ts index 57b0ff0680..0ece03b2da 100644 --- a/packages/neuron-ui/src/components/DataSetting/hooks.ts +++ b/packages/neuron-ui/src/components/DataSetting/hooks.ts @@ -1,4 +1,4 @@ -import React, { useCallback, useState } from 'react' +import React, { useCallback, useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' import { getCkbNodeDataPath, @@ -7,11 +7,11 @@ import { stopProcessMonitor, setCkbNodeDataPath, } from 'services/remote' -import { isSuccessResponse, useDidMount } from 'utils' +import { isSuccessResponse } from 'utils' const type = 'ckb' -export const useDataPath = () => { +export const useDataPath = (network?: State.Network) => { const [t] = useTranslation() const [isSaving, setIsSaving] = useState(false) const [savingType, setSavingType] = useState() @@ -20,13 +20,13 @@ export const useDataPath = () => { const [isDialogOpen, setIsDialogOpen] = useState(false) const [faidMessage, setFaidMessage] = useState('') - useDidMount(() => { + useEffect(() => { getCkbNodeDataPath().then(res => { if (isSuccessResponse(res)) { setPrevPath(res.result!) } }) - }) + }, [network?.chain, network?.id]) const onSetting = useCallback(() => { invokeShowOpenDialog({ buttonLabel: t('settings.data.set', { lng: navigator.language }), diff --git a/packages/neuron-ui/src/components/DataSetting/index.tsx b/packages/neuron-ui/src/components/DataSetting/index.tsx index 7e83d99684..a7165e761e 100644 --- a/packages/neuron-ui/src/components/DataSetting/index.tsx +++ b/packages/neuron-ui/src/components/DataSetting/index.tsx @@ -1,11 +1,9 @@ -import React, { useCallback, useEffect, useState, useRef, useMemo } from 'react' +import React, { useCallback, useRef, useMemo } from 'react' import { useTranslation } from 'react-i18next' import Button from 'widgets/Button' import ClearCache from 'components/ClearCache' import { useDispatch, useState as useGlobalState } from 'states' import { shell } from 'electron' -import { getIsCkbRunExternal } from 'services/remote' -import { isSuccessResponse } from 'utils' import Tooltip from 'widgets/Tooltip' import Dialog from 'widgets/Dialog' import AlertDialog from 'widgets/AlertDialog' @@ -42,6 +40,12 @@ const PathItem = ({ const DataSetting = () => { const dispatch = useDispatch() const [t] = useTranslation() + const resyncRef = useRef(null) + const { + chain: { networkID }, + settings: { networks = [] }, + } = useGlobalState() + const network = useMemo(() => networks.find(n => n.id === networkID), [networkID, networks]) const { onSetting, prevPath, @@ -53,38 +57,19 @@ const DataSetting = () => { savingType, faidMessage, setFaidMessage, - } = useDataPath() - - const resyncRef = useRef(null) + } = useDataPath(network) const openPath = useCallback(() => { if (prevPath) { shell.openPath(prevPath!) } }, [prevPath]) - const [isCkbRunExternal, setIsCkbRunExternal] = useState() - useEffect(() => { - getIsCkbRunExternal().then(res => { - if (isSuccessResponse(res)) { - setIsCkbRunExternal(res.result ?? false) - } else { - // ignore - } - }) - }, []) - const { - chain: { networkID }, - settings: { networks = [] }, - } = useGlobalState() - const isLightClient = useMemo( - () => networks.find(n => n.id === networkID)?.type === LIGHT_NETWORK_TYPE, - [networkID, networks] - ) + const isLightClient = network?.type === LIGHT_NETWORK_TYPE return ( <>
- {isLightClient ? null : ( + {isLightClient || !network?.readonly ? null : (
{t('settings.data.ckb-node-data')}
{
- {isLightClient ? null : ( - + {isLightClient || !network?.readonly ? null : ( + )} void id: 'new' | string onSuccess: () => void + url?: string }) => { const { settings: { networks = [] }, @@ -31,7 +33,7 @@ const NetworkEditorDialog = ({ const [editor, setEditor] = useState({ name: '', nameError: '', - url: '', + url: url ?? '', urlError: '', }) const [isUpdating, setIsUpdating] = useState(false) @@ -115,6 +117,7 @@ const NetworkEditorDialog = ({ error={editor.urlError} placeholder={t('settings.network.edit-network.input-rpc')} autoFocus + disabled={!!url} /> { + if (chain === 'ckb' || chain === LIGHT_CLIENT_MAINNET) { + return 'testnet' + } + return 'mainnet' +} + const NetworkSetting = ({ chain = chainState, settings: { networks = [] } }: State.AppWithNeuronWallet) => { const [t] = useTranslation() const [showEditorDialog, setShowEditorDialog] = useState(false) @@ -43,7 +52,6 @@ const NetworkSetting = ({ chain = chainState, settings: { networks = [] } }: Sta break } default: - // @ts-ignore } }, [setNetId, setShowEditorDialog, setShowDeleteDialog] @@ -75,20 +83,43 @@ const NetworkSetting = ({ chain = chainState, settings: { networks = [] } }: Sta } }, [setShowEditorDialog, setNotice, netId]) + const onSwitchNetworkType = useCallback>(() => { + switchCurrentNetworkType() + }, []) + return (
({ value: network.id, - label: ( -
-

{`${network.name} (${network.remote})`}

-
{t(getNetworkLabelI18nkey(network.chain))}
-
- ), + label: + currentId === network.id && network.readonly ? ( +
+

{`${network.name} (${network.remote})`}

+ + {t('settings.network.switch-network-type', { type: getAnotherNetworkType(network.chain) })} + + } + placement="top" + showTriangle + > +
+ {t(getNetworkLabelI18nkey(network.chain))} + +
+
+
+ ) : ( +
+

{`${network.name} (${network.remote})`}

+
{t(getNetworkLabelI18nkey(network.chain))}
+
+ ), suffix: (
{network.readonly ? null : ( diff --git a/packages/neuron-ui/src/components/NetworkSetting/networkSetting.module.scss b/packages/neuron-ui/src/components/NetworkSetting/networkSetting.module.scss index 3fbdf69edd..c5f3c0744b 100644 --- a/packages/neuron-ui/src/components/NetworkSetting/networkSetting.module.scss +++ b/packages/neuron-ui/src/components/NetworkSetting/networkSetting.module.scss @@ -25,6 +25,17 @@ background: var(--table-head-background-color); border: 1px solid var(--divide-line-color); border-radius: 40px; + display: flex; + align-items: center; + + & > svg { + margin-left: 4px; + } + } + .switchBtn { + border: none; + background-color: transparent; + cursor: pointer; } } diff --git a/packages/neuron-ui/src/containers/Main/hooks.ts b/packages/neuron-ui/src/containers/Main/hooks.ts index 2049222fdd..31ca663dfa 100644 --- a/packages/neuron-ui/src/containers/Main/hooks.ts +++ b/packages/neuron-ui/src/containers/Main/hooks.ts @@ -1,5 +1,5 @@ -import { useEffect, useCallback } from 'react' -import { useLocation, NavigateFunction } from 'react-router-dom' +import { useEffect, useCallback, useState } from 'react' +import { useLocation, NavigateFunction, useNavigate } from 'react-router-dom' import { NeuronWalletActions, StateDispatch, AppActions } from 'states/stateProvider/reducer' import { updateTransactionList, @@ -10,7 +10,13 @@ import { showGlobalAlertDialog, } from 'states/stateProvider/actionCreators' -import { getCurrentWallet, getWinID } from 'services/remote' +import { + getCurrentWallet, + getIsNodeRunExternal, + getWinID, + setCurrentNetwork, + startNodeIgnoreExternal, +} from 'services/remote' import { DataUpdate as DataUpdateSubject, NetworkList as NetworkListSubject, @@ -311,6 +317,69 @@ export const useSubscription = ({ }, [walletID, pageNo, pageSize, keywords, isAllowedToFetchList, navigate, dispatch, location.pathname]) } +export const useCheckNode = (networks: State.Network[], network?: State.Network) => { + const [showSwitchNetwork, setShowSwitchNetwork] = useState(false) + const [selectNetwork, setSelectNetwork] = useState(networks[0]?.id) + const navigate = useNavigate() + useEffect(() => { + if (!network) return () => {} + let timer: ReturnType + const checkNode = () => { + return getIsNodeRunExternal().then(res => { + if (isSuccessResponse(res) && res.result !== null) { + if (res.result) { + navigate(RoutePath.Settings) + setShowSwitchNetwork(true) + } + } else { + timer = setTimeout(() => { + checkNode() + }, 2_000) + } + }) + } + if (network.readonly) { + checkNode() + } else { + setShowSwitchNetwork(false) + } + return () => { + clearTimeout(timer) + } + }, [network?.readonly, network?.type, navigate]) + useEffect(() => { + if (showSwitchNetwork) { + setSelectNetwork(networks[0]?.id) + } + }, [networks, showSwitchNetwork]) + const [showEditorDialog, setShowEditorDialog] = useState(false) + const onConfirm = useCallback(() => { + if (selectNetwork) { + setCurrentNetwork(selectNetwork) + setShowSwitchNetwork(false) + } + }, [selectNetwork]) + const onCloseEditorDialog = useCallback(() => { + setShowEditorDialog(false) + }, []) + const onOpenEditorDialog = useCallback(() => { + setShowEditorDialog(true) + }, []) + return { + selectNetwork, + onChangeSelected: setSelectNetwork, + showSwitchNetwork, + onCancel: useCallback(() => { + setShowSwitchNetwork(false) + startNodeIgnoreExternal() + }, []), + onConfirm, + showEditorDialog, + onCloseEditorDialog, + onOpenEditorDialog, + } +} + export default { useSyncChainData, useOnCurrentWalletChange, diff --git a/packages/neuron-ui/src/containers/Main/index.tsx b/packages/neuron-ui/src/containers/Main/index.tsx index 7d77ae8242..b32f5cd382 100644 --- a/packages/neuron-ui/src/containers/Main/index.tsx +++ b/packages/neuron-ui/src/containers/Main/index.tsx @@ -6,7 +6,11 @@ import { useMigrate, useOnDefaultContextMenu, useOnLocaleChange } from 'utils' import AlertDialog from 'widgets/AlertDialog' import Dialog from 'widgets/Dialog' import Button from 'widgets/Button' -import { useSubscription, useSyncChainData, useOnCurrentWalletChange } from './hooks' +import RadioGroup from 'widgets/RadioGroup' +import NetworkEditorDialog from 'components/NetworkEditorDialog' +import { AddSimple } from 'widgets/Icons/icon' +import styles from './main.module.scss' +import { useSubscription, useSyncChainData, useOnCurrentWalletChange, useCheckNode } from './hooks' const MainContent = () => { const navigate = useNavigate() @@ -30,13 +34,15 @@ const MainContent = () => { location, }) - const chainURL = useMemo(() => { - const network = networks.find(n => n.id === networkID) - return network ? network.remote : '' - }, [networks, networkID]) + const network = useMemo(() => networks.find(n => n.id === networkID), [networks, networkID]) + + const sameUrlNetworks = useMemo( + () => networks.filter(v => v.remote === network?.remote && !v.readonly), + [network, networks] + ) useSyncChainData({ - chainURL, + chainURL: network?.remote ?? '', dispatch, }) @@ -52,6 +58,15 @@ const MainContent = () => { dismissGlobalAlertDialog()(dispatch) }, [dispatch]) const { isMigrateDialogShow, onCancel, onBackUp, onConfirm } = useMigrate() + const { + showSwitchNetwork, + onCancel: onCloseSwitchNetwork, + onConfirm: onSwitchNetwork, + onChangeSelected, + showEditorDialog, + onCloseEditorDialog, + onOpenEditorDialog, + } = useCheckNode(sameUrlNetworks, network) return (
@@ -76,6 +91,50 @@ const MainContent = () => {
+ + {sameUrlNetworks.length ? ( + + {t('main.external-node-detected-dialog.body-tips-with-network')} + + ) : ( + t('main.external-node-detected-dialog.body-tips-without-network') + )} + {sameUrlNetworks.length ? ( + <> +
+ ({ + value: v.id, + label: `${v.name} (${v.remote})`, + }))} + inputIdPrefix="main-switch" + /> +
+
+ +
+ + ) : null} +
+ {showEditorDialog ? ( + + ) : null}
) } diff --git a/packages/neuron-ui/src/containers/Main/main.module.scss b/packages/neuron-ui/src/containers/Main/main.module.scss new file mode 100644 index 0000000000..0e01fceaa4 --- /dev/null +++ b/packages/neuron-ui/src/containers/Main/main.module.scss @@ -0,0 +1,27 @@ +.networkDialog { + width: 680px; + + .chooseNetworkTip { + color: var(--secondary-text-color); + } + + .networks { + max-height: 124px; + border-radius: 8px; + margin-top: 16px; + border: 1px solid var(--divide-line-color); + overflow-y: auto; + } + + .addNetwork { + text-align: center; + + & > button { + font-weight: 400; + } + + svg { + margin-right: 4px; + } + } +} diff --git a/packages/neuron-ui/src/containers/Navbar/index.tsx b/packages/neuron-ui/src/containers/Navbar/index.tsx index 8020a12b53..6c38bdbfe7 100644 --- a/packages/neuron-ui/src/containers/Navbar/index.tsx +++ b/packages/neuron-ui/src/containers/Navbar/index.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useEffect, useState } from 'react' +import React, { useCallback, useEffect, useMemo, useState } from 'react' import { createPortal } from 'react-dom' import { useLocation, NavLink, useNavigate } from 'react-router-dom' import { useTranslation } from 'react-i18next' @@ -63,8 +63,9 @@ const Navbar = () => { const dispatch = useDispatch() const neuronWallet = useGlobalState() const { + chain: { networkID }, wallet: { name }, - settings: { wallets = [] }, + settings: { wallets = [], networks = [] }, updater: { version, isUpdated }, } = neuronWallet const [t, i18n] = useTranslation() @@ -95,13 +96,16 @@ const Navbar = () => { const [verifyCkbResult, setVerifyCkbResult] = useState() + const network = useMemo(() => networks.find(n => n.id === networkID), [networkID, networks]) useEffect(() => { - verifyExternalCkbNode().then(res => { - if (isSuccessResponse(res) && res.result) { - setVerifyCkbResult(res.result) - } - }) - }, []) + if (!network?.readonly) { + verifyExternalCkbNode().then(res => { + if (isSuccessResponse(res) && res.result) { + setVerifyCkbResult(res.result) + } + }) + } + }, [network?.readonly]) useEffect(() => { // isUpdated is true or version is not empty means check update has return diff --git a/packages/neuron-ui/src/locales/en.json b/packages/neuron-ui/src/locales/en.json index 4878419ed2..e54bc8216e 100644 --- a/packages/neuron-ui/src/locales/en.json +++ b/packages/neuron-ui/src/locales/en.json @@ -400,7 +400,9 @@ "mainnet": "Mainnet", "testnet": "Testnet", "lightTestnet": "Light Testnet", - "devnet": "Devnet" + "lightMainnet": "Light Mainnet", + "devnet": "Devnet", + "switch-network-type": "Switch to {{type}}" }, "locale": { "en": "English", @@ -1137,6 +1139,14 @@ "input-place-holder": "Please enter the start sync block number, eg: 10,100,101", "locate-first-tx": "Locate the first transaction", "view-block": "View the block" + }, + "main": { + "external-node-detected-dialog": { + "title": "External node detected", + "body-tips-without-network": "You selected an internal node, but activated an external node. If you need to continue using external nodes, please add a new network.", + "body-tips-with-network": "You have currently selected an internal node, but have activated an external node. If you need to continue using external nodes, please switch to another external network or add a new network.", + "add-network": "Add Network" + } } } } diff --git a/packages/neuron-ui/src/locales/zh-tw.json b/packages/neuron-ui/src/locales/zh-tw.json index a1f2bde2c7..c066c31f47 100644 --- a/packages/neuron-ui/src/locales/zh-tw.json +++ b/packages/neuron-ui/src/locales/zh-tw.json @@ -394,7 +394,9 @@ "edit-success": "編輯網絡成功", "mainnet": "主網", "testnet": "測試網", - "devnet": "開發網" + "lightMainnet": "輕節點主網", + "devnet": "開發網", + "switch-network-type": "切換到 {{type}}" }, "locale": { "en": "English", @@ -1108,6 +1110,14 @@ "input-place-holder": "輸入同步起始區塊高度,例如:10,100,101", "locate-first-tx": "定位第一筆交易", "view-block": "查看區塊" + }, + "main": { + "external-node-detected-dialog": { + "title": "檢測到外部節點", + "body-tips-without-network": "您選擇了內部節點,但啟動了外部節點。如果您需要繼續使用外部節點,請添加壹個新網絡。", + "body-tips-with-network": "您目前選擇了內部節點,但啟動了外部節點。如果您需要繼續使用外部節點,請切換到另壹個外部網絡或添加壹個新網絡。", + "add-network": "添加網絡" + } } } } diff --git a/packages/neuron-ui/src/locales/zh.json b/packages/neuron-ui/src/locales/zh.json index 67d31df813..102688abf9 100644 --- a/packages/neuron-ui/src/locales/zh.json +++ b/packages/neuron-ui/src/locales/zh.json @@ -393,7 +393,9 @@ "mainnet": "主网", "testnet": "测试网", "lightTestnet": "轻节点测试网", - "devnet": "开发网" + "lightMainnet": "轻节点主网", + "devnet": "开发网", + "switch-network-type": "切换到 {{type}}" }, "locale": { "en": "English", @@ -1129,6 +1131,14 @@ "input-place-holder": "输入同步起始区块高度,例如:10,100,101", "locate-first-tx": "定位第一笔交易", "view-block": "查看区块" + }, + "main": { + "external-node-detected-dialog": { + "title": "检测到外部节点", + "body-tips-without-network": "您选择了内部节点,但启动了外部节点。如果您需要继续使用外部节点,请添加一个新网络。", + "body-tips-with-network": "您目前选择了内部节点,但启动了外部节点。如果您需要继续使用外部节点,请切换到另一个外部网络或添加一个新网络。", + "add-network": "添加网络" + } } } } diff --git a/packages/neuron-ui/src/services/remote/app.ts b/packages/neuron-ui/src/services/remote/app.ts index 65e69fa9ca..401bb9c3ce 100644 --- a/packages/neuron-ui/src/services/remote/app.ts +++ b/packages/neuron-ui/src/services/remote/app.ts @@ -22,7 +22,8 @@ export const setCkbNodeDataPath = remoteApi<{ dataPath: string; clearCache?: boo ) export const stopProcessMonitor = remoteApi<'ckb'>('stop-process-monitor') export const startProcessMonitor = remoteApi<'ckb'>('start-process-monitor') -export const getIsCkbRunExternal = remoteApi('is-ckb-run-external') +export const getIsNodeRunExternal = remoteApi('is-node-run-external') +export const startNodeIgnoreExternal = remoteApi('start-node-ignore-external') export type VerifyExternalCkbNodeRes = | { diff --git a/packages/neuron-ui/src/services/remote/networks.ts b/packages/neuron-ui/src/services/remote/networks.ts index df97ec78d5..ac87c89742 100644 --- a/packages/neuron-ui/src/services/remote/networks.ts +++ b/packages/neuron-ui/src/services/remote/networks.ts @@ -6,3 +6,4 @@ export const updateNetwork = remoteApi('update-n export const getCurrentNetworkID = remoteApi('get-current-network-id') export const setCurrentNetwork = remoteApi('set-current-network-id') export const deleteNetwork = remoteApi('delete-network') +export const switchCurrentNetworkType = remoteApi('switch-network-type') diff --git a/packages/neuron-ui/src/services/remote/remoteApiWrapper.ts b/packages/neuron-ui/src/services/remote/remoteApiWrapper.ts index d8f798b382..94e2942f7b 100644 --- a/packages/neuron-ui/src/services/remote/remoteApiWrapper.ts +++ b/packages/neuron-ui/src/services/remote/remoteApiWrapper.ts @@ -48,8 +48,9 @@ type Action = | 'start-process-monitor' | 'is-dark' | 'set-theme' - | 'is-ckb-run-external' + | 'is-node-run-external' | 'verify-external-ckb-node' + | 'start-node-ignore-external' // Wallets | 'get-all-wallets' | 'get-current-wallet' @@ -91,6 +92,7 @@ type Action = | 'get-current-network-id' | 'set-current-network-id' | 'delete-network' + | 'switch-network-type' // Updater | 'check-for-updates' | 'cancel-check-updates' diff --git a/packages/neuron-ui/src/utils/const.ts b/packages/neuron-ui/src/utils/const.ts index abd90e982b..a258c1b304 100644 --- a/packages/neuron-ui/src/utils/const.ts +++ b/packages/neuron-ui/src/utils/const.ts @@ -68,6 +68,7 @@ export const DEPRECATED_CODE_HASH: Record = { export const HIDE_BALANCE = '******' export const LIGHT_CLIENT_TESTNET = 'light_client_testnet' +export const LIGHT_CLIENT_MAINNET = 'light_client_mainnet' export const LIGHT_NETWORK_TYPE = 2 export const METHOD_NOT_FOUND = -32601 diff --git a/packages/neuron-ui/src/utils/getNetworkLabel.ts b/packages/neuron-ui/src/utils/getNetworkLabel.ts index 29ac0d5e29..461adcb7fd 100644 --- a/packages/neuron-ui/src/utils/getNetworkLabel.ts +++ b/packages/neuron-ui/src/utils/getNetworkLabel.ts @@ -1,6 +1,6 @@ -import { LIGHT_CLIENT_TESTNET } from './const' +import { LIGHT_CLIENT_MAINNET, LIGHT_CLIENT_TESTNET } from './const' -export const getNetworkLabelI18nkey = (type: 'ckb' | 'ckb_testnet' | 'ckb_dev' | string) => { +export const getNetworkLabelI18nkey = (type: State.Network['chain']) => { switch (type) { case 'ckb': { return 'settings.network.mainnet' @@ -11,6 +11,9 @@ export const getNetworkLabelI18nkey = (type: 'ckb' | 'ckb_testnet' | 'ckb_dev' | case LIGHT_CLIENT_TESTNET: { return 'settings.network.lightTestnet' } + case LIGHT_CLIENT_MAINNET: { + return 'settings.network.lightMainnet' + } default: { return 'settings.network.devnet' } diff --git a/packages/neuron-ui/src/widgets/Icons/Switch.svg b/packages/neuron-ui/src/widgets/Icons/Switch.svg new file mode 100644 index 0000000000..d998334223 --- /dev/null +++ b/packages/neuron-ui/src/widgets/Icons/Switch.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/neuron-ui/src/widgets/Icons/icon.tsx b/packages/neuron-ui/src/widgets/Icons/icon.tsx index b05e00b7cf..542cb2843d 100644 --- a/packages/neuron-ui/src/widgets/Icons/icon.tsx +++ b/packages/neuron-ui/src/widgets/Icons/icon.tsx @@ -44,6 +44,8 @@ import { ReactComponent as OverviewSendSvg } from './OverviewSend.svg' import { ReactComponent as OverviewReceiveSvg } from './OverviewReceive.svg' import { ReactComponent as AddressbookSvg } from './Addressbook.svg' import { ReactComponent as AddSvg } from './Add.svg' +import { ReactComponent as AddSimpleSvg } from './AddSimple.svg' +import { ReactComponent as SwitchSvg } from './Switch.svg' import styles from './icon.module.scss' @@ -104,3 +106,5 @@ export const OverviewSend = WrapSvg(OverviewSendSvg) export const OverviewReceive = WrapSvg(OverviewReceiveSvg) export const Addressbook = WrapSvg(AddressbookSvg) export const Add = WrapSvg(AddSvg) +export const Switch = WrapSvg(SwitchSvg) +export const AddSimple = WrapSvg(AddSimpleSvg) diff --git a/packages/neuron-ui/src/widgets/RadioGroup/index.tsx b/packages/neuron-ui/src/widgets/RadioGroup/index.tsx index fb56c07757..1ca7197d1a 100644 --- a/packages/neuron-ui/src/widgets/RadioGroup/index.tsx +++ b/packages/neuron-ui/src/widgets/RadioGroup/index.tsx @@ -13,22 +13,32 @@ export interface RadioGroupProps { options: RadioGroupOptions[] onChange?: (arg: string | number) => void defaultValue?: string | number + value?: string | number itemClassName?: string className?: string + inputIdPrefix?: string } -const RadioGroup = ({ defaultValue, options, onChange, itemClassName = '', className = '' }: RadioGroupProps) => { - const [checkedValue, setCheckedValue] = useState(defaultValue || options[0]?.value) +const RadioGroup = ({ + defaultValue, + options, + onChange, + itemClassName = '', + className = '', + value, + inputIdPrefix = '', +}: RadioGroupProps) => { + const [checkedValue, setCheckedValue] = useState(defaultValue ?? options[0]?.value) const handleChange = useCallback( (e: React.SyntheticEvent) => { - const { value } = e.target as HTMLInputElement - if (value !== checkedValue) { - setCheckedValue(value) - onChange?.(value) + const { value: selectedValue } = e.target as HTMLInputElement + if (selectedValue !== value ?? checkedValue) { + setCheckedValue(selectedValue) + onChange?.(selectedValue) } }, - [onChange, checkedValue, setCheckedValue] + [onChange, checkedValue, setCheckedValue, value] ) return ( @@ -36,12 +46,12 @@ const RadioGroup = ({ defaultValue, options, onChange, itemClassName = '', class {options.map(item => (
-