diff --git a/.ckb-version b/.ckb-version index a0633b073d..efb66e359e 100644 --- a/.ckb-version +++ b/.ckb-version @@ -1 +1 @@ -v0.116.1 +v0.117.0 diff --git a/.github/workflows/check-code-style.yml b/.github/workflows/check-code-style.yml index 87de0e3831..292eca11e9 100644 --- a/.github/workflows/check-code-style.yml +++ b/.github/workflows/check-code-style.yml @@ -18,7 +18,7 @@ jobs: cache: "yarn" - name: Restore - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: | node_modules @@ -31,7 +31,7 @@ jobs: - name: Changed Files id: changed-files - uses: tj-actions/changed-files@v41 + uses: tj-actions/changed-files@v44 with: files: "packages/**/*.{js,cjs,mjs,jsx,ts,tsx,css,scss}" diff --git a/.github/workflows/check_storybook.yml b/.github/workflows/check_storybook.yml index 1f351e7fb1..26cf157f13 100644 --- a/.github/workflows/check_storybook.yml +++ b/.github/workflows/check_storybook.yml @@ -9,7 +9,7 @@ jobs: strategy: matrix: node: - - 18.12.0 + - 20.15.1 os: - macos-latest - ubuntu-20.04 @@ -35,7 +35,7 @@ jobs: cache: "yarn" - name: Restore - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: | node_modules diff --git a/.github/workflows/merge_released_into_develop.yml b/.github/workflows/merge_released_into_develop.yml index 5870eacb21..fc1af620da 100644 --- a/.github/workflows/merge_released_into_develop.yml +++ b/.github/workflows/merge_released_into_develop.yml @@ -13,7 +13,7 @@ jobs: permissions: pull-requests: write steps: - - uses: actions/checkout@master + - uses: actions/checkout@v4 - name: Request uses: repo-sync/pull-request@v2 with: diff --git a/.github/workflows/package.yml b/.github/workflows/package.yml index e4a0171d42..126b544948 100644 --- a/.github/workflows/package.yml +++ b/.github/workflows/package.yml @@ -12,7 +12,7 @@ jobs: strategy: matrix: node: - - 18.12.0 + - 20.15.1 os: - macos-latest - ubuntu-20.04 @@ -42,7 +42,7 @@ jobs: cache: "yarn" - name: Restore - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: | node_modules @@ -51,7 +51,7 @@ jobs: - name: Add msbuild to PATH if: matrix.os == 'windows-2019' - uses: microsoft/setup-msbuild@v1.3.1 + uses: microsoft/setup-msbuild@v2 env: ACTIONS_ALLOW_UNSECURE_COMMANDS: "true" @@ -103,6 +103,11 @@ jobs: env: CI: false + # Inject LOG_ENCRYPTION_PUBLIC_KEY to encrypt sensitive log + - name: Inject environment variables + run: | + echo "LOG_ENCRYPTION_PUBLIC_KEY=${{ secrets.LOG_ENCRYPTION_PUBLIC_KEY }}" >> packages/neuron-wallet/.env + - name: Package for MacOS if: matrix.os == 'macos-latest' run: | diff --git a/.github/workflows/package_for_test.yml b/.github/workflows/package_for_test.yml index 757811fc6d..6527d3f7f2 100644 --- a/.github/workflows/package_for_test.yml +++ b/.github/workflows/package_for_test.yml @@ -12,7 +12,7 @@ jobs: strategy: matrix: node: - - 18.12.0 + - 20.15.1 os: - macos-latest - ubuntu-20.04 @@ -46,6 +46,37 @@ jobs: with: ref: refs/pull/${{ github.event.issue.number }}/merge + - name: Ensure no more commits after the triggering comment + uses: actions/github-script@v7 + if: ${{ github.event_name == 'issue_comment' }} + env: + ISSUE_NUMBER: ${{github.event.issue.number}} + COMMENT_ID: ${{ github.event.comment.id }} + with: + script: | + const { ISSUE_NUMBER, COMMENT_ID } = process.env + let page = 1 + let hasFoundComment = false + while(true) { + const { data: timelines } = await github.rest.issues.listEventsForTimeline({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: ISSUE_NUMBER, + page, + per_page: 100, + }) + if (timelines.some(v => { + hasFoundComment = hasFoundComment || (v.event === 'commented' && `${v.id}` === `${COMMENT_ID}`) + return hasFoundComment && v.event === 'committed' + })) { + throw new Error('The last commit comes after the comment, please comment and package after last commit') + } + if (timelines.length === 0) { + return + } + page += 1 + } + - name: Setup Node uses: actions/setup-node@v4 with: @@ -53,7 +84,7 @@ jobs: cache: "yarn" - name: Restore - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: | node_modules @@ -62,7 +93,7 @@ jobs: - name: Add msbuild to PATH if: matrix.os == 'windows-2019' - uses: microsoft/setup-msbuild@v1.3.1 + uses: microsoft/setup-msbuild@v2 env: ACTIONS_ALLOW_UNSECURE_COMMANDS: "true" @@ -225,7 +256,7 @@ jobs: - name: Comment by pull request comment event if: ${{ github.event_name == 'issue_comment' }} - uses: peter-evans/create-or-update-comment@v3 + uses: peter-evans/create-or-update-comment@v4 with: comment-id: ${{ github.event.comment.id }} body: | @@ -242,7 +273,7 @@ jobs: steps: - name: Comment by pull request comment event when package failed if: ${{ github.event_name == 'issue_comment' }} - uses: peter-evans/create-or-update-comment@v3 + uses: peter-evans/create-or-update-comment@v4 with: comment-id: ${{ github.event.comment.id }} body: Packageing failed in [${{ github.run_id }}](https://github.com/${{github.repository}}/actions/runs/${{github.run_id}}). @${{ github.event.comment.user.login }} diff --git a/.github/workflows/unit_tests.yml b/.github/workflows/unit_tests.yml index e77a974953..0fb3c58f1d 100644 --- a/.github/workflows/unit_tests.yml +++ b/.github/workflows/unit_tests.yml @@ -9,7 +9,7 @@ jobs: strategy: matrix: node: - - 20.11.1 + - 20.15.1 os: - macos-latest - ubuntu-20.04 @@ -35,7 +35,7 @@ jobs: cache: "yarn" - name: Restore - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: | node_modules diff --git a/.github/workflows/update_ckb_client_versions.yml b/.github/workflows/update_ckb_client_versions.yml index b2834a34bf..8a2ba945d9 100644 --- a/.github/workflows/update_ckb_client_versions.yml +++ b/.github/workflows/update_ckb_client_versions.yml @@ -20,7 +20,7 @@ jobs: - name: Setup Node.js uses: actions/setup-node@v4 with: - node-version: 18.x + node-version: 20.15.1 - name: Update versions id: update_versions @@ -37,7 +37,7 @@ jobs: git_commit_gpgsign: true - name: Open PR to develop branch - uses: peter-evans/create-pull-request@v5 + uses: peter-evans/create-pull-request@v6 with: title: Update ckb client versions commit-message: 'feat: update ckb client versions' diff --git a/.github/workflows/update_neuron_compatible.yml b/.github/workflows/update_neuron_compatible.yml index 0442127f93..2f2e1d5d71 100644 --- a/.github/workflows/update_neuron_compatible.yml +++ b/.github/workflows/update_neuron_compatible.yml @@ -23,7 +23,7 @@ jobs: - name: Setup Node.js uses: actions/setup-node@v4 with: - node-version: 20.x + node-version: 20.15.1 - name: Update versions id: update_versions @@ -40,7 +40,7 @@ jobs: git_commit_gpgsign: true - name: Open PR to RC branch - uses: peter-evans/create-pull-request@v5 + uses: peter-evans/create-pull-request@v6 with: title: Update Neuron compatibility table commit-message: 'feat: Update Neuron compatibility table' diff --git a/.github/workflows/update_wallet_env.yml b/.github/workflows/update_wallet_env.yml index f02a69b70b..62fd3985fe 100644 --- a/.github/workflows/update_wallet_env.yml +++ b/.github/workflows/update_wallet_env.yml @@ -14,22 +14,22 @@ jobs: if: ${{ startsWith(github.ref_name, 'rc/') }} steps: - name: Create Branch - uses: peterjgrainger/action-create-branch@v2.4.0 + uses: peterjgrainger/action-create-branch@v3.0.0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: branch: 'chore-update-wallet-env/${{github.ref_name}}' - sha: '${{ github.event.create.head.sha }}' + sha: '${{ github.sha }}' - name: Checkout uses: actions/checkout@v4 with: ref: 'chore-update-wallet-env/${{github.ref_name}}' - + - name: Setup Node uses: actions/setup-node@v4 with: - node-version: 18.12.0 + node-version: 20.15.1 - name: Write env file run: | diff --git a/CHANGELOG.md b/CHANGELOG.md index 14a324aac9..0f48947346 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,45 @@ +# 0.117.0 (2024-08-12) + +### CKB Node & Light Client + +- [CKB@v0.117.0](https://github.com/nervosnetwork/ckb/releases/tag/v0.117.0) was released on Jul. 29th, 2024. This version of CKB node is now bundled and preconfigured in Neuron. +- [CKB Light Client@v0.3.7](https://github.com/nervosnetwork/ckb-light-client/releases/tag/v0.3.7) was released on Apr. 13th, 2024. This version of CKB Light Client is now bundled and preconfigured in Neuron + +### Assumed valid target + +Block before `0xca44ae8f7bc12ba8eab3224cbe3156c913e2284693e36dc1d01e4d30f362f3c2`(at height `13,705,152`) will be skipped in validation.(https://github.com/nervosnetwork/neuron/pull/3227) + +--- + +[![Neuron@v0.117.0](https://github.com/user-attachments/assets/7d2eba67-e33e-4fca-a714-7ba1709d8bd3)](https://youtu.be/zf78Y094m60) + +YouTube: https://youtu.be/zf78Y094m60 + +--- + +## New features + +- #3206: Support XUDT asset management.(@yanguoyu) +- #3207: Support connecting to an external light client.(@devchenyan) +- #3167: Support cells consolidation.(@devchenyan) +- #3199: Validate pending transactions periodically.(@devchenyan) +- #3200: Optimize the process of generating a wallet.(@devchenyan) +- #3176: Support setting start block numbers of multisig addresses.(@yanguoyu) +- #3160: Optimize synchronization in light client mode for multiple wallets.(@yanguoyu) +- #3169: Be compatible with multisig transaction JSON file exported from CKB CLI.(@devchenyan) +- #3197: Support resetting pin code for window lock.(@yanguoyu) +- #3194: Add a tip for multisig addresses.(@yanguoyu) + +## Bug fixes + +- #3195: Fix the synchronization status check.(@yanguoyu) + +## New Contributors + +- @tcpdumppy made their first contribution in https://github.com/nervosnetwork/neuron/pull/3182 + +**Full Changelog**: https://github.com/nervosnetwork/neuron/compare/v0.116.2...v0.117.0 + # 0.116.2 (2024-05-29) ### CKB Node & Light Client @@ -57,18 +99,18 @@ YouTube: https://youtu.be/QXv8by2C8zU ## New features -- 3134: Support 'replace-by-fee' nervos dao transactions and sudt transactions.(@devchenyan) -- 3144: Reduce size of light client log in debug information and reveal start-block-number in log.(@yanguoyu) -- 3064: Support locking window by pin code.(@yanguoyu) -- 3131: Add detailed result for nervos dao transaction.(@devchenyan) +- #3134: Support 'replace-by-fee' nervos dao transactions and sudt transactions.(@devchenyan) +- #3144: Reduce size of light client log in debug information and reveal start-block-number in log.(@yanguoyu) +- #3064: Support locking window by pin code.(@yanguoyu) +- #3131: Add detailed result for nervos dao transaction.(@devchenyan) ## Bug fixes -- 3121: Locate the first transaction on Explorer directly when users want to set the start-block-number for light client.(@yanguoyu) -- 3101: Show migration instruction properly.(@devchenyan) -- 3062: Migrate legacy ACP to active ACP account(@yanguoyu) -- 3141: Fix some issues about light client synchronizaiton.(@yanguoyu) -- 3120: Remove all sync data when start-block-number is set less than before.(@yanguoyu) +- #3121: Locate the first transaction on Explorer directly when users want to set the start-block-number for light client.(@yanguoyu) +- #3101: Show migration instruction properly.(@devchenyan) +- #3062: Migrate legacy ACP to active ACP account(@yanguoyu) +- #3141: Fix some issues about light client synchronizaiton.(@yanguoyu) +- #3120: Remove all sync data when start-block-number is set less than before.(@yanguoyu) **Full Changelog**: https://github.com/nervosnetwork/neuron/compare/v0.114.3...v0.116.0 diff --git a/_typos.toml b/_typos.toml index 0afbdd74c5..7a852c75b4 100644 --- a/_typos.toml +++ b/_typos.toml @@ -3,6 +3,7 @@ thur = "thur" numer = "numer" HD = "HD" hd = "hd" +passin = 'passin' # defined in database schema lastest = "lastest" diff --git a/compatible.json b/compatible.json index b2865b7a18..125c5a0fc3 100644 --- a/compatible.json +++ b/compatible.json @@ -1,5 +1,6 @@ { "fullVersions": [ + "0.117", "0.116", "0.115", "0.114", @@ -22,6 +23,7 @@ "compatible": { "0.111": { "full": [ + "0.117", "0.116", "0.115", "0.114", @@ -38,6 +40,7 @@ }, "0.110": { "full": [ + "0.117", "0.116", "0.115", "0.114", @@ -70,6 +73,7 @@ }, "0.112": { "full": [ + "0.117", "0.116", "0.115", "0.114", @@ -86,6 +90,7 @@ }, "0.114": { "full": [ + "0.117", "0.116", "0.115", "0.114", @@ -102,6 +107,24 @@ }, "0.116": { "full": [ + "0.117", + "0.116", + "0.115", + "0.114", + "0.113", + "0.112", + "0.111", + "0.110", + "0.109" + ], + "light": [ + "0.3", + "0.2" + ] + }, + "0.117": { + "full": [ + "0.117", "0.116", "0.115", "0.114", diff --git a/lerna.json b/lerna.json index 42a9f9ef22..6e01055745 100644 --- a/lerna.json +++ b/lerna.json @@ -1,6 +1,6 @@ { "packages": ["packages/*"], - "version": "0.116.2", + "version": "0.117.0", "npmClient": "yarn", "$schema": "node_modules/lerna/schemas/lerna-schema.json" } diff --git a/package.json b/package.json index 4a5cc5abac..460937a772 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "neuron", "productName": "Neuron", "description": "CKB Neuron Wallet", - "version": "0.116.2", + "version": "0.117.0", "private": true, "author": { "name": "Nervos Core Dev", @@ -53,11 +53,12 @@ "eslint-config-prettier": "9.1.0", "eslint-plugin-prettier": "4.2.1", "husky": "8.0.3", - "lerna": "8.0.1", - "lint-staged": "15.2.0", + "lerna": "8.1.5", + "lint-staged": "15.2.7", "ncp": "2.0.0", "prettier": "2.8.8", - "ts-jest": "29.1.1", + "ts-jest": "29.2.0", + "ts-node": "^10.9.2", "typescript": "5.3.3", "wait-on": "7.0.1" }, @@ -66,7 +67,15 @@ "@types/react": "18.2.45", "react-i18next": ">=11.16.4", "react-refresh": "0.14.0", - "node-fetch": "2.6.13" + "node-fetch": "2.6.13", + "@ckb-lumos/ckb-indexer": "0.23.0", + "@ckb-lumos/base": "0.23.0", + "@ckb-lumos/bi": "0.23.0", + "@ckb-lumos/codec": "0.23.0", + "@ckb-lumos/common-scripts": "0.23.0", + "@ckb-lumos/config-manager": "0.23.0", + "@ckb-lumos/lumos": "0.23.0", + "@ckb-lumos/rpc": "0.23.0" }, "volta": { "node": "20.10.0" diff --git a/packages/neuron-ui/.storybook/main.ts b/packages/neuron-ui/.storybook/main.ts index 3cf0dbf366..a8bbc1d2bc 100644 --- a/packages/neuron-ui/.storybook/main.ts +++ b/packages/neuron-ui/.storybook/main.ts @@ -12,6 +12,11 @@ export default { options: {}, }, webpackFinal: config => { + config.resolve.fallback = { + fs: false, + crypto: false, + buffer: false, + } config.resolve.alias = { ...config.resolve.alias, electron: require.resolve('./electron'), diff --git a/packages/neuron-ui/config-overrides.js b/packages/neuron-ui/config-overrides.js index 6222691d70..8ffe287136 100644 --- a/packages/neuron-ui/config-overrides.js +++ b/packages/neuron-ui/config-overrides.js @@ -4,6 +4,10 @@ const path = require('path') module.exports = function override(config) { const webpackConfig = { ...config } webpackConfig.resolve.alias.electron = path.join(__dirname, 'src/electron-modules') - webpackConfig.resolve.fallback = { fs: false } + webpackConfig.resolve.fallback = { + fs: false, + crypto: false, + buffer: false, + } return webpackConfig } diff --git a/packages/neuron-ui/package.json b/packages/neuron-ui/package.json index 1319e32973..9f0ef450d2 100644 --- a/packages/neuron-ui/package.json +++ b/packages/neuron-ui/package.json @@ -1,6 +1,6 @@ { "name": "neuron-ui", - "version": "0.116.2", + "version": "0.117.0", "private": true, "author": { "name": "Nervos Core Dev", @@ -50,13 +50,15 @@ "last 2 chrome versions" ], "dependencies": { - "@ckb-lumos/bi": "0.21.1", - "@ckb-lumos/rpc": "0.21.1", - "@ckb-lumos/base": "0.21.1", - "@ckb-lumos/codec": "0.21.1", - "@ckb-lumos/helpers": "0.21.1", - "@ckb-lumos/config-manager": "0.21.1", - "@ckb-lumos/common-scripts": "0.21.1", + "@ckb-lumos/lumos": "0.23.0", + "@ckb-lumos/bi": "0.23.0", + "@ckb-lumos/rpc": "0.23.0", + "@ckb-lumos/base": "0.23.0", + "@ckb-lumos/codec": "0.23.0", + "@ckb-lumos/hd": "0.23.0", + "@ckb-lumos/helpers": "0.23.0", + "@ckb-lumos/config-manager": "0.23.0", + "@ckb-lumos/common-scripts": "0.23.0", "canvg": "2.0.0", "i18next": "23.7.11", "immer": "9.0.21", @@ -69,7 +71,7 @@ "react-router-dom": "6.21.1", "react-transition-group": "4.4.5", "sass": "1.69.5", - "tslib": "2.6.2" + "tslib": "2.6.3" }, "devDependencies": { "@babel/plugin-proposal-private-property-in-object": "7.21.11", @@ -95,7 +97,7 @@ "babel-jest": "25.5.1", "electron": "30.0.0", "enzyme": "3.11.0", - "enzyme-adapter-react-16": "1.15.7", + "enzyme-adapter-react-16": "1.15.8", "eslint-config-airbnb": "19.0.4", "eslint-plugin-import": "2.29.1", "eslint-plugin-jsx-a11y": "6.7.1", diff --git a/packages/neuron-ui/src/components/AmendPendingTransactionDialog/hooks.ts b/packages/neuron-ui/src/components/AmendPendingTransactionDialog/hooks.ts index cef45ef8e9..dfb4afc26a 100644 --- a/packages/neuron-ui/src/components/AmendPendingTransactionDialog/hooks.ts +++ b/packages/neuron-ui/src/components/AmendPendingTransactionDialog/hooks.ts @@ -41,12 +41,11 @@ export const useInitialize = ({ const fetchInitData = useCallback(async () => { const res = await getOnChainTransaction(tx.hash) const { - // @ts-expect-error Replace-By-Fee (RBF) - min_replace_fee: minFee, + minReplaceFee, transaction: { outputsData }, } = res - if (!minFee) { + if (!minReplaceFee) { setIsConfirmedAlertShown(true) } @@ -60,8 +59,8 @@ export const useInitialize = ({ }) setSize(txResult.size) - if (minFee) { - const mPrice = ((BigInt(minFee) * BigInt(FEE_RATIO)) / BigInt(txResult.size)).toString() + if (minReplaceFee) { + const mPrice = ((BigInt(minReplaceFee) * BigInt(FEE_RATIO)) / BigInt(txResult.size)).toString() setMinPrice(mPrice) setPrice(mPrice) } @@ -83,9 +82,8 @@ export const useInitialize = ({ const onSubmit = useCallback(async () => { try { - // @ts-expect-error Replace-By-Fee (RBF) - const { min_replace_fee: minFee } = await getOnChainTransaction(tx.hash) - if (!minFee) { + const { minReplaceFee } = await getOnChainTransaction(tx.hash) + if (!minReplaceFee) { setIsConfirmedAlertShown(true) return } diff --git a/packages/neuron-ui/src/components/AmendSUDTSend/amendSUDTSend.module.scss b/packages/neuron-ui/src/components/AmendSUDTSend/amendSUDTSend.module.scss index 2ef4fcb512..7b971044b1 100644 --- a/packages/neuron-ui/src/components/AmendSUDTSend/amendSUDTSend.module.scss +++ b/packages/neuron-ui/src/components/AmendSUDTSend/amendSUDTSend.module.scss @@ -9,6 +9,7 @@ $noticeHeight: 60px; .left { flex: 1; position: relative; + min-width: 480px; .addressCell { margin-bottom: 10px; @@ -37,9 +38,6 @@ $noticeHeight: 60px; padding: 20px 16px 18px; border-radius: 16px; margin-bottom: 16px; - .addresstField { - font-family: 'JetBrains Mono'; - } .textFieldClass { margin-bottom: 10px; } diff --git a/packages/neuron-ui/src/components/AmendSUDTSend/hooks.ts b/packages/neuron-ui/src/components/AmendSUDTSend/hooks.ts index 199ec5f37c..b82355cec2 100644 --- a/packages/neuron-ui/src/components/AmendSUDTSend/hooks.ts +++ b/packages/neuron-ui/src/components/AmendSUDTSend/hooks.ts @@ -46,11 +46,10 @@ export const useInitialize = ({ const fetchInitData = useCallback(async () => { const { - // @ts-expect-error Replace-By-Fee (RBF) - min_replace_fee: minFee, + minReplaceFee, transaction: { outputsData }, } = await getOnChainTransaction(hash) - if (!minFee) { + if (!minReplaceFee) { setIsConfirmedAlertShown(true) } @@ -76,8 +75,8 @@ export const useInitialize = ({ setTransaction({ ...tx, outputsData }) setSize(tx.size) - if (minFee) { - const mPrice = ((BigInt(minFee) * BigInt(FEE_RATIO)) / BigInt(tx.size)).toString() + if (minReplaceFee) { + const mPrice = ((BigInt(minReplaceFee) * BigInt(FEE_RATIO)) / BigInt(tx.size)).toString() setMinPrice(mPrice) setPrice(mPrice) } @@ -98,9 +97,8 @@ export const useInitialize = ({ return } try { - // @ts-expect-error Replace-By-Fee (RBF) - const { min_replace_fee: minFee } = await getOnChainTransaction(hash) - if (!minFee) { + const { minReplaceFee } = await getOnChainTransaction(hash) + if (!minReplaceFee) { setIsConfirmedAlertShown(true) return } diff --git a/packages/neuron-ui/src/components/AmendSUDTSend/index.tsx b/packages/neuron-ui/src/components/AmendSUDTSend/index.tsx index 5cdbc79812..66250d03b6 100644 --- a/packages/neuron-ui/src/components/AmendSUDTSend/index.tsx +++ b/packages/neuron-ui/src/components/AmendSUDTSend/index.tsx @@ -198,7 +198,7 @@ const AmendSUDTSend = () => {
-
diff --git a/packages/neuron-ui/src/components/AmendSend/amendSend.module.scss b/packages/neuron-ui/src/components/AmendSend/amendSend.module.scss index 58bd47f31f..10302555ae 100644 --- a/packages/neuron-ui/src/components/AmendSend/amendSend.module.scss +++ b/packages/neuron-ui/src/components/AmendSend/amendSend.module.scss @@ -9,6 +9,7 @@ $noticeHeight: 60px; .left { flex: 1; position: relative; + min-width: 480px; .addressCell { margin-bottom: 10px; @@ -37,9 +38,6 @@ $noticeHeight: 60px; padding: 20px 16px 18px; border-radius: 16px; margin-bottom: 16px; - .addresstField { - font-family: 'JetBrains Mono'; - } .textFieldClass { margin-bottom: 10px; } diff --git a/packages/neuron-ui/src/components/AmendSend/hooks.ts b/packages/neuron-ui/src/components/AmendSend/hooks.ts index 26402bea26..5e82ccdc02 100644 --- a/packages/neuron-ui/src/components/AmendSend/hooks.ts +++ b/packages/neuron-ui/src/components/AmendSend/hooks.ts @@ -70,11 +70,10 @@ export const useInitialize = ({ const fetchInitData = useCallback(async () => { const res = await getOnChainTransaction(hash) const { - // @ts-expect-error Replace-By-Fee (RBF) - min_replace_fee: minFee, + minReplaceFee, transaction: { outputsData }, } = res - if (!minFee) { + if (!minReplaceFee) { setIsConfirmedAlertShown(true) } @@ -87,8 +86,8 @@ export const useInitialize = ({ }) setSize(tx.size) - if (minFee) { - const mPrice = ((BigInt(minFee) * BigInt(FEE_RATIO)) / BigInt(tx.size)).toString() + if (minReplaceFee) { + const mPrice = ((BigInt(minReplaceFee) * BigInt(FEE_RATIO)) / BigInt(tx.size)).toString() setMinPrice(mPrice) updateTransactionPrice(mPrice) } @@ -113,9 +112,8 @@ export const useInitialize = ({ return } try { - // @ts-expect-error Replace-By-Fee (RBF) - const { min_replace_fee: minFee } = await getOnChainTransaction(hash) - if (!minFee) { + const { minReplaceFee } = await getOnChainTransaction(hash) + if (!minReplaceFee) { setIsConfirmedAlertShown(true) return } diff --git a/packages/neuron-ui/src/components/AmendSend/index.tsx b/packages/neuron-ui/src/components/AmendSend/index.tsx index 7ba3873d1e..453eef097b 100644 --- a/packages/neuron-ui/src/components/AmendSend/index.tsx +++ b/packages/neuron-ui/src/components/AmendSend/index.tsx @@ -250,7 +250,7 @@ const AmendSend = () => { {t('send.allow-use-sent-cell')}
-
diff --git a/packages/neuron-ui/src/components/CellInfoDialog/cellInfoDialog.module.scss b/packages/neuron-ui/src/components/CellInfoDialog/cellInfoDialog.module.scss index 1831f13f93..1b6d32159d 100644 --- a/packages/neuron-ui/src/components/CellInfoDialog/cellInfoDialog.module.scss +++ b/packages/neuron-ui/src/components/CellInfoDialog/cellInfoDialog.module.scss @@ -180,10 +180,10 @@ border: 1px solid var(--divide-line-color); background-color: var(--secondary-background-color); position: relative; + overflow: hidden; & > div { position: absolute; - left: -1px; top: -1px; height: 100%; border-radius: 8px; diff --git a/packages/neuron-ui/src/components/CellInfoDialog/index.tsx b/packages/neuron-ui/src/components/CellInfoDialog/index.tsx index e75da3363a..df5cdad484 100644 --- a/packages/neuron-ui/src/components/CellInfoDialog/index.tsx +++ b/packages/neuron-ui/src/components/CellInfoDialog/index.tsx @@ -4,7 +4,7 @@ import { calculateUsedCapacity, getExplorerUrl, shannonToCKBFormatter, truncateM import { useTranslation } from 'react-i18next' import Tabs from 'widgets/Tabs' import { type TFunction } from 'i18next' -import { Script } from '@ckb-lumos/base' +import { Script } from '@ckb-lumos/lumos' import Switch from 'widgets/Switch' import { Copy, ExplorerIcon } from 'widgets/Icons/icon' import Alert from 'widgets/Alert' diff --git a/packages/neuron-ui/src/components/CellManagement/cellManagement.module.scss b/packages/neuron-ui/src/components/CellManagement/cellManagement.module.scss index c286a5007a..a36bad6e1e 100644 --- a/packages/neuron-ui/src/components/CellManagement/cellManagement.module.scss +++ b/packages/neuron-ui/src/components/CellManagement/cellManagement.module.scss @@ -51,13 +51,14 @@ } .actions { - & > svg { + display: flex; + gap: 16px; + & svg { cursor: pointer; &[data-disabled='true'] { cursor: not-allowed; opacity: 50%; } - margin-right: 16px; } } diff --git a/packages/neuron-ui/src/components/CellManagement/hooks.ts b/packages/neuron-ui/src/components/CellManagement/hooks.ts index e38c6cec29..4977cc6ced 100644 --- a/packages/neuron-ui/src/components/CellManagement/hooks.ts +++ b/packages/neuron-ui/src/components/CellManagement/hooks.ts @@ -18,7 +18,8 @@ import { ErrorCode, LockScriptCategory, RoutePath, TypeScriptCategory, isSuccess import { SortType } from 'widgets/Table' const cellTypeOrder: Record = { - [TypeScriptCategory.SUDT]: 1, + [TypeScriptCategory.SUDT]: 0, + [TypeScriptCategory.XUDT]: 1, [TypeScriptCategory.NFT]: 2, [TypeScriptCategory.Spore]: 3, [TypeScriptCategory.Unknown]: 4, @@ -47,6 +48,9 @@ const getLockStatusAndReason = (item: State.LiveCellWithLocalInfo) => { case TypeScriptCategory.DAO: lockedReason = { key: 'cell-manage.locked-reason.NFT-SUDT-DAO', params: { type: 'Nervos DAO' } } break + case TypeScriptCategory.XUDT: + lockedReason = { key: 'cell-manage.locked-reason.NFT-SUDT-DAO', params: { type: 'XUDT' } } + break case TypeScriptCategory.Unknown: lockedReason = { key: 'cell-manage.locked-reason.Unknown' } break @@ -82,6 +86,7 @@ const getCellType = (item: State.LiveCellWithLocalInfo) => { switch (item.typeScriptType) { case TypeScriptCategory.NFT: case TypeScriptCategory.SUDT: + case TypeScriptCategory.XUDT: case TypeScriptCategory.Spore: case TypeScriptCategory.Unknown: return item.typeScriptType @@ -202,6 +207,7 @@ export enum Actions { Lock = 'lock', Unlock = 'unlock', Consume = 'consume', + Consolidate = 'consolidate', } export const useAction = ({ @@ -213,6 +219,7 @@ export const useAction = ({ setError, password, verifyDeviceStatus, + wallet, }: { liveCells: State.LiveCellWithLocalInfo[] currentPageLiveCells: State.LiveCellWithLocalInfo[] @@ -222,6 +229,7 @@ export const useAction = ({ setError: (error: string) => void password: string verifyDeviceStatus: () => Promise + wallet: State.Wallet }) => { const dispatch = useDispatch() const navigate = useNavigate() @@ -253,10 +261,21 @@ export const useAction = ({ }, [liveCells, selectedOutPoints, setOperateCells, dispatch, navigate] ) + + const getConsolidateAddress = useCallback(() => { + const { addresses } = wallet + if (addresses.length === 1) { + return addresses[0].address + } + const unusedReceiveAddress = addresses.find(a => a.type === 0 && a.txCount === 0)?.address ?? '' + + return unusedReceiveAddress + }, [wallet]) + const onActionConfirm = useCallback(async () => { switch (action) { - case 'lock': - case 'unlock': + case Actions.Lock: + case Actions.Unlock: if (!(await verifyDeviceStatus())) return setLoading(true) updateLiveCellsLockStatus({ @@ -276,17 +295,24 @@ export const useAction = ({ setLoading(false) }) break - case 'consume': + case Actions.Consume: dispatch({ type: AppActions.UpdateConsumeCells, payload: operateCells.map(v => ({ outPoint: v.outPoint, capacity: v.capacity })), }) navigate(`${RoutePath.Send}?isSendMax=true`) break + case Actions.Consolidate: + dispatch({ + type: AppActions.UpdateConsumeCells, + payload: operateCells.map(v => ({ outPoint: v.outPoint, capacity: v.capacity })), + }) + navigate(`${RoutePath.Send}?isSendMax=true&toAddress=${getConsolidateAddress()}`) + break default: break } - }, [action, operateCells, dispatch, navigate, password]) + }, [action, operateCells, dispatch, navigate, password, getConsolidateAddress]) const onActionCancel = useCallback(() => { setAction(undefined) setOperateCells([]) @@ -377,7 +403,7 @@ export const usePassword = () => { } export const useHardWallet = ({ wallet, t }: { wallet: State.WalletIdentity; t: TFunction }) => { - const isWin32 = useMemo(() => { + const isWin32 = useMemo(() => { return getPlatform() === 'win32' }, []) const [error, setError] = useState() diff --git a/packages/neuron-ui/src/components/CellManagement/index.tsx b/packages/neuron-ui/src/components/CellManagement/index.tsx index 48f94566e2..7fbb4860bc 100644 --- a/packages/neuron-ui/src/components/CellManagement/index.tsx +++ b/packages/neuron-ui/src/components/CellManagement/index.tsx @@ -1,5 +1,5 @@ import React, { useCallback, useMemo, useState } from 'react' -import { Attention, Consume, DetailIcon, EyesClose, EyesOpen, LockCell, UnLock } from 'widgets/Icons/icon' +import { Attention, Consume, DetailIcon, EyesClose, EyesOpen, LockCell, UnLock, Consolidate } from 'widgets/Icons/icon' import PageContainer from 'components/PageContainer' import { useTranslation } from 'react-i18next' import Breadcrum from 'widgets/Breadcrum' @@ -23,7 +23,7 @@ import { TFunction } from 'i18next' import TextField from 'widgets/TextField' import { useSearchParams } from 'react-router-dom' import CellInfoDialog from 'components/CellInfoDialog' -import { computeScriptHash } from '@ckb-lumos/base/lib/utils' +import { computeScriptHash } from '@ckb-lumos/lumos/utils' import Hardware from 'widgets/Icons/Hardware.png' import Button from 'widgets/Button' import Alert from 'widgets/Alert' @@ -186,28 +186,36 @@ const getColumns = ({ const { locked, lockedReason } = item return (
- + + + {locked ? ( - + + + ) : ( - + + + )} + + - )} - +
) }, @@ -265,6 +273,7 @@ const CellManagement = () => { resetPassword, password, verifyDeviceStatus, + wallet, }) const columns = useMemo( () => @@ -331,6 +340,10 @@ const CellManagement = () => { {t('cell-manage.consume')} +
) : null} @@ -432,6 +445,15 @@ const CellManagement = () => { > {t('cell-manage.cell-consume-dialog.warn-consume')} + + {t('cell-manage.cell-consolidate-dialog.warn-consume')} + ) } diff --git a/packages/neuron-ui/src/components/FormattedTokenAmount/index.tsx b/packages/neuron-ui/src/components/FormattedTokenAmount/index.tsx index 4a180ba15f..b31da32d3c 100644 --- a/packages/neuron-ui/src/components/FormattedTokenAmount/index.tsx +++ b/packages/neuron-ui/src/components/FormattedTokenAmount/index.tsx @@ -12,7 +12,7 @@ type FormattedTokenAmountProps = { item: State.Transaction; show: boolean; symbo type AmountProps = Omit & { sudtAmount?: string isReceive: boolean - amount: string + amount?: string symbolClassName?: string } @@ -30,18 +30,18 @@ const Amount = ({ sudtAmount, show, item, isReceive, amount, symbolClassName, sy ) : (
- - {amount} + + {amount ?? '--'} -  {symbol} +  {amount ? symbol : ''}
) } export const FormattedTokenAmount = ({ item, show, symbolClassName }: FormattedTokenAmountProps) => { - let amount = '--' + let amount: string | undefined let sudtAmount = '' - let copyText = amount + let copyText: string | undefined = amount let isReceive = false let symbol = '' @@ -61,16 +61,22 @@ export const FormattedTokenAmount = ({ item, show, symbolClassName }: FormattedT isReceive = !sudtAmount.includes('-') } } else { - amount = show ? `${shannonToCKBFormatter(item.value, true)}` : `${HIDE_BALANCE}` - isReceive = !amount.includes('-') + amount = show + ? `${shannonToCKBFormatter(item.nervosDao ? item.daoCapacity ?? '--' : item.value, true)}` + : `${HIDE_BALANCE}` + isReceive = !amount?.includes('-') copyText = `${amount} CKB` symbol = 'CKB' + if (item.nervosDao && item.daoCapacity === undefined) { + amount = undefined + copyText = undefined + } } } const props = { sudtAmount, show, item, isReceive, amount, symbolClassName, symbol } - return show ? ( + return show && copyText ? ( diff --git a/packages/neuron-ui/src/components/GeneralSetting/LockWindowDialog/hooks.ts b/packages/neuron-ui/src/components/GeneralSetting/LockWindowDialog/hooks.ts index a1213dc389..fe2e933eec 100644 --- a/packages/neuron-ui/src/components/GeneralSetting/LockWindowDialog/hooks.ts +++ b/packages/neuron-ui/src/components/GeneralSetting/LockWindowDialog/hooks.ts @@ -71,7 +71,7 @@ export const useRepeatPassword = ({ password: string t: TFunction encryptedPassword?: string - onCancel: () => void + onCancel: (success: boolean) => void }) => { const dispatch = useDispatch() const [errMsg, setErrMsg] = useState('') @@ -89,7 +89,7 @@ export const useRepeatPassword = ({ updateLockWindowInfo( encryptedPassword ? { password: updatedRepeatPassword } : { password: updatedRepeatPassword, locked: true } )(dispatch) - onCancel() + onCancel(true) } } else { setErrMsg('') diff --git a/packages/neuron-ui/src/components/GeneralSetting/LockWindowDialog/index.tsx b/packages/neuron-ui/src/components/GeneralSetting/LockWindowDialog/index.tsx index 629eda59f6..c9f0226413 100644 --- a/packages/neuron-ui/src/components/GeneralSetting/LockWindowDialog/index.tsx +++ b/packages/neuron-ui/src/components/GeneralSetting/LockWindowDialog/index.tsx @@ -13,7 +13,7 @@ const LockWindowDialog = ({ encryptedPassword, }: { show: boolean - onCancel: () => void + onCancel: (success?: boolean) => void encryptedPassword?: string }) => { const [t] = useTranslation() diff --git a/packages/neuron-ui/src/components/GeneralSetting/generalSetting.module.scss b/packages/neuron-ui/src/components/GeneralSetting/generalSetting.module.scss index 92e5f8d34f..39881944b7 100644 --- a/packages/neuron-ui/src/components/GeneralSetting/generalSetting.module.scss +++ b/packages/neuron-ui/src/components/GeneralSetting/generalSetting.module.scss @@ -9,7 +9,7 @@ $action-button-width: 11.25rem; gap: 16px; .content { - width: 176px; + min-width: 176px; padding: 16px; background: var(--input-disabled-color); border-radius: 8px; diff --git a/packages/neuron-ui/src/components/GeneralSetting/index.tsx b/packages/neuron-ui/src/components/GeneralSetting/index.tsx index 45216d4d9b..046c90c4b1 100644 --- a/packages/neuron-ui/src/components/GeneralSetting/index.tsx +++ b/packages/neuron-ui/src/components/GeneralSetting/index.tsx @@ -97,6 +97,7 @@ const UpdateDownloadStatus = ({ } if (downloaded) { + const isWin = process.platform === 'win32' return ( { - // @ts-expect-error Replace-By-Fee (RBF) - const { min_replace_fee: minReplaceFee } = tx + const { minReplaceFee } = tx if (minReplaceFee) { setAmendabled(true) } diff --git a/packages/neuron-ui/src/components/History/index.tsx b/packages/neuron-ui/src/components/History/index.tsx index 6e1f19c2d3..f012415c87 100644 --- a/packages/neuron-ui/src/components/History/index.tsx +++ b/packages/neuron-ui/src/components/History/index.tsx @@ -126,7 +126,7 @@ const History = () => { sortable: true, }, { - title: t('history.table.amount'), + title: t('history.table.asset'), dataIndex: 'amount', align: 'left', isBalance: true, diff --git a/packages/neuron-ui/src/components/HistoryDetailPage/index.tsx b/packages/neuron-ui/src/components/HistoryDetailPage/index.tsx index 8d6f553ec5..8db87ebf76 100644 --- a/packages/neuron-ui/src/components/HistoryDetailPage/index.tsx +++ b/packages/neuron-ui/src/components/HistoryDetailPage/index.tsx @@ -3,7 +3,7 @@ import { useNavigate, useParams } from 'react-router-dom' import { useTranslation } from 'react-i18next' import { calculateUnlockDaoMaximumWithdraw, getTransaction } from 'services/remote' import { showPageNotice, transactionState, useDispatch, useState as useGlobalState } from 'states' -import { type CKBComponents } from '@ckb-lumos/rpc/lib/types/api' +import { type CKBComponents } from '@ckb-lumos/lumos/rpc' import PageContainer from 'components/PageContainer' import LockInfoDialog from 'components/LockInfoDialog' import ScriptTag from 'components/ScriptTag' diff --git a/packages/neuron-ui/src/components/ImportFailureDialog/importFailureDialog.module.scss b/packages/neuron-ui/src/components/ImportFailureDialog/importFailureDialog.module.scss new file mode 100644 index 0000000000..1162951ae5 --- /dev/null +++ b/packages/neuron-ui/src/components/ImportFailureDialog/importFailureDialog.module.scss @@ -0,0 +1,23 @@ +@import '../../styles/mixin.scss'; + +.container { + width: 680px; + + .content { + max-width: 60%; + text-align: center; + margin: 0 auto; + font-size: 14px; + line-height: 24px; + color: var(--main-text-color); + + button { + border: none; + background: none; + color: var(--primary-color); + text-decoration: underline; + text-underline-offset: 4px; + cursor: pointer; + } + } +} diff --git a/packages/neuron-ui/src/components/ImportFailureDialog/index.tsx b/packages/neuron-ui/src/components/ImportFailureDialog/index.tsx new file mode 100644 index 0000000000..b67b984db5 --- /dev/null +++ b/packages/neuron-ui/src/components/ImportFailureDialog/index.tsx @@ -0,0 +1,38 @@ +import React, { useCallback } from 'react' +import { useTranslation } from 'react-i18next' +import Dialog from 'widgets/Dialog' +import { openExternal } from 'services/remote' +import styles from './importFailureDialog.module.scss' + +const ImportFailureDialog = ({ show, onClose }: { show: boolean; onClose: () => void }) => { + const [t] = useTranslation() + + const onBtnClick = useCallback(() => { + openExternal( + 'https://github.com/nervosnetwork/ckb-cli/wiki/Import-ckb-cli-keystore-from%26to-Neuron-wallet#ckb-cli-and-neuron-use-the-keystore-in-different-way' + ) + }, []) + + return ( + +
+

+ {t('import-keystore.import-failure-msg')} + +

+
+
+ ) +} + +ImportFailureDialog.displayName = 'ImportFailureDialog' + +export default ImportFailureDialog diff --git a/packages/neuron-ui/src/components/ImportKeystore/index.tsx b/packages/neuron-ui/src/components/ImportKeystore/index.tsx index e3b71e74ac..3e7f211fd1 100644 --- a/packages/neuron-ui/src/components/ImportKeystore/index.tsx +++ b/packages/neuron-ui/src/components/ImportKeystore/index.tsx @@ -19,6 +19,7 @@ import ReplaceDuplicateWalletDialog, { useReplaceDuplicateWallet } from 'compone import { FinishCreateLoading, CreateFirstWalletNav } from 'components/WalletWizard' import TextField from 'widgets/TextField' import { importedWalletDialogShown } from 'services/localCache' +import ImportFailureDialog from '../ImportFailureDialog' import styles from './importKeystore.module.scss' const { MAX_WALLET_NAME_LENGTH, MAX_PASSWORD_LENGTH } = CONSTANTS @@ -49,6 +50,7 @@ const ImportKeystore = () => { const navigate = useNavigate() const [fields, setFields] = useState(defaultFields) const [openingFile, setOpeningFile] = useState(false) + const [isImportFailureDialogShow, setIsImportFailureDialogShow] = useState(false) const { onImportingExitingWalletError, dialogProps } = useReplaceDuplicateWallet() const goBack = useGoBack() @@ -122,6 +124,11 @@ const ImportKeystore = () => { return } + if (res.status === ErrorCode.UnsupportedCkbCliKeystore) { + setIsImportFailureDialogShow(true) + return + } + if (res.message) { const msg = typeof res.message === 'string' ? res.message : res.message.content || '' if (msg) { @@ -140,7 +147,18 @@ const ImportKeystore = () => { closeDialog() }) }, - [fields.name, fields.password, fields.path, navigate, openDialog, closeDialog, disabled, setFields, t] + [ + fields.name, + fields.password, + fields.path, + navigate, + openDialog, + closeDialog, + disabled, + setFields, + t, + setIsImportFailureDialogShow, + ] ) const handleChange = useCallback( @@ -199,6 +217,10 @@ const ImportKeystore = () => { [setFields, wallets, t] ) + const onCloseImportFailureDialog = useCallback(() => { + setIsImportFailureDialogShow(false) + }, [setIsImportFailureDialogShow]) + return ( <>
@@ -248,6 +270,8 @@ const ImportKeystore = () => {
+ + ) } diff --git a/packages/neuron-ui/src/components/LockInfoDialog/index.tsx b/packages/neuron-ui/src/components/LockInfoDialog/index.tsx index 7e747e18c8..2242042484 100644 --- a/packages/neuron-ui/src/components/LockInfoDialog/index.tsx +++ b/packages/neuron-ui/src/components/LockInfoDialog/index.tsx @@ -1,6 +1,6 @@ import { useTranslation } from 'react-i18next' import React, { useRef } from 'react' -import { type CKBComponents } from '@ckb-lumos/rpc/lib/types/api' +import { type CKBComponents } from '@ckb-lumos/lumos/rpc' import Dialog from 'widgets/Dialog' import { useCopy, useDialog, scriptToAddress } from 'utils' import { Copy } from 'widgets/Icons/icon' diff --git a/packages/neuron-ui/src/components/MultisigAddress/hooks.ts b/packages/neuron-ui/src/components/MultisigAddress/hooks.ts index 20853bdbca..9c3b55bca4 100644 --- a/packages/neuron-ui/src/components/MultisigAddress/hooks.ts +++ b/packages/neuron-ui/src/components/MultisigAddress/hooks.ts @@ -15,7 +15,7 @@ import { OfflineSignJSON, getMultisigSyncProgress, } from 'services/remote' -import { computeScriptHash } from '@ckb-lumos/base/lib/utils' +import { computeScriptHash } from '@ckb-lumos/lumos/utils' export const useSearch = (clearSelected: () => void, onFilterConfig: (searchKey: string) => void) => { const [keywords, setKeywords] = useState('') @@ -79,16 +79,21 @@ export const useConfigManage = ({ walletId, isMainnet }: { walletId: string; isM } }) }, [setEntities]) - const updateConfig = useCallback( + const onUpdateConfig = useCallback((values: Partial & { id: number }) => { + return updateMultisigConfig(values).then(res => { + if (isSuccessResponse(res)) { + setEntities(v => v.map(config => (res.result && config.id === res.result?.id ? res.result : config))) + } else { + throw new Error(typeof res.message === 'string' ? res.message : res.message.content!) + } + }) + }, []) + const onUpdateConfigAlias = useCallback( (id: number) => (e: React.SyntheticEvent) => { const { value } = e.target as HTMLInputElement - updateMultisigConfig({ id, alias: value || '' }).then(res => { - if (isSuccessResponse(res)) { - setEntities(v => v.map(config => (res.result && config.id === res.result?.id ? res.result : config))) - } - }) + onUpdateConfig({ id, alias: value || '' }) }, - [setEntities] + [onUpdateConfig] ) const deleteConfigById = useCallback( (id: number) => { @@ -141,7 +146,8 @@ export const useConfigManage = ({ walletId, isMainnet }: { walletId: string; isM return { saveConfig, allConfigs, - updateConfig, + onUpdateConfigAlias, + onUpdateConfig, deleteConfigById, onImportConfig, configs, @@ -365,3 +371,61 @@ export const useSubscription = ({ }, [isLightClient, getAndSaveMultisigSyncProgress]) return { multisigBanlances, multisigSyncProgress } } + +export const useCancelWithLightClient = () => { + const [isCloseWarningDialogShow, setIsCloseWarningDialogShow] = useState(false) + const onCancel = useCallback(() => { + setIsCloseWarningDialogShow(true) + }, [setIsCloseWarningDialogShow]) + const onCancelCloseMultisigDialog = useCallback(() => { + setIsCloseWarningDialogShow(false) + }, [setIsCloseWarningDialogShow]) + return { + isCloseWarningDialogShow, + onCancel, + onCancelCloseMultisigDialog, + } +} + +export const useSetStartBlockNumber = ({ + onUpdateConfig, +}: { + onUpdateConfig: (v: Partial & { id: number }) => Promise +}) => { + const [isSetStartBlockShown, setIsSetStartBlockShown] = useState(false) + const [editId, setEditId] = useState() + const [address, setAddress] = useState() + const [lastStartBlockNumber, setLastStartBlockNumber] = useState() + const onConfirm = useCallback( + (startBlockNumber: number) => { + if (editId) { + return onUpdateConfig({ + id: editId, + startBlockNumber, + }).then(() => { + setIsSetStartBlockShown(false) + }) + } + return Promise.reject(new Error('The Edit multisig config is empty')) + }, + [editId] + ) + const openDialog = useCallback>(e => { + const { id, address: editAddress, startBlockNumber } = e.currentTarget.dataset + if (id) { + setEditId(+id) + } + setAddress(editAddress) + setLastStartBlockNumber(startBlockNumber ? +startBlockNumber : undefined) + setIsSetStartBlockShown(true) + }, []) + return { + openDialog, + closeDialog: useCallback(() => setIsSetStartBlockShown(false), []), + isSetStartBlockShown, + onConfirm, + address, + lastStartBlockNumber, + onCancel: useCallback(() => setIsSetStartBlockShown(false), []), + } +} diff --git a/packages/neuron-ui/src/components/MultisigAddress/index.tsx b/packages/neuron-ui/src/components/MultisigAddress/index.tsx index 262fbe8596..50b0c5a485 100644 --- a/packages/neuron-ui/src/components/MultisigAddress/index.tsx +++ b/packages/neuron-ui/src/components/MultisigAddress/index.tsx @@ -1,34 +1,52 @@ -import React, { useCallback, useMemo, useState } from 'react' -import { useTranslation } from 'react-i18next' +import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react' +import { Trans, useTranslation } from 'react-i18next' import { - useOnLocaleChange, isMainnet as isMainnetUtil, shannonToCKBFormatter, useExitOnWalletChange, useGoBack, + useOnWindowResize, } from 'utils' import { useState as useGlobalState } from 'states' import MultisigAddressCreateDialog from 'components/MultisigAddressCreateDialog' import MultisigAddressInfo from 'components/MultisigAddressInfo' import SendFromMultisigDialog from 'components/SendFromMultisigDialog' -import { MultisigConfig } from 'services/remote' +import { MultisigConfig, changeMultisigSyncStatus, openExternal } from 'services/remote' import ApproveMultisigTxDialog from 'components/ApproveMultisigTxDialog' import Dialog from 'widgets/Dialog' import Table from 'widgets/Table' import Tooltip from 'widgets/Tooltip' import AlertDialog from 'widgets/AlertDialog' -import { ReactComponent as AddSimple } from 'widgets/Icons/AddSimple.svg' -import { ReactComponent as Details } from 'widgets/Icons/Details.svg' -import { ReactComponent as Delete } from 'widgets/Icons/Delete.svg' -import { ReactComponent as Confirm } from 'widgets/Icons/Confirm.svg' -import { ReactComponent as Transfer } from 'widgets/Icons/Transfer.svg' -import { ReactComponent as Upload } from 'widgets/Icons/Upload.svg' -import { ReactComponent as Edit } from 'widgets/Icons/Edit.svg' -import { Download, Search } from 'widgets/Icons/icon' +import { + Download, + Search, + AddSimple, + Details, + Delete, + Confirm, + Transfer, + Upload, + Edit, + Confirming, + Question, + LineDownArrow, +} from 'widgets/Icons/icon' +import AttentionCloseDialog from 'widgets/Icons/Attention.png' import { HIDE_BALANCE, NetworkType } from 'utils/const' import { onEnter } from 'utils/inputDevice' import getMultisigSignStatus from 'utils/getMultisigSignStatus' -import { useSearch, useConfigManage, useExportConfig, useActions, useSubscription } from './hooks' +import Button from 'widgets/Button' +import SetStartBlockNumberDialog from 'components/SetStartBlockNumberDialog' +import { type TFunction } from 'i18next' +import { + useSearch, + useConfigManage, + useExportConfig, + useActions, + useSubscription, + useCancelWithLightClient, + useSetStartBlockNumber, +} from './hooks' import styles from './multisigAddress.module.scss' @@ -52,13 +70,29 @@ const tableActions = [ }, ] +const LearnMore = React.memo(({ t }: { t: TFunction }) => ( + +)) + const MultisigAddress = () => { - const [t, i18n] = useTranslation() - useOnLocaleChange(i18n) + const [t] = useTranslation() useExitOnWalletChange() const { wallet: { id: walletId, addresses }, - chain: { networkID }, + chain: { + syncState: { bestKnownBlockNumber }, + networkID, + connectionStatus, + }, settings: { networks = [] }, } = useGlobalState() const isMainnet = isMainnetUtil(networks, networkID) @@ -67,11 +101,19 @@ const MultisigAddress = () => { [networks, networkID] ) const [isCreateDialogOpen, setIsCreateDialogOpen] = useState(false) - const { allConfigs, saveConfig, updateConfig, deleteConfigById, onImportConfig, configs, onFilterConfig } = - useConfigManage({ - walletId, - isMainnet, - }) + const { + allConfigs, + saveConfig, + onUpdateConfig, + onUpdateConfigAlias, + deleteConfigById, + onImportConfig, + configs, + onFilterConfig, + } = useConfigManage({ + walletId, + isMainnet, + }) const { multisigBanlances, multisigSyncProgress } = useSubscription({ walletId, isMainnet, @@ -81,10 +123,6 @@ const MultisigAddress = () => { const { deleteAction, infoAction, sendAction, approveAction } = useActions({ deleteConfigById }) const [showDeleteDialog, setShowDeleteDialog] = useState(false) - const showMainDialog = useMemo( - () => !(infoAction.isDialogOpen || sendAction.isDialogOpen || approveAction.isDialogOpen || isCreateDialogOpen), - [infoAction.isDialogOpen, sendAction.isDialogOpen, approveAction.isDialogOpen, isCreateDialogOpen] - ) const onClickItem = useCallback( (multisigConfig: MultisigConfig) => (e: React.SyntheticEvent) => { const { @@ -141,10 +179,68 @@ const MultisigAddress = () => { }, [multisigBanlances, sendAction.sendFromMultisig]) const onBack = useGoBack() + const { + onCancel: onCancelWithLight, + isCloseWarningDialogShow, + onCancelCloseMultisigDialog, + } = useCancelWithLightClient() + const { + isSetStartBlockShown, + openDialog: openSetStartBlockNumber, + lastStartBlockNumber, + address, + onConfirm, + onCancel, + } = useSetStartBlockNumber({ onUpdateConfig }) + + useEffect(() => { + if (isLightClient) { + changeMultisigSyncStatus(true) + } + return () => { + if (isLightClient) { + changeMultisigSyncStatus(false) + } + } + }, [isLightClient]) + const titleRef = useRef(null) + const [tipPosition, setTipPosition] = useState<{ left?: number; top?: number }>({}) + const updateTipPosition = useCallback(() => { + if (titleRef.current) { + const boundingClientRect = titleRef.current.getBoundingClientRect() + setTipPosition({ + left: boundingClientRect.left - 18, + top: boundingClientRect.top - boundingClientRect.height, + }) + } + }, [titleRef.current, setTipPosition]) + useEffect(() => { + updateTipPosition() + }, [updateTipPosition]) + useOnWindowResize(updateTipPosition) return (
- + + {t('multisig-address.window-title')} + ]} />} + placement="top" + showTriangle + tipClassName={styles.multiGuide} + tipStyles={tipPosition} + > + + +
+ } + onCancel={isLightClient ? onCancelWithLight : onBack} + showFooter={false} + >
@@ -221,8 +317,8 @@ const MultisigAddress = () => {