diff --git a/examples/vite/package.json b/examples/vite/package.json index 0d8cb871..b3e17c14 100644 --- a/examples/vite/package.json +++ b/examples/vite/package.json @@ -4,6 +4,8 @@ "private": true, "dependencies": { "@ar.io/sdk": "alpha", + "@tanstack/react-query": "^5.64.1", + "@tanstack/react-table": "^8.20.6", "react": "^18.3.1", "react-dom": "^18.3.1", "react-markdown": "^9.0.1", diff --git a/examples/vite/src/App.tsx b/examples/vite/src/App.tsx index e5ace208..9ea923ae 100644 --- a/examples/vite/src/App.tsx +++ b/examples/vite/src/App.tsx @@ -1,345 +1,291 @@ +import { ARIO, ARIO_DEVNET_PROCESS_ID, mARIOToken } from '@ar.io/sdk/web'; +import { useQuery, useQueryClient } from '@tanstack/react-query'; import { - ARIO, - ARIO_DEVNET_PROCESS_ID, - AoArNSNameDataWithName, - AoGatewayWithAddress, - AoReturnedName, - AoWeightedObserver, - PaginationResult, -} from '@ar.io/sdk/web'; -import { useEffect, useState } from 'react'; -import { - Label, - Line, - LineChart, - ReferenceLine, - ResponsiveContainer, - Tooltip, - XAxis, - YAxis, -} from 'recharts'; + flexRender, + getCoreRowModel, + useReactTable, +} from '@tanstack/react-table'; import './App.css'; +import { useArNSRecords, useArNSReturnedNames } from './hooks/useArNS'; +import { useGatewayDelegations, useGateways } from './hooks/useGatewayRegistry'; const ario = ARIO.init({ processId: ARIO_DEVNET_PROCESS_ID }); -type ReturnedNameWithPrices = AoReturnedName & { - currentPrice: number; -}; - function App() { - const [returnedNames, setReturnedNames] = useState([]); - const [selectedReturnedName, setSelectedReturnedName] = - useState(null); - const [names, setNames] = useState([]); - const [gateways, setGateways] = useState([]); - const [totalGateways, setTotalGateways] = useState(0); - const [totalNames, setTotalNames] = useState(0); - const [totalReturnedNames, setTotalReturnedNames] = useState(0); - const [prescribedObservers, setPrescribedObservers] = useState< - AoWeightedObserver[] - >([]); + const { + data: names, + isLoading: namesLoading, + error: namesError, + } = useArNSRecords({ + ario, + limit: 10, + cursor: undefined, + sortBy: 'name', + sortOrder: 'asc', + }); - useEffect(() => { - // fetch first page of arns names - ario - .getArNSRecords({ limit: 10 }) - .then((page: PaginationResult) => { - setNames(page.items); - setTotalNames(page.totalItems); - }); + const { + data: gateways, + isLoading: gatewaysLoading, + error: gatewaysError, + } = useGateways({ + ario, + limit: 10, + cursor: undefined, + sortBy: 'startTimestamp', + sortOrder: 'asc', + }); - // fetch first page of gateways - ario - .getGateways({ limit: 10 }) - .then((page: PaginationResult) => { - setGateways(page.items); - setTotalGateways(page.totalItems); - }); + const { + data: returnedNames, + isLoading: returnedNamesLoading, + error: returnedNamesError, + } = useArNSReturnedNames({ + ario, + limit: 10, + cursor: undefined, + sortBy: 'name', + sortOrder: 'asc', + }); - // get returned names and prices for each returned name - ario - .getArNSReturnedNames({ limit: 10 }) - .then((page: PaginationResult) => { - setReturnedNames(page.items); - setTotalReturnedNames(page.totalItems); - page.items.forEach((returnedName: AoReturnedName) => { - ario - .getTokenCost({ - name: returnedName.name, - intent: 'Buy-Record', - type: 'lease', - intervalMs: 1000 * 60 * 60 * 24, // 1 day - }) - .then((price: number) => { - setSelectedReturnedName({ - ...returnedName, - currentPrice: price / 10 ** 6, - }); + const namesTable = useReactTable({ + data: names?.items ?? [], + columns: [ + { + accessorKey: 'name', + header: 'Name', + }, + { + accessorKey: 'processId', + header: 'Process', + }, + { + accessorKey: 'type', + header: 'Type', + }, + { + accessorKey: 'purchasePrice', + header: 'Purchase Price', + cell: ({ row }) => { + return row.original.purchasePrice + ? `${new mARIOToken(row.original.purchasePrice).toARIO().valueOf().toFixed(2)} ARIO` + : 'N/A'; + }, + }, + { + accessorKey: 'startTimestamp', + header: 'Purchased', + cell: ({ row }) => { + return new Date(row.original.startTimestamp).toLocaleDateString(); + }, + }, + { + accessorKey: 'endTimestamp', + header: 'Expiry', + cell: ({ row }) => { + const endTimestamp = + row.original.type === 'lease' + ? row.original.endTimestamp + : undefined; + return endTimestamp + ? new Date(endTimestamp).toLocaleDateString() + : 'Infinite'; + }, + }, + ], + getCoreRowModel: getCoreRowModel(), + }); + + const gatewaysTable = useReactTable({ + data: gateways?.items ?? [], + columns: [ + { + accessorKey: 'gatewayAddress', + header: 'Address', + }, + { + accessorKey: 'observerAddress', + header: 'Observer', + }, + { + accessorKey: 'status', + header: 'Status', + }, + { + accessorKey: 'startTimestamp', + header: 'Joined', + cell: ({ row }) => { + return new Date(row.original.startTimestamp).toLocaleDateString(); + }, + }, + { + accessorKey: 'operatorStake', + header: 'Operator Stake', + }, + { + accessorKey: 'totalDelegatedStake', + header: 'Total Delegated Stake', + }, + { + accessorKey: 'totalDelegations', + header: 'Total Delegations', + cell: ({ row }) => { + const { data: delegations, isLoading: delegationsLoading } = + useGatewayDelegations({ + ario, + gatewayAddress: row.original.gatewayAddress, + limit: 10, + cursor: undefined, }); - }); - }); + if (delegationsLoading) return 'Loading...'; + return delegations?.totalItems; + }, + }, + ], + getCoreRowModel: getCoreRowModel(), + }); - ario.getPrescribedObservers().then((observers: AoWeightedObserver[]) => { - setPrescribedObservers(observers); - }); - }, []); + const returnedNamesTable = useReactTable({ + data: returnedNames?.items ?? [], + columns: [ + { + accessorKey: 'name', + header: 'Name', + }, + { + accessorKey: 'startTimestamp', + header: 'Starts', + }, + { + accessorKey: 'endTimestamp', + header: 'Ends', + }, + { + accessorKey: 'premiumMultiplier', + header: 'Base Fee', + }, + { + accessorKey: 'initiator', + header: 'Initiator', + }, + ], + getCoreRowModel: getCoreRowModel(), + }); return ( -
-
-
-

ArNS Names

-
- Total Names: {totalNames} -
-
- - - - - - - +
+
+

AR.IO Network Explorer

+
+
+

ArNS Names

+
Name - Process - Type - Expiry -
+ + {namesTable.getHeaderGroups().map((headerGroup) => { + return ( + + {headerGroup.headers.map((header) => ( + + ))} - - - {names.map((record) => ( - - - - - - + ); + })} + + + {namesTable.getRowModel().rows.map((row) => ( + + {row.getVisibleCells().map((cell) => ( + ))} - -
+ {flexRender( + header.column.columnDef.header, + header.getContext(), + )} +
{record.name} - {record.processId.slice(0, 8)}... - {record.type} - {record.type === 'lease' && record.endTimestamp - ? new Date(record.endTimestamp).toLocaleDateString() - : 'N/A'} -
+ {flexRender(cell.column.columnDef.cell, cell.getContext())} +
-
-
- -
- {' '} -

Active Gateways

-
- Total Gateways: {totalGateways} -
-
- - - - - - - - - - - {gateways.map((gateway) => ( - + ))} + +
- Address - - Status - - Operator Stake (ARIO) - - Total Delegated Stake (ARIO) -
+
+
+

ArNS Returned Names

+ + + {returnedNamesTable.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => ( + - - - - - ))} - -
- - {gateway.gatewayAddress.slice(0, 8)}... - {gateway.status} - {gateway.operatorStake / 10 ** 6} - - {gateway.totalDelegatedStake / 10 ** 6} -
-
-
-
- {' '} -

Active Returned names

-
- Total Returned names: {totalReturnedNames} -
-
- - - - - - - - - - - - {returnedNames.map((returnedName) => ( - - - - - - - ))} - -
Name - Starts - Ends - Base Fee + {flexRender( + header.column.columnDef.header, + header.getContext(), + )} - Initiator -
{returnedName.name} - {new Date( - returnedName.startTimestamp, - ).toLocaleDateString()} - - {new Date(returnedName.endTimestamp).toLocaleDateString()} - - {returnedName.premiumMultiplier} - - {returnedName.initiator.slice(0, 8)}... -
-
- {selectedReturnedName && ( -
- - - - - - - - - - - - -
- )} -
-
-

Prescribed Observers

- - - - - - - - - - - {prescribedObservers.map((observer) => ( - - + {returnedNamesTable.getRowModel().rows.map((row) => ( + + {row.getVisibleCells().map((cell) => ( + - - - - + ))} + +
- Observer - Stake - Tenure Weight - - Stake Weight - - Normalized Weight -
- {observer.gatewayAddress.slice(0, 8)}... + ))} + +
+ {flexRender(cell.column.columnDef.cell, cell.getContext())} {observer.stake}{observer.tenureWeight}{observer.stakeWeight} - {observer.compositeWeight} + ))} +
+
+
+

Gateways

+ + + {gatewaysTable.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => ( + + ))} + + ))} + + + {gatewaysTable.getRowModel().rows.map((row) => ( + + {row.getVisibleCells().map((cell) => ( + - - ))} - -
+ {header.isPlaceholder + ? null + : flexRender( + header.column.columnDef.header, + header.getContext(), + )} +
+ {flexRender(cell.column.columnDef.cell, cell.getContext())}
-
+ ))} + + ))} + +
); diff --git a/examples/vite/src/hooks/useArNS.tsx b/examples/vite/src/hooks/useArNS.tsx new file mode 100644 index 00000000..36c5526d --- /dev/null +++ b/examples/vite/src/hooks/useArNS.tsx @@ -0,0 +1,51 @@ +import { AoARIORead } from '@ar.io/sdk'; +import { useQuery } from '@tanstack/react-query'; + +export const useArNSReturnedNames = ({ + ario, + limit, + cursor, + sortBy, + sortOrder, +}: { + ario: AoARIORead; + limit: number; + cursor: string | undefined; + sortBy: 'name' | 'startTimestamp' | 'initiator'; + sortOrder: 'asc' | 'desc'; +}) => { + const { data, isLoading, error } = useQuery({ + queryKey: ['ar-ns-returned-names'], + queryFn: () => + ario.getArNSReturnedNames({ limit, cursor, sortBy, sortOrder }), + }); + + return { data, isLoading, error }; +}; + +export const useArNSRecords = ({ + ario, + limit, + cursor, + sortBy, + sortOrder, +}: { + ario: AoARIORead; + limit: number; + cursor: string | undefined; + sortBy: + | 'processId' + | 'startTimestamp' + | 'type' + | 'undernameLimit' + | 'purchasePrice' + | 'name'; + sortOrder: 'asc' | 'desc'; +}) => { + const { data, isLoading, error } = useQuery({ + queryKey: ['ar-ns-records'], + queryFn: () => ario.getArNSRecords({ limit, cursor, sortBy, sortOrder }), + }); + + return { data, isLoading, error }; +}; diff --git a/examples/vite/src/hooks/useGatewayRegistry.tsx b/examples/vite/src/hooks/useGatewayRegistry.tsx new file mode 100644 index 00000000..850afee3 --- /dev/null +++ b/examples/vite/src/hooks/useGatewayRegistry.tsx @@ -0,0 +1,47 @@ +import { AoARIORead } from '@ar.io/sdk'; +import { useQuery } from '@tanstack/react-query'; + +export const useGateways = ({ + ario, + limit, + cursor, + sortBy, + sortOrder, +}: { + ario: AoARIORead; + limit: number; + cursor: string | undefined; + sortBy: 'startTimestamp' | 'operatorStake' | 'totalDelegatedStake'; + sortOrder: 'asc' | 'desc'; +}) => { + const { data, isLoading, error } = useQuery({ + queryKey: ['gateways'], + queryFn: () => ario.getGateways({ limit, cursor, sortBy, sortOrder }), + }); + + return { data, isLoading, error }; +}; + +export const useGatewayDelegations = ({ + ario, + gatewayAddress, + limit, + cursor, +}: { + ario: AoARIORead; + gatewayAddress: string; + limit: number; + cursor: string | undefined; +}) => { + const { data, isLoading, error } = useQuery({ + queryKey: ['gateway-delegations', gatewayAddress], + queryFn: () => + ario.getDelegations({ + address: gatewayAddress, + limit, + cursor, + }), + }); + + return { data, isLoading, error }; +}; diff --git a/examples/vite/src/index.tsx b/examples/vite/src/index.tsx index 3f83eeae..d1a38f7f 100644 --- a/examples/vite/src/index.tsx +++ b/examples/vite/src/index.tsx @@ -1,14 +1,19 @@ +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; import React from 'react'; import ReactDOM from 'react-dom/client'; import App from './App'; import './index.css'; +const queryClient = new QueryClient(); + const root = ReactDOM.createRoot( document.getElementById('root') as HTMLElement, ); root.render( - + + + , ); diff --git a/examples/vite/yarn.lock b/examples/vite/yarn.lock index c4731a1d..2a87ea25 100644 --- a/examples/vite/yarn.lock +++ b/examples/vite/yarn.lock @@ -876,6 +876,30 @@ resolved "https://registry.yarnpkg.com/@sinclair/typebox/-/typebox-0.27.8.tgz#6667fac16c436b5434a387a34dedb013198f6e6e" integrity sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA== +"@tanstack/query-core@5.64.1": + version "5.64.1" + resolved "https://registry.yarnpkg.com/@tanstack/query-core/-/query-core-5.64.1.tgz#d56e26b3e29fc68a89d140f1fd92900bc8f3fc86" + integrity sha512-978Wx4Wl4UJZbmvU/rkaM9cQtXXrbhK0lsz/UZhYIbyKYA8E4LdomTwyh2GHZ4oU0BKKoDH4YlKk2VscCUgNmg== + +"@tanstack/react-query@^5.64.1": + version "5.64.1" + resolved "https://registry.yarnpkg.com/@tanstack/react-query/-/react-query-5.64.1.tgz#46b4182f5b045299e4be8d0a91c549ac5dc0a20c" + integrity sha512-vW5ggHpIO2Yjj44b4sB+Fd3cdnlMJppXRBJkEHvld6FXh3j5dwWJoQo7mGtKI2RbSFyiyu/PhGAy0+Vv5ev9Eg== + dependencies: + "@tanstack/query-core" "5.64.1" + +"@tanstack/react-table@^8.20.6": + version "8.20.6" + resolved "https://registry.yarnpkg.com/@tanstack/react-table/-/react-table-8.20.6.tgz#a1f3103327aa59aa621931f4087a7604a21054d0" + integrity sha512-w0jluT718MrOKthRcr2xsjqzx+oEM7B7s/XXyfs19ll++hlId3fjTm+B2zrR3ijpANpkzBAr15j1XGVOMxpggQ== + dependencies: + "@tanstack/table-core" "8.20.5" + +"@tanstack/table-core@8.20.5": + version "8.20.5" + resolved "https://registry.yarnpkg.com/@tanstack/table-core/-/table-core-8.20.5.tgz#3974f0b090bed11243d4107283824167a395cf1d" + integrity sha512-P9dF7XbibHph2PFRz8gfBKEXEY/HJPOhym8CHmjF8y3q5mWpKx9xtZapXQUWCgkqvsK0R46Azuz+VaxD4Xl+Tg== + "@testing-library/dom@^8.5.0": version "8.20.1" resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-8.20.1.tgz#2e52a32e46fc88369eef7eef634ac2a192decd9f"