diff --git a/packages/browser-wallet/CHANGELOG.md b/packages/browser-wallet/CHANGELOG.md index e0f38168d..614c55153 100644 --- a/packages/browser-wallet/CHANGELOG.md +++ b/packages/browser-wallet/CHANGELOG.md @@ -2,8 +2,9 @@ ## Unreleased -### Added - +- Remove check for redirectUri when launching identity issuance. This check was causing issues with an upcoming identity provider and seems to provide no value. +- Increased padding for QR code background. In dark mode, QR code not blending with background. +- Added new option to edit account name. Name saved in local storage. Changed name displayed across all BrowserWallet. - Additional error message. Now instead of not showing invalid tokens, they displayed in token list with corresponding error. In order to show, that we found tokens in contract, but they have error. ## 1.5.1 diff --git a/packages/browser-wallet/src/assets/svg/edit-secondary.svg b/packages/browser-wallet/src/assets/svg/edit-secondary.svg new file mode 100644 index 000000000..59d3a1c36 --- /dev/null +++ b/packages/browser-wallet/src/assets/svg/edit-secondary.svg @@ -0,0 +1 @@ + diff --git a/packages/browser-wallet/src/background/credential-deployment.ts b/packages/browser-wallet/src/background/credential-deployment.ts index 717609aa9..97ce6c848 100644 --- a/packages/browser-wallet/src/background/credential-deployment.ts +++ b/packages/browser-wallet/src/background/credential-deployment.ts @@ -46,6 +46,7 @@ async function createAndSendCredential(credIn: CredentialInput): Promise { + const setAccount = useWritableSelectedAccount(account.address); + const [isEditing, setIsEditing] = useState(false); + const [name, setName] = useState(displayNameOrSplitAddress(account)); + + const handleSubmitName = useCallback(() => { + if (isEditing) { + setAccount({ credName: name } as WalletCredential); + setIsEditing(false); + } + }, [name]); + + const keyHandlerEnter: KeyboardEventHandler = (e) => { + if (e.key === 'Enter') { + e.preventDefault(); + handleSubmitName(); + } + }; + + return [ + () => ( +
+ {isEditing ? ( + e.stopPropagation()} + autoFocus + maxLength={ACCOUNT_NAME_MAX_LENGTH} + fixedWidth={ACCOUNT_NAME_INPUT_WIDTH} + /> + ) : ( + displayNameOrSplitAddress(account) + )} +
+ ), + () => ( + { + e.stopPropagation(); + e.preventDefault(); + handleSubmitName(); + setIsEditing(!isEditing); + }} + > + {isEditing ? : } + + ), + ]; +}; + export type Account = { address: string }; type ItemProps = { @@ -36,6 +100,7 @@ function BakerOrDelegatorIcon({ accountInfo, className }: { accountInfo: Account function AccountListItem({ account, checked, selected }: ItemProps) { const accountInfo = useAccountInfo(account); + const [EditableName, EditNameIcon] = useEditableAccountName(account); const totalBalance = useMemo( () => accountInfo?.accountAmount?.microCcdAmount || 0n, [accountInfo?.accountAmount.microCcdAmount] @@ -46,9 +111,7 @@ function AccountListItem({ account, checked, selected }: ItemProps) {
- {/* TODO add account name */} - {displaySplitAddress(account.address)}{' '} - {selected && } + {selected && }
{accountInfo && } e.stopPropagation()} tabIndex={-1} /> +
{identityName}
{displayAsCcd(totalBalance)}
@@ -86,7 +150,7 @@ const AccountList = forwardRef(({ className, onSelect }, getKey={(a) => a.address} newText={t('accountList.new')} ref={ref} - searchableKeys={['address']} + searchableKeys={['address', 'credName']} > {(a, checked) => } diff --git a/packages/browser-wallet/src/popup/page-layouts/MainLayout/EntityList/EntityList.scss b/packages/browser-wallet/src/popup/page-layouts/MainLayout/EntityList/EntityList.scss index e522eae18..98cbfad99 100644 --- a/packages/browser-wallet/src/popup/page-layouts/MainLayout/EntityList/EntityList.scss +++ b/packages/browser-wallet/src/popup/page-layouts/MainLayout/EntityList/EntityList.scss @@ -120,4 +120,18 @@ $entity-list-top-height: rem(25px); fill: $color-text; } } + + &__edit { + width: rem(18px); + height: rem(18px); + top: rem(15px); + + path { + fill: $color-text; + } + + svg { + transform: scale(1.2); + } + } } diff --git a/packages/browser-wallet/src/popup/pages/Account/AccountDetails/AccountDetails.tsx b/packages/browser-wallet/src/popup/pages/Account/AccountDetails/AccountDetails.tsx index 85c5501b8..42ee785cb 100644 --- a/packages/browser-wallet/src/popup/pages/Account/AccountDetails/AccountDetails.tsx +++ b/packages/browser-wallet/src/popup/pages/Account/AccountDetails/AccountDetails.tsx @@ -2,7 +2,7 @@ import clsx from 'clsx'; import React, { useEffect, useState } from 'react'; import { displayAsCcd, getPublicAccountAmounts, PublicAccountAmounts } from 'wallet-common-helpers'; import { useTranslation } from 'react-i18next'; -import { displaySplitAddress, useIdentityName } from '@popup/shared/utils/account-helpers'; +import { displayNameOrSplitAddress, useIdentityName } from '@popup/shared/utils/account-helpers'; import VerifiedIcon from '@assets/svg/verified-stamp.svg'; import { CreationStatus, WalletCredential } from '@shared/storage/types'; import { useAccountInfo } from '@popup/shared/AccountInfoListenerContext'; @@ -68,7 +68,7 @@ export default function AccountDetails({ expanded, account, className }: Props) return (
-
{displaySplitAddress(account.address)}
+
{displayNameOrSplitAddress(account)}
{identityName}
diff --git a/packages/browser-wallet/src/popup/pages/Account/DisplayAddress/DisplayAddress.scss b/packages/browser-wallet/src/popup/pages/Account/DisplayAddress/DisplayAddress.scss index 605e00c68..d1ae4c46f 100644 --- a/packages/browser-wallet/src/popup/pages/Account/DisplayAddress/DisplayAddress.scss +++ b/packages/browser-wallet/src/popup/pages/Account/DisplayAddress/DisplayAddress.scss @@ -2,8 +2,10 @@ $display-address-copy-left-margin: rem(5px); .display-address { &__address-qr { - height: rem(130px); - width: rem(130px); + height: rem(140px); + width: rem(140px); + padding: rem(5px); + background-color: $color-white; } &__address-text { diff --git a/packages/browser-wallet/src/popup/pages/Allowlist/AllowlistEntryView.tsx b/packages/browser-wallet/src/popup/pages/Allowlist/AllowlistEntryView.tsx index d8df80839..2edced699 100644 --- a/packages/browser-wallet/src/popup/pages/Allowlist/AllowlistEntryView.tsx +++ b/packages/browser-wallet/src/popup/pages/Allowlist/AllowlistEntryView.tsx @@ -1,5 +1,5 @@ import AccountInfoListenerContextProvider, { useAccountInfo } from '@popup/shared/AccountInfoListenerContext'; -import { displaySplitAddress, useIdentityName } from '@popup/shared/utils/account-helpers'; +import { displayNameOrSplitAddress, useIdentityName } from '@popup/shared/utils/account-helpers'; import React, { useMemo, useState } from 'react'; import { Checkbox } from '@popup/shared/Form/Checkbox'; import { WalletCredential } from '@shared/storage/types'; @@ -25,7 +25,7 @@ function AccountListItem({ account, checked, onToggleChecked }: ItemProps) { return (
-
{displaySplitAddress(account.address)}
+
{displayNameOrSplitAddress(account)}
{ diff --git a/packages/browser-wallet/src/popup/pages/ConnectionRequest/ConnectionRequest.tsx b/packages/browser-wallet/src/popup/pages/ConnectionRequest/ConnectionRequest.tsx index 868325a20..70fa38f33 100644 --- a/packages/browser-wallet/src/popup/pages/ConnectionRequest/ConnectionRequest.tsx +++ b/packages/browser-wallet/src/popup/pages/ConnectionRequest/ConnectionRequest.tsx @@ -1,6 +1,6 @@ import { absoluteRoutes } from '@popup/constants/routes'; import { fullscreenPromptContext } from '@popup/page-layouts/FullscreenPromptLayout'; -import { selectedAccountAtom, storedAllowlistAtom } from '@popup/store/account'; +import { selectedCredentialAtom, storedAllowlistAtom } from '@popup/store/account'; import { sessionPasscodeAtom } from '@popup/store/settings'; import { useAtom, useAtomValue } from 'jotai'; import React, { useContext, useEffect, useState } from 'react'; @@ -9,7 +9,7 @@ import { useLocation, Navigate } from 'react-router-dom'; import ExternalRequestLayout from '@popup/page-layouts/ExternalRequestLayout'; import Button from '@popup/shared/Button'; import { displayUrl } from '@popup/shared/utils/string-helpers'; -import { displaySplitAddress } from '@popup/shared/utils/account-helpers'; +import { displayNameOrSplitAddress } from '@popup/shared/utils/account-helpers'; import { handleAllowlistEntryUpdate } from '../Allowlist/util'; type Props = { @@ -21,7 +21,7 @@ export default function ConnectionRequest({ onAllow, onReject }: Props) { const { state } = useLocation(); const { t } = useTranslation('connectionRequest'); const { onClose, withClose } = useContext(fullscreenPromptContext); - const selectedAccount = useAtomValue(selectedAccountAtom); + const selectedAccount = useAtomValue(selectedCredentialAtom); const [allowlistLoading, setAllowlist] = useAtom(storedAllowlistAtom); const allowlist = allowlistLoading.value; const passcode = useAtomValue(sessionPasscodeAtom); @@ -56,7 +56,7 @@ export default function ConnectionRequest({ onAllow, onReject }: Props) {
{t('waiting')}
-

{t('title', { dApp: urlDisplay, account: displaySplitAddress(selectedAccount) })}

+

{t('title', { dApp: urlDisplay, account: displayNameOrSplitAddress(selectedAccount) })}

{t('descriptionP1', { dApp: urlDisplay })}

{t('descriptionP2')}

@@ -69,7 +69,7 @@ export default function ConnectionRequest({ onAllow, onReject }: Props) { disabled={connectButtonDisabled} onClick={() => { setConnectButtonDisabled(true); - connectAccount(selectedAccount, new URL(url).origin).then(withClose(onAllow)); + connectAccount(selectedAccount.address, new URL(url).origin).then(withClose(onAllow)); }} > {t('actions.connect')} diff --git a/packages/browser-wallet/src/popup/pages/Recovery/RecoveryFinish.tsx b/packages/browser-wallet/src/popup/pages/Recovery/RecoveryFinish.tsx index eee3b88e9..117dabcaf 100644 --- a/packages/browser-wallet/src/popup/pages/Recovery/RecoveryFinish.tsx +++ b/packages/browser-wallet/src/popup/pages/Recovery/RecoveryFinish.tsx @@ -7,7 +7,7 @@ import { identitiesAtom } from '@popup/store/identity'; import Button from '@popup/shared/Button'; import { absoluteRoutes } from '@popup/constants/routes'; import { noOp, displayAsCcd } from 'wallet-common-helpers'; -import { displaySplitAddress } from '@popup/shared/utils/account-helpers'; +import { displayNameOrSplitAddress } from '@popup/shared/utils/account-helpers'; import { IdentityIdentifier, BackgroundResponseStatus, RecoveryBackgroundResponse } from '@shared/utils/types'; import { fullscreenPromptContext } from '@popup/page-layouts/FullscreenPromptLayout'; import PageHeader from '@popup/shared/PageHeader'; @@ -69,7 +69,7 @@ export function DisplaySuccess({ added }: Props) {

{addedAccounts.filter(isIdentityOfCredential(identity)).map((cred) => (
-

{displaySplitAddress(cred.address)}

+

{displayNameOrSplitAddress(cred)}

{displayAsCcd( added.accounts.find((pair) => pair.address === cred.address)?.balance || diff --git a/packages/browser-wallet/src/popup/pages/Web3ProofRequest/AccountStatement.tsx b/packages/browser-wallet/src/popup/pages/Web3ProofRequest/AccountStatement.tsx index 8e7e53f00..d20fafcdb 100644 --- a/packages/browser-wallet/src/popup/pages/Web3ProofRequest/AccountStatement.tsx +++ b/packages/browser-wallet/src/popup/pages/Web3ProofRequest/AccountStatement.tsx @@ -6,7 +6,7 @@ import { RevealStatementV2, StatementTypes, } from '@concordium/web-sdk'; -import { displaySplitAddress, useIdentityName, useIdentityOf } from '@popup/shared/utils/account-helpers'; +import { displayNameOrSplitAddress, useIdentityName, useIdentityOf } from '@popup/shared/utils/account-helpers'; import { useDisplayAttributeValue } from '@popup/shared/utils/identity-helpers'; import { ConfirmedIdentity, WalletCredential } from '@shared/storage/types'; import { getCredentialIdFromSubjectDID } from '@shared/utils/verifiable-credential-helpers'; @@ -29,7 +29,7 @@ export function DisplayAccount({ option }: { option: WalletCredential }) { return (

-
{displaySplitAddress(option.address)}
+
{displayNameOrSplitAddress(option)}
{identityName}
diff --git a/packages/browser-wallet/src/popup/shared/Form/InlineInput/InlineInput.tsx b/packages/browser-wallet/src/popup/shared/Form/InlineInput/InlineInput.tsx index 9cb831022..b2b2981bb 100644 --- a/packages/browser-wallet/src/popup/shared/Form/InlineInput/InlineInput.tsx +++ b/packages/browser-wallet/src/popup/shared/Form/InlineInput/InlineInput.tsx @@ -5,11 +5,15 @@ import { scaleFieldWidth } from '../../utils/html-helpers'; import { CommonFieldProps, RequiredControlledFieldProps } from '../common/types'; import { makeControlled } from '../common/utils'; -type Props = Pick, 'type' | 'className' | 'autoFocus'> & +type Props = Pick< + InputHTMLAttributes, + 'type' | 'className' | 'autoFocus' | 'onKeyUp' | 'onMouseUp' | 'maxLength' +> & RequiredControlledFieldProps & CommonFieldProps & { fallbackValue?: string; fallbackOnError?: boolean; + fixedWidth?: number; }; export function InlineInput({ @@ -20,6 +24,7 @@ export function InlineInput({ fallbackOnError = false, onChange = noOp, onBlur = noOp, + fixedWidth, error, ...props }: Props) { @@ -27,7 +32,9 @@ export function InlineInput({ const [innerValue, setInnerValue] = useState(value ?? fallbackValue); useLayoutEffect(() => { - scaleFieldWidth(ref.current); + if (!fixedWidth) { + scaleFieldWidth(ref.current); + } }, [innerValue]); useUpdateEffect(() => { @@ -53,7 +60,7 @@ export function InlineInput({ autoComplete="off" spellCheck="false" {...props} - style={{ width: 6 }} // To prevent initial UI jitter. + style={{ width: fixedWidth || 6 }} // To prevent initial UI jitter. /> ); } 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 cb7e49383..6d0564e0b 100644 --- a/packages/browser-wallet/src/popup/shared/utils/account-helpers.ts +++ b/packages/browser-wallet/src/popup/shared/utils/account-helpers.ts @@ -1,6 +1,6 @@ -import { credentialsAtom, selectedAccountAtom } from '@popup/store/account'; +import { credentialsAtom, selectedAccountAtom, writableCredentialAtom } from '@popup/store/account'; import { networkConfigurationAtom } from '@popup/store/settings'; -import { useAtomValue } from 'jotai'; +import { useAtom, useAtomValue } from 'jotai'; import { useEffect, useMemo, useState } from 'react'; import { identitiesAtom } from '@popup/store/identity'; import { AccountInfo, ConcordiumHdWallet } from '@concordium/web-sdk'; @@ -10,7 +10,10 @@ import { isIdentityOfCredential } from '@shared/utils/identity-helpers'; import { getNextUnused } from '@shared/utils/number-helpers'; import { useDecryptedSeedPhrase } from './seed-phrase-helpers'; -export const displaySplitAddress = (address: string) => `${address.slice(0, 4)}...${address.slice(address.length - 4)}`; +export const displayNameOrSplitAddress = (account: WalletCredential | undefined) => { + const { credName, address } = account || { address: '' }; + return credName || `${address.slice(0, 4)}...${address.slice(address.length - 4)}`; +}; export function useIdentityOf(cred?: WalletCredential) { const identities = useAtomValue(identitiesAtom); @@ -40,6 +43,16 @@ export function useIdentityName(credential: WalletCredential, fallback?: string) return identityName; } +export function useWritableSelectedAccount(accountAddress: string) { + const [accounts, setAccounts] = useAtom(writableCredentialAtom); + const setAccount = (update: WalletCredential) => + setAccounts( + accounts.map((account) => (account.address === accountAddress ? { ...account, ...update } : account)) + ); + + return setAccount; +} + export function useCredential(accountAddress?: string) { const credentials = useAtomValue(credentialsAtom); diff --git a/packages/browser-wallet/src/popup/store/account.ts b/packages/browser-wallet/src/popup/store/account.ts index aa21acf2b..f3cfcf273 100644 --- a/packages/browser-wallet/src/popup/store/account.ts +++ b/packages/browser-wallet/src/popup/store/account.ts @@ -13,6 +13,13 @@ export const credentialsAtomWithLoading = atomWithChromeStorage v.value); +export const writableCredentialAtom = atom( + (get) => get(credentialsAtom), + async (_, set, update) => { + await set(credentialsAtomWithLoading, update); + } +); + export const storedConnectedSitesAtom = atomWithChromeStorage>( ChromeStorageKey.ConnectedSites, {}, @@ -34,6 +41,12 @@ export const selectedAccountAtom = atom((get) => { + const selectedAccount = get(selectedAccountAtom); + const credentials = get(credentialsAtom); + return credentials.find((cred) => cred.address === selectedAccount); +}); + export const accountsAtom = selectAtom(credentialsAtom, (cs) => cs.map((c) => c.address)); export const accountsPerIdentityAtom = selectAtom(credentialsAtom, (cs) => { diff --git a/packages/browser-wallet/src/shared/storage/types.ts b/packages/browser-wallet/src/shared/storage/types.ts index 0c58130f1..631c2942e 100644 --- a/packages/browser-wallet/src/shared/storage/types.ts +++ b/packages/browser-wallet/src/shared/storage/types.ts @@ -147,6 +147,7 @@ export interface BaseCredential { address: string; credId: string; credNumber: number; + credName: string; status: CreationStatus; identityIndex: number; providerIndex: number;