From 38d7d7ea4947e5eb784bc6c93b24f5c02ddef9cb Mon Sep 17 00:00:00 2001 From: Ignacio Date: Wed, 24 Apr 2024 13:26:21 +0800 Subject: [PATCH] feat: removing the transaction filter for cheqd --- .../components/desktop/index.tsx | 166 ++++++++++++++++++ .../src/screens/transactions/hooks.ts | 143 +++++++++++++++ 2 files changed, 309 insertions(+) create mode 100644 apps/web-cheqd/src/components/transactions_list/components/desktop/index.tsx create mode 100644 apps/web-cheqd/src/screens/transactions/hooks.ts diff --git a/apps/web-cheqd/src/components/transactions_list/components/desktop/index.tsx b/apps/web-cheqd/src/components/transactions_list/components/desktop/index.tsx new file mode 100644 index 0000000000..59d43128d7 --- /dev/null +++ b/apps/web-cheqd/src/components/transactions_list/components/desktop/index.tsx @@ -0,0 +1,166 @@ +import Loading from '@/components/loading'; +import Result from '@/components/result'; +import Tag from '@/components/tag'; +import Timestamp from '@/components/Timestamp'; +import useStyles from '@/components/transactions_list/components/desktop/styles'; +import { columns } from '@/components/transactions_list/components/desktop/utils'; +import type { TransactionsListState } from '@/components/transactions_list/types'; +import { useGrid } from '@/hooks/use_react_window'; +import { getMiddleEllipsis } from '@/utils/get_middle_ellipsis'; +import { BLOCK_DETAILS, TRANSACTION_DETAILS } from '@/utils/go_to_page'; +import { mergeRefs } from '@/utils/merge_refs'; +import Typography from '@mui/material/Typography'; +import useAppTranslation from '@/hooks/useAppTranslation'; +import Link from 'next/link'; +import numeral from 'numeral'; +import { FC, LegacyRef } from 'react'; +import AutoSizer from 'react-virtualized-auto-sizer'; +import { VariableSizeGrid as Grid } from 'react-window'; +import InfiniteLoader from 'react-window-infinite-loader'; + +const Desktop: FC = ({ + className, + itemCount, + loadMoreItems, + isItemLoaded, + transactions, +}) => { + const { gridRef, columnRef, onResize, getColumnWidth, getRowHeight } = useGrid(columns); + + const { classes, cx } = useStyles(); + const { t } = useAppTranslation('transactions'); + + const items = transactions.map((x) => ({ + block: ( + + {numeral(x.height).format('0,0')} + + ), + hash: ( + + {getMiddleEllipsis(x.hash, { + beginning: 4, + ending: 4, + })} + + ), + type: ( +
+ + {x.messages.count > 1 && ` + ${x.messages.count - 1}`} +
+ ), + result: , + time: , + messages: numeral(x.messages.count).format('0,0'), + })); + return ( +
+ + {({ height, width }) => ( + <> + {/* ======================================= */} + {/* Table Header */} + {/* ======================================= */} + } + columnCount={columns.length} + columnWidth={(index) => getColumnWidth(width ?? 0, index)} + height={50} + rowCount={1} + rowHeight={() => 50} + width={width ?? 0} + > + {({ columnIndex, style }) => { + const { key, align } = columns[columnIndex]; + + return ( +
+ + {t(key)} + +
+ ); + }} +
+ {/* ======================================= */} + {/* Table Body */} + {/* ======================================= */} + true)} + itemCount={itemCount} + loadMoreItems={ + loadMoreItems ?? + (() => { + // do nothing + }) + } + > + {({ onItemsRendered, ref }) => ( + { + onItemsRendered({ + overscanStartIndex: overscanRowStartIndex, + overscanStopIndex: overscanRowStopIndex, + visibleStartIndex: visibleRowStartIndex, + visibleStopIndex: visibleRowStopIndex, + }); + }} + ref={mergeRefs(gridRef, ref)} + columnCount={columns.length} + columnWidth={(index) => getColumnWidth(width ?? 0, index)} + height={(height ?? 0) - 50} + rowCount={itemCount} + rowHeight={getRowHeight} + width={width ?? 0} + className="scrollbar" + > + {({ columnIndex, rowIndex, style }) => { + if (!isItemLoaded?.(rowIndex) && columnIndex === 0) { + return ( +
+ +
+ ); + } + + if (!isItemLoaded?.(rowIndex)) { + return null; + } + + const { key, align } = columns[columnIndex]; + const item = items[rowIndex][key as keyof (typeof items)[number]]; + return ( +
+ + {item} + +
+ ); + }} +
+ )} +
+ + )} +
+
+ ); +}; + +export default Desktop; diff --git a/apps/web-cheqd/src/screens/transactions/hooks.ts b/apps/web-cheqd/src/screens/transactions/hooks.ts new file mode 100644 index 0000000000..72c3fe7565 --- /dev/null +++ b/apps/web-cheqd/src/screens/transactions/hooks.ts @@ -0,0 +1,143 @@ +import * as R from 'ramda'; +import { useState } from 'react'; +import { convertMsgsToModels } from '@/components/msg/utils'; +import { + TransactionsListenerSubscription, + useTransactionsListenerSubscription, + useTransactionsQuery, +} from '@/graphql/types/general_types'; +import type { TransactionsState } from '@/screens/transactions/types'; +import { convertMsgType } from '@/utils/convert_msg_type'; + +// This is a bandaid as it can get extremely +// expensive if there is too much data +/** + * Helps remove any possible duplication + * and sorts by height in case it bugs out + */ +const uniqueAndSort = R.pipe( + R.uniqBy((r: Transactions) => r?.hash), + R.sort(R.descend((r) => r?.height)) +); + +const formatTransactions = (data: TransactionsListenerSubscription): TransactionsState['items'] => { + let formattedData = data.transactions; + if (data.transactions.length === 51) { + formattedData = data.transactions.slice(0, 51); + } + + return formattedData.map((x) => { + const messages = convertMsgsToModels(x); + const msgType = + x.messages?.map((eachMsg: unknown) => { + const eachMsgType = R.pathOr('none type', ['@type'], eachMsg); + return eachMsgType ?? ''; + }) ?? []; + const convertedMsgType = convertMsgType(msgType); + return { + height: x.height, + hash: x.hash, + type: convertedMsgType, + messages: { + count: x.messages.length, + items: messages, + }, + success: x.success, + timestamp: x.block.timestamp, + }; + }); +}; + +export const useTransactions = () => { + const [state, setState] = useState({ + loading: true, + exists: true, + hasNextPage: false, + isNextPageLoading: true, + items: [], + }); + + const handleSetState = (stateChange: (prevState: TransactionsState) => TransactionsState) => { + setState((prevState) => { + const newState = stateChange(prevState); + return R.equals(prevState, newState) ? prevState : newState; + }); + }; + + // ================================ + // tx subscription + // ================================ + useTransactionsListenerSubscription({ + variables: { + limit: 1, + offset: 0, + }, + onData: (data) => { + const newItems = uniqueAndSort([ + ...(data.data.data ? formatTransactions(data.data.data) : []), + ...state.items, + ]); + handleSetState((prevState) => ({ + ...prevState, + loading: false, + items: newItems, + })); + }, + }); + + // ================================ + // tx query + // ================================ + const LIMIT = 51; + const transactionQuery = useTransactionsQuery({ + variables: { + limit: LIMIT, + offset: 1, + }, + onError: () => { + handleSetState((prevState) => ({ ...prevState, loading: false })); + }, + onCompleted: (data) => { + const itemsLength = data.transactions.length; + const newItems = uniqueAndSort([...state.items, ...formatTransactions(data)]); + handleSetState((prevState) => ({ + ...prevState, + loading: false, + items: newItems, + hasNextPage: itemsLength === 51, + isNextPageLoading: false, + })); + }, + }); + + const loadNextPage = async () => { + handleSetState((prevState) => ({ ...prevState, isNextPageLoading: true })); + // refetch query + await transactionQuery + .fetchMore({ + variables: { + offset: state.items.length, + limit: LIMIT, + }, + }) + .then(({ data }) => { + const itemsLength = data.transactions.length; + const newItems = uniqueAndSort([ + ...state.items, + ...formatTransactions(data), + ]) as TransactionsState['items']; + // set new state + handleSetState((prevState) => ({ + ...prevState, + items: newItems, + isNextPageLoading: false, + hasNextPage: itemsLength === 51, + })); + }); + }; + + return { + state, + loadNextPage, + }; +};