diff --git a/packages/neuron-ui/src/components/DataSetting/dataSetting.module.scss b/packages/neuron-ui/src/components/DataSetting/dataSetting.module.scss
index e260687f30..e90dea89ae 100644
--- a/packages/neuron-ui/src/components/DataSetting/dataSetting.module.scss
+++ b/packages/neuron-ui/src/components/DataSetting/dataSetting.module.scss
@@ -50,14 +50,16 @@
.itemBtn {
color: var(--secondary-text-color);
- padding: 0 16px;
- flex-shrink: 0;
+ padding: 0 8px;
border-radius: 8px;
height: 100%;
background: var(--input-disabled-color);
border: 1px solid var(--divide-line-color);
&:hover:not([disabled]) {
- color: var(--primary-color);
+ g,
+ path {
+ stroke: var(--primary-color);
+ }
}
&[disabled] {
cursor: not-allowed;
@@ -160,3 +162,29 @@
word-break: normal;
white-space: normal;
}
+
+.moreTooltip {
+ height: 100%;
+}
+
+.moreTip {
+ margin-top: 4px;
+ margin-left: 60px;
+}
+
+.actions {
+ button {
+ background: none;
+ font-size: 14px;
+ line-height: 30px;
+ color: var(--main-text-color);
+ border: none;
+ text-align: left;
+ display: flex;
+ align-items: center;
+ cursor: pointer;
+ &:hover {
+ color: var(--primary-color);
+ }
+ }
+}
diff --git a/packages/neuron-ui/src/components/DataSetting/hooks.ts b/packages/neuron-ui/src/components/DataSetting/hooks.ts
index e945e4823d..1df113502a 100644
--- a/packages/neuron-ui/src/components/DataSetting/hooks.ts
+++ b/packages/neuron-ui/src/components/DataSetting/hooks.ts
@@ -18,21 +18,27 @@ export const useDataPath = (network?: State.Network) => {
}
})
}, [network?.id])
- const onSetting = useCallback(() => {
- invokeShowOpenDialog({
- buttonLabel: t('settings.data.set', { lng: navigator.language }),
- properties: ['openDirectory', 'createDirectory', 'promptToCreate', 'treatPackageAsDirectory'],
- }).then(res => {
- if (isSuccessResponse(res) && !res.result?.canceled && res.result?.filePaths?.length) {
- setCurrentPath(res.result?.filePaths?.[0])
- stopProcessMonitor(type).then(stopRes => {
- if (isSuccessResponse(stopRes)) {
- setIsDialogOpen(true)
- }
- })
- }
- })
- }, [t, type])
+
+ const onSetting = useCallback(
+ (onSuccess?: (path: string) => void) => {
+ invokeShowOpenDialog({
+ buttonLabel: t('settings.data.set', { lng: navigator.language }),
+ properties: ['openDirectory', 'createDirectory', 'promptToCreate', 'treatPackageAsDirectory'],
+ }).then(res => {
+ if (isSuccessResponse(res) && !res.result?.canceled && res.result?.filePaths?.length) {
+ const path = res.result?.filePaths?.[0]
+ setCurrentPath(path)
+ stopProcessMonitor(type).then(stopRes => {
+ if (isSuccessResponse(stopRes)) {
+ onSuccess?.(path)
+ }
+ })
+ }
+ })
+ },
+ [t, type]
+ )
+
const onCancel = useCallback(() => {
startProcessMonitor(type).then(res => {
if (isSuccessResponse(res)) {
@@ -40,6 +46,12 @@ export const useDataPath = (network?: State.Network) => {
}
})
}, [setIsDialogOpen, type])
+
+ const onResync = useCallback(async () => {
+ await stopProcessMonitor(type)
+ return startProcessMonitor(type)
+ }, [type])
+
const onConfirm = useCallback(
(dataPath: string) => {
setPrevPath(dataPath)
@@ -47,6 +59,12 @@ export const useDataPath = (network?: State.Network) => {
},
[setIsDialogOpen, setPrevPath]
)
+
+ const openDialog = useCallback(() => {
+ setCurrentPath('')
+ setIsDialogOpen(true)
+ }, [setIsDialogOpen, setCurrentPath])
+
return {
prevPath,
currentPath,
@@ -54,6 +72,8 @@ export const useDataPath = (network?: State.Network) => {
onCancel,
onConfirm,
isDialogOpen,
+ openDialog,
+ onResync,
}
}
diff --git a/packages/neuron-ui/src/components/DataSetting/index.tsx b/packages/neuron-ui/src/components/DataSetting/index.tsx
index 6c284eff8c..c9cbb8ecea 100644
--- a/packages/neuron-ui/src/components/DataSetting/index.tsx
+++ b/packages/neuron-ui/src/components/DataSetting/index.tsx
@@ -1,12 +1,15 @@
-import React, { useCallback, useMemo } from 'react'
+import React, { useCallback, useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next'
import ClearCache from 'components/ClearCache'
import { useDispatch, useState as useGlobalState } from 'states'
import { shell } from 'electron'
import Tooltip from 'widgets/Tooltip'
import { NetworkType } from 'utils/const'
-import { Attention } from 'widgets/Icons/icon'
-import MigrateCkbDataDialog from 'widgets/MigrateCkbDataDialog'
+import { Attention, More } from 'widgets/Icons/icon'
+import Toast from 'widgets/Toast'
+import ModifyPathDialog from 'components/ModifyPathDialog'
+import AlertDialog from 'widgets/AlertDialog'
+import { isSuccessResponse } from 'utils'
import { useDataPath } from './hooks'
import styles from './dataSetting.module.scss'
@@ -28,9 +31,33 @@ const PathItem = ({
-
+
+ {[
+ {
+ label: 'settings.data.browse-local-files',
+ onClick: openPath,
+ },
+ {
+ label: 'settings.data.modify-path',
+ onClick: handleClick,
+ },
+ ].map(({ label, onClick }) => (
+
+ ))}
+
+ }
+ trigger="click"
+ >
+
+
)
}
@@ -42,16 +69,31 @@ const DataSetting = () => {
chain: { networkID },
settings: { networks = [] },
} = useGlobalState()
+ const [notice, setNotice] = useState('')
+ const [showLostDialog, setShowLostDialog] = useState(false)
const network = useMemo(() => networks.find(n => n.id === networkID), [networkID, networks])
- const { onSetting, prevPath, currentPath, isDialogOpen, onCancel, onConfirm } = useDataPath(network)
+ const { isDialogOpen, openDialog, onSetting, prevPath, currentPath, onCancel, onConfirm, onResync } =
+ useDataPath(network)
const openPath = useCallback(() => {
- if (prevPath) {
- shell.openPath(prevPath!)
- }
- }, [prevPath])
+ shell.openPath(prevPath).then(res => {
+ if (res) {
+ setShowLostDialog(true)
+ }
+ })
+ }, [prevPath, onResync])
const isLightClient = network?.type === NetworkType.Light
const hiddenDataPath = isLightClient || !network?.readonly
+
+ const handleResync = useCallback(() => {
+ setShowLostDialog(false)
+ onResync().then(res => {
+ if (isSuccessResponse(res)) {
+ openPath()
+ }
+ })
+ }, [openPath])
+
return (
<>
@@ -82,17 +124,36 @@ const DataSetting = () => {
- {hiddenDataPath ? null :
}
+ {hiddenDataPath ? null :
}
-
+
+ {isDialogOpen && (
+
+ )}
+
+ {showLostDialog && (
+ setShowLostDialog(false)}
+ />
+ )}
+
+ setNotice('')} />
>
)
}
diff --git a/packages/neuron-ui/src/components/ModifyPathDialog/index.tsx b/packages/neuron-ui/src/components/ModifyPathDialog/index.tsx
new file mode 100644
index 0000000000..f8c5f5a87a
--- /dev/null
+++ b/packages/neuron-ui/src/components/ModifyPathDialog/index.tsx
@@ -0,0 +1,135 @@
+import React, { useState, useCallback, useEffect } from 'react'
+import { useTranslation } from 'react-i18next'
+import Dialog from 'widgets/Dialog'
+import AlertDialog from 'widgets/AlertDialog'
+import MigrateCkbDataDialog from 'widgets/MigrateCkbDataDialog'
+import { setCkbNodeDataPath, getCkbNodeDataNeedSize } from 'services/remote'
+import { Attention } from 'widgets/Icons/icon'
+import { isSuccessResponse } from 'utils'
+import styles from './modifyPathDialog.module.scss'
+
+const ModifyPathDialog = ({
+ prevPath,
+ currentPath,
+ onCancel,
+ onConfirm,
+ onSetting,
+ setNotice,
+}: {
+ onCancel?: () => void
+ prevPath: string
+ currentPath: string
+ onConfirm: (dataPath: string) => void
+ onSetting: (onSuccess?: (path: string) => void) => void
+ setNotice: (notice: string) => void
+}) => {
+ const [t] = useTranslation()
+ const [isMigrateOpen, setIsMigrateOpen] = useState(false)
+ const [failureMessage, setFailureMessage] = useState('')
+ const [isRetainPreviousData, setIsRetainPreviousData] = useState(false)
+ const [needSize, setNeedSize] = useState(0)
+
+ useEffect(() => {
+ getCkbNodeDataNeedSize().then(res => {
+ if (isSuccessResponse(res)) {
+ setNeedSize(res.result!)
+ }
+ })
+ }, [])
+
+ const handleResync = useCallback(async () => {
+ setFailureMessage('')
+ onSetting(path => {
+ setCkbNodeDataPath({
+ dataPath: path,
+ clearCache: true,
+ }).then(res => {
+ if (isSuccessResponse(res)) {
+ onConfirm(path)
+ } else {
+ setFailureMessage(typeof res.message === 'string' ? res.message : res.message.content!)
+ }
+ })
+ })
+ }, [onSetting, onConfirm, setFailureMessage])
+
+ if (isMigrateOpen) {
+ return (
+ setIsMigrateOpen(false)}
+ onConfirm={onConfirm}
+ onClose={onCancel}
+ setNotice={setNotice}
+ />
+ )
+ }
+
+ if (isRetainPreviousData) {
+ return (
+
+ )
+ }
+
+ return (
+ <>
+
+ setFailureMessage('')}
+ />
+ >
+ )
+}
+
+ModifyPathDialog.displayName = 'ModifyPathDialog'
+
+export default ModifyPathDialog
diff --git a/packages/neuron-ui/src/components/ModifyPathDialog/modifyPathDialog.module.scss b/packages/neuron-ui/src/components/ModifyPathDialog/modifyPathDialog.module.scss
new file mode 100644
index 0000000000..5c75df38ce
--- /dev/null
+++ b/packages/neuron-ui/src/components/ModifyPathDialog/modifyPathDialog.module.scss
@@ -0,0 +1,85 @@
+@import '../../styles/mixin.scss';
+
+.passwordInput {
+ margin-top: 16px;
+}
+
+.dialog {
+ width: 700px;
+}
+
+.tip {
+ color: var(--warn-text-color);
+ background: var(--warn-background-color);
+ margin: -20px -16px 0;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ height: 32px;
+ font-size: 12px;
+ gap: 4px;
+ font-weight: 500;
+ border-bottom: 1px solid var(--warn-border-color);
+}
+
+.label {
+ font-weight: 500;
+ color: var(--main-text-color);
+ font-size: 14px;
+}
+
+.copy {
+ display: flex;
+ align-items: center;
+ margin-left: 6px;
+}
+
+.notice {
+ @include dialog-copy-animation;
+}
+
+.modifyTip {
+ max-width: 450px;
+ margin: 0 auto;
+ text-align: center;
+ margin-top: 16px;
+ font-size: 14px;
+ line-height: 24px;
+ color: var(--main-text-color);
+}
+
+.pathItem {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ width: 488px;
+ height: 40px;
+ margin: 24px auto 0;
+ background: var(--secondary-background-color);
+ border: 1px solid var(--divide-line-color);
+ border-radius: 8px;
+ button {
+ height: 100%;
+ border-radius: 0 8px 8px 0;
+ min-width: 60px;
+ border: none;
+ border-left: 1px solid var(--divide-line-color);
+ background: var(--input-disabled-color);
+ cursor: pointer;
+ color: var(--secondary-text-color);
+ &:hover {
+ color: var(--primary-color);
+ }
+ }
+ .path {
+ color: var(--input-hint-color);
+ }
+ p {
+ padding: 0;
+ white-space: nowrap;
+ text-overflow: ellipsis;
+ overflow: hidden;
+ padding: 0 16px;
+ color: var(--disable-button-text-color);
+ }
+}
diff --git a/packages/neuron-ui/src/containers/Main/index.tsx b/packages/neuron-ui/src/containers/Main/index.tsx
index ac887ec701..8555aef211 100644
--- a/packages/neuron-ui/src/containers/Main/index.tsx
+++ b/packages/neuron-ui/src/containers/Main/index.tsx
@@ -195,13 +195,14 @@ const MainContent = () => {
onConfirm={needConfirm ? onMigrate : onContinueSync}
onChangeDataPath={setNewCkbDataPath}
/>
-
+ {isMigrateDataDialogShow && (
+
+ )}
('open-in-wi
export const requestOpenInExplorer = remoteApi('request-open-in-explorer')
export const handleViewError = remoteApi('handle-view-error')
export const setLocale = remoteApi<(typeof LOCALES)[number]>('set-locale')
+export const getCkbNodeDataNeedSize = remoteApi('get-ckb-node-data-need-size')
export const getCkbNodeDataPath = remoteApi('get-ckb-node-data-path')
export const setCkbNodeDataPath = remoteApi<{ dataPath: string; clearCache?: boolean; onlySetPath?: boolean }, string>(
'set-ckb-node-data-path'
diff --git a/packages/neuron-ui/src/services/remote/remoteApiWrapper.ts b/packages/neuron-ui/src/services/remote/remoteApiWrapper.ts
index 69bc7c1711..af1d9b6f04 100644
--- a/packages/neuron-ui/src/services/remote/remoteApiWrapper.ts
+++ b/packages/neuron-ui/src/services/remote/remoteApiWrapper.ts
@@ -42,6 +42,7 @@ type Action =
| 'open-context-menu'
| 'get-all-displays-size'
| 'show-message-box'
+ | 'get-ckb-node-data-need-size'
| 'get-ckb-node-data-path'
| 'set-ckb-node-data-path'
| 'stop-process-monitor'
diff --git a/packages/neuron-ui/src/styles/theme.scss b/packages/neuron-ui/src/styles/theme.scss
index 4ef564a699..e9e8c827a9 100644
--- a/packages/neuron-ui/src/styles/theme.scss
+++ b/packages/neuron-ui/src/styles/theme.scss
@@ -67,7 +67,7 @@ body {
--third-text-color: #cccccc;
--link-color: #{$main-color};
--third-background-color: #2d3534;
- --fourth-background-color: var(--secondary-background-color);
+ --fourth-background-color: #2d3638;
--notice-background-color: #093f29;
--notice-text-color: #f78c2a;
--border-color: #343e3c;
diff --git a/packages/neuron-ui/src/widgets/AlertDialog/index.tsx b/packages/neuron-ui/src/widgets/AlertDialog/index.tsx
index 4955d0fac2..b0d5320a85 100644
--- a/packages/neuron-ui/src/widgets/AlertDialog/index.tsx
+++ b/packages/neuron-ui/src/widgets/AlertDialog/index.tsx
@@ -18,6 +18,10 @@ const AlertDialog = ({
onOk,
onCancel,
action,
+ cancelText,
+ okText,
+ cancelProps,
+ okProps,
}: {
show?: boolean
title?: string
@@ -26,6 +30,10 @@ const AlertDialog = ({
onOk?: () => void
onCancel?: () => void
action?: Action
+ cancelText?: string
+ okText?: string
+ cancelProps?: object
+ okProps?: object
}) => {
const [t] = useTranslation()
const dialogRef = useRef(null)
@@ -47,9 +55,9 @@ const AlertDialog = ({
{actions.map(v =>
v === 'cancel' ? (
-
+
) : (
-
+
)
)}
diff --git a/packages/neuron-ui/src/widgets/Button/index.tsx b/packages/neuron-ui/src/widgets/Button/index.tsx
index a94e5c186d..b29a61b5e9 100644
--- a/packages/neuron-ui/src/widgets/Button/index.tsx
+++ b/packages/neuron-ui/src/widgets/Button/index.tsx
@@ -36,7 +36,6 @@ const Button = React.forwardRef(
data-type={type}
onClick={onClick}
aria-label={label}
- title={label}
disabled={disabled || loading}
{...rest}
>
diff --git a/packages/neuron-ui/src/widgets/Dialog/index.tsx b/packages/neuron-ui/src/widgets/Dialog/index.tsx
index b4c6621faa..b793125381 100644
--- a/packages/neuron-ui/src/widgets/Dialog/index.tsx
+++ b/packages/neuron-ui/src/widgets/Dialog/index.tsx
@@ -11,6 +11,7 @@ interface DialogProps {
subTitle?: string
onConfirm?: (e: React.FormEvent) => void
onCancel?: () => void
+ onClose?: () => void
confirmText?: string
cancelText?: string
disabled?: boolean | undefined
@@ -34,6 +35,7 @@ const Dialog = ({
subTitle,
onConfirm,
onCancel,
+ onClose,
disabled,
confirmText,
cancelText,
@@ -81,7 +83,8 @@ const Dialog = ({
className={`${styles.dialogWrap} ${className}`}
onKeyDown={e => {
if (e.key === 'Escape' && enableCloseWithEsc) {
- onCancel?.()
+ // eslint-disable-next-line no-unused-expressions
+ onClose ? onClose() : onCancel?.()
} else if (e.key === 'Enter' && showFooter && showConfirm) {
handleConfirm(e)
}
@@ -94,7 +97,7 @@ const Dialog = ({
{title}
{subTitle ? {subTitle} : null}
-
+
) : null}
{children}
diff --git a/packages/neuron-ui/src/widgets/Icons/More.svg b/packages/neuron-ui/src/widgets/Icons/More.svg
new file mode 100644
index 0000000000..a512cb6126
--- /dev/null
+++ b/packages/neuron-ui/src/widgets/Icons/More.svg
@@ -0,0 +1,5 @@
+
diff --git a/packages/neuron-ui/src/widgets/Icons/icon.tsx b/packages/neuron-ui/src/widgets/Icons/icon.tsx
index 062596ad18..bfb84cbda1 100644
--- a/packages/neuron-ui/src/widgets/Icons/icon.tsx
+++ b/packages/neuron-ui/src/widgets/Icons/icon.tsx
@@ -65,6 +65,7 @@ import { ReactComponent as UploadSvg } from './Upload.svg'
import { ReactComponent as PrivateKeySvg } from './PrivateKey.svg'
import { ReactComponent as DAODepositSvg } from './DAODeposit.svg'
import { ReactComponent as DAOWithdrawalSvg } from './DAOWithdrawal.svg'
+import { ReactComponent as MoreSvg } from './More.svg'
import styles from './icon.module.scss'
@@ -146,3 +147,4 @@ export const Upload = WrapSvg(UploadSvg)
export const PrivateKey = WrapSvg(PrivateKeySvg)
export const DAODeposit = WrapSvg(DAODepositSvg)
export const DAOWithdrawal = WrapSvg(DAOWithdrawalSvg)
+export const More = WrapSvg(MoreSvg)
diff --git a/packages/neuron-ui/src/widgets/MigrateCkbDataDialog/index.tsx b/packages/neuron-ui/src/widgets/MigrateCkbDataDialog/index.tsx
index 536c1b17ff..3589351406 100644
--- a/packages/neuron-ui/src/widgets/MigrateCkbDataDialog/index.tsx
+++ b/packages/neuron-ui/src/widgets/MigrateCkbDataDialog/index.tsx
@@ -1,90 +1,106 @@
-import React, { useCallback, useRef, useState } from 'react'
+import React, { useCallback, useState, useEffect, useRef } from 'react'
import Dialog from 'widgets/Dialog'
+import { shell } from 'electron'
import { useTranslation } from 'react-i18next'
import { setCkbNodeDataPath } from 'services/remote'
import { isSuccessResponse } from 'utils'
-import { Attention } from 'widgets/Icons/icon'
import Button from 'widgets/Button'
import AlertDialog from 'widgets/AlertDialog'
import styles from './migrateCkbDataDialog.module.scss'
const MigrateCkbDataDialog = ({
- show,
prevPath,
currentPath,
onCancel,
onConfirm,
+ onClose,
+ setNotice,
}: {
- show: boolean
prevPath: string
currentPath: string
onCancel: () => void
onConfirm: (dataPath: string) => void
+ onClose?: () => void
+ setNotice?: (notice: string) => void
}) => {
const [t] = useTranslation()
- const resyncRef = useRef(null)
const [failureMessage, setFailureMessage] = useState('')
- const [savingType, setSavingType] = useState()
- const startSync = useCallback(
- (syncType: string) => {
- setFailureMessage('')
- setSavingType(syncType)
- setCkbNodeDataPath({
- dataPath: currentPath,
- clearCache: syncType === 'resync',
- })
- .then(res => {
- if (isSuccessResponse(res)) {
- onConfirm(currentPath)
- } else {
- setFailureMessage(typeof res.message === 'string' ? res.message : res.message.content!)
- }
- })
- .finally(() => {
- setSavingType(undefined)
- })
- },
- [currentPath, onConfirm]
- )
- const onActionClick = useCallback(
- (e: React.MouseEvent) => {
- if (e.currentTarget.dataset.syncType) {
- startSync(e.currentTarget.dataset.syncType)
+ const [isSaving, setIsSaving] = useState(false)
+ const timerRef = useRef(null)
+ const [seconds, setSeconds] = useState(5)
+
+ const openPath = (e: React.SyntheticEvent) => {
+ const elm = e.target
+ if (!(elm instanceof HTMLButtonElement)) return
+ const { path } = elm.dataset
+ if (!path) return
+ shell.openPath(path)
+ }
+
+ useEffect(() => {
+ if (seconds !== 0) {
+ timerRef.current = setTimeout(() => {
+ setSeconds(seconds - 1)
+ }, 1000)
+ }
+
+ return () => {
+ if (timerRef.current) {
+ clearTimeout(timerRef.current)
}
- },
- [startSync]
- )
+ }
+ }, [seconds])
+
+ const handleMigrate = useCallback(() => {
+ setFailureMessage('')
+ setIsSaving(true)
+ setCkbNodeDataPath({
+ dataPath: currentPath,
+ clearCache: false,
+ })
+ .then(res => {
+ if (isSuccessResponse(res)) {
+ onConfirm(currentPath)
+ setNotice?.(t('settings.data.migrate-data-successful'))
+ } else {
+ setFailureMessage(typeof res.message === 'string' ? res.message : res.message.content!)
+ }
+ })
+ .finally(() => {
+ setIsSaving(false)
+ })
+ }, [currentPath, onConfirm, setNotice])
+
return (
<>
-