Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature: merge recent recipients with contacts list #886

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
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>
);
};
Loading