diff --git a/packages/browser-wallet/src/popup/popupX/pages/IdCards/IdCards.tsx b/packages/browser-wallet/src/popup/popupX/pages/IdCards/IdCards.tsx index aa4be8cc6..6695bce71 100644 --- a/packages/browser-wallet/src/popup/popupX/pages/IdCards/IdCards.tsx +++ b/packages/browser-wallet/src/popup/popupX/pages/IdCards/IdCards.tsx @@ -6,7 +6,7 @@ import { useTranslation } from 'react-i18next'; import { identitiesAtom } from '@popup/store/identity'; import { useAtom } from 'jotai'; import { CreationStatus } from '@shared/storage/types'; -import { ConfirmedIdCard } from '@popup/popupX/shared/IdCard'; +import { ConfirmedIdCard, RejectedIdCard } from '@popup/popupX/shared/IdCard'; export default function IdCards() { const { t } = useTranslation('x', { keyPrefix: 'idCards' }); @@ -35,7 +35,13 @@ export default function IdCards() { case CreationStatus.Pending: return null; case CreationStatus.Rejected: - return null; + return ( + + ); default: return <>Unsupported; } diff --git a/packages/browser-wallet/src/popup/popupX/pages/Onboarding/IdSubmitted.tsx b/packages/browser-wallet/src/popup/popupX/pages/Onboarding/IdSubmitted.tsx index 3f705e4e2..00bb1d88f 100644 --- a/packages/browser-wallet/src/popup/popupX/pages/Onboarding/IdSubmitted.tsx +++ b/packages/browser-wallet/src/popup/popupX/pages/Onboarding/IdSubmitted.tsx @@ -5,9 +5,8 @@ import Page from '@popup/popupX/shared/Page'; import Text from '@popup/popupX/shared/Text'; import { useTranslation } from 'react-i18next'; import IdCard from '@popup/popupX/shared/IdCard'; -import { IdCardAttributeInfo } from '@popup/popupX/shared/IdCard/IdCard'; -const rowsIdInfo: IdCardAttributeInfo[] = [ +const rowsIdInfo = [ { key: 'Identity document type', value: 'Drivers licence' }, { key: 'Identity document number', value: 'BXM680515' }, ]; @@ -21,7 +20,16 @@ export default function IdSubmitted() { {t('idSubmitInfo')} - + + + {rowsIdInfo.map((info) => ( + + {info.key} + {info.value} + + ))} + + navToNext()} /> diff --git a/packages/browser-wallet/src/popup/popupX/pages/Restore/RestoreResult.tsx b/packages/browser-wallet/src/popup/popupX/pages/Restore/RestoreResult.tsx index 065df7700..cb8b000f1 100644 --- a/packages/browser-wallet/src/popup/popupX/pages/Restore/RestoreResult.tsx +++ b/packages/browser-wallet/src/popup/popupX/pages/Restore/RestoreResult.tsx @@ -5,7 +5,6 @@ import Page from '@popup/popupX/shared/Page'; import Text from '@popup/popupX/shared/Text'; import { useTranslation } from 'react-i18next'; import IdCard from '@popup/popupX/shared/IdCard'; -import { IdCardAttributeInfo } from '@popup/popupX/shared/IdCard/IdCard'; function AccountLink({ account, balance }: { account: string; balance: string }) { return ( @@ -17,7 +16,7 @@ function AccountLink({ account, balance }: { account: string; balance: string }) ); } -const rowsIdInfo: IdCardAttributeInfo[] = [ +const rowsIdInfo = [ { key: '', value: }, { key: '', value: }, ]; @@ -30,7 +29,17 @@ export default function RestoreResult() { {t('recoveredIds')} - + + + + {rowsIdInfo.map((info) => ( + + {info.key} + {info.value} + + ))} + + diff --git a/packages/browser-wallet/src/popup/popupX/shared/IdCard/ConfirmedIdCard.tsx b/packages/browser-wallet/src/popup/popupX/shared/IdCard/ConfirmedIdCard.tsx index 93dbe9e5d..ef6232d3a 100644 --- a/packages/browser-wallet/src/popup/popupX/shared/IdCard/ConfirmedIdCard.tsx +++ b/packages/browser-wallet/src/popup/popupX/shared/IdCard/ConfirmedIdCard.tsx @@ -1,4 +1,4 @@ -import React, { useMemo } from 'react'; +import React, { ReactNode, useMemo } from 'react'; import { useDisplayAttributeValue, useGetAttributeName } from '@popup/shared/utils/identity-helpers'; import { identityProvidersAtom } from '@popup/store/identity'; import { useAtomValue } from 'jotai'; @@ -8,7 +8,10 @@ import { compareAttributes, displayAsCcd } from 'wallet-common-helpers'; import { credentialsAtomWithLoading } from '@popup/store/account'; import { AttributeKey } from '@concordium/web-sdk'; import { displayNameAndSplitAddress } from '@popup/shared/utils/account-helpers'; -import IdCard, { IdCardAccountInfo, IdCardAttributeInfo } from './IdCard'; +import { useTranslation } from 'react-i18next'; +import Text from '@popup/popupX/shared/Text'; +import IdCard from './IdCard'; +import useEditableName from './useEditableName'; function CcdBalance({ credential }: { credential: WalletCredential }) { const accountInfo = useAccountInfo(credential); @@ -18,9 +21,15 @@ function CcdBalance({ credential }: { credential: WalletCredential }) { return <>{balance}; } -function fallbackIdentityName(index: number) { - return `Identity ${index + 1}`; -} +type IdCardAttributeInfo = { + key: string; + value: ReactNode; +}; + +type IdCardAccountInfo = { + address: string; + amount: ReactNode; +}; export type ConfirmedIdentityProps = { /** Identity to show. */ @@ -39,12 +48,14 @@ export default function ConfirmedIdCard({ shownAttributes, onNewName, }: ConfirmedIdentityProps) { + const { t } = useTranslation('x', { keyPrefix: 'sharedX' }); + const editable = useEditableName(identity, onNewName); const displayAttribute = useDisplayAttributeValue(); const getAttributeName = useGetAttributeName(); const providers = useAtomValue(identityProvidersAtom); const credentials = useAtomValue(credentialsAtomWithLoading); const provider = providers.find((p) => p.ipInfo.ipIdentity === identity.providerIndex); - const providerName = provider?.ipInfo.ipDescription.name ?? 'Unknown'; + const idProviderName = provider?.ipInfo.ipDescription.name ?? 'Unknown'; const rowsIdInfo: IdCardAttributeInfo[] = useMemo( () => Object.entries(identity.idObject.value.attributeList.chosenAttributes) @@ -76,12 +87,30 @@ export default function ConfirmedIdCard({ }, [credentials, identity, hideAccounts]); return ( + title={editable.value} + titleAction={editable.actions} + subtitle={t('idCard.verifiedBy', { idProviderName })} + > + {rowsIdInfo && ( + + {rowsIdInfo.map((info) => ( + + {info.key} + {info.value} + + ))} + + )} + {rowsConnectedAccounts && ( + + {rowsConnectedAccounts.map((account) => ( + + {account.address} + {account.amount} + + ))} + + )} + ); } diff --git a/packages/browser-wallet/src/popup/popupX/shared/IdCard/IdCard.scss b/packages/browser-wallet/src/popup/popupX/shared/IdCard/IdCard.scss index 5f5ca9bd4..b54a88223 100644 --- a/packages/browser-wallet/src/popup/popupX/shared/IdCard/IdCard.scss +++ b/packages/browser-wallet/src/popup/popupX/shared/IdCard/IdCard.scss @@ -54,4 +54,18 @@ } } } + + .card-x.rejected { + justify-content: center; + align-items: center; + + svg.icon-rejected { + width: rem(48px); + height: rem(48px); + + path { + fill: $color-red-attention; + } + } + } } diff --git a/packages/browser-wallet/src/popup/popupX/shared/IdCard/IdCard.tsx b/packages/browser-wallet/src/popup/popupX/shared/IdCard/IdCard.tsx index cb901de54..541a43549 100644 --- a/packages/browser-wallet/src/popup/popupX/shared/IdCard/IdCard.tsx +++ b/packages/browser-wallet/src/popup/popupX/shared/IdCard/IdCard.tsx @@ -1,74 +1,26 @@ import React, { ReactNode } from 'react'; import Card from '@popup/popupX/shared/Card'; import Text from '@popup/popupX/shared/Text'; -import Button from '@popup/popupX/shared/Button'; -import useEditableValue from '@popup/popupX/shared/EditableValue'; -import { useTranslation } from 'react-i18next'; -export type IdCardAttributeInfo = { - key: string; - value: string | ReactNode; +export type IdCardBaseProps = { + title: ReactNode; + subtitle: ReactNode; + titleAction?: ReactNode; + children?: ReactNode; }; -export type IdCardAccountInfo = { - address: string; - amount: ReactNode; -}; - -export type IdCardProps = { - idProviderName: string; - identityName: string; - rowsIdInfo?: IdCardAttributeInfo[]; - rowsConnectedAccounts?: IdCardAccountInfo[]; - onNewName?: (newName: string) => void; - identityNameFallback?: string; -}; - -export default function IdCard({ - idProviderName, - identityName, - rowsIdInfo, - rowsConnectedAccounts, - onNewName, - identityNameFallback, -}: IdCardProps) { - const editable = useEditableValue(identityName, identityNameFallback ?? '', onNewName ?? (() => {})); - const { t } = useTranslation('x', { keyPrefix: 'sharedX' }); - +export default function IdCard({ title, subtitle, titleAction, children }: IdCardBaseProps) { return ( - {editable.value} - {editable.isEditing ? ( - <> - - - - ) : ( - onNewName && - )} + {title} + {titleAction} - {t('idCard.verifiedBy', { idProviderName })} - {rowsIdInfo && ( - - {rowsIdInfo.map((info) => ( - - {info.key} - {info.value} - - ))} - - )} - {rowsConnectedAccounts && ( - - {rowsConnectedAccounts.map((account) => ( - - {account.address} - {account.amount} - - ))} - - )} + {subtitle} + {children} ); } + +IdCard.Content = Card; +IdCard.ContentRow = Card.Row; diff --git a/packages/browser-wallet/src/popup/popupX/shared/IdCard/RejectedIdCard.tsx b/packages/browser-wallet/src/popup/popupX/shared/IdCard/RejectedIdCard.tsx new file mode 100644 index 000000000..593b543d3 --- /dev/null +++ b/packages/browser-wallet/src/popup/popupX/shared/IdCard/RejectedIdCard.tsx @@ -0,0 +1,29 @@ +import React from 'react'; +import Close from '@assets/svgX/close.svg'; +import { identityProvidersAtom } from '@popup/store/identity'; +import { useAtomValue } from 'jotai'; +import { RejectedIdentity } from '@shared/storage/types'; +import { useTranslation } from 'react-i18next'; +import Text from '@popup/popupX/shared/Text'; +import IdCard from './IdCard'; + +export type RejectedIdentityProps = { + /** Identity to show. */ + identity: RejectedIdentity; +}; + +export default function RejectedIdCard({ identity }: RejectedIdentityProps) { + const { t } = useTranslation('x', { keyPrefix: 'sharedX' }); + const providers = useAtomValue(identityProvidersAtom); + const provider = providers.find((p) => p.ipInfo.ipIdentity === identity.providerIndex); + const idProviderName = provider?.ipInfo.ipDescription.name ?? 'Unknown'; + return ( + + + + {t('idCard.itentityRejected')} + {identity.error} + + + ); +} diff --git a/packages/browser-wallet/src/popup/popupX/shared/IdCard/index.ts b/packages/browser-wallet/src/popup/popupX/shared/IdCard/index.ts index b8ec25862..4c637214b 100644 --- a/packages/browser-wallet/src/popup/popupX/shared/IdCard/index.ts +++ b/packages/browser-wallet/src/popup/popupX/shared/IdCard/index.ts @@ -1,2 +1,3 @@ export { default } from './IdCard'; export { default as ConfirmedIdCard } from './ConfirmedIdCard'; +export { default as RejectedIdCard } from './RejectedIdCard'; diff --git a/packages/browser-wallet/src/popup/popupX/shared/IdCard/useEditableName.tsx b/packages/browser-wallet/src/popup/popupX/shared/IdCard/useEditableName.tsx new file mode 100644 index 000000000..df15b7160 --- /dev/null +++ b/packages/browser-wallet/src/popup/popupX/shared/IdCard/useEditableName.tsx @@ -0,0 +1,28 @@ +import React from 'react'; +import { BaseIdentity } from '@shared/storage/types'; +import { useTranslation } from 'react-i18next'; +import useEditableValue from '@popup/popupX/shared/EditableValue'; +import Button from '@popup/popupX/shared/Button'; + +function fallbackIdentityName(index: number) { + return `Identity ${index + 1}`; +} + +export type NewNameHandler = (name: string) => void; + +export default function useEditableName(identity: BaseIdentity, onNewName?: NewNameHandler) { + const { t } = useTranslation('x', { keyPrefix: 'sharedX' }); + const editable = useEditableValue(identity.name, fallbackIdentityName(identity.index), onNewName ?? (() => {})); + const editNameAction = editable.isEditing ? ( + <> + + + + ) : ( + onNewName && + ); + return { + value: editable.value, + actions: editNameAction, + }; +} diff --git a/packages/browser-wallet/src/popup/popupX/shared/i18n/en.ts b/packages/browser-wallet/src/popup/popupX/shared/i18n/en.ts index a104419a4..e5accb532 100644 --- a/packages/browser-wallet/src/popup/popupX/shared/i18n/en.ts +++ b/packages/browser-wallet/src/popup/popupX/shared/i18n/en.ts @@ -39,6 +39,8 @@ const t = { abort: 'Abort', }, verifiedBy: 'Verified by {{idProviderName}}', + rejectedBy: 'Rejected by {{idProviderName}}', + itentityRejected: 'This identity has been rejected', }, };