Skip to content

Commit

Permalink
refactor: swaps redesign (#90)
Browse files Browse the repository at this point in the history
secondl1ght authored Oct 9, 2024
1 parent cc49a75 commit 70d0a35
Showing 10 changed files with 234 additions and 100 deletions.
5 changes: 3 additions & 2 deletions messages/en.json
Original file line number Diff line number Diff line change
@@ -26,7 +26,6 @@
"unlock": "Unlock to Decrypt"
},
"Transactions": {
"ago": "ago",
"all": "All",
"amount": "Amount",
"asset": "Asset",
@@ -36,7 +35,6 @@
"date": "Date",
"details": "Transaction Details",
"fees": "Fees",
"no-results": "No results.",
"paid": "Paid",
"pending": "Pending",
"received": "Received",
@@ -57,6 +55,7 @@
},
"add-contact": "Add Contact",
"add-desc": "Add Description",
"ago": "ago",
"amount": "Please specify an amount.",
"amount-custom": "Customize Amount",
"assets": "Assets",
@@ -67,6 +66,7 @@
"enter-desc": "Enter an optional description.",
"fee": "Fee",
"locked": "Your vault is locked.",
"no-results": "No results.",
"receive": "Receive",
"receive-warn": "Please do not send to this address more than once or after 24 hours from generation.",
"recipient": "Enter recipient",
@@ -107,6 +107,7 @@
"accounts": "Accounts",
"contacts": "Contacts",
"settings": "Settings",
"swaps": "Swaps",
"transactions": "Transactions",
"wallet": "Wallet"
},
5 changes: 3 additions & 2 deletions messages/es.json
Original file line number Diff line number Diff line change
@@ -26,7 +26,6 @@
"unlock": "Desbloquear para Desencriptar"
},
"Transactions": {
"ago": "Hace",
"all": "Todas",
"amount": "Cantidad",
"asset": "Activo",
@@ -36,7 +35,6 @@
"date": "Fecha",
"details": "Detalles de la Transacción",
"fees": "Comisiónes",
"no-results": "Sin resultados.",
"paid": "Pagado",
"pending": "Procesando",
"received": "Recivido",
@@ -57,6 +55,7 @@
},
"add-contact": "Agregar Contacto",
"add-desc": "Agrega una descripción",
"ago": "Hace",
"amount": "Especifíca una cantidad.",
"amount-custom": "Cambia la cantidad",
"assets": "Activos",
@@ -67,6 +66,7 @@
"enter-desc": "Opcionalmente agrega una descripción.",
"fee": "Comisión",
"locked": "Tu bóveda esta bloqueada.",
"no-results": "Sin resultados.",
"receive": "Recibir",
"receive-warn": "Por favor no enviar a esta dirección más de una vez.",
"recipient": "Ingrese el destinatario",
@@ -107,6 +107,7 @@
"accounts": "Cuentas",
"contacts": "Contactos",
"settings": "Configuración",
"swaps": "Swaps",
"transactions": "Transacciones",
"wallet": "Billetera"
},
Binary file added public/images/bitcoin.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 1 addition & 3 deletions src/views/dashboard/RecentTransactions.tsx
Original file line number Diff line number Diff line change
@@ -98,9 +98,7 @@ export const RecentTransactions: FC<{ id: string }> = ({ id }) => {
/>
))
) : (
<p className="font-semibold">
{t('Wallet.Transactions.no-results')}
</p>
<p className="font-semibold">{t('Wallet.no-results')}</p>
)}
</>
)}
90 changes: 90 additions & 0 deletions src/views/swaps/Swap.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import { format, formatDistanceToNowStrict } from 'date-fns';
import { es } from 'date-fns/locale';
import { ArrowRight } from 'lucide-react';
import Image from 'next/image';
import { useLocale, useTranslations } from 'next-intl';
import { FC } from 'react';

import bitcoin from '/public/images/bitcoin.png';
import liquid from '/public/images/liquid.jpg';
import { SimpleSwap } from '@/graphql/types';
import { numberWithPrecisionAndDecimals } from '@/utils/numbers';

const AssetLogo: FC<{ coin: string }> = ({ coin }) => {
const classname = 'h-10 w-10 rounded-full object-cover';

switch (coin) {
case 'BTC':
return <Image src={bitcoin} alt="bitcoin" className={classname} />;
case 'L-BTC':
return <Image src={liquid} alt="liquid" className={classname} />;
default:
return <div className="h-10 w-10 rounded-full bg-primary" />;
}
};

export const Swap: FC<{ data: SimpleSwap }> = ({ data }) => {
const t = useTranslations();
const locale = useLocale();

return (
<div className="w-full space-y-1 overflow-x-auto whitespace-nowrap rounded-xl bg-slate-100 px-2 py-1.5 dark:bg-neutral-900">
<p className="text-center text-xs font-medium text-slate-600 dark:text-neutral-400">
{data.provider}
</p>

<div className="flex w-full items-center justify-between space-x-2">
<div className="flex shrink-0 items-center space-x-2">
<AssetLogo coin={data.deposit_coin} />

<div>
<p className="font-medium">
{data.deposit_amount
? numberWithPrecisionAndDecimals(data.deposit_amount, 0)
: '-'}
</p>

<p className="text-sm font-medium text-slate-600 dark:text-neutral-400">
{data.deposit_coin}
</p>
</div>
</div>

<ArrowRight size={20} className="shrink-0" />

<div className="flex shrink-0 items-center space-x-2">
<div className="text-right">
<p className="font-medium">
{data.settle_amount
? numberWithPrecisionAndDecimals(data.settle_amount, 0)
: '-'}
</p>

<p className="text-sm font-medium text-slate-600 dark:text-neutral-400">
{data.settle_coin}
</p>
</div>

<AssetLogo coin={data.settle_coin} />
</div>
</div>

<p className="text-center text-xs font-medium text-slate-600 dark:text-neutral-400">
{format(data.created_at, 'MMM dd, yyyy - HH:mm', {
locale: locale === 'es' ? es : undefined,
})}{' '}
(
{locale === 'es'
? t('App.Wallet.ago') +
' ' +
formatDistanceToNowStrict(data.created_at, {
locale: es,
})
: formatDistanceToNowStrict(data.created_at) +
' ' +
t('App.Wallet.ago')}
)
</p>
</div>
);
};
108 changes: 29 additions & 79 deletions src/views/swaps/Swaps.tsx
Original file line number Diff line number Diff line change
@@ -1,105 +1,55 @@
'use client';

import { ColumnDef } from '@tanstack/react-table';
import { format, formatDistanceToNowStrict } from 'date-fns';
import { ChevronRight, Loader2 } from 'lucide-react';
import { useTranslations } from 'next-intl';
import { useLocalStorage } from 'usehooks-ts';

import { useToast } from '@/components/ui/use-toast';
import { useGetWalletSwapsQuery } from '@/graphql/queries/__generated__/swaps.generated';
import { SimpleSwap } from '@/graphql/types';
import { LOCALSTORAGE_KEYS } from '@/utils/constants';
import { numberWithPrecisionAndDecimals } from '@/utils/numbers';
import { handleApolloError } from '@/utils/error';

import { SimpleTable } from '../wallet/SimpleTable';
import { Swap } from './Swap';
import { SwapsTable } from './SwapsTable';

export const columns: ColumnDef<SimpleSwap>[] = [
{
accessorKey: 'date',
header: 'Date',
cell: ({ row }) =>
row.original.created_at ? (
<div>
{`${formatDistanceToNowStrict(row.original.created_at)} ago`}
<p className="mt-1 text-xs text-slate-500 dark:text-slate-400">
{format(row.original.created_at, 'MMM do, yyyy - HH:mm')}
</p>
</div>
) : (
'Pending'
),
},
{
accessorKey: 'pair',
header: 'Pair',
cell: ({ row }) => (
<div className="flex items-center justify-start gap-2">
<div>
<p className="mt-1 text-xs text-slate-500 dark:text-slate-400">
From
</p>
<div className="flex gap-1">
{row.original.deposit_amount ? (
<p>
{numberWithPrecisionAndDecimals(row.original.deposit_amount, 0)}
</p>
) : null}
<p>{row.original.deposit_coin}</p>
</div>
</div>

<ChevronRight className="size-4" />

<div>
<p className="mt-1 text-xs text-slate-500 dark:text-slate-400">To</p>
<div className="flex gap-1">
{row.original.settle_amount ? (
<p>
{numberWithPrecisionAndDecimals(row.original.settle_amount, 0)}
</p>
) : null}
<p>{row.original.settle_coin}</p>
</div>
</div>
</div>
),
},
{
accessorKey: 'provider',
header: 'Provider',
cell: ({ row }) => <div>{row.original.provider}</div>,
id: 'swap',
cell: ({ row }) => <Swap data={row.original} />,
},
];

export const Swaps = () => {
const t = useTranslations('Index');
const { toast } = useToast();

const [value] = useLocalStorage(LOCALSTORAGE_KEYS.currentWalletId, '');

const { data, loading, error } = useGetWalletSwapsQuery({
const { data, loading } = useGetWalletSwapsQuery({
variables: { id: value },
onError: err => {
const messages = handleApolloError(err);

toast({
variant: 'destructive',
title: 'Error getting swaps.',
description: messages.join(', '),
});
},
});

console.log(data?.wallets?.find_one?.swaps?.find_many?.[0]);
const swaps = data?.wallets.find_one.swaps.find_many || [];

return (
<div className="py-4">
<h2 className="scroll-m-20 pb-4 text-xl font-semibold tracking-tight">
Swaps
</h2>
{loading ? (
<div className="my-10 flex w-full justify-center">
<Loader2 className="animate-spin" />
</div>
) : error ? (
<div className="my-4 flex w-full justify-center">
<p className="text-sm text-muted-foreground">
Error loading wallet swaps
</p>
</div>
) : (
<SimpleTable<SimpleSwap>
data={data?.wallets.find_one.swaps.find_many || []}
columns={columns}
/>
)}
<div className="mx-auto w-full max-w-lg py-4 lg:py-10">
<h1 className="mb-6 text-3xl font-semibold">{t('swaps')}</h1>

<SwapsTable<SimpleSwap>
data={swaps}
columns={columns}
loading={loading}
/>
</div>
);
};
96 changes: 96 additions & 0 deletions src/views/swaps/SwapsTable.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
'use client';

import {
ColumnDef,
flexRender,
getCoreRowModel,
getPaginationRowModel,
useReactTable,
} from '@tanstack/react-table';
import { SquareArrowLeft, SquareArrowRight } from 'lucide-react';
import { useTranslations } from 'next-intl';
import { Fragment } from 'react';

import { Skeleton } from '@/components/ui/skeleton';

type TableProps<T> = {
data: T[];
columns: ColumnDef<T>[];
loading: boolean;
};

export const SwapsTable = <T,>({
data,
columns,
loading,
}: TableProps<T>): JSX.Element => {
const t = useTranslations();

const table = useReactTable({
data,
columns,
getCoreRowModel: getCoreRowModel(),
getPaginationRowModel: getPaginationRowModel(),
initialState: { pagination: { pageSize: 5 } },
});

return (
<div className="w-full max-w-[calc(100dvw-32px)]">
<div className="mb-4 space-y-2">
{loading ? (
Array.from({ length: 5 }).map((_, i) => (
<Skeleton key={i} className="h-24 w-full rounded-xl" />
))
) : (
<>
{table.getRowModel().rows?.length ? (
table.getRowModel().rows.map(row => (
<Fragment key={row.id}>
{row.getVisibleCells().map(cell => (
<Fragment key={cell.id}>
{flexRender(
cell.column.columnDef.cell,
cell.getContext()
)}
</Fragment>
))}
</Fragment>
))
) : (
<p className="font-semibold">{t('App.Wallet.no-results')}</p>
)}
</>
)}
</div>

<div className="flex items-center justify-between space-x-2">
<div className="text-sm text-slate-600 dark:text-neutral-400">
{loading ? null : (
<>
{table.getFilteredRowModel().rows.length}{' '}
{t('Index.swaps').slice(0, -1).toLowerCase()}(s)
</>
)}
</div>

<div className="flex items-center space-x-2">
<button
onClick={() => table.previousPage()}
disabled={!table.getCanPreviousPage() || loading}
className="transition-opacity hover:opacity-75 disabled:cursor-not-allowed disabled:opacity-50"
>
<SquareArrowLeft size={24} />
</button>

<button
onClick={() => table.nextPage()}
disabled={!table.getCanNextPage() || loading}
className="transition-opacity hover:opacity-75 disabled:cursor-not-allowed disabled:opacity-50"
>
<SquareArrowRight size={24} />
</button>
</div>
</div>
</div>
);
};
10 changes: 5 additions & 5 deletions src/views/transactions/Transaction.tsx
Original file line number Diff line number Diff line change
@@ -46,18 +46,18 @@ export const Transaction: FC<{
<div>
<p className="font-medium">
{locale === 'es'
? t('App.Wallet.Transactions.ago') +
? t('App.Wallet.ago') +
' ' +
formatDistanceToNowStrict(date, {
locale: es,
})
: formatDistanceToNowStrict(date) +
' ' +
t('App.Wallet.Transactions.ago')}
: formatDistanceToNowStrict(date) + ' ' + t('App.Wallet.ago')}
</p>

<p className="text-sm text-slate-600 dark:text-neutral-400">
{format(date, 'MMM dd, yyyy')}
{format(date, 'MMM dd, yyyy', {
locale: locale === 'es' ? es : undefined,
})}
</p>
</div>
) : (
4 changes: 1 addition & 3 deletions src/views/transactions/TransactionsTable.tsx
Original file line number Diff line number Diff line change
@@ -98,9 +98,7 @@ export const TransactionsTable = <T,>({
</Fragment>
))
) : (
<p className="font-semibold">
{t('App.Wallet.Transactions.no-results')}
</p>
<p className="font-semibold">{t('App.Wallet.no-results')}</p>
)}
</>
)}
12 changes: 6 additions & 6 deletions src/views/wallet/WalletInfo.tsx
Original file line number Diff line number Diff line change
@@ -529,7 +529,7 @@ export const WalletInfo: FC<{
<div className="mb-3 flex w-full justify-between space-x-2">
<button
onClick={() => setView('assets')}
className="z-10 h-fit font-semibold text-primary transition-colors hover:text-primary-hover"
className="z-[1] h-fit font-semibold text-primary transition-colors hover:text-primary-hover"
>
{data?.wallets.find_one.name}
</button>
@@ -538,7 +538,7 @@ export const WalletInfo: FC<{
<IconButton
icon={hideBalance ? <EyeOff size={20} /> : <Eye size={20} />}
onClick={() => setHideBalance(h => !h)}
className="z-10"
className="z-[1]"
/>

<RefreshButton className="z-[1]" />
@@ -547,7 +547,7 @@ export const WalletInfo: FC<{

<button
onClick={() => setView('assets')}
className="relative z-10 mb-1 text-4xl font-semibold"
className="relative z-[1] mb-1 text-4xl font-semibold"
>
{hideBalance ? '***' : totalBalance}
</button>
@@ -556,7 +556,7 @@ export const WalletInfo: FC<{
{balancePercentages.map(b => (
<p
key={b.assetId}
className="z-10 text-sm font-medium text-slate-600 dark:text-neutral-400 lg:text-base"
className="z-[1] text-sm font-medium text-slate-600 dark:text-neutral-400 lg:text-base"
>
{hideBalance ? '***' : b.formatted_balance} {b.ticker}
</p>
@@ -567,7 +567,7 @@ export const WalletInfo: FC<{
<Button asChild>
<Link
href={ROUTES.wallet.receive}
className="z-10 flex w-full items-center justify-center space-x-2 sm:max-w-32"
className="z-[1] flex w-full items-center justify-center space-x-2 sm:max-w-32"
>
<p>{t('receive')}</p> <ArrowDown size={16} />
</Link>
@@ -576,7 +576,7 @@ export const WalletInfo: FC<{
<Button asChild variant="secondary">
<Link
href={ROUTES.wallet.send}
className="z-10 flex w-full items-center justify-center space-x-2 sm:max-w-32"
className="z-[1] flex w-full items-center justify-center space-x-2 sm:max-w-32"
>
<p>{t('send')}</p> <ArrowUp size={16} />
</Link>

0 comments on commit 70d0a35

Please sign in to comment.