Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Support lock window for Neuron #3064

Merged
merged 2 commits into from
Apr 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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('')
homura marked this conversation as resolved.
Show resolved Hide resolved
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,
}
}
Original file line number Diff line number Diff line change
@@ -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 = ({
yanguoyu marked this conversation as resolved.
Show resolved Hide resolved
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 (
<>
<span className={styles.secTitle}>{t('settings.general.lock-window.enter-current-password')}</span>
<div className={styles.password}>
<SplitPasswordInput values={oldPassword} onChange={onUpdateOldPassword} />
<div className={styles.err}>{oldPasswordErr}</div>
</div>
</>
)
}
if (joinedPassword.length !== passwordLength) {
// enter password
return (
<>
<span className={styles.secTitle}>{t(`settings.general.lock-window.set-password`)}</span>
<div className={styles.password}>
<SplitPasswordInput values={password} onChange={onUpdatePassword} />
</div>
</>
)
}
// is verify repeat password
return (
<>
<span className={styles.secTitle}>{t(`settings.general.lock-window.confirm-password`)}</span>
<div className={styles.password}>
<SplitPasswordInput values={repeatPassword} onChange={onUpdateRepeatPassword} />
<div className={styles.err}>{errMsg}</div>
{errMsg ? (
<Button type="primary" onClick={onReset}>
{t('settings.general.lock-window.reset')}
</Button>
) : null}
</div>
</>
)
}

return (
<>
<Dialog
show={show}
title={t(`settings.general.${encryptedPassword ? 'change-lock-password' : 'set-lock-password'}`)}
onCancel={onCancel}
showFooter={false}
className={styles.dialog}
>
<div className={styles.content}>{content()}</div>
</Dialog>
{isSuccess ? (
<Alert status="success" className={styles.notice}>
{t(`settings.general.lock-window.${encryptedPassword ? 'change-password-success' : 'set-password-success'}`)}
</Alert>
) : null}
</>
)
}

LockWindowDialog.displayName = 'LockWindowDialog'
export default LockWindowDialog
Original file line number Diff line number Diff line change
@@ -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;
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,11 @@ $action-button-width: 11.25rem;
margin-right: 4px;
}
}

&.lockWindow {
width: auto;
min-width: 176px;
}
}
.showVersion {
position: relative;
Expand Down
31 changes: 29 additions & 2 deletions packages/neuron-ui/src/components/GeneralSetting/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -221,6 +224,22 @@ const GeneralSetting = ({ updater }: GeneralSettingProps) => {
<Switch checked={isScreenKeepAwake} onChange={onChangeScreenKeepAwake} />
</div>

<div className={clsx(styles.content, styles.lockWindow)}>
<p>{t('settings.general.lock-password')}</p>
<button
type="button"
data-button-type="text"
onClick={() => {
setIsLockDialogShow(true)
}}
>
<UnLock />
{t(
`settings.general.${app.lockWindowInfo?.encryptedPassword ? 'change-lock-password' : 'set-lock-password'}`
)}
</button>
</div>

<AlertDialog
show={!!errorMsg}
title={t(`updates.check-updates`)}
Expand Down Expand Up @@ -260,6 +279,14 @@ const GeneralSetting = ({ updater }: GeneralSettingProps) => {
setShowLangDialog(false)
}}
/>

<LockWindowDialog
show={isLockDialogShow}
encryptedPassword={app.lockWindowInfo?.encryptedPassword}
onCancel={() => {
setIsLockDialogShow(false)
}}
/>
</div>
)
}
Expand Down
Loading
Loading