Skip to content

Commit

Permalink
feature: merge recent recipients with contacts list (#886)
Browse files Browse the repository at this point in the history
* added contact book to the wallet

* added contact sort and fixed the issue with updating contact

* merged recent recipient with contacts

* list height fix

---------

Co-authored-by: ost-ptk <[email protected]>
Co-authored-by: Vynnyk Dmytro <[email protected]>
  • Loading branch information
3 people authored Dec 13, 2023
1 parent 50526b4 commit ce0b3dc
Show file tree
Hide file tree
Showing 8 changed files with 148 additions and 44 deletions.
10 changes: 8 additions & 2 deletions src/apps/popup/pages/transfer-nft/content.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useMemo } from 'react';
import React, { useMemo, useState } from 'react';
import { Trans, useTranslation } from 'react-i18next';
import styled from 'styled-components';
import { UseFormReturn, useWatch } from 'react-hook-form';
Expand Down Expand Up @@ -53,6 +53,8 @@ export const TransferNftContent = ({
amountForm,
haveReverseOwnerLookUp
}: TransferNftContentProps) => {
const [recipientName, setRecipientName] = useState('');

const { t } = useTranslation();
const location = useTypedLocation();

Expand Down Expand Up @@ -137,7 +139,11 @@ export const TransferNftContent = ({
</Typography>
</ParagraphContainer>
)}
<RecipientDropdownInput recipientForm={recipientForm} />
<RecipientDropdownInput
recipientForm={recipientForm}
recipientName={recipientName}
setRecipientName={setRecipientName}
/>
<VerticalSpaceContainer top={SpacingSize.XXL}>
<Input
label={t('Transaction fee')}
Expand Down
5 changes: 4 additions & 1 deletion src/apps/popup/pages/transfer/confirm-step.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,16 @@ interface ConfirmStepProps {
symbol: string | null;
isCSPR: boolean;
paymentAmount: string;
recipientName?: string;
}
export const ConfirmStep = ({
recipientPublicKey,
amount,
balance,
symbol,
isCSPR,
paymentAmount
paymentAmount,
recipientName
}: ConfirmStepProps) => {
const { t } = useTranslation();

Expand Down Expand Up @@ -123,6 +125,7 @@ export const ConfirmStep = ({
recipientLabel={recipientLabel}
publicKey={recipientPublicKey}
showFullPublicKey
name={recipientName}
/>
</VerticalSpaceContainer>
<List
Expand Down
7 changes: 6 additions & 1 deletion src/apps/popup/pages/transfer/content.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from 'react';
import React, { useState } from 'react';
import { UseFormReturn } from 'react-hook-form';

import {
Expand Down Expand Up @@ -33,6 +33,8 @@ export const TransferPageContent = ({
symbol,
paymentAmount
}: TransferPageContentProps) => {
const [recipientName, setRecipientName] = useState('');

const isCSPR = symbol === 'CSPR';

switch (transferStep) {
Expand All @@ -42,6 +44,8 @@ export const TransferPageContent = ({
recipientForm={recipientForm}
symbol={symbol}
balance={balance}
setRecipientName={setRecipientName}
recipientName={recipientName}
/>
);
}
Expand All @@ -59,6 +63,7 @@ export const TransferPageContent = ({
symbol={symbol}
isCSPR={isCSPR}
paymentAmount={paymentAmount}
recipientName={recipientName}
/>
);
}
Expand Down
12 changes: 10 additions & 2 deletions src/apps/popup/pages/transfer/recipient-step.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,16 @@ interface RecipientStepProps {
recipientForm: UseFormReturn<TransferRecipientFormValues>;
balance: string | null;
symbol: string | null;
setRecipientName: React.Dispatch<React.SetStateAction<string>>;
recipientName: string;
}

export const RecipientStep = ({
recipientForm,
balance,
symbol
symbol,
setRecipientName,
recipientName
}: RecipientStepProps) => {
const { t } = useTranslation();

Expand All @@ -36,7 +40,11 @@ export const RecipientStep = ({
</ParagraphContainer>
<ActiveAccountPlate label="From" balance={balance} symbol={symbol} />

<RecipientDropdownInput recipientForm={recipientForm} />
<RecipientDropdownInput
recipientForm={recipientForm}
setRecipientName={setRecipientName}
recipientName={recipientName}
/>
</ContentContainer>
);
};
3 changes: 3 additions & 0 deletions src/assets/icons/contact.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 3 additions & 1 deletion src/libs/ui/components/input/input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,9 @@ const StyledInput = styled('input')<InputProps>(({ theme }) => ({
border: '0'
},
'::placeholder': {
color: theme.color.contentSecondary
color: theme.color.contentSecondary,
fontFamily: theme.typography.fontFamily.primary,
fontSize: '1.5rem'
},
// Hiding the password reveal button in the MS Edge
// https://github.com/make-software/casper-wallet/issues/547
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,20 @@ import { TransferRecipientFormValues } from '@libs/ui/forms/transfer';
import { useClickAway } from '@libs/ui/hooks/use-click-away';
import { selectVaultActiveAccount } from '@background/redux/vault/selectors';
import { TransferNftRecipientFormValues } from '@libs/ui/forms/transfer-nft';
import { selectAllContacts } from '@background/redux/contacts/selectors';

interface RecipientDropdownInputProps {
recipientForm: UseFormReturn<
TransferRecipientFormValues | TransferNftRecipientFormValues
>;
setRecipientName: React.Dispatch<React.SetStateAction<string>>;
recipientName: string;
}

export const RecipientDropdownInput = ({
recipientForm
recipientForm,
setRecipientName,
recipientName
}: RecipientDropdownInputProps) => {
const [
isOpenRecentRecipientPublicKeysList,
Expand All @@ -32,6 +37,7 @@ export const RecipientDropdownInput = ({
selectRecentRecipientPublicKeys
);
const activeAccount = useSelector(selectVaultActiveAccount);
const contacts = useSelector(selectAllContacts);

const { register, formState, setValue, control, trigger } = recipientForm;
const { errors } = formState;
Expand All @@ -57,24 +63,49 @@ export const RecipientDropdownInput = ({
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

const optionsRow = useMemo(
const recentRecipient = useMemo(
() =>
recentRecipientPublicKeys
.filter(item => item !== activeAccount?.publicKey)
.map((item, index) => ({
publicKey: item,
id: index
}))
.filter(item => item.publicKey.includes(inputValue || '')),
[activeAccount?.publicKey, inputValue, recentRecipientPublicKeys]
recentRecipientPublicKeys.map(key => {
const contact = contacts.find(contact => contact.publicKey === key);
if (contact) {
return {
name: contact.name,
publicKey: key,
isContact: false
};
}
return {
name: '',
publicKey: key,
isContact: false
};
}),
[contacts, recentRecipientPublicKeys]
);

const getUniquePublicKeyItemsWithId = useMemo(() => {
const items = [...recentRecipient, ...contacts].filter(
item => item.publicKey !== activeAccount?.publicKey
);

return items.map((item, index) => ({ ...item, id: index }));
}, [activeAccount?.publicKey, contacts, recentRecipient]);

const optionsRow = useMemo(() => {
return getUniquePublicKeyItemsWithId.filter(
item =>
item.publicKey.includes(inputValue || '') ||
item.name.includes(inputValue || '')
);
}, [getUniquePublicKeyItemsWithId, inputValue]);

const recipientLabel = t('To recipient');

return showRecipientPlate ? (
<VerticalSpaceContainer top={SpacingSize.XL}>
<RecipientPlate
publicKey={inputValue}
name={recipientName}
recipientLabel={recipientLabel}
showFullPublicKey
handleClick={() => {
Expand All @@ -95,7 +126,7 @@ export const RecipientDropdownInput = ({
monotype
label={recipientLabel}
prefixIcon={<SvgIcon src="assets/icons/search.svg" size={24} />}
placeholder={t('Recipient public address')}
placeholder={t('Public address or contact name')}
{...register('recipientPublicKey')}
error={!!errors?.recipientPublicKey}
validationText={errors?.recipientPublicKey?.message}
Expand All @@ -105,15 +136,20 @@ export const RecipientDropdownInput = ({
<List
contentTop={SpacingSize.Tiny}
rows={optionsRow}
renderRow={({ publicKey }) => (
maxHeight={193}
renderRow={row => (
<RecipientPlate
publicKey={publicKey}
publicKey={row.publicKey}
name={row.name}
isContact={!('isContact' in row)}
handleClick={async () => {
setValue('recipientPublicKey', publicKey);
await trigger('recipientPublicKey');
setValue('recipientPublicKey', row.publicKey);

setIsOpenRecentRecipientPublicKeysList(false);
setShowRecipientPlate(true);
setRecipientName(row.name);

await trigger('recipientPublicKey');
}}
/>
)}
Expand Down
85 changes: 63 additions & 22 deletions src/libs/ui/components/recipient-plate/recipient-plate.tsx
Original file line number Diff line number Diff line change
@@ -1,14 +1,28 @@
import React from 'react';
import styled from 'styled-components';

import { FlexRow, SpacingSize } from '@libs/layout';
import { Avatar, FormField, Hash, HashVariant } from '@libs/ui';
import {
AlignedFlexRow,
FlexRow,
LeftAlignedFlexColumn,
SpacingSize
} from '@libs/layout';
import {
Avatar,
FormField,
Hash,
HashVariant,
SvgIcon,
Typography
} from '@libs/ui';

interface RecipientPlateProps {
handleClick?: () => void;
publicKey: string;
recipientLabel?: string;
showFullPublicKey?: boolean;
name?: string;
isContact?: boolean;
}

const PublicKeyOptionContainer = styled(FlexRow)<{ onClick?: () => void }>`
Expand All @@ -20,11 +34,21 @@ const PublicKeyOptionContainer = styled(FlexRow)<{ onClick?: () => void }>`
border-radius: ${({ theme }) => theme.borderRadius.base}px;
`;

const Container = styled(PublicKeyOptionContainer)`
align-items: center;
padding: 8px 16px;
min-height: 64px;
`;

export const RecipientPlate = ({
handleClick,
publicKey,
recipientLabel,
showFullPublicKey
showFullPublicKey,
name,
isContact
}: RecipientPlateProps) => {
if (recipientLabel) {
return (
Expand All @@ -34,31 +58,48 @@ export const RecipientPlate = ({
onClick={handleClick}
>
<Avatar publicKey={publicKey} size={24} />
<Hash
value={publicKey}
variant={HashVariant.CaptionHash}
truncated={!showFullPublicKey}
truncatedSize="medium"
withCopyOnSelfClick={false}
color="contentPrimary"
/>
<LeftAlignedFlexColumn>
<Hash
value={publicKey}
variant={HashVariant.CaptionHash}
truncated={!showFullPublicKey}
truncatedSize="medium"
withCopyOnSelfClick={false}
color="contentPrimary"
/>
{name && (
<Typography type="captionRegular" color="contentSecondary">
{name}
</Typography>
)}
</LeftAlignedFlexColumn>
</PublicKeyOptionContainer>
</FormField>
);
}

return (
<PublicKeyOptionContainer gap={SpacingSize.Medium} onClick={handleClick}>
<Container gap={SpacingSize.Medium} onClick={handleClick}>
<Avatar publicKey={publicKey} size={24} />
<Hash
value={publicKey}
variant={HashVariant.CaptionHash}
truncated={!showFullPublicKey}
truncatedSize="medium"
withCopyOnSelfClick={false}
color="contentPrimary"
withoutTooltip
/>
</PublicKeyOptionContainer>
<LeftAlignedFlexColumn>
<Hash
value={publicKey}
variant={HashVariant.CaptionHash}
truncated={!showFullPublicKey}
truncatedSize="medium"
withCopyOnSelfClick={false}
color="contentPrimary"
withoutTooltip
/>
{name && (
<AlignedFlexRow gap={SpacingSize.Tiny}>
{isContact && <SvgIcon src="assets/icons/contact.svg" size={16} />}
<Typography type="captionRegular" color="contentSecondary">
{name}
</Typography>
</AlignedFlexRow>
)}
</LeftAlignedFlexColumn>
</Container>
);
};

0 comments on commit ce0b3dc

Please sign in to comment.