From a3686cdb6c5a61c8ecef8aa523b150e69ff80b75 Mon Sep 17 00:00:00 2001
From: yanguoyu <841185308@qq.com>
Date: Fri, 8 Mar 2024 11:07:38 +0800
Subject: [PATCH] feat: Support lock window for Neuron
---
.../GeneralSetting/LockWindowDialog/hooks.ts | 113 ++++++++++++
.../GeneralSetting/LockWindowDialog/index.tsx | 103 +++++++++++
.../lockWindowDialog.module.scss | 30 +++
.../GeneralSetting/generalSetting.module.scss | 4 +
.../src/components/GeneralSetting/index.tsx | 31 +++-
.../src/containers/LockWindow/index.tsx | 172 ++++++++++++++++++
.../LockWindow/lockWindow.module.scss | 55 ++++++
.../neuron-ui/src/containers/Main/hooks.ts | 16 ++
.../neuron-ui/src/containers/Main/index.tsx | 15 +-
packages/neuron-ui/src/locales/en.json | 22 ++-
packages/neuron-ui/src/locales/es.json | 22 ++-
packages/neuron-ui/src/locales/fr.json | 22 ++-
packages/neuron-ui/src/locales/zh-tw.json | 22 ++-
packages/neuron-ui/src/locales/zh.json | 22 ++-
packages/neuron-ui/src/router.tsx | 5 +-
packages/neuron-ui/src/services/localCache.ts | 18 ++
packages/neuron-ui/src/services/remote/app.ts | 6 +
.../src/services/remote/remoteApiWrapper.ts | 4 +
.../stateProvider/actionCreators/app.ts | 28 ++-
.../src/states/stateProvider/reducer.ts | 8 +
packages/neuron-ui/src/types/App/index.d.ts | 4 +
.../neuron-ui/src/types/Subject/index.d.ts | 1 +
.../neuron-ui/src/types/global/index.d.ts | 5 +
.../neuron-ui/src/utils/getSyncLeftTime.ts | 6 +-
.../neuron-ui/src/widgets/Icons/Locked.png | Bin 0 -> 221987 bytes
.../src/widgets/Icons/dark-unlock.mp4 | Bin 0 -> 248770 bytes
.../neuron-ui/src/widgets/Icons/unlock.mp4 | Bin 0 -> 211408 bytes
.../src/widgets/SplitPasswordInput/index.tsx | 65 +++++++
.../splitPasswordInput.module.scss | 19 ++
packages/neuron-wallet/src/controllers/api.ts | 36 ++++
.../neuron-wallet/src/controllers/app/menu.ts | 22 ++-
packages/neuron-wallet/src/locales/en.ts | 1 +
packages/neuron-wallet/src/locales/es.ts | 1 +
packages/neuron-wallet/src/locales/fr.ts | 1 +
packages/neuron-wallet/src/locales/zh-tw.ts | 1 +
packages/neuron-wallet/src/locales/zh.ts | 1 +
.../src/models/subjects/command.ts | 1 +
.../neuron-wallet/src/services/settings.ts | 37 +++-
38 files changed, 902 insertions(+), 17 deletions(-)
create mode 100644 packages/neuron-ui/src/components/GeneralSetting/LockWindowDialog/hooks.ts
create mode 100644 packages/neuron-ui/src/components/GeneralSetting/LockWindowDialog/index.tsx
create mode 100644 packages/neuron-ui/src/components/GeneralSetting/LockWindowDialog/lockWindowDialog.module.scss
create mode 100644 packages/neuron-ui/src/containers/LockWindow/index.tsx
create mode 100644 packages/neuron-ui/src/containers/LockWindow/lockWindow.module.scss
create mode 100644 packages/neuron-ui/src/widgets/Icons/Locked.png
create mode 100644 packages/neuron-ui/src/widgets/Icons/dark-unlock.mp4
create mode 100644 packages/neuron-ui/src/widgets/Icons/unlock.mp4
create mode 100644 packages/neuron-ui/src/widgets/SplitPasswordInput/index.tsx
create mode 100644 packages/neuron-ui/src/widgets/SplitPasswordInput/splitPasswordInput.module.scss
diff --git a/packages/neuron-ui/src/components/GeneralSetting/LockWindowDialog/hooks.ts b/packages/neuron-ui/src/components/GeneralSetting/LockWindowDialog/hooks.ts
new file mode 100644
index 0000000000..a1213dc389
--- /dev/null
+++ b/packages/neuron-ui/src/components/GeneralSetting/LockWindowDialog/hooks.ts
@@ -0,0 +1,113 @@
+import { TFunction } from 'i18next'
+import { useCallback, useState } from 'react'
+import { verifyLockWindowPassword } from 'services/remote'
+import { updateLockWindowInfo, useDispatch } from 'states'
+import { isSuccessResponse } from 'utils'
+
+export const passwordLength = 4
+
+export const useOldPassword = ({ t }: { t: TFunction }) => {
+ const [oldPasswordErr, setOldPasswordErr] = useState('')
+ const [oldPassword, setOldPassword] = useState(new Array(passwordLength).fill(''))
+ const [verifySuccess, setVerifySuccess] = useState(false)
+ const onUpdateOldPassword = useCallback(
+ (v: string, idx: number) => {
+ const updatedOldPassword = oldPassword.toSpliced(idx, 1, v).join('')
+ if (updatedOldPassword.length === passwordLength) {
+ verifyLockWindowPassword(updatedOldPassword)
+ .then(res => {
+ if (isSuccessResponse(res)) {
+ // verify success
+ setVerifySuccess(true)
+ }
+ throw new Error('verify failed')
+ })
+ .catch(() => {
+ setOldPassword(new Array(passwordLength).fill(''))
+ setOldPasswordErr(t('settings.general.lock-window.password-error'))
+ })
+ } else {
+ setOldPassword(value => value.toSpliced(idx, 1, v))
+ }
+ },
+ [oldPassword]
+ )
+ const resetOldPassword = useCallback(() => {
+ setOldPasswordErr('')
+ setOldPassword(new Array(passwordLength).fill(''))
+ setVerifySuccess(false)
+ }, [])
+ return {
+ oldPasswordErr,
+ oldPassword,
+ onUpdateOldPassword,
+ verifySuccess,
+ setVerifySuccess,
+ resetOldPassword,
+ }
+}
+
+export const usePassword = () => {
+ const [password, setPassword] = useState(new Array(passwordLength).fill(''))
+ const onUpdatePassword = useCallback((v: string, idx: number) => {
+ setPassword(value => value.toSpliced(idx, 1, v))
+ }, [])
+ const resetPassword = useCallback(() => {
+ setPassword(new Array(passwordLength).fill(''))
+ }, [])
+ return {
+ password,
+ onUpdatePassword,
+ resetPassword,
+ }
+}
+
+export const useRepeatPassword = ({
+ password,
+ t,
+ encryptedPassword,
+ onCancel,
+}: {
+ password: string
+ t: TFunction
+ encryptedPassword?: string
+ onCancel: () => void
+}) => {
+ const dispatch = useDispatch()
+ const [errMsg, setErrMsg] = useState('')
+ const [repeatPassword, setRepeatPassword] = useState(new Array(passwordLength).fill(''))
+ const [isSuccess, setIsSuccess] = useState(false)
+ const onUpdateRepeatPassword = useCallback(
+ (v: string, idx: number) => {
+ const updatedRepeatPassword = repeatPassword.toSpliced(idx, 1, v).join('')
+ if (updatedRepeatPassword.length === passwordLength) {
+ if (updatedRepeatPassword !== password) {
+ setErrMsg(t('settings.general.lock-window.different-password'))
+ setRepeatPassword(new Array(passwordLength).fill(''))
+ } else {
+ setIsSuccess(true)
+ updateLockWindowInfo(
+ encryptedPassword ? { password: updatedRepeatPassword } : { password: updatedRepeatPassword, locked: true }
+ )(dispatch)
+ onCancel()
+ }
+ } else {
+ setErrMsg('')
+ setRepeatPassword(value => value.toSpliced(idx, 1, v))
+ }
+ },
+ [password, t, repeatPassword]
+ )
+ const resetRepeatPassword = useCallback(() => {
+ setErrMsg('')
+ setRepeatPassword(new Array(passwordLength).fill(''))
+ setIsSuccess(false)
+ }, [])
+ return {
+ errMsg,
+ repeatPassword,
+ onUpdateRepeatPassword,
+ resetRepeatPassword,
+ isSuccess,
+ }
+}
diff --git a/packages/neuron-ui/src/components/GeneralSetting/LockWindowDialog/index.tsx b/packages/neuron-ui/src/components/GeneralSetting/LockWindowDialog/index.tsx
new file mode 100644
index 0000000000..629eda59f6
--- /dev/null
+++ b/packages/neuron-ui/src/components/GeneralSetting/LockWindowDialog/index.tsx
@@ -0,0 +1,103 @@
+import React, { useCallback, useEffect, useMemo } from 'react'
+import { useTranslation } from 'react-i18next'
+import Dialog from 'widgets/Dialog'
+import SplitPasswordInput from 'widgets/SplitPasswordInput'
+import Alert from 'widgets/Alert'
+import Button from 'widgets/Button'
+import { passwordLength, useOldPassword, usePassword, useRepeatPassword } from './hooks'
+import styles from './lockWindowDialog.module.scss'
+
+const LockWindowDialog = ({
+ show,
+ onCancel,
+ encryptedPassword,
+}: {
+ show: boolean
+ onCancel: () => void
+ encryptedPassword?: string
+}) => {
+ const [t] = useTranslation()
+ const { oldPasswordErr, verifySuccess, oldPassword, onUpdateOldPassword, resetOldPassword } = useOldPassword({ t })
+ const { password, onUpdatePassword, resetPassword } = usePassword()
+ const joinedPassword = useMemo(() => password.join(''), [password])
+ const { errMsg, repeatPassword, onUpdateRepeatPassword, resetRepeatPassword, isSuccess } = useRepeatPassword({
+ t,
+ password: joinedPassword,
+ encryptedPassword,
+ onCancel,
+ })
+ useEffect(() => {
+ // when dialog open, reset all status
+ if (show) {
+ resetOldPassword()
+ resetPassword()
+ resetRepeatPassword()
+ }
+ }, [show, resetOldPassword, resetPassword, resetRepeatPassword])
+ const onReset = useCallback(() => {
+ resetPassword()
+ resetRepeatPassword()
+ }, [resetPassword, resetRepeatPassword])
+ const content = () => {
+ if (encryptedPassword && !verifySuccess) {
+ // is verify old password
+ return (
+ <>
+ {t('settings.general.lock-window.enter-current-password')}
+
+ >
+ )
+ }
+ if (joinedPassword.length !== passwordLength) {
+ // enter password
+ return (
+ <>
+ {t(`settings.general.lock-window.set-password`)}
+
+
+
+ >
+ )
+ }
+ // is verify repeat password
+ return (
+ <>
+ {t(`settings.general.lock-window.confirm-password`)}
+
+
+
{errMsg}
+ {errMsg ? (
+
+ ) : null}
+
+ >
+ )
+ }
+
+ return (
+ <>
+
+ {isSuccess ? (
+
+ {t(`settings.general.lock-window.${encryptedPassword ? 'change-password-success' : 'set-password-success'}`)}
+
+ ) : null}
+ >
+ )
+}
+
+LockWindowDialog.displayName = 'LockWindowDialog'
+export default LockWindowDialog
diff --git a/packages/neuron-ui/src/components/GeneralSetting/LockWindowDialog/lockWindowDialog.module.scss b/packages/neuron-ui/src/components/GeneralSetting/LockWindowDialog/lockWindowDialog.module.scss
new file mode 100644
index 0000000000..f4c099a097
--- /dev/null
+++ b/packages/neuron-ui/src/components/GeneralSetting/LockWindowDialog/lockWindowDialog.module.scss
@@ -0,0 +1,30 @@
+@import '../../../styles/mixin.scss';
+
+.content {
+ text-align: center;
+
+ .secTitle {
+ color: var(--secondary-text-color);
+ font-size: 14px;
+ font-style: normal;
+ font-weight: 400;
+ line-height: normal;
+ }
+
+ .password {
+ margin-top: 24px;
+ }
+
+ .err {
+ color: var(--error-color);
+ margin: 16px 0 24px 0;
+ }
+}
+
+.dialog {
+ width: 680px;
+}
+
+.notice {
+ @include dialog-copy-animation;
+}
diff --git a/packages/neuron-ui/src/components/GeneralSetting/generalSetting.module.scss b/packages/neuron-ui/src/components/GeneralSetting/generalSetting.module.scss
index bd41cc7b53..892c71197b 100644
--- a/packages/neuron-ui/src/components/GeneralSetting/generalSetting.module.scss
+++ b/packages/neuron-ui/src/components/GeneralSetting/generalSetting.module.scss
@@ -38,6 +38,10 @@ $action-button-width: 11.25rem;
margin-right: 4px;
}
}
+
+ &.lockWindow {
+ width: auto;
+ }
}
.showVersion {
position: relative;
diff --git a/packages/neuron-ui/src/components/GeneralSetting/index.tsx b/packages/neuron-ui/src/components/GeneralSetting/index.tsx
index bc0dcc5ba0..45216d4d9b 100644
--- a/packages/neuron-ui/src/components/GeneralSetting/index.tsx
+++ b/packages/neuron-ui/src/components/GeneralSetting/index.tsx
@@ -9,11 +9,12 @@ import { ReactComponent as ArrowNext } from 'widgets/Icons/ArrowNext.svg'
import { ReactComponent as Update } from 'widgets/Icons/Update.svg'
import { cancelCheckUpdates, downloadUpdate, installUpdate, getVersion } from 'services/remote'
import { uniformTimeFormatter, bytesFormatter, clsx, wakeScreen, releaseWakeLock } from 'utils'
-import { LanguageSelect } from 'widgets/Icons/icon'
import Switch from 'widgets/Switch'
import { keepScreenAwake } from 'services/localCache'
+import { LanguageSelect, UnLock } from 'widgets/Icons/icon'
import styles from './generalSetting.module.scss'
import { useCheckUpdate, useUpdateDownloadStatus } from './hooks'
+import LockWindowDialog from './LockWindowDialog'
interface UpdateDownloadStatusProps {
show: boolean
@@ -136,11 +137,13 @@ const UpdateDownloadStatus = ({
interface GeneralSettingProps {
updater: State.AppUpdater
+ app: State.App
}
-const GeneralSetting = ({ updater }: GeneralSettingProps) => {
+const GeneralSetting = ({ updater, app }: GeneralSettingProps) => {
const [t, i18n] = useTranslation()
const [showLangDialog, setShowLangDialog] = useState(false)
+ const [isLockDialogShow, setIsLockDialogShow] = useState(false)
const [searchParams] = useSearchParams()
const [errorMsg, setErrorMsg] = useState('')
const { showCheckDialog, setShowCheckDialog, onCancelCheckUpdates } = useCheckUpdate()
@@ -221,6 +224,22 @@ const GeneralSetting = ({ updater }: GeneralSettingProps) => {
+
+
{t('settings.general.lock-password')}
+
+
+
{
setShowLangDialog(false)
}}
/>
+
+ {
+ setIsLockDialogShow(false)
+ }}
+ />
)
}
diff --git a/packages/neuron-ui/src/containers/LockWindow/index.tsx b/packages/neuron-ui/src/containers/LockWindow/index.tsx
new file mode 100644
index 0000000000..3afb787ed2
--- /dev/null
+++ b/packages/neuron-ui/src/containers/LockWindow/index.tsx
@@ -0,0 +1,172 @@
+/* eslint-disable jsx-a11y/media-has-caption */
+import React, { useCallback, useEffect, useState } from 'react'
+import { AppActions, getLockWindowInfo, useDispatch, useState as useGlobalState } from 'states'
+import Spinner from 'widgets/Spinner'
+import Locked from 'widgets/Icons/Locked.png'
+import DarkUnLockMp4 from 'widgets/Icons/dark-unlock.mp4'
+import UnLockMp4 from 'widgets/Icons/unlock.mp4'
+import SplitPasswordInput from 'widgets/SplitPasswordInput'
+import { useTranslation } from 'react-i18next'
+import { clsx, isSuccessResponse } from 'utils'
+import { isDark, unlockWindow } from 'services/remote'
+import { retryUnlockWindow } from 'services/localCache'
+import { MILLISECS_PER_HOUR, MILLISECS_PER_MIN, MILLISECS_PER_SEC } from 'utils/getSyncLeftTime'
+import styles from './lockWindow.module.scss'
+
+const passwordLen = 4
+const wrongEnterTimes = 3
+const formatterLockMillisecs = (lockMillisecs: number) => {
+ const hrs = Math.floor(lockMillisecs / MILLISECS_PER_HOUR)
+ const mins = Math.floor((lockMillisecs - hrs * MILLISECS_PER_HOUR) / MILLISECS_PER_MIN)
+ const secs = Math.floor((lockMillisecs - hrs * MILLISECS_PER_HOUR - mins * MILLISECS_PER_MIN) / MILLISECS_PER_SEC)
+ return (hrs > 0 ? [hrs, mins] : [mins, secs]).map(v => v.toString().padStart(2, '0')).join(':')
+}
+
+const getWaitMillisecs = (retryTimes: number) => {
+ if (retryTimes % wrongEnterTimes === 0) {
+ if (retryTimes >= 3 * wrongEnterTimes) {
+ return 24 * MILLISECS_PER_HOUR
+ }
+ if (retryTimes > wrongEnterTimes) {
+ return 30 * MILLISECS_PER_MIN
+ }
+ return 5 * MILLISECS_PER_MIN
+ }
+ return undefined
+}
+
+const LockWindow = ({ children }: { children: React.ReactNode }) => {
+ const dispatch = useDispatch()
+ const [t] = useTranslation()
+ useEffect(() => {
+ getLockWindowInfo(dispatch)
+ }, [])
+ const { app } = useGlobalState()
+ const [password, setPassword] = useState(new Array(passwordLen).fill(''))
+ const [errMsg, setErrMsg] = useState('')
+ const [retryUnlockInfo, setRetryUnlockInfo] = useState(retryUnlockWindow.get())
+ const [verifySuccess, setVerifySuccess] = useState(false)
+ const onUpdatePassword = useCallback(
+ (v: string, idx: number) => {
+ const updatedPassword = password.toSpliced(idx, 1, v).join('')
+ if (updatedPassword.length === passwordLen) {
+ unlockWindow(updatedPassword)
+ .then(res => {
+ if (isSuccessResponse(res)) {
+ setVerifySuccess(true)
+ setTimeout(() => {
+ dispatch({
+ type: AppActions.SetLockWindowInfo,
+ payload: { locked: false },
+ })
+ setVerifySuccess(false)
+ }, 1000)
+ retryUnlockWindow.reset()
+ setRetryUnlockInfo({ retryTimes: 0 })
+ setPassword(i => i.toSpliced(idx, 1, v))
+ return
+ }
+ throw new Error('verify failed')
+ })
+ .catch(() => {
+ const { retryTimes } = retryUnlockWindow.get()
+ const newRetryUnlockInfo = { lastRetryTime: Date.now(), retryTimes: retryTimes + 1 }
+ const needWaitMillisecs = getWaitMillisecs(retryTimes + 1)
+ if (needWaitMillisecs) {
+ setErrMsg(
+ t('lock-window.failed-times', {
+ frequency: retryTimes + 1,
+ time: formatterLockMillisecs(needWaitMillisecs),
+ })
+ )
+ } else {
+ setErrMsg(t('lock-window.lock-password-error'))
+ }
+ retryUnlockWindow.save(newRetryUnlockInfo)
+ setRetryUnlockInfo(newRetryUnlockInfo)
+ setPassword(new Array(passwordLen).fill(''))
+ })
+ } else {
+ setErrMsg('')
+ setPassword(i => i.toSpliced(idx, 1, v))
+ }
+ },
+ [password]
+ )
+ const [theme, setTheme] = useState<'dark' | 'light'>()
+ useEffect(() => {
+ isDark().then(res => {
+ if (isSuccessResponse(res)) {
+ setTheme(res.result ? 'dark' : 'light')
+ }
+ })
+ }, [])
+ useEffect(() => {
+ if (app.lockWindowInfo?.locked) {
+ setPassword(new Array(passwordLen).fill(''))
+ setErrMsg('')
+ }
+ }, [app.lockWindowInfo?.locked])
+ useEffect(() => {
+ let interval: ReturnType
+ const needWaitMillisecs = getWaitMillisecs(retryUnlockInfo.retryTimes)
+ if (needWaitMillisecs && retryUnlockInfo.lastRetryTime) {
+ interval = setInterval(() => {
+ const hasWaitMillisecs = Date.now() - retryUnlockInfo.lastRetryTime!
+ if (needWaitMillisecs > hasWaitMillisecs) {
+ setErrMsg(
+ t('lock-window.failed-times', {
+ frequency: retryUnlockInfo.retryTimes,
+ time: formatterLockMillisecs(needWaitMillisecs - hasWaitMillisecs),
+ })
+ )
+ } else {
+ const newRetryUnlockInfo = { retryTimes: retryUnlockInfo.retryTimes }
+ retryUnlockWindow.save(newRetryUnlockInfo)
+ setRetryUnlockInfo(newRetryUnlockInfo)
+ setErrMsg('')
+ }
+ }, 1_000)
+ }
+ return () => clearInterval(interval)
+ }, [retryUnlockInfo])
+ if (!app.lockWindowInfo) {
+ return (
+
+
+
+ )
+ }
+ if (app.lockWindowInfo.locked) {
+ return (
+
+ {verifySuccess ? (
+
+ ) : (
+
![Locked]({Locked})
+ )}
+
{t('lock-window.neuron-is-locked')}
+
+
+
+
+ {errMsg || t('lock-window.enter-lock-password')}
+
+
+ )
+ }
+ return children
+}
+
+LockWindow.displayName = 'LockWindow'
+
+export default LockWindow
diff --git a/packages/neuron-ui/src/containers/LockWindow/lockWindow.module.scss b/packages/neuron-ui/src/containers/LockWindow/lockWindow.module.scss
new file mode 100644
index 0000000000..a35391d16b
--- /dev/null
+++ b/packages/neuron-ui/src/containers/LockWindow/lockWindow.module.scss
@@ -0,0 +1,55 @@
+.loading {
+ text-align: center;
+ margin-top: 20vh;
+}
+
+.lockContainer {
+ text-align: center;
+ margin-top: 25vh;
+
+ .title {
+ margin: 10px 0 24px 0;
+ font-size: 20px;
+ font-weight: 500;
+ color: var(--main-text-color);
+ }
+
+ .notice {
+ margin-top: 24px;
+ color: var(--secondary-text-color);
+ &[data-has-err='true'] {
+ color: var(--error-color);
+ }
+ }
+
+ .passwordContainer {
+ &[data-has-err='true'] {
+ input {
+ border-color: var(--error-color);
+ }
+ }
+ }
+
+ @keyframes rotating {
+ from {
+ transform: rotate(0deg);
+ }
+ to {
+ transform: rotate(-10deg);
+ }
+ }
+
+ .lockedImg {
+ width: 88px;
+ height: 88px;
+
+ &.animation {
+ animation: rotating 0.5s linear 2 alternate;
+ }
+ }
+
+ .video {
+ width: 88px;
+ height: 88px;
+ }
+}
diff --git a/packages/neuron-ui/src/containers/Main/hooks.ts b/packages/neuron-ui/src/containers/Main/hooks.ts
index 9938e20ff3..e6dd1b7363 100644
--- a/packages/neuron-ui/src/containers/Main/hooks.ts
+++ b/packages/neuron-ui/src/containers/Main/hooks.ts
@@ -8,6 +8,7 @@ import {
updateAddressListAndBalance,
initAppState,
showGlobalAlertDialog,
+ updateLockWindowInfo,
} from 'states/stateProvider/actionCreators'
import {
@@ -114,6 +115,8 @@ export const useSubscription = ({
dispatch,
location,
showSwitchNetwork,
+ lockWindowInfo,
+ setIsLockDialogShow,
}: {
walletID: string
chain: State.Chain
@@ -122,6 +125,8 @@ export const useSubscription = ({
location: ReturnType
dispatch: StateDispatch
showSwitchNetwork: () => void
+ lockWindowInfo: State.App['lockWindowInfo']
+ setIsLockDialogShow: (v: boolean) => void
}) => {
const { pageNo, pageSize, keywords } = chain.transactions
@@ -304,6 +309,15 @@ export const useSubscription = ({
case 'multisig-address':
navigateToolsRouter(type)
break
+ case 'lock-window':
+ if (lockWindowInfo?.encryptedPassword) {
+ if (!lockWindowInfo.locked) {
+ updateLockWindowInfo({ locked: true })(dispatch)
+ }
+ } else {
+ setIsLockDialogShow(true)
+ }
+ break
default: {
break
}
@@ -332,6 +346,8 @@ export const useSubscription = ({
dispatch,
location.pathname,
showSwitchNetwork,
+ lockWindowInfo,
+ setIsLockDialogShow,
])
}
diff --git a/packages/neuron-ui/src/containers/Main/index.tsx b/packages/neuron-ui/src/containers/Main/index.tsx
index 28421862c7..038ca40a31 100644
--- a/packages/neuron-ui/src/containers/Main/index.tsx
+++ b/packages/neuron-ui/src/containers/Main/index.tsx
@@ -1,4 +1,4 @@
-import React, { useCallback, useMemo, useEffect } from 'react'
+import React, { useCallback, useMemo, useEffect, useState } from 'react'
import { useNavigate, useLocation, Outlet } from 'react-router-dom'
import { Trans, useTranslation } from 'react-i18next'
import { useState as useGlobalState, useDispatch, dismissGlobalAlertDialog } from 'states'
@@ -13,6 +13,7 @@ import DataPathDialog from 'widgets/DataPathDialog'
import NoDiskSpaceWarn from 'widgets/Icons/NoDiskSpaceWarn.png'
import MigrateCkbDataDialog from 'widgets/MigrateCkbDataDialog'
import { keepScreenAwake } from 'services/localCache'
+import LockWindowDialog from 'components/GeneralSetting/LockWindowDialog'
import styles from './main.module.scss'
import { useSubscription, useSyncChainData, useOnCurrentWalletChange, useCheckNode, useNoDiskSpace } from './hooks'
@@ -20,7 +21,7 @@ const MainContent = () => {
const navigate = useNavigate()
const location = useLocation()
const {
- app: { isAllowedToFetchList = true, globalAlertDialog },
+ app: { isAllowedToFetchList = true, globalAlertDialog, lockWindowInfo },
wallet: { id: walletID = '' },
chain,
settings: { networks = [] },
@@ -52,6 +53,7 @@ const MainContent = () => {
onOpenEditorDialog,
} = useCheckNode(sameUrlNetworks, networkID)
+ const [isLockDialogShow, setIsLockDialogShow] = useState(false)
useSubscription({
walletID,
chain,
@@ -60,6 +62,8 @@ const MainContent = () => {
dispatch,
location,
showSwitchNetwork,
+ lockWindowInfo,
+ setIsLockDialogShow,
})
useOnCurrentWalletChange({
@@ -181,6 +185,13 @@ const MainContent = () => {
onCancel={onCloseMigrateDialog}
onConfirm={onConfirmMigrate}
/>
+ {
+ setIsLockDialogShow(false)
+ }}
+ />
)
}
diff --git a/packages/neuron-ui/src/locales/en.json b/packages/neuron-ui/src/locales/en.json
index 5e16c40df8..8d0e298fe6 100644
--- a/packages/neuron-ui/src/locales/en.json
+++ b/packages/neuron-ui/src/locales/en.json
@@ -395,7 +395,21 @@
"language": "Language",
"select-language": "Select Language",
"apply": "Apply",
- "keep-awake": "Keep the screen awake during synchronization"
+ "keep-awake": "Keep the screen awake during synchronization",
+ "lock-password": "Lock window password",
+ "set-lock-password": "Set lock window password",
+ "change-lock-password": "Change lock window password",
+ "lock-window": {
+ "set-password": "Set Password",
+ "confirm-password": "Confirm Password",
+ "enter-current-password": "Enter Password",
+ "enter-new-password": "Enter new password",
+ "password-error": "Password error",
+ "different-password": "Two entered passwords do not match",
+ "set-password-success": "Password set successfully",
+ "change-password-success": "Password change successfully.",
+ "reset": "Reset"
+ }
},
"wallet-manager": {
"edit-wallet": {
@@ -1254,6 +1268,12 @@
"size": "Size",
"lock-time": "Lock Time"
}
+ },
+ "lock-window": {
+ "neuron-is-locked": "Neuron's window has been locked",
+ "enter-lock-password": "Enter lock password",
+ "lock-password-error": "Lock windows password error",
+ "failed-times": "Failed more than {{frequency}} times, please retry after {{time}}"
}
}
}
diff --git a/packages/neuron-ui/src/locales/es.json b/packages/neuron-ui/src/locales/es.json
index 590e452f5f..0b501ca7c7 100644
--- a/packages/neuron-ui/src/locales/es.json
+++ b/packages/neuron-ui/src/locales/es.json
@@ -387,7 +387,21 @@
"language": "Idioma",
"select-language": "Seleccionar Idioma",
"apply": "Aplicar",
- "keep-awake": "Mantener la pantalla encendida durante la sincronización"
+ "keep-awake": "Mantener la pantalla encendida durante la sincronización",
+ "lock-password": "Contraseña de bloqueo de pantalla",
+ "set-lock-password": "Establecer contraseña de bloqueo de pantalla",
+ "change-lock-password": "Modificar contraseña de bloqueo de pantalla",
+ "lock-window": {
+ "set-password": "Establecer contraseña",
+ "confirm-password": "Confirmar contraseña",
+ "enter-current-password": "Ingresar contraseña actual",
+ "enter-new-password": "Ingresar nueva contraseña",
+ "password-error": "Contraseña incorrecta",
+ "different-password": "Las contraseñas ingresadas no coinciden",
+ "set-password-success": "Contraseña establecida exitosamente",
+ "change-password-success": "Contraseña modificada exitosamente",
+ "reset": "Reestablecer"
+ }
},
"wallet-manager": {
"edit-wallet": {
@@ -1233,6 +1247,12 @@
"size": "tamaño",
"lock-time": "Período de Bloqueo"
}
+ },
+ "lock-window": {
+ "neuron-is-locked": "La ventana de Neuron está bloqueada",
+ "enter-lock-password": "Ingresar contraseña de bloqueo de pantalla",
+ "lock-password-error": "Contraseña de bloqueo de pantalla incorrecta",
+ "failed-times": "Fallo más de {{frequency}} veces, por favor inténtalo de nuevo después de {{time}}"
}
}
}
diff --git a/packages/neuron-ui/src/locales/fr.json b/packages/neuron-ui/src/locales/fr.json
index c42bfc3594..9065be440d 100644
--- a/packages/neuron-ui/src/locales/fr.json
+++ b/packages/neuron-ui/src/locales/fr.json
@@ -394,7 +394,21 @@
"language": "Langue",
"select-language": "Sélectionner la langue",
"apply": "Appliquer",
- "keep-awake": "Maintenir l'écran allumé pendant la synchronisation"
+ "keep-awake": "Maintenir l'écran allumé pendant la synchronisation",
+ "lock-password": "Mot de passe de verrouillage de l'écran",
+ "set-lock-password": "Configurer le mot de passe de verrouillage de l'écran",
+ "change-lock-password": "Modifier le mot de passe de verrouillage de l'écran",
+ "lock-window": {
+ "set-password": "Créer un mot de passe",
+ "confirm-password": "Confirmer le mot de passe",
+ "enter-current-password": "Entrer le mot de passe actuel",
+ "enter-new-password": "Entrer le nouveau mot de passe",
+ "password-error": "Mot de passe incorrect",
+ "different-password": "Les deux mots de passe entrés ne correspondent pas",
+ "set-password-success": "Mot de passe défini avec succès",
+ "change-password-success": "Mot de passe modifié avec succès",
+ "reset": "Réinitialiser"
+ }
},
"wallet-manager": {
"edit-wallet": {
@@ -1244,6 +1258,12 @@
"size": "taille",
"lock-time": "Période de verrouillage"
}
+ },
+ "lock-window": {
+ "neuron-is-locked": "a fenêtre de Neuron est verrouillée",
+ "enter-lock-password": "Entrer le mot de passe de verrouillage d'écran",
+ "lock-password-error": "Mot de passe de verrouillage d'écran incorrect",
+ "failed-times": "Échec plus de {{frequency}} fois, veuillez réessayer après {{time}}"
}
}
}
diff --git a/packages/neuron-ui/src/locales/zh-tw.json b/packages/neuron-ui/src/locales/zh-tw.json
index b6b4baad1d..747241e5a0 100644
--- a/packages/neuron-ui/src/locales/zh-tw.json
+++ b/packages/neuron-ui/src/locales/zh-tw.json
@@ -390,7 +390,21 @@
"language": "語言",
"select-language": "選擇語言",
"apply": "應用",
- "keep-awake": "同步期間保持屏幕喚醒"
+ "keep-awake": "同步期間保持屏幕喚醒",
+ "lock-password": "鎖屏密碼",
+ "set-lock-password": "設置鎖屏密碼",
+ "change-lock-password": "修改鎖屏密碼",
+ "lock-window": {
+ "set-password": "設置密碼",
+ "confirm-password": "確認密碼",
+ "enter-current-password": "輸入當前密碼",
+ "enter-new-password": "輸入新密碼",
+ "password-error": "密碼錯誤",
+ "different-password": "兩次輸入密碼不壹致",
+ "set-password-success": "密碼設置成功",
+ "change-password-success": "密碼修改成功",
+ "reset": "重新設置"
+ }
},
"wallet-manager": {
"edit-wallet": {
@@ -1222,6 +1236,12 @@
"size": "大小",
"lock-time": "鎖定時間"
}
+ },
+ "lock-window": {
+ "neuron-is-locked": "Neuron 窗口已鎖定",
+ "enter-lock-password": "輸入鎖屏密碼",
+ "lock-password-error": "鎖屏密碼錯誤",
+ "failed-times": "失敗超過 {{frequency}} 次, 請在 {{time}} 後重試"
}
}
}
diff --git a/packages/neuron-ui/src/locales/zh.json b/packages/neuron-ui/src/locales/zh.json
index c837c54415..9a7ff8f0db 100644
--- a/packages/neuron-ui/src/locales/zh.json
+++ b/packages/neuron-ui/src/locales/zh.json
@@ -388,7 +388,21 @@
"language": "系统语言",
"select-language": "选择语言",
"apply": "应用",
- "keep-awake": "同步期间保持屏幕唤醒"
+ "keep-awake": "同步期间保持屏幕唤醒",
+ "lock-password": "锁屏密码",
+ "set-lock-password": "设置锁屏密码",
+ "change-lock-password": "修改锁屏密码",
+ "lock-window": {
+ "set-password": "设置密码",
+ "confirm-password": "确认密码",
+ "enter-current-password": "输入当前密码",
+ "enter-new-password": "输入新密码",
+ "password-error": "密码错误",
+ "different-password": "两次输入密码不一致",
+ "set-password-success": "密码设置成功",
+ "change-password-success": "密码修改成功",
+ "reset": "重新设置"
+ }
},
"wallet-manager": {
"edit-wallet": {
@@ -1246,6 +1260,12 @@
"size": "大小",
"lock-time": "锁定时间"
}
+ },
+ "lock-window": {
+ "neuron-is-locked": "Neuron 窗口已锁定",
+ "enter-lock-password": "输入锁屏密码",
+ "lock-password-error": "锁屏密码错误",
+ "failed-times": "失败超过 {{frequency}} 次, 请在 {{time}} 后重试"
}
}
}
diff --git a/packages/neuron-ui/src/router.tsx b/packages/neuron-ui/src/router.tsx
index 319ebc3274..042c32b7c7 100644
--- a/packages/neuron-ui/src/router.tsx
+++ b/packages/neuron-ui/src/router.tsx
@@ -1,5 +1,6 @@
import React from 'react'
import { Outlet, RouteObject } from 'react-router-dom'
+import LockWindow from 'containers/LockWindow'
import Main from 'containers/Main'
import Navbar from 'containers/Navbar'
import { RoutePath } from 'utils'
@@ -50,11 +51,11 @@ const mainRouterConfig: RouteObject[] = [
{
path: '/',
element: (
- <>
+
- >
+
),
children: [
{
diff --git a/packages/neuron-ui/src/services/localCache.ts b/packages/neuron-ui/src/services/localCache.ts
index a53f75513c..84851f4c03 100644
--- a/packages/neuron-ui/src/services/localCache.ts
+++ b/packages/neuron-ui/src/services/localCache.ts
@@ -12,6 +12,7 @@ export enum LocalCacheKey {
ImportedWallet = 'ImportedWallet',
ShownNodeId = 'ShownNodeId',
ScreenAwake = 'ScreenAwake',
+ RetryUnlockWindowInfo = 'RetryUnlockWindowInfo',
}
export const addresses = {
@@ -176,3 +177,20 @@ export const keepScreenAwake = {
window.localStorage.setItem(LocalCacheKey.ScreenAwake, value.toString())
},
}
+
+export const retryUnlockWindow = {
+ reset: () => {
+ window.localStorage.setItem(LocalCacheKey.RetryUnlockWindowInfo, JSON.stringify({ retryTimes: 0 }))
+ },
+ save: (info: { lastRetryTime?: number; retryTimes: number }) => {
+ window.localStorage.setItem(LocalCacheKey.RetryUnlockWindowInfo, JSON.stringify(info))
+ },
+ get: (): { lastRetryTime?: number; retryTimes: number } => {
+ try {
+ const info = window.localStorage.getItem(LocalCacheKey.RetryUnlockWindowInfo)
+ return info ? JSON.parse(info) : { retryTimes: 0 }
+ } catch (error) {
+ return { retryTimes: 0 }
+ }
+ },
+}
diff --git a/packages/neuron-ui/src/services/remote/app.ts b/packages/neuron-ui/src/services/remote/app.ts
index ce391b86dc..48a16e2962 100644
--- a/packages/neuron-ui/src/services/remote/app.ts
+++ b/packages/neuron-ui/src/services/remote/app.ts
@@ -47,3 +47,9 @@ export const invokeGetAllDisplaysSize = remoteApi('get-all-display
export const invokeShowMessageBox = remoteApi('show-message-box')
export const isDark = remoteApi('is-dark')
export const setTheme = remoteApi<'light' | 'dark', void>('set-theme')
+export const getLockWindowInfo = remoteApi('get-lock-window-info')
+export const updateLockWindowInfo = remoteApi<{ locked?: boolean; password?: string }, State.App['lockWindowInfo']>(
+ 'update-lock-window-info'
+)
+export const verifyLockWindowPassword = remoteApi('verify-lock-window-password')
+export const unlockWindow = remoteApi('unlock-window')
diff --git a/packages/neuron-ui/src/services/remote/remoteApiWrapper.ts b/packages/neuron-ui/src/services/remote/remoteApiWrapper.ts
index 5be98553fc..92e70066f5 100644
--- a/packages/neuron-ui/src/services/remote/remoteApiWrapper.ts
+++ b/packages/neuron-ui/src/services/remote/remoteApiWrapper.ts
@@ -52,6 +52,10 @@ type Action =
| 'start-node-ignore-external'
| 'get-first-sync-info'
| 'start-sync'
+ | 'get-lock-window-info'
+ | 'update-lock-window-info'
+ | 'verify-lock-window-password'
+ | 'unlock-window'
// Wallets
| 'get-all-wallets'
| 'get-current-wallet'
diff --git a/packages/neuron-ui/src/states/stateProvider/actionCreators/app.ts b/packages/neuron-ui/src/states/stateProvider/actionCreators/app.ts
index dd63438b57..1a92c5b6b0 100644
--- a/packages/neuron-ui/src/states/stateProvider/actionCreators/app.ts
+++ b/packages/neuron-ui/src/states/stateProvider/actionCreators/app.ts
@@ -1,5 +1,9 @@
import { NeuronWalletActions, AppActions, StateDispatch } from 'states/stateProvider/reducer'
-import { getNeuronWalletState } from 'services/remote'
+import {
+ getLockWindowInfo as getLockWindowInfoAPI,
+ updateLockWindowInfo as updateLockWindowInfoAPI,
+ getNeuronWalletState,
+} from 'services/remote'
import initStates from 'states/init'
import { RoutePath, ErrorCode, addressesToBalance, isSuccessResponse } from 'utils'
import { WalletWizardPath } from 'components/WalletWizard'
@@ -150,3 +154,25 @@ export const showPageNotice =
})
}, 2000)
}
+
+export const getLockWindowInfo = (dispatch: StateDispatch) => {
+ return getLockWindowInfoAPI().then(res => {
+ if (isSuccessResponse(res) && res.result) {
+ dispatch({
+ type: AppActions.SetLockWindowInfo,
+ payload: res.result,
+ })
+ }
+ })
+}
+
+export const updateLockWindowInfo = (params: { locked?: boolean; password?: string }) => (dispatch: StateDispatch) => {
+ updateLockWindowInfoAPI(params).then(res => {
+ if (isSuccessResponse(res) && res.result) {
+ dispatch({
+ type: AppActions.SetLockWindowInfo,
+ payload: res.result,
+ })
+ }
+ })
+}
diff --git a/packages/neuron-ui/src/states/stateProvider/reducer.ts b/packages/neuron-ui/src/states/stateProvider/reducer.ts
index 5579dcd438..2d64d0c029 100644
--- a/packages/neuron-ui/src/states/stateProvider/reducer.ts
+++ b/packages/neuron-ui/src/states/stateProvider/reducer.ts
@@ -69,6 +69,8 @@ export enum AppActions {
// Cell manage
UpdateConsumeCells = 'UpdateConsumeCells',
+ // lock window
+ SetLockWindowInfo = 'SetLockWindowInfo',
}
export type StateAction =
@@ -121,6 +123,7 @@ export type StateAction =
| { type: NeuronWalletActions.GetSUDTAccountList; payload: Controller.GetSUDTAccountList.Response }
| { type: AppActions.SignVerify; payload: string }
| { type: AppActions.UpdateConsumeCells; payload?: { outPoint: OutPoint; capacity: string }[] }
+ | { type: AppActions.SetLockWindowInfo; payload: Required['lockWindowInfo'] }
export type StateDispatch = React.Dispatch // TODO: add type of payload
@@ -420,6 +423,11 @@ export const reducer = produce((state: Draft, action:
break
}
+ case AppActions.SetLockWindowInfo: {
+ state.app.lockWindowInfo = { ...(state.app.lockWindowInfo ?? {}), ...action.payload }
+ break
+ }
+
default: {
break
}
diff --git a/packages/neuron-ui/src/types/App/index.d.ts b/packages/neuron-ui/src/types/App/index.d.ts
index 4ba289b716..4bf65bf429 100644
--- a/packages/neuron-ui/src/types/App/index.d.ts
+++ b/packages/neuron-ui/src/types/App/index.d.ts
@@ -176,6 +176,10 @@ declare namespace State {
loadedTransaction: any
pageNotice?: PageNotice
showWaitForFullySynced: boolean
+ lockWindowInfo?: {
+ locked: boolean
+ encryptedPassword?: string
+ }
}
interface NetworkProperty {
diff --git a/packages/neuron-ui/src/types/Subject/index.d.ts b/packages/neuron-ui/src/types/Subject/index.d.ts
index fcba2123dd..aaf34f6a82 100644
--- a/packages/neuron-ui/src/types/Subject/index.d.ts
+++ b/packages/neuron-ui/src/types/Subject/index.d.ts
@@ -16,6 +16,7 @@ declare namespace Command {
| 'migrate-acp'
| 'sign-verify'
| 'multisig-address'
+ | 'lock-window'
type Payload = string | null
}
diff --git a/packages/neuron-ui/src/types/global/index.d.ts b/packages/neuron-ui/src/types/global/index.d.ts
index e4bc9f3091..168a43dc32 100644
--- a/packages/neuron-ui/src/types/global/index.d.ts
+++ b/packages/neuron-ui/src/types/global/index.d.ts
@@ -24,6 +24,11 @@ declare module '*.png' {
export default value
}
+declare module '*.mp4' {
+ const value: string
+ export default value
+}
+
declare module '*.scss'
declare namespace Fixture {
diff --git a/packages/neuron-ui/src/utils/getSyncLeftTime.ts b/packages/neuron-ui/src/utils/getSyncLeftTime.ts
index 9b45a9f274..812c3f2ca4 100644
--- a/packages/neuron-ui/src/utils/getSyncLeftTime.ts
+++ b/packages/neuron-ui/src/utils/getSyncLeftTime.ts
@@ -1,6 +1,6 @@
-const MILLISECS_PER_SEC = 1_000
-const MILLISECS_PER_MIN = 60_000
-const MILLISECS_PER_HOUR = 3600_000
+export const MILLISECS_PER_SEC = 1_000
+export const MILLISECS_PER_MIN = 60_000
+export const MILLISECS_PER_HOUR = 3600_000
export const getSyncLeftTime = (estimate: number | undefined) => {
let leftTime = '-'
diff --git a/packages/neuron-ui/src/widgets/Icons/Locked.png b/packages/neuron-ui/src/widgets/Icons/Locked.png
new file mode 100644
index 0000000000000000000000000000000000000000..ff6636f8cc235334884b91a748cf3828de473ef2
GIT binary patch
literal 221987
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
ceZaQ7BW84r0nAjE_by?NiineBvPl>
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