Skip to content

Commit

Permalink
Merge pull request #55 from SmolDapp/feat/ab-tweaks
Browse files Browse the repository at this point in the history
Feat/ab tweaks
  • Loading branch information
w84april authored Mar 21, 2024
2 parents ff5c6ee + da6822a commit 9d76267
Show file tree
Hide file tree
Showing 7 changed files with 196 additions and 96 deletions.
10 changes: 5 additions & 5 deletions components/designSystem/Curtains/AddressBookCurtain.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import {AvatarWrapper} from '../Avatar';
import {NetworkDropdownSelector} from '../NetworkSelector/Dropdown';
import {SmolAddressInputSimple} from '../SmolAddressInput.simple';

import type {TAddressBookEntryReducer} from 'pages/apps/address-book';
import type {TAddressBookEntryReducer} from 'contexts/useAddressBookCurtain';
import type {Dispatch, ReactElement, SetStateAction} from 'react';
import type {TInputAddressLike} from '@utils/tools.address';

Expand Down Expand Up @@ -122,7 +122,7 @@ function NameInput(props: {
useEffect(() => {
const entry = getCachedEntry({label: selectedEntry.label});
const currentCustomValidity = inputRef.current?.validationMessage;
if (entry !== undefined && entry.id !== selectedEntry.id) {
if (entry !== undefined && entry.id !== selectedEntry.id && !entry.isHidden) {
inputRef.current?.setCustomValidity('This name is already used in your address book');
props.onRefresh?.();
} else if (currentCustomValidity !== '') {
Expand Down Expand Up @@ -220,7 +220,7 @@ function AddressInput(props: {
const entry = getCachedEntry({address: props.addressLike.address});
const currentCustomValidity = inputRef.current?.validationMessage;

if (entry !== undefined && entry.id !== props.selectedEntry.id) {
if (entry !== undefined && entry.id !== props.selectedEntry.id && !entry.isHidden) {
inputRef.current?.setCustomValidity('This address is already in your address book');
onChange();
} else if (currentCustomValidity !== '') {
Expand Down Expand Up @@ -316,10 +316,10 @@ export function AddressBookCurtain(props: {
});
}
if (props.selectedEntry.id === undefined) {
updateEntry({...currentEntry, address: addressLike.address});
updateEntry({...currentEntry, address: addressLike.address, isHidden: false});
props.onOpenChange({isOpen: false, isEditing: false});
} else {
updateEntry({...currentEntry, address: addressLike.address});
updateEntry({...currentEntry, address: addressLike.address, isHidden: false});
set_isEditMode(false);
props.onOpenChange({isOpen: true, isEditing: false});
}
Expand Down
33 changes: 18 additions & 15 deletions components/designSystem/Layout.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {type ReactElement, type ReactNode} from 'react';
import {WithAddressBook} from 'contexts/useAddressBook';
import {WithAddressBookCurtain} from 'contexts/useAddressBookCurtain';
import {AnimatePresence, motion} from 'framer-motion';
import {cl} from '@builtbymom/web3/utils';
import {IconQuestionMark} from '@icons/IconQuestionMark';
Expand Down Expand Up @@ -89,21 +90,23 @@ export default function Layout(props: AppProps): ReactElement {
initial={'initial'}
className={'relative mb-10 min-h-app w-full overflow-x-hidden rounded-lg bg-neutral-0'}>
<WithAddressBook>
<App
title={appName}
description={appDescription}
action={appAction()}>
<motion.div
initial={{scale: 0.9, opacity: 0}}
animate={{scale: 1, opacity: 1}}
transition={{
delay: router.isReady ? 0.2 : 0.4,
duration: 0.6,
ease: 'easeInOut'
}}>
{getLayout(<Component {...props} />, router)}
</motion.div>
</App>
<WithAddressBookCurtain>
<App
title={appName}
description={appDescription}
action={appAction()}>
<motion.div
initial={{scale: 0.9, opacity: 0}}
animate={{scale: 1, opacity: 1}}
transition={{
delay: router.isReady ? 0.2 : 0.4,
duration: 0.6,
ease: 'easeInOut'
}}>
{getLayout(<Component {...props} />, router)}
</motion.div>
</App>
</WithAddressBookCurtain>
</WithAddressBook>
</motion.main>
</AnimatePresence>
Expand Down
5 changes: 2 additions & 3 deletions components/designSystem/SmolAddressInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export function useValidateAddressInput(): {
** Check if the input is an address from the address book
**********************************************************/
const fromAddressBook = await getEntry({label: input, address: toAddress(input)});
if (fromAddressBook) {
if (fromAddressBook && !fromAddressBook.isHidden) {
if (signal?.aborted) {
console.log('aborted');
throw new Error('Aborted!');
Expand Down Expand Up @@ -68,8 +68,7 @@ export function useValidateAddressInput(): {
if (isAddress(address)) {
set_isCheckingValidity(false);
const fromAddressBook = await getEntry({label: input, address: toAddress(address)});
if (fromAddressBook) {
console.log(3);
if (fromAddressBook && !fromAddressBook.isHidden) {
return {
address: toAddress(fromAddressBook.address),
label: fromAddressBook.label || fromAddressBook.ens || input,
Expand Down
50 changes: 46 additions & 4 deletions components/sections/Send/SendStatus.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import {useState} from 'react';
import Link from 'next/link';
import {useAddressBook} from 'contexts/useAddressBook';
import {useAddressBookCurtain} from 'contexts/useAddressBookCurtain';
import {useAsyncTrigger} from '@builtbymom/web3/hooks/useAsyncTrigger';
import {useChainID} from '@builtbymom/web3/hooks/useChainID';
import {isEthAddress} from '@builtbymom/web3/utils';
Expand All @@ -10,9 +11,34 @@ import {Warning} from '@common/Primitives/Warning';

import {useSendFlow} from './useSendFlow';

import type {ReactElement} from 'react';
import type {ReactElement, ReactNode} from 'react';
import type {TWarningType} from '@common/Primitives/Warning';

function TriggerAddressBookButton({children}: {children: ReactNode}): ReactElement {
const {set_curtainStatus, dispatchConfiguration} = useAddressBookCurtain();
const {configuration} = useSendFlow();

return (
<button
className={'font-bold transition-all'}
onClick={() => {
set_curtainStatus({isOpen: true, isEditing: true});
dispatchConfiguration({
type: 'SET_SELECTED_ENTRY',
payload: {
address: configuration.receiver.address,
label: '',
slugifiedLabel: '',
chains: [],
isFavorite: false
}
});
}}>
{children}
</button>
);
}

export function SendStatus({isReceiverERC20}: {isReceiverERC20: boolean}): ReactElement | null {
const {configuration} = useSendFlow();
const {safeChainID} = useChainID();
Expand Down Expand Up @@ -63,15 +89,31 @@ export function SendStatus({isReceiverERC20}: {isReceiverERC20: boolean}): React
});
}

if (configuration.receiver.address && !fromAddressBook) {
if (
configuration.receiver.address &&
(!fromAddressBook || (fromAddressBook?.numberOfInteractions === 0 && fromAddressBook.isHidden))
) {
return set_status({
message: 'This is the first time you interact with this address, please be careful',
message: (
<>
{'This is the first time you interact with this address, please be careful.'}
<TriggerAddressBookButton>{'Wanna add it to Address Book?'}</TriggerAddressBookButton>
</>
),
type: 'warning'
});
}

if (configuration.receiver.address && fromAddressBook?.isHidden) {
return set_status({message: 'This address isn’t in your address book. Wanna add it?', type: 'warning'});
return set_status({
message: (
<>
{'This address isn’t in your address book.'}{' '}
<TriggerAddressBookButton>{'Wanna add it?'}</TriggerAddressBookButton>
</>
),
type: 'warning'
});
}

if (configuration.receiver.address && !fromAddressBook?.chains.includes(safeChainID)) {
Expand Down
16 changes: 8 additions & 8 deletions contexts/useAddressBook.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ export type TAddressBookEntry = {
tags?: string[]; // List of tags associated with the address.
};
export type TSelectCallback = (item: TAddressBookEntry) => void;
export type TAddressBookCurtainProps = {
export type TAddressBookProps = {
shouldOpenCurtain: boolean;
listEntries: () => Promise<TAddressBookEntry[]>;
listCachedEntries: () => TAddressBookEntry[];
Expand All @@ -39,7 +39,7 @@ export type TAddressBookCurtainProps = {
onOpenCurtain: (callbackFn: TSelectCallback) => void;
onCloseCurtain: () => void;
};
const defaultProps: TAddressBookCurtainProps = {
const defaultProps: TAddressBookProps = {
shouldOpenCurtain: false,
listEntries: async (): Promise<TAddressBookEntry[]> => [],
listCachedEntries: (): TAddressBookEntry[] => [],
Expand Down Expand Up @@ -78,13 +78,13 @@ const addressBookIDBConfig: IndexedDBConfig = {
]
};

const AddressBookContext = createContext<TAddressBookCurtainProps>(defaultProps);
const AddressBookContext = createContext<TAddressBookProps>(defaultProps);
export const WithAddressBook = ({children}: {children: React.ReactElement}): React.ReactElement => {
const [shouldOpenCurtain, set_shouldOpenCurtain] = useState(false);
const [cachedEntries, set_cachedEntries] = useState<TAddressBookEntry[]>([]);
const [entryNonce, set_entryNonce] = useState<number>(0);
const [currentCallbackFunction, set_currentCallbackFunction] = useState<TSelectCallback | undefined>(undefined);
const {add, getAll, getOneByKey, deleteByID, update} = useIndexedDBStore<TAddressBookEntry>('address-book');
const {add, getAll, getOneByKey, update} = useIndexedDBStore<TAddressBookEntry>('address-book');
const {safeChainID} = useChainID();

useMountEffect(async () => setupIndexedDB(addressBookIDBConfig));
Expand Down Expand Up @@ -246,14 +246,14 @@ export const WithAddressBook = ({children}: {children: React.ReactElement}): Rea
try {
const existingEntry = await getEntry({address: address});
if (existingEntry) {
deleteByID(existingEntry.id);
update({...existingEntry, isHidden: true});
set_entryNonce(nonce => nonce + 1);
}
} catch {
// Do nothing
}
},
[deleteByID, getEntry]
[getEntry, update]
);

/**************************************************************************
Expand Down Expand Up @@ -295,7 +295,7 @@ export const WithAddressBook = ({children}: {children: React.ReactElement}): Rea
* Context value that is passed to all children of this component.
*************************************************************************/
const contextValue = useMemo(
(): TAddressBookCurtainProps => ({
(): TAddressBookProps => ({
shouldOpenCurtain,
listEntries,
listCachedEntries,
Expand Down Expand Up @@ -336,4 +336,4 @@ export const WithAddressBook = ({children}: {children: React.ReactElement}): Rea
);
};

export const useAddressBook = (): TAddressBookCurtainProps => useContext(AddressBookContext);
export const useAddressBook = (): TAddressBookProps => useContext(AddressBookContext);
110 changes: 110 additions & 0 deletions contexts/useAddressBookCurtain.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import {createContext, type ReactElement, useContext, useReducer, useState} from 'react';
import {AddressBookCurtain} from 'components/designSystem/Curtains/AddressBookCurtain';
import {toAddress} from '@builtbymom/web3/utils';

import {useAddressBook} from './useAddressBook';

import type {TAddress} from '@builtbymom/web3/types';
import type {TAddressBookEntry} from './useAddressBook';

export type TAddressBookEntryReducer =
| {type: 'SET_SELECTED_ENTRY'; payload: TAddressBookEntry}
| {type: 'SET_ADDRESS'; payload: TAddress | undefined}
| {type: 'SET_LABEL'; payload: string}
| {type: 'SET_CHAINS'; payload: number[]}
| {type: 'SET_IS_FAVORITE'; payload: boolean};

type TCurtainStatus = {isOpen: boolean; isEditing: boolean; label?: string};

type TAddressBookCurtainProps = {
selectedEntry: TAddressBookEntry | undefined;
dispatchConfiguration: React.Dispatch<TAddressBookEntryReducer>;
curtainStatus: TCurtainStatus;
set_curtainStatus: React.Dispatch<React.SetStateAction<TCurtainStatus>>;
};

const deafultCurtainStatus = {
isOpen: false,
isEditing: false
};

const defaultProps: TAddressBookCurtainProps = {
selectedEntry: undefined,
dispatchConfiguration: (): void => undefined,
curtainStatus: deafultCurtainStatus,
set_curtainStatus: (): void => undefined
};
const AddressBookCurtainContext = createContext<TAddressBookCurtainProps>(defaultProps);

export const WithAddressBookCurtain = ({children}: {children: ReactElement}): ReactElement => {
const {updateEntry} = useAddressBook();
const [curtainStatus, set_curtainStatus] = useState<TCurtainStatus>(deafultCurtainStatus);

const entryReducer = (state: TAddressBookEntry, action: TAddressBookEntryReducer): TAddressBookEntry => {
switch (action.type) {
case 'SET_SELECTED_ENTRY':
return action.payload;
case 'SET_ADDRESS':
return {...state, address: toAddress(action.payload)};
case 'SET_LABEL':
return {...state, label: action.payload};
case 'SET_CHAINS':
return {...state, chains: action.payload};
case 'SET_IS_FAVORITE':
updateEntry({...state, isFavorite: action.payload});
return {...state, isFavorite: action.payload};
}
};

const [selectedEntry, dispatch] = useReducer(entryReducer, {
address: undefined,
label: '',
slugifiedLabel: '',
chains: [],
isFavorite: false
});

const contextValue = {
selectedEntry,
dispatchConfiguration: dispatch,
curtainStatus,
set_curtainStatus
};

return (
<AddressBookCurtainContext.Provider value={contextValue}>
{children}

<AddressBookCurtain
selectedEntry={selectedEntry}
dispatch={dispatch}
isOpen={curtainStatus.isOpen}
isEditing={curtainStatus.isEditing}
initialLabel={curtainStatus.label}
onOpenChange={status => {
set_curtainStatus(status);
if (!status.isOpen) {
dispatch({
type: 'SET_SELECTED_ENTRY',
payload: {
address: undefined,
label: '',
slugifiedLabel: '',
chains: [],
isFavorite: false
}
});
}
}}
/>
</AddressBookCurtainContext.Provider>
);
};

export const useAddressBookCurtain = (): TAddressBookCurtainProps => {
const ctx = useContext(AddressBookCurtainContext);
if (!ctx) {
throw new Error('AddressBookCurtainContext not found');
}
return ctx;
};
Loading

0 comments on commit 9d76267

Please sign in to comment.