Skip to content
This repository has been archived by the owner on Oct 10, 2023. It is now read-only.

Business logic for renaming wallets #2358

Merged
merged 3 commits into from
Aug 17, 2022
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
2 changes: 1 addition & 1 deletion src/renderer/components/header/lock/HeaderLock.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ export const HeaderLock: React.FC<Props> = (props): JSX.Element => {
text-text2 dark:bg-gray0d
dark:text-text2d
">
{truncateMiddle(name, { start: 4, end: 4, max: 16 })}
{truncateMiddle(name, { start: 3, end: 3, max: 11 })}
</p>
)
)
Expand Down
63 changes: 48 additions & 15 deletions src/renderer/components/settings/WalletSettings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,9 @@ import {
WalletAddressAsync,
KeystoreUnlocked,
ChangeKeystoreWalletHandler,
ChangeKeystoreWalletRD
ChangeKeystoreWalletRD,
RenameKeystoreWalletHandler,
RenameKeystoreWalletRD
} from '../../services/wallet/types'
import { walletTypeToI18n } from '../../services/wallet/util'
import { AttentionIcon } from '../icons'
Expand All @@ -77,8 +79,9 @@ type Props = {
network: Network
walletAccounts: O.Option<WalletAccounts>
lockWallet: FP.Lazy<void>
removeKeystore: RemoveKeystoreWalletHandler
changeKeystore$: ChangeKeystoreWalletHandler
removeKeystoreWallet: RemoveKeystoreWalletHandler
changeKeystoreWallet$: ChangeKeystoreWalletHandler
renameKeystoreWallet$: RenameKeystoreWalletHandler
exportKeystore: () => Promise<void>
addLedgerAddress: (chain: Chain, walletIndex: number) => void
verifyLedgerAddress: (chain: Chain, walletIndex: number) => Promise<boolean>
Expand All @@ -100,13 +103,14 @@ export const WalletSettings: React.FC<Props> = (props): JSX.Element => {
network,
walletAccounts: oWalletAccounts,
lockWallet,
removeKeystore,
changeKeystore$,
removeKeystoreWallet,
changeKeystoreWallet$,
renameKeystoreWallet$,
exportKeystore,
addLedgerAddress,
verifyLedgerAddress,
removeLedgerAddress,
keystore: { phrase, name: walletName },
keystore: { phrase, name: walletName, id: walletId },
wallets,
clickAddressLinkHandler,
validatePassword$,
Expand All @@ -125,15 +129,15 @@ export const WalletSettings: React.FC<Props> = (props): JSX.Element => {
const closeQrModal = useCallback(() => setShowQRModal(O.none), [setShowQRModal])

const removeWalletHandler = useCallback(async () => {
const noWallets = await removeKeystore()
const noWallets = await removeKeystoreWallet()
if (noWallets >= 1) {
// goto unlock screen to unlock another wallet
navigate(walletRoutes.locked.path())
} else {
// no wallet -> go to homepage
navigate(appRoutes.base.template)
}
}, [removeKeystore, navigate])
}, [removeKeystoreWallet, navigate])

const onSuccessPassword = useCallback(() => {
setShowPasswordModal(false)
Expand Down Expand Up @@ -448,11 +452,11 @@ export const WalletSettings: React.FC<Props> = (props): JSX.Element => {

const changeWalletHandler = useCallback(
(id: KeystoreId) => {
subscribeChangeWalletState(changeKeystore$(id))
subscribeChangeWalletState(changeKeystoreWallet$(id))
// Jump to `UnlockView` to avoid staying at wallet settings
navigate(walletRoutes.locked.path())
},
[changeKeystore$, navigate, subscribeChangeWalletState]
[changeKeystoreWallet$, navigate, subscribeChangeWalletState]
)

const renderChangeWalletError = useMemo(
Expand All @@ -474,9 +478,33 @@ export const WalletSettings: React.FC<Props> = (props): JSX.Element => {
[changeWalletState, intl]
)

const changeWalletNameHandler = useCallback((walletName: string) => {
console.log('walletName:', walletName)
}, [])
const { state: renameWalletState, subscribe: subscribeRenameWalletState } =
useSubscriptionState<RenameKeystoreWalletRD>(RD.initial)

const changeWalletNameHandler = useCallback(
(walletName: string) => {
subscribeRenameWalletState(renameKeystoreWallet$(walletId, walletName))
},
[renameKeystoreWallet$, subscribeRenameWalletState, walletId]
)

const renderRenameWalletError = useMemo(
() =>
FP.pipe(
renameWalletState,
RD.fold(
() => <></>,
() => <></>,
(error) => (
<p className="text-center font-main text-[14px] uppercase text-error0">
{intl.formatMessage({ id: 'wallet.name.error.rename' })} {error?.message ?? error.toString()}
</p>
),
() => <></>
)
),
[intl, renameWalletState]
)

return (
<div className="mt-40px bg-bg0 py-10px px-40px dark:bg-bg0d">
Expand Down Expand Up @@ -518,8 +546,13 @@ export const WalletSettings: React.FC<Props> = (props): JSX.Element => {
<h1 className="p-20px font-main text-18 uppercase text-text0 dark:text-text0d">
{intl.formatMessage({ id: 'setting.wallet.management' })}
</h1>
<EditableWalletName className="mb-30px" name={walletName} onChange={changeWalletNameHandler} />
<div className="flex flex-col items-center md:flex-row">
<EditableWalletName
name={walletName}
onChange={changeWalletNameHandler}
loading={RD.isPending(renameWalletState)}
/>
{renderRenameWalletError}
<div className="mt-30px flex flex-col items-center md:flex-row">
<div className="flex w-full justify-center md:w-1/2">
<TextButton
className="m-0 min-w-[200px] md:m-20px"
Expand Down
46 changes: 28 additions & 18 deletions src/renderer/components/uielements/wallet/EditableWalletName.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@ import { useForm } from 'react-hook-form'
import { useIntl } from 'react-intl'

import { MAX_WALLET_NAME_CHARS } from '../../../services/wallet/const'
import { BaseButton } from '../button'
// import { LoadingIcon } from '../../icons'
import { BaseButton, TextButton } from '../button'
import { Input } from '../input/Input'

export type Props = {
name: string
loading?: boolean
onChange: (name: string) => void
className?: string
}
Expand All @@ -21,9 +23,10 @@ type FormData = {
}

export const EditableWalletName: React.FC<Props> = (props): JSX.Element => {
const { name, onChange, className = '' } = props
const { name: initialName, onChange, loading = false, className = '' } = props

const [editableName, setEditableName] = useState<O.Option<string>>(O.none)
const [name, setName] = useState<string>(initialName)

const intl = useIntl()

Expand All @@ -39,44 +42,45 @@ export const EditableWalletName: React.FC<Props> = (props): JSX.Element => {
setEditableName(O.some(name))
}
return (
<div
className={`flex cursor-pointer items-center font-main text-[18px] text-text0 dark:text-text0d`}
<TextButton
color="neutral"
uppercase={false}
disabled={loading}
size="large"
loading={loading}
className={`flex ${loading ? 'cursor-not-allowed' : 'cursor-pointer'} items-center text-[18px]`}
onClick={edit}>
{name}
<PencilAltIcon className="dark:text0d ml-[5px] h-[20px] w-[20px] text-turquoise" />
</div>
</TextButton>
)
}, [name])
}, [loading, name])

const renderEditableName = useCallback(
(name: string) => {
const confirm = () => FP.pipe(editableName, O.fold(FP.constVoid, onChange))
const cancel = () => {
reset()
setEditableName(O.none)
}
const submit = ({ name }: FormData) => {
setEditableName(O.none)
onChange(name)
setName(name)
reset()
}

const keyDownHandler = (e: React.KeyboardEvent<HTMLElement>) => {
console.log('key down', e.key)
if (e.key === 'Escape') {
cancel()
}
}

const error = !!errors.name

return (
<form className="items-top flex w-full flex-col items-center" onSubmit={handleSubmit(submit)}>
<div className="flex w-full items-center justify-center">
<Input
// 60px offset of icons width to stay in center
className={`${error ? 'ring-error0' : 'dark:gray1d ring-gray1'} w-full max-w-[380px] text-[18px]
`}
id="name"
className="w-full max-w-[380px]"
size="large"
defaultValue={name}
autoFocus
Expand All @@ -86,8 +90,8 @@ export const EditableWalletName: React.FC<Props> = (props): JSX.Element => {
error={!!errors.name}
onKeyDown={keyDownHandler}
/>
<BaseButton className="ml-[5px] h-[24px] w-[24px] !p-0 text-turquoise" onClick={confirm} type="submit">
<CheckCircleIcon className="" />
<BaseButton className="!p-0 text-turquoise" onClick={handleSubmit(submit)} type="submit">
<CheckCircleIcon className="ml-[5px] h-[24px] w-[24px]" />
</BaseButton>
<XCircleIcon className="ml-[5px] h-[24px] w-[24px] cursor-pointer text-error0" onClick={cancel} />
</div>
Expand All @@ -99,13 +103,14 @@ export const EditableWalletName: React.FC<Props> = (props): JSX.Element => {
</form>
)
},
[editableName, errors.name, handleSubmit, intl, onChange, register, reset]
[errors.name, handleSubmit, intl, onChange, register, reset]
)

return (
<div className={`flex w-full flex-col items-center justify-center ${className}`}>
<h2 className="w-full text-center font-main text-[12px] uppercase text-text2 dark:text-text2d">
{intl.formatMessage({ id: 'wallet.name' })}{' '}
{intl.formatMessage({ id: 'wallet.name' })}
{/* show info about max. chars in editable mode only */}
{FP.pipe(
editableName,
O.fold(
Expand All @@ -118,7 +123,12 @@ export const EditableWalletName: React.FC<Props> = (props): JSX.Element => {
)
)}
</h2>
{FP.pipe(editableName, O.fold(renderName, renderEditableName))}
<div
className={`flex items-center ${loading ? 'opacity-65' : 'opacity-100'} ${
O.isSome(editableName) ? 'w-full' : ''
}`}>
{FP.pipe(editableName, O.fold(renderName, renderEditableName))}
</div>
</div>
)
}
15 changes: 12 additions & 3 deletions src/renderer/hooks/useKeystoreState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ import {
KeystoreState,
Phrase,
RemoveKeystoreWalletHandler,
ChangeKeystoreWalletHandler
ChangeKeystoreWalletHandler,
RenameKeystoreWalletHandler
} from '../services/wallet/types'
import { getPhrase, getWalletName, isLocked } from '../services/wallet/util'

Expand All @@ -19,12 +20,20 @@ export const useKeystoreState = (): {
walletName: O.Option<string>
remove: RemoveKeystoreWalletHandler
change$: ChangeKeystoreWalletHandler
rename$: RenameKeystoreWalletHandler
unlock: (password: string) => Promise<void>
lock: FP.Lazy<void>
locked: boolean
} => {
const {
keystoreService: { keystore$, unlock, lock, removeKeystoreWallet: remove, changeKeystoreWallet: change$ }
keystoreService: {
keystore$,
unlock,
lock,
removeKeystoreWallet: remove,
changeKeystoreWallet: change$,
renameKeystoreWallet: rename$
}
} = useWalletContext()

const state = useObservableState(keystore$, INITIAL_KEYSTORE_STATE)
Expand All @@ -33,5 +42,5 @@ export const useKeystoreState = (): {
const [walletName] = useObservableState(() => FP.pipe(keystore$, RxOp.map(FP.flow(getWalletName))), O.none)
const [locked] = useObservableState(() => FP.pipe(keystore$, RxOp.map(FP.flow(isLocked))), false)

return { state, phrase, walletName, unlock, lock, locked, remove, change$ }
return { state, phrase, walletName, unlock, lock, locked, remove, change$, rename$ }
}
1 change: 1 addition & 0 deletions src/renderer/i18n/de/wallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ const wallet: WalletMessages = {
'wallet.name': 'Wallet Name',
'wallet.name.maxChars': 'Max. {max} Zeichen',
'wallet.name.error.empty': 'Bitte Namen der Wallet angeben',
'wallet.name.error.rename': 'Error beim Umbenennen der Wallet',
'wallet.nav.deposits': 'Einzahlungen',
'wallet.nav.bonds': 'Bonds',
'wallet.nav.poolshares': 'Anteile',
Expand Down
1 change: 1 addition & 0 deletions src/renderer/i18n/en/wallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ const wallet: WalletMessages = {
'wallet.name': 'Wallet name',
'wallet.name.maxChars': 'Max. {max} chars',
'wallet.name.error.empty': 'Please enter a name for your wallet',
'wallet.name.error.rename': 'Error while renaming the wallet',
'wallet.nav.deposits': 'Deposits',
'wallet.nav.bonds': 'Bonds',
'wallet.nav.poolshares': 'Shares',
Expand Down
1 change: 1 addition & 0 deletions src/renderer/i18n/fr/wallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ const wallet: WalletMessages = {
'wallet.name': 'Wallet name - FR',
'wallet.name.maxChars': 'Max. {max} chars - FR',
'wallet.name.error.empty': 'Please enter a name for your wallet - FR',
'wallet.name.error.rename': 'Error while renaming the wallet - FR',
'wallet.nav.deposits': 'Dépots',
'wallet.nav.bonds': 'Cautions',
'wallet.nav.poolshares': 'Quote-part',
Expand Down
1 change: 1 addition & 0 deletions src/renderer/i18n/ru/wallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ const wallet: WalletMessages = {
'wallet.name': 'Wallet name - RU',
'wallet.name.maxChars': 'Max. {max} chars - RU',
'wallet.name.error.empty': 'Please enter a name for your wallet - RU',
'wallet.name.error.rename': 'Error while renaming the wallet - RU',
'wallet.nav.deposits': 'Вклады',
'wallet.nav.bonds': 'Бонды',
'wallet.nav.poolshares': 'Доли',
Expand Down
1 change: 1 addition & 0 deletions src/renderer/i18n/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ type WalletMessageKey =
| 'wallet.name'
| 'wallet.name.maxChars'
| 'wallet.name.error.empty'
| 'wallet.name.error.rename'
| 'wallet.nav.deposits'
| 'wallet.nav.bonds'
| 'wallet.nav.poolshares'
Expand Down
Loading