diff --git a/packages/browser-wallet/src/assets/svgX/checkmark.svg b/packages/browser-wallet/src/assets/svgX/checkmark.svg new file mode 100644 index 000000000..e4db7bbc7 --- /dev/null +++ b/packages/browser-wallet/src/assets/svgX/checkmark.svg @@ -0,0 +1,3 @@ + + + diff --git a/packages/browser-wallet/src/popup/popupX/pages/Accounts/Accounts.scss b/packages/browser-wallet/src/popup/popupX/pages/Accounts/Accounts.scss index 610b83a29..9f6052779 100644 --- a/packages/browser-wallet/src/popup/popupX/pages/Accounts/Accounts.scss +++ b/packages/browser-wallet/src/popup/popupX/pages/Accounts/Accounts.scss @@ -29,8 +29,25 @@ overflow-wrap: anywhere; } + input.editable { + background: none; + color: inherit; + border: none; + font-weight: inherit; + font-size: inherit; + font-family: inherit; + padding: 0; + margin: 0; + + &:focus { + outline: none; + } + } + .button__icon { gap: rem(16px); + height: rem(16px); + width: rem(16px); .label__main { color: $color-white; diff --git a/packages/browser-wallet/src/popup/popupX/pages/Accounts/Accounts.tsx b/packages/browser-wallet/src/popup/popupX/pages/Accounts/Accounts.tsx index 526d51084..7ad3d8c5f 100644 --- a/packages/browser-wallet/src/popup/popupX/pages/Accounts/Accounts.tsx +++ b/packages/browser-wallet/src/popup/popupX/pages/Accounts/Accounts.tsx @@ -1,8 +1,10 @@ -import React from 'react'; +import React, { ChangeEvent, KeyboardEvent, useState } from 'react'; import Plus from '@assets/svgX/plus.svg'; import Arrows from '@assets/svgX/arrows-down-up.svg'; import MagnifyingGlass from '@assets/svgX/magnifying-glass.svg'; import Pencil from '@assets/svgX/pencil-simple.svg'; +import Checkmark from '@assets/svgX/checkmark.svg'; +import Close from '@assets/svgX/close.svg'; import Copy from '@assets/svgX/copy.svg'; import ArrowRight from '@assets/svgX/arrow-right.svg'; import Page from '@popup/popupX/shared/Page'; @@ -16,7 +18,7 @@ import { copyToClipboard } from '@popup/popupX/shared/utils/helpers'; import { useAtomValue } from 'jotai'; import { credentialsAtom } from '@popup/store/account'; import { WalletCredential } from '@shared/storage/types'; -import { useIdentityName } from '@popup/shared/utils/account-helpers'; +import { useIdentityName, useWritableSelectedAccount } from '@popup/shared/utils/account-helpers'; import { useAccountInfo } from '@popup/shared/AccountInfoListenerContext'; import { displayAsCcd } from 'wallet-common-helpers'; @@ -24,6 +26,65 @@ function fallbackAccountName(credentialNumber: number): string { return `Account ${1 + credentialNumber}`; } +type EditableAccountNameProps = { + currentName: string; + fallbackName: string; + onNewName: (newName: string) => void; +}; + +function EditableAccountName({ currentName, fallbackName, onNewName }: EditableAccountNameProps) { + const [isEditingName, setIsEditingName] = useState(false); + const [editedName, setEditedName] = useState(currentName); + // Using editedName instead of currentName to avoid flickering after completing. + const displayName = editedName === '' ? fallbackName : editedName; + const onAbort = () => { + setIsEditingName(false); + setEditedName(currentName); + }; + const onComplete = () => { + onNewName(editedName.trim()); + setIsEditingName(false); + }; + const onEdit = () => { + setEditedName(currentName); + setIsEditingName(true); + }; + const onInputChange = (event: ChangeEvent) => { + setEditedName(event.target.value); + }; + const onKeyUp = (event: KeyboardEvent) => { + if (event.key === 'Enter') { + event.preventDefault(); + onComplete(); + } + }; + if (isEditingName) { + return ( + <> + + + + } onClick={onComplete} /> + } onClick={onAbort} /> + + ); + } + return ( + <> + {displayName} + } onClick={onEdit} /> + + ); +} + type AccountListItemProps = { credential: WalletCredential; }; @@ -34,19 +95,23 @@ function AccountListItem({ credential }: AccountListItemProps) { const navToPrivateKey = () => nav(absoluteRoutes.settings.accounts.privateKey.path); const navToConnectedSites = () => nav(absoluteRoutes.settings.accounts.connectedSites.path); const navToIdCards = () => nav(absoluteRoutes.settings.idCards.path); - const identityName = useIdentityName(credential); const accountInfo = useAccountInfo(credential); - - const accountName = credential.credName !== '' ? credential.credName : fallbackAccountName(credential.credNumber); + const setAccount = useWritableSelectedAccount(credential.address); + const fallbackName = fallbackAccountName(credential.credNumber); + const accountName = credential.credName !== '' ? credential.credName : fallbackName; const { address } = credential; const ccdBalance = accountInfo === undefined ? 'Loading' : displayAsCcd(accountInfo.accountAmount.microCcdAmount, false); + const onNewAccountName = (newName: string) => setAccount({ credName: newName }); return ( - {accountName} - } /> + {address} diff --git a/packages/browser-wallet/src/popup/shared/utils/account-helpers.ts b/packages/browser-wallet/src/popup/shared/utils/account-helpers.ts index 18d4e2816..82bb89e83 100644 --- a/packages/browser-wallet/src/popup/shared/utils/account-helpers.ts +++ b/packages/browser-wallet/src/popup/shared/utils/account-helpers.ts @@ -51,9 +51,11 @@ export function useIdentityName(credential: WalletCredential, fallback?: string) export function useWritableSelectedAccount(accountAddress: string) { const [accounts, setAccounts] = useAtom(writableCredentialAtom); - const setAccount = (update: WalletCredential) => + const setAccount = (update: Partial) => setAccounts( - accounts.map((account) => (account.address === accountAddress ? { ...account, ...update } : account)) + accounts.map((account) => + account.address === accountAddress ? ({ ...account, ...update } as WalletCredential) : account + ) ); return setAccount;