Skip to content

Commit

Permalink
feat: hovers + some comments + popular tokens partial render
Browse files Browse the repository at this point in the history
  • Loading branch information
Karlen9 committed Aug 22, 2024
1 parent f2e0612 commit 28aeae4
Show file tree
Hide file tree
Showing 11 changed files with 231 additions and 126 deletions.
14 changes: 9 additions & 5 deletions components/Controls.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,11 @@ export function Controls(): ReactElement | null {
}

/**********************************************************************************************
** TODO: write comment of what it does
** onAddReceivers function adds a specified number of new receivers to the configuration state.
** It dispatches an action of type 'ADD_RECEIVERS' with a payload that is an array of new
** receiver objects. Еhe array is created by filling it with null values and then mapping each
** one to a new receiver object generated by the newDisperseVoidRow function.
** The 'amount' parameter specifies how many new receivers should be added.
*********************************************************************************************/
const onAddReceivers = (amount: number): void => {
dispatchConfiguration({
Expand All @@ -28,14 +32,14 @@ export function Controls(): ReactElement | null {
return (
<div className={'mb-10 mt-[100px] flex w-full justify-between'}>
<div className={'flex gap-2'}>
<ImportConfigurationButton className={'!bg-primary !text-secondary'} />
<ExportConfigurationButton />
<DownloadTemplateButton />
<ImportConfigurationButton className={'!bg-primary !text-secondary hover:!bg-primary/90'} />
<ExportConfigurationButton className={'hover:!bg-primary/90'} />
<DownloadTemplateButton className={'hover:bg-primary/20'} />
</div>
<div>
<button
onClick={() => onAddReceivers(1)}
className={'bg-primary text-secondary flex items-center gap-2 rounded-lg p-2 font-bold'}>
className={'flex items-center gap-2 rounded-lg bg-primary p-2 font-bold text-secondary hover:!bg-primary/90'}>
<IconPlus className={'size-4'} />
{'Add receiver'}
</button>
Expand Down
32 changes: 18 additions & 14 deletions components/HeaderSection.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import {useMemo} from 'react';
import Image from 'next/image';
import {useWeb3} from '@builtbymom/web3/contexts/useWeb3';
import {truncateHex} from '@builtbymom/web3/utils';
import {cl, truncateHex} from '@builtbymom/web3/utils';

import {SelectedTokenButton} from './SelectedTokenButton';
import {TokenSelectButton} from './TokenSelectButton';

import type {ReactElement} from 'react';
import type {ReactElement} from 'react';

export function HeaderSection(): ReactElement {
const {onConnect, address, ens, clusters, openLoginModal} = useWeb3();
Expand All @@ -22,36 +22,40 @@ export function HeaderSection(): ReactElement {
/>
<div className={'z-30 w-full max-w-[1200px] pb-[100px]'}>
<div className={'relative flex w-full justify-between px-6 py-4'}>
<div className={'font-2xl flex items-center gap-2 font-bold text-primary'}>
<div className={'font-2xl flex items-center gap-2 font-bold text-primary'}>
<div className={'size-8 rounded-full bg-primary'} />
{'DILOGO'}
</div>
<button
suppressHydrationWarning
onClick={address ? openLoginModal : onConnect}
className={
'rounded-lg bg-primary/10 p-3 text-xs font-bold text-primary md:px-[30px] md:text-sm'
}>
className={cl(
'rounded-lg bg-primary/10 p-3 text-xs',
'font-bold text-primary md:px-[30px] md:text-sm',
'hover:bg-primary/20'
)}>
{ensOrClusters ? ensOrClusters : address ? truncateHex(address, 6) : 'Connect Wallet'}
</button>
</div>

<div className={'z-10 mt-10 flex w-full flex-col justify-center'}>
<div className={'flex flex-col items-center justify-center gap-6 md:flex-row md:gap-0'}>
<span
className={
'mr-6 text-center text-[40px] font-medium leading-[48px] text-primary md:text-[80px] md:leading-[88px]'
}>
className={cl(
'mr-6 text-center text-[40px] font-medium leading-[48px]',
'text-primary md:text-[80px] md:leading-[88px]'
)}>
{'Send'}
</span>
<SelectedTokenButton />
<TokenSelectButton />
</div>
<div className={'weofiwe wefowe'}></div>
<div className={'mt-[20px] flex justify-center'}>
<p
className={
'w-fit text-center text-[40px] font-medium leading-[48px] text-primary md:text-[80px] md:leading-[88px]'
}>
className={cl(
'w-fit text-center text-[40px] font-medium leading-[48px]',
'text-primary md:text-[80px] md:leading-[88px]'
)}>
{'to receivers:'}
</p>
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {type ReactElement, useState} from 'react';
import {toAddress} from '@builtbymom/web3/utils';
import {cl, toAddress} from '@builtbymom/web3/utils';

import {useDisperse} from './common/contexts/useDisperse';
import {usePrices} from './common/contexts/usePrices';
Expand All @@ -9,7 +9,7 @@ import {TokenButton} from './common/TokenButton';

import {APP_CHAIN_ID} from '@/constants';

export function SelectedTokenButton(): ReactElement {
export function TokenSelectButton(): ReactElement {
const [isSelectTokenModalOpen, set_isSelectTokenModalOpen] = useState(false);
const {configuration} = useDisperse();
const {getPrice} = usePrices();
Expand All @@ -24,17 +24,20 @@ export function SelectedTokenButton(): ReactElement {
isOpen={isSelectTokenModalOpen}
onClose={() => set_isSelectTokenModalOpen(false)}
/>
<div className={'rounded-[64px] bg-black'}>
<div className={'rounded-[64px] bg-secondary'}>
<button
onClick={() => set_isSelectTokenModalOpen(true)}
className={
'flex h-min items-center justify-center rounded-[64px] border border-accent bg-background-modal/90 px-6 py-4'
}>
className={cl(
'flex h-min items-center justify-center rounded-[64px]',
'border border-accent bg-background-modal/90 px-6 py-4',
'hover:bg-primary/10 hover:border-accent',
configuration?.tokenToSend?.address && 'border-primary/10'
)}>
{configuration.tokenToSend?.address ? (
<TokenButton
price={price}
token={configuration.tokenToSend}
className={'mr-3 !p-0'}
className={'mr-3 !p-0 hover:!bg-transparent'}
/>
) : (
<span className={'mr-[22px] text-[32px] leading-[32px] text-primary'}>{'Token'}</span>
Expand Down
4 changes: 3 additions & 1 deletion components/common/AddReceiverCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,9 @@ export function AddReceiverCard({className}: {className?: string}): ReactElement
onAddReceivers(1);
}}
className={cl(
'flex h-[152px] w-[282px] flex-col items-center justify-center gap-2 rounded-3xl border border-accent bg-background-modal/90',
'flex h-[152px] w-[282px] flex-col items-center justify-center',
'gap-2 rounded-3xl border border-accent bg-background-modal/90',
'hover:bg-primary/10',
className
)}>
<IconPlus className={'size-6 text-primary'} />
Expand Down
49 changes: 39 additions & 10 deletions components/common/AmountInput.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,22 @@
import {type ReactElement,useCallback, useState} from 'react';
import {type ReactElement, useCallback, useMemo, useState} from 'react';
import React from 'react';
import InputNumber from 'rc-input-number';
import {cl} from '@builtbymom/web3/utils';
import {cl, formatAmount, toAddress, toBigInt} from '@builtbymom/web3/utils';
import {useDeepCompareEffect, useUpdateEffect} from '@react-hookz/web';

import {useDisperse} from './contexts/useDisperse';
import {usePrices} from './contexts/usePrices';
import {useValidateAmountInput} from './hooks/useValidateAmountInput';

import type {TNormalizedBN,TToken} from '@builtbymom/web3/types';
import type {TNormalizedBN, TToken} from '@builtbymom/web3/types';
import type {TAmountInputElement} from './types/disperse.types';


import {APP_CHAIN_ID} from '@/constants';

type TAmountInput = {
onSetValue: (value: Partial<TAmountInputElement>) => void;
value: TAmountInputElement;
token: TToken | undefined;
token: TToken | undefined;
price?: TNormalizedBN | undefined;
};

Expand All @@ -22,7 +25,13 @@ export function AmountInput({value, token, onSetValue}: TAmountInput): ReactElem

const [isFocused, set_isFocused] = useState<boolean>(false);
const {result, validate} = useValidateAmountInput();
const {getPrice} = usePrices();
const price = getPrice({chainID: APP_CHAIN_ID, address: toAddress(token?.address)});

/**********************************************************************************************
** getBorderColor function determines the border color of an element based on its focus state
** and validity.
*********************************************************************************************/
const getBorderColor = useCallback((): string => {
if (isFocused) {
return 'border-neutral-600';
Expand All @@ -33,6 +42,22 @@ export function AmountInput({value, token, onSetValue}: TAmountInput): ReactElem
return 'border-neutral-400';
}, [isFocused, value.isValid]);

/**********************************************************************************************
** The amountValue memoized value contains the string representation of the token value,
** in USD. If the token value is zero, it will display 'N/A'.
*********************************************************************************************/
const amountValue = useMemo(() => {
if (!token) {
return 'N/A';
}
if (toBigInt(price?.raw) === 0n) {
return 'N/A';
}

const formatedValue = formatAmount(value.amount ?? '', 2);
return `$${formatedValue}`;
}, [price?.raw, token, value.amount]);

/** Set the validation result to the context */
useDeepCompareEffect(() => {
if (!result) {
Expand All @@ -50,7 +75,7 @@ export function AmountInput({value, token, onSetValue}: TAmountInput): ReactElem
}, [token?.address]);

return (
<label className={'h-14 rounded-2xl border border-primary/10 px-4 py-2'}>
<label className={'h-14 rounded-2xl border border-primary/10 px-4 pb-1'}>
<InputNumber
// ref={inputRef}
value={value.amount}
Expand All @@ -61,21 +86,25 @@ export function AmountInput({value, token, onSetValue}: TAmountInput): ReactElem
'focus:border-primary/10 focus:outline-0',
'placeholder:transition-colors overflow-hidden',
getBorderColor(),
!value.amount ? 'mt-2' : 'mt-0'

'mt-2'
)}
min={'0'}
step={0.1}
decimalSeparator={'.'}
placeholder={'0.00'}
autoComplete={'off'}
autoComplete={'off'}
autoCorrect={'off'}
spellCheck={'false'}
onChange={value => validate(value || '', token)}
onFocus={() => set_isFocused(true)}
onBlur={() => set_isFocused(false)}
/>
{!configuration.tokenToSend && <span className={'text-xs text-primary/40'}>{'Token not selected'}</span>}

{!configuration.tokenToSend?.address ? (
<span className={'mb-1 text-xs text-primary/40'}>{'Token not selected'}</span>
) : (
<p className={cl('text-xs', 'text-primary/40 mt-1')}>{amountValue}</p>
)}
</label>
);
}
36 changes: 23 additions & 13 deletions components/common/ReceiverCard.tsx
Original file line number Diff line number Diff line change
@@ -1,43 +1,53 @@
import {type ReactElement,useRef} from 'react';
import {type ReactElement, useRef} from 'react';

import {AddressInput} from './AddressInput';
import {AmountInput} from './AmountInput';
import {useDisperse} from './contexts/useDisperse';
import {IconCross} from './icons/IconCross';
import {type TInputAddressLike} from './utils/tools.address';

import type {TAmountInputElement,TDisperseInput} from './types/disperse.types';
import type {TAmountInputElement, TDisperseInput} from './types/disperse.types';

export function ReceiverCard({input}: {input: TDisperseInput}): ReactElement {
const inputRef = useRef<HTMLInputElement>(null);
const {configuration, dispatchConfiguration} = useDisperse();

/**********************************************************************************************
** onSetAmount function updates the amount-related data in the configuration state.
** It dispatches an action of type 'SET_VALUE' with a payload that combines the given 'value'
** object(containing partial amount details) with the existing UUID of the input element.
*********************************************************************************************/
const onSetAmount = (value: Partial<TAmountInputElement>): void => {
dispatchConfiguration({type: 'SET_VALUE', payload: {...value, UUID: input.UUID}});
dispatchConfiguration({type: 'SET_VALUE', payload: {...value, UUID: input.UUID}});
};

/**********************************************************************************************
** TODO: write comment of what it does
** onDeleteReceiver function, dispatches an action to delete a receiver from the configuration
** state. The action type is 'DEL_RECEIVER_BY_UUID' and the payload contains the UUID of the
** receiver to be deleted.
*********************************************************************************************/
const onDeleteReceiver = (): void => {
dispatchConfiguration({type: 'DEL_RECEIVER_BY_UUID', payload: input.UUID});
};

/**********************************************************************************************
** TODO: write comment of what it does
** onSetReceiver function updates the configuration state by dispatching an action of type
** 'SET_RECEIVER'. It takes an object 'value' containing partial receiver details and merges
** it with the existing UUID of the receiver. The updated receiver data is then sent as the
** payload of the dispatched action.
*********************************************************************************************/
const onSetReceiver = (value: Partial<TInputAddressLike>): void => {
dispatchConfiguration({type: 'SET_RECEIVER', payload: {...value, UUID: input.UUID}});
};

return (
return (
<div className={'relative col-span-1 w-full rounded-3xl bg-background-modal/90 p-4 md:!w-[282px]'}>
<button
onClick={onDeleteReceiver}
// eslint-disable-next-line tailwindcss/enforces-negative-arbitrary-values
className={
'absolute -right-[7px] -top-[7px] flex size-6 items-center justify-center rounded-full bg-primary'
}>
'absolute -right-[7px] -top-[7px] flex size-6 items-center justify-center rounded-full bg-primary hover:bg-primary/90'
}>
<IconCross className={'size-2'} />
</button>
<div className={'flex h-full flex-col gap-2'}>
Expand All @@ -46,10 +56,10 @@ export function ReceiverCard({input}: {input: TDisperseInput}): ReactElement {
onSetValue={onSetReceiver}
inputRef={inputRef}
/>
<AmountInput
onSetValue={onSetAmount}
value={input.value}
token={configuration.tokenToSend}
<AmountInput
onSetValue={onSetAmount}
value={input.value}
token={configuration.tokenToSend}
/>
</div>
</div>
Expand Down
4 changes: 2 additions & 2 deletions components/common/Recievers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ export function Recievers(): ReactElement {
<div className={'flex flex-col items-center gap-10'}>
<AddReceiverCard />
<div className={'flex gap-2'}>
<ImportConfigurationButton />
<DownloadTemplateButton />
<ImportConfigurationButton className={'hover:bg-primary/20'} />
<DownloadTemplateButton className={'hover:bg-primary/20'} />
</div>
</div>
)}
Expand Down
Loading

0 comments on commit 28aeae4

Please sign in to comment.