diff --git a/apps/web-cheqd/src/components/transaction_type_filter/components/transaction_type_search/hooks.ts b/apps/web-cheqd/src/components/transaction_type_filter/components/transaction_type_search/hooks.ts new file mode 100644 index 0000000000..7e35e2c4ea --- /dev/null +++ b/apps/web-cheqd/src/components/transaction_type_filter/components/transaction_type_search/hooks.ts @@ -0,0 +1,34 @@ +import { ChangeEventHandler, KeyboardEventHandler, useState } from 'react'; + +export const useTxTypeSearch = (callback: (value: string, clear?: () => void) => void) => { + const [value, setValue] = useState(''); + const handleOnChange: ChangeEventHandler = (e) => { + const newValue = e?.target?.value ?? ''; + setValue(newValue); + callback(newValue, clear); + }; + + const handleOnSubmit = () => { + callback(value, clear); + }; + + const handleKeyDown: KeyboardEventHandler = (e) => { + const shift = e?.shiftKey; + const isEnter = e?.keyCode === 13 || e?.key === 'Enter'; + if (isEnter && !shift) { + e.preventDefault(); + callback(value, clear); + } + }; + + const clear = () => { + setValue(''); + }; + + return { + handleOnChange, + handleOnSubmit, + value, + handleKeyDown, + }; +}; diff --git a/apps/web-cheqd/src/components/transaction_type_filter/components/transaction_type_search/index.tsx b/apps/web-cheqd/src/components/transaction_type_filter/components/transaction_type_search/index.tsx new file mode 100644 index 0000000000..8e0da13137 --- /dev/null +++ b/apps/web-cheqd/src/components/transaction_type_filter/components/transaction_type_search/index.tsx @@ -0,0 +1,37 @@ +import InputAdornment from '@mui/material/InputAdornment'; +import InputBase from '@mui/material/InputBase'; +import { FC } from 'react'; +import { useTxTypeSearch } from '@/components/transaction_type_filter/components/transaction_type_search/hooks'; +import useStyles from '@/components/transaction_type_filter/components/transaction_type_search/styles'; +import IconSearch from 'shared-utils/assets/icon-search.svg'; + +type TxTypeSearchProps = { + className?: string; + placeholder: string; + callback: (value: string) => void; +}; + +const TxTypeSearch: FC = ({ className, placeholder, callback }) => { + const { classes, cx } = useStyles(); + const { handleOnSubmit, handleOnChange, handleKeyDown, value } = useTxTypeSearch(callback); + return ( +
+ + + + } + /> + + ); +}; + +export default TxTypeSearch; diff --git a/apps/web-cheqd/src/components/transaction_type_filter/components/transaction_type_search/styles.ts b/apps/web-cheqd/src/components/transaction_type_filter/components/transaction_type_search/styles.ts new file mode 100644 index 0000000000..b391f5cab5 --- /dev/null +++ b/apps/web-cheqd/src/components/transaction_type_filter/components/transaction_type_search/styles.ts @@ -0,0 +1,25 @@ +import { makeStyles } from 'tss-react/mui'; + +const useStyles = makeStyles()((theme) => ({ + root: { + '& .MuiInputBase-root': { + width: '300px', + height: '32px', + background: + theme.palette.mode === 'dark' ? theme.palette.divider : theme.palette.background.default, + padding: theme.spacing(0.4, 1.2), + borderRadius: '8px', + }, + '& .MuiInputBase-input': { + textOverflow: 'ellipsis', + '&::placeholder': { + color: theme.palette.custom.fonts.fontFour, + }, + }, + }, + iconSearch: { + fill: 'none', + }, +})); + +export default useStyles; diff --git a/apps/web-cheqd/src/components/transaction_type_filter/hooks.ts b/apps/web-cheqd/src/components/transaction_type_filter/hooks.ts new file mode 100644 index 0000000000..f2a906bfad --- /dev/null +++ b/apps/web-cheqd/src/components/transaction_type_filter/hooks.ts @@ -0,0 +1,279 @@ +// useTransactionTypeFilter hook +import { useEffect, useMemo, useState, ChangeEvent, useCallback } from 'react'; +import { useMessageTypesQuery, useMsgTypesByAddressQuery } from '@/graphql/types/general_types'; +import { SetterOrUpdater, useRecoilState } from 'recoil'; +import { writeFilter, writeOpenDialog, writeSelectedMsgTypes } from '@/recoil/transactions_filter'; +import { useRouter } from 'next/router'; + +// Define types for message type and message types +export type MessageType = { + __typename: string; + type: string; + module: string; + label: string; +}; + +export type MessageTypes = { + message_type: MessageType; +}; + +export const useTransactionTypeFilter = () => { + const router = useRouter(); + + // Fetch message types data based on address or all message types + const { + data: messageTypesData, + error: messageTypesError, + loading: messageTypesLoading, + refetch: messageTypesRefetch, + } = useMessageTypesQuery(); + const { + data: msgTypesByAddressData, + error: msgTypesByAddressError, + loading: msgTypesByAddressLoading, + refetch: msgTypesByAddressRefetch, + } = useMsgTypesByAddressQuery({ + variables: { + addresses: `{${router.query.address}}` as string, + }, + }); + + // Determine page context + const isAccountsPage = useMemo(() => window.location.pathname.includes('/accounts'), []); + const isValidatorDetailsPage = useMemo( + () => window.location.pathname.includes('/validators/'), + [] + ); + + // Determine data, error, loading, and refetch function based on page context + const data = isAccountsPage || isValidatorDetailsPage ? msgTypesByAddressData : messageTypesData; + const error = + isAccountsPage || isValidatorDetailsPage ? msgTypesByAddressError : messageTypesError; + const loading = + isAccountsPage || isValidatorDetailsPage ? msgTypesByAddressLoading : messageTypesLoading; + const refetch = + isAccountsPage || isValidatorDetailsPage ? msgTypesByAddressRefetch : messageTypesRefetch; + + // State for filtered message types and selected filters + const [filteredTypes, setFilteredTypes] = useState<{ module: string; msgTypes: MessageType[] }[]>( + [] + ); + const [selectedFilters, setSelectedFilters] = useState([]); + const [selectAllChecked, setSelectAllChecked] = useState(false); + + // Recoil state for managing filters and dialog state + const [, setFilter] = useRecoilState(writeFilter) as [string, SetterOrUpdater]; + const [, setSelectedMsgs] = useRecoilState(writeSelectedMsgTypes) as [ + string[], + SetterOrUpdater, + ]; + const [__, setOpenDialog] = useRecoilState(writeOpenDialog) as [ + boolean, + SetterOrUpdater, + ]; + + // Fetch data again if there's an error + useEffect(() => { + if (error) refetch(); + }, [error, refetch]); + + // Open and cancel dialog functions + const handleOpen = () => { + setOpenDialog(true); + }; + + const handleCancel = () => { + setOpenDialog(false); + }; + + // Merge all messages by label for transactions page + const mergeAllMsgsByLabelForTxsPage = (messages: MessageType[] | undefined): MessageType[] => { + // Initialize label map + const labelMap: { [key: string]: string } = {}; + + // Iterate over messages to merge by label + messages?.forEach((message) => { + if (!labelMap[message.label]) { + labelMap[message.label] = message.type; + } else { + labelMap[message.label] += `,${message.type}`; + } + }); + + // Initialize reduced messages array + const reducedMessages: MessageType[] = []; + + // Iterate over label map to create reduced messages + Object.entries(labelMap).forEach(([label, type]) => { + reducedMessages.push({ + __typename: 'message_type', + type, + module: messages?.find((msg) => msg.label === label)?.module || '', + label, + }); + }); + + return reducedMessages; + }; + + // Merge messages by label for address pages + const mergeAddressMsgsByLabel = (messages: MessageTypes[] | undefined): MessageType[] => { + // Initialize label map + const labelMap: { [key: string]: string } = {}; + + // Iterate over messages to merge by label + messages?.forEach((message) => { + if (!labelMap[message?.message_type?.label]) { + labelMap[message?.message_type?.label] = message?.message_type?.type; + } else { + labelMap[message?.message_type?.label] += `,${message?.message_type?.type}`; + } + }); + + // Initialize reduced messages array + const reducedMessages: MessageType[] = []; + + // Iterate over label map to create reduced messages + Object.entries(labelMap).forEach(([label, type]) => { + reducedMessages.push({ + __typename: 'message_type', + type, + module: + messages?.find((msg) => msg?.message_type?.label === label)?.message_type?.module || '', + label, + }); + }); + + return reducedMessages; + }; + + // Format message types based on page context + const formatTypes = useCallback( + ( + messages: MessageTypes[] | MessageType[] | null | undefined + ): { module: string; msgTypes: MessageType[] }[] => { + if (!messages) { + return []; + } + const msgs = [...messages]; + + // Merge labels based on page context + const updatedMessages = + isAccountsPage || isValidatorDetailsPage + ? mergeAddressMsgsByLabel(msgs as MessageTypes[]) + : mergeAllMsgsByLabelForTxsPage(msgs as MessageType[]); + + // Initialize module messages map + const moduleMessagesMap: { [key: string]: MessageType[] } = {}; + + // Iterate over updated messages to group by module + updatedMessages.forEach((msgType) => { + if (!moduleMessagesMap[msgType.module]) { + moduleMessagesMap[msgType.module] = []; + } + if (!moduleMessagesMap[msgType.module].some((msg) => msg.label === msgType.label)) { + moduleMessagesMap[msgType.module].push(msgType); + } + }); + + return Object.entries(moduleMessagesMap).map(([module, msgTypes]) => ({ + module, + msgTypes, + })); + }, + [isAccountsPage, isValidatorDetailsPage] + ); + + // Handle filtering transactions based on selected filters + const handleFilterTxs = () => { + const str = selectedFilters.join(','); + const query = `{${str}}`; + setFilter(query); + setSelectedFilters(selectedFilters); + setSelectAllChecked(false); + handleCancel(); + }; + + // Handle selection of transaction types + const handleTxTypeSelection = (event: ChangeEvent) => { + const { checked, value } = event.target; + if (checked) { + setSelectedFilters((prevFilters) => [...prevFilters, value]); + setSelectedMsgs((prevFilters) => [...prevFilters, value]); + } else { + setSelectedFilters((prevFilters) => prevFilters.filter((item) => item !== value)); + setSelectedMsgs((prevFilters) => prevFilters.filter((item) => item !== value)); + setSelectAllChecked(false); + } + }; + + // Handle selection of all transaction types + const handleSelectAllTxTypes = (event: ChangeEvent) => { + const { checked } = event.target; + setSelectAllChecked(checked); + if (checked) { + const allTypes = filteredTypes.flatMap((msgData) => msgData.msgTypes.map((msg) => msg.type)); + setSelectedFilters(allTypes); + setSelectedMsgs(allTypes); + } else { + setSelectedFilters([]); + setSelectedMsgs([]); + } + }; + + // Memoized computation of message type list + const msgTypeList = useMemo(() => { + const typesList = formatTypes( + isAccountsPage || isValidatorDetailsPage + ? (data?.msgTypes as MessageTypes[]) + : (data?.msgTypes as MessageType[]) + ); + typesList.sort((a, b) => a.module.localeCompare(b.module)); + setFilteredTypes(typesList); + return typesList; + }, [data?.msgTypes, formatTypes, isAccountsPage, isValidatorDetailsPage]); + + // Function to search/filter transaction types + const txTypeSearchFilter = useCallback( + (value: string) => { + const parsedValue = value.replace(/\s+/g, '').toLowerCase(); + if (parsedValue === '' || parsedValue === null) { + const typesList = formatTypes( + isAccountsPage || isValidatorDetailsPage + ? (data?.msgTypes as MessageTypes[]) + : (data?.msgTypes as MessageType[]) + ); + typesList.sort((a, b) => a.module.localeCompare(b.module)); + setFilteredTypes(typesList); + } else { + const typesList = formatTypes( + isAccountsPage || isValidatorDetailsPage + ? (data?.msgTypes as MessageTypes[]) + : (data?.msgTypes as MessageType[]) + ); + typesList.sort((a, b) => a.module.localeCompare(b.module)); + const types = typesList.filter( + (v: { module: string; msgTypes: { type: string; label: string }[] }) => + v.msgTypes.some((ms) => ms.type.toLowerCase().indexOf(parsedValue) !== -1) + ); + setFilteredTypes(types); + } + }, + [data?.msgTypes, formatTypes, isAccountsPage, isValidatorDetailsPage] + ); + + return { + data, + loading, + msgTypeList, + filteredTypes, + selectedFilters, + selectAllChecked, + txTypeSearchFilter, + handleCancel, + handleOpen, + handleFilterTxs, + handleTxTypeSelection, + handleSelectAllTxTypes, + }; +}; diff --git a/apps/web-cheqd/src/components/transaction_type_filter/index.tsx b/apps/web-cheqd/src/components/transaction_type_filter/index.tsx new file mode 100644 index 0000000000..25d2f6e7a0 --- /dev/null +++ b/apps/web-cheqd/src/components/transaction_type_filter/index.tsx @@ -0,0 +1,153 @@ +import React, { FC } from 'react'; +import Dialog from '@mui/material/Dialog'; +import DialogContent from '@mui/material/DialogContent'; +import DialogTitle from '@mui/material/DialogTitle'; +import Typography from '@mui/material/Typography'; +import useAppTranslation from '@/hooks/useAppTranslation'; +import FilterTxsIcon from 'shared-utils/assets/icon-filter-transactions.svg'; +import { useTransactionTypeFilter, MessageType } from '@/components/transaction_type_filter/hooks'; +import DialogActions from '@mui/material/DialogActions'; +import Button from '@mui/material/Button'; +import TxTypeSearch from '@/components/transaction_type_filter/components/transaction_type_search'; +import { useRecoilValue } from 'recoil'; +import { readOpenDialog, readSelectedMsgTypes } from '@/recoil/transactions_filter'; +import Checkbox from '@mui/material/Checkbox'; +import Loading from '@/components/loading'; +import useStyles from './styles'; + +const FilterTxsByType: FC = () => { + const { classes } = useStyles(); + const { t } = useAppTranslation('common'); + const { + loading, + filteredTypes, + txTypeSearchFilter, + selectAllChecked, + handleFilterTxs, + handleTxTypeSelection, + handleSelectAllTxTypes, + handleCancel, + handleOpen, + } = useTransactionTypeFilter(); + + const open = useRecoilValue(readOpenDialog) ?? false; + const selectedMsgTypes = useRecoilValue(readSelectedMsgTypes); + + return ( + <> +
+ +
+ + +
+ {t('filter')} +
+
+ +
+
+ {t('selectAll')} + +
+
+ + {!loading ? ( + filteredTypes?.map((msgData) => ( +
+
+ + {msgData?.module?.includes('ibc') + ? msgData.module.charAt(0).toUpperCase() + + msgData.module.charAt(1).toUpperCase() + + msgData.module.charAt(2).toUpperCase() + + msgData.module.slice(3) + : msgData?.module} + +
+
+
+ {msgData?.msgTypes + ?.slice(0, Math.ceil(msgData.msgTypes.length / 2)) + .map((msg: MessageType) => ( +
+ + handleTxTypeSelection(e)} + /> + + {msg?.label + ? msg?.label + .substring(3) + .match(/[A-Z][a-z]+|[0-9]+/g) + ?.join(' ') + : ''} + + +
+ ))} +
+
+ {msgData?.msgTypes + ?.slice(Math.ceil(msgData.msgTypes.length / 2)) + .map((msg: MessageType) => ( +
+ + handleTxTypeSelection(e)} + /> + + {msg?.label + ? msg?.label + .substring(3) + .match(/[A-Z][a-z]+|[0-9]+/g) + ?.join(' ') + : ''} + + +
+ ))} +
+
+
+ )) + ) : ( +
+ +
+ )} +
+ + + +
+ + ); +}; + +export default FilterTxsByType; diff --git a/apps/web-cheqd/src/components/transaction_type_filter/styles.ts b/apps/web-cheqd/src/components/transaction_type_filter/styles.ts new file mode 100644 index 0000000000..2e80f9e39e --- /dev/null +++ b/apps/web-cheqd/src/components/transaction_type_filter/styles.ts @@ -0,0 +1,132 @@ +import { makeStyles } from 'tss-react/mui'; + +const useStyles = makeStyles()((theme) => ({ + checkBox: { + height: '16px', + marginTop: '0.5px', + marginRight: '8px', + width: '16px', + '& svg': { + height: '16px', + width: '16px', + }, + }, + column: { + flexBasis: 'calc(50% - 20px)', + }, + columnContainer: { + display: 'flex', + flexDirection: 'row', + flexWrap: 'wrap', + justifyContent: 'space-between', + }, + dialog: { + zIndex: '1', + '& .MuiDialog-paper': { + borderRadius: '8px', + height: '650px', + width: '500px', + }, + }, + header: { + alignItems: 'center', + display: 'flex', + justifyContent: 'space-between', + paddingBottom: '30px', + paddingTop: '30px', + '& .MuiButtonBase-root': { + padding: 0, + }, + }, + icon: { + '& svg': { + fill: theme.palette.custom.general.icon, + '& path': { + fill: theme.palette.custom.general.icon, + }, + }, + display: 'inline-flex', + marginLeft: theme.spacing(5), + position: 'absolute', + width: '20px', + '&:hover': { + cursor: 'pointer', + }, + }, + loading: { + marginTop: theme.spacing(25), + }, + moduleName: { + paddingBottom: theme.spacing(1), + paddingTop: theme.spacing(3), + '& .MuiTypography-root': { + color: theme.palette.primary.main, + display: 'flex', + fontSize: '16px', + fontStyle: 'normal', + fontWeight: 590, + letterSpacing: '-0.544px', + lineHeight: '20px', + textTransform: 'capitalize', + }, + }, + moduleNameTypography: { + display: 'flex', + fontSize: '16px', + }, + msgLabel: { + color: theme.palette.text.secondary, + display: 'flex', + fontFamily: 'Helvetica Neue', + fontSize: '16px', + fontStyle: 'normal', + fontWeight: 400, + letterSpacing: '0.024px', + lineHeight: '19.09px', + }, + msgOption: { + color: theme.palette.custom.fonts.fontFour, + display: 'flex', + fontFamily: 'Helvetica Neue', + fontSize: '16px', + fontStyle: 'normal', + fontWeight: 400, + letterSpacing: '0.024px', + }, + msgType: { + display: 'flex', + paddingBottom: theme.spacing(0.5), + }, + selectAll: { + alignItems: 'center', + display: 'flex', + left: '401px', + position: 'absolute', + top: '70px', + '& .MuiTypography-root': { + color: theme.palette.text.secondary, + display: 'flex', + fontSize: '14px', + fontStyle: 'normal', + fontWeight: 400, + letterSpacing: '-0.544px', + lineHeight: '16px', + textTransform: 'capitalize', + }, + '& svg': { + height: '16px', + marginRight: '8px', + marginTop: '-3px', + width: '16px', + }, + }, + selectAllText: { + marginRight: '10px', + }, + title: { + alignItems: 'center', + display: 'flex', + }, +})); + +export default useStyles; diff --git a/apps/web-cheqd/src/components/transactions_list/__snapshots__/index.test.tsx.snap b/apps/web-cheqd/src/components/transactions_list/__snapshots__/index.test.tsx.snap new file mode 100644 index 0000000000..a712cf45ca --- /dev/null +++ b/apps/web-cheqd/src/components/transactions_list/__snapshots__/index.test.tsx.snap @@ -0,0 +1,52 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`component: TransactionsList it renders 1`] = ` +[ + .emotion-0 { + height: 100%; +} + +@media (max-width:1279.95px) { + .emotion-0 { + display: none; + } +} + +
+
+
, + .emotion-0 { + height: 100%; +} + +@media (min-width:1280px) { + .emotion-0 { + display: none; + } +} + +
+
+
, +] +`; 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 index 59d43128d7..d161f1f294 100644 --- a/apps/web-cheqd/src/components/transactions_list/components/desktop/index.tsx +++ b/apps/web-cheqd/src/components/transactions_list/components/desktop/index.tsx @@ -13,10 +13,34 @@ 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 React, { FC, MutableRefObject, LegacyRef } from 'react'; import AutoSizer from 'react-virtualized-auto-sizer'; import { VariableSizeGrid as Grid } from 'react-window'; import InfiniteLoader from 'react-window-infinite-loader'; +import FilterTxsByType from '@/components/transaction_type_filter'; +import NoData from '@/components/no_data'; + +const useRenderHeaderCell = ({ + columnIndex, + style, +}: { + columnIndex: number; + style: React.CSSProperties; +}) => { + const { key, align } = columns[columnIndex]; + const isTypeKey = key === 'type'; + const { t } = useAppTranslation('transactions'); + const { classes } = useStyles(); + + return ( +
+ + {t(key)} + + {isTypeKey && } +
+ ); +}; const Desktop: FC = ({ className, @@ -25,10 +49,8 @@ const Desktop: FC = ({ isItemLoaded, transactions, }) => { - const { gridRef, columnRef, onResize, getColumnWidth, getRowHeight } = useGrid(columns); - + const { gridRef, onResize, getColumnWidth, getRowHeight, columnRef } = useGrid(columns); const { classes, cx } = useStyles(); - const { t } = useAppTranslation('transactions'); const items = transactions.map((x) => ({ block: ( @@ -54,6 +76,18 @@ const Desktop: FC = ({ time: , messages: numeral(x.messages.count).format('0,0'), })); + + // Default isItemLoaded function + const defaultIsItemLoaded = () => true; + + // Render NoData component if itemCount is 0 + const noDataComponent = + itemCount === 0 ? ( +
+ +
+ ) : null; + return (
@@ -71,30 +105,15 @@ const Desktop: FC = ({ rowHeight={() => 50} width={width ?? 0} > - {({ columnIndex, style }) => { - const { key, align } = columns[columnIndex]; - - return ( -
- - {t(key)} - -
- ); - }} + {useRenderHeaderCell} {/* ======================================= */} {/* Table Body */} {/* ======================================= */} true)} + isItemLoaded={isItemLoaded ?? defaultIsItemLoaded} itemCount={itemCount} - loadMoreItems={ - loadMoreItems ?? - (() => { - // do nothing - }) - } + loadMoreItems={loadMoreItems ?? (() => Promise.resolve())} > {({ onItemsRendered, ref }) => ( = ({ visibleStopIndex: visibleRowStopIndex, }); }} - ref={mergeRefs(gridRef, ref)} + ref={mergeRefs(gridRef, ref) as MutableRefObject | null>} columnCount={columns.length} columnWidth={(index) => getColumnWidth(width ?? 0, index)} height={(height ?? 0) - 50} @@ -159,6 +178,7 @@ const Desktop: FC = ({ )}
+ {noDataComponent}
); }; diff --git a/apps/web-cheqd/src/components/transactions_list/components/desktop/styles.ts b/apps/web-cheqd/src/components/transactions_list/components/desktop/styles.ts new file mode 100644 index 0000000000..85c742b615 --- /dev/null +++ b/apps/web-cheqd/src/components/transactions_list/components/desktop/styles.ts @@ -0,0 +1,19 @@ +import { CSSObject } from '@emotion/react'; +import { makeStyles } from 'tss-react/mui'; + +const useStyles = makeStyles()((theme) => ({ + root: { + height: '100%', + }, + cell: { + ...(theme.mixins.tableCell as CSSObject), + }, + body: { + color: theme.palette.custom.fonts.fontTwo, + }, + noData: { + marginTop: '120px', + }, +})); + +export default useStyles; diff --git a/apps/web-cheqd/src/components/transactions_list/components/desktop/utils.tsx b/apps/web-cheqd/src/components/transactions_list/components/desktop/utils.tsx new file mode 100644 index 0000000000..eebb94378e --- /dev/null +++ b/apps/web-cheqd/src/components/transactions_list/components/desktop/utils.tsx @@ -0,0 +1,33 @@ +export const columns: { + key: string; + align?: 'left' | 'center' | 'right' | 'justify' | 'inherit'; + width: number; +}[] = [ + { + key: 'block', + width: 15, + }, + { + key: 'hash', + width: 15, + }, + { + key: 'type', + width: 25, + }, + { + key: 'messages', + align: 'right', + width: 10, + }, + { + key: 'result', + align: 'right', + width: 15, + }, + { + key: 'time', + align: 'right', + width: 20, + }, +]; diff --git a/apps/web-cheqd/src/components/transactions_list/components/index.ts b/apps/web-cheqd/src/components/transactions_list/components/index.ts new file mode 100644 index 0000000000..8d5aad328c --- /dev/null +++ b/apps/web-cheqd/src/components/transactions_list/components/index.ts @@ -0,0 +1,4 @@ +import Desktop from '@/components/transactions_list/components/desktop'; +import Mobile from '@/components/transactions_list/components/mobile'; + +export { Desktop, Mobile }; diff --git a/apps/web-cheqd/src/components/transactions_list/components/mobile/index.tsx b/apps/web-cheqd/src/components/transactions_list/components/mobile/index.tsx new file mode 100644 index 0000000000..9fa40f00a5 --- /dev/null +++ b/apps/web-cheqd/src/components/transactions_list/components/mobile/index.tsx @@ -0,0 +1,135 @@ +import Loading from '@/components/loading'; +import Result from '@/components/result'; +import SingleTransactionMobile from '@/components/single_transaction_mobile'; +import Tag from '@/components/tag'; +import Timestamp from '@/components/Timestamp'; +import useStyles from '@/components/transactions_list/components/mobile/styles'; +import type { TransactionsListState } from '@/components/transactions_list/types'; +import { useList, useListRow } 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 Divider from '@mui/material/Divider'; +import Link from 'next/link'; +import numeral from 'numeral'; +import { FC } from 'react'; +import AutoSizer from 'react-virtualized-auto-sizer'; +import { ListChildComponentProps, VariableSizeList as List } from 'react-window'; +import InfiniteLoader from 'react-window-infinite-loader'; + +type ListItemProps = Pick & { + setRowHeight: Parameters[1]; + isItemLoaded: ((index: number) => boolean) | undefined; + transaction: TransactionsListState['transactions'][number]; + isLast: boolean; +}; + +const ListItem: FC = ({ + index, + style, + setRowHeight, + isItemLoaded, + transaction, + isLast, +}) => { + const { rowRef } = useListRow(index, setRowHeight); + if (!isItemLoaded?.(index)) { + return ( +
+
+ +
+
+ ); + } + + const item = { + block: ( + + {numeral(transaction.height).format('0,0')} + + ), + hash: ( + + {getMiddleEllipsis(transaction.hash, { + beginning: 15, + ending: 5, + })} + + ), + type: ( +
+ + {transaction.messages.count > 1 && ` + ${transaction.messages.count - 1}`} +
+ ), + result: , + time: , + messages: numeral(transaction.messages.count).format('0,0'), + }; + return ( +
+
+ + {!isLast && } +
+
+ ); +}; + +const Mobile: FC = ({ + className, + itemCount, + loadMoreItems, + isItemLoaded, + transactions, +}) => { + const { classes, cx } = useStyles(); + + const { listRef, getRowHeight, setRowHeight } = useList(); + + return ( +
+ + {({ height, width }) => ( + true)} + itemCount={itemCount} + loadMoreItems={ + loadMoreItems ?? + (() => { + // do nothing + }) + } + > + {({ onItemsRendered, ref }) => ( + + {({ index, style }) => ( + + )} + + )} + + )} + +
+ ); +}; + +export default Mobile; diff --git a/apps/web-cheqd/src/components/transactions_list/components/mobile/styles.ts b/apps/web-cheqd/src/components/transactions_list/components/mobile/styles.ts new file mode 100644 index 0000000000..920476b7cf --- /dev/null +++ b/apps/web-cheqd/src/components/transactions_list/components/mobile/styles.ts @@ -0,0 +1,9 @@ +import { makeStyles } from 'tss-react/mui'; + +const useStyles = makeStyles()(() => ({ + root: { + height: '100%', + }, +})); + +export default useStyles; diff --git a/apps/web-cheqd/src/components/transactions_list/index.test.tsx b/apps/web-cheqd/src/components/transactions_list/index.test.tsx new file mode 100644 index 0000000000..4dc08d2afe --- /dev/null +++ b/apps/web-cheqd/src/components/transactions_list/index.test.tsx @@ -0,0 +1,72 @@ +import { ComponentProps } from 'react'; +import renderer from 'react-test-renderer'; +import TransactionsList from '@/components/transactions_list'; +import MockTheme from '@/tests/mocks/MockTheme'; + +// ================================== +// global setup +// ================================== +let component: renderer.ReactTestRenderer; + +// ================================== +// mocks +// ================================== +jest.mock('@/components/no_data', () => (props: JSX.IntrinsicElements['div']) => ( +
+)); + +const txs: ComponentProps['transactions'] = [ + { + height: 1212131, + hash: 'D0243447726B8BD7AE94BF4F98E536A647959194E870AB8566CB833A3CC847F6', + type: ['cosmos.distribution.v1beta1.MsgWithdrawDelegatorReward'], + success: true, + timestamp: '2021-05-24T05:28:05.839448', + messages: { + count: 2, + items: [ + { + '@type': '/cosmos.distribution.v1beta1.MsgWithdrawDelegatorReward', + delegator_address: 'desmos1r574p4zzhe0rq7w4acjwdqrejszjjz2lc4an7a', + validator_address: 'desmosvaloper1r574p4zzhe0rq7w4acjwdqrejszjjz2lxc4850', + }, + { + '@type': '/cosmos.distribution.v1beta1.MsgWithdrawValidatorCommission', + validator_address: 'desmosvaloper1r574p4zzhe0rq7w4acjwdqrejszjjz2lxc4850', + }, + ], + }, + }, +]; + +const props: ComponentProps = { + hasNextPage: true, + isNextPageLoading: false, + loadNextPage: () => jest.fn(), + loadMoreItems: () => jest.fn(), + isItemLoaded: () => false, + itemCount: 1, + transactions: txs, +}; + +// ================================== +// unit tests +// ================================== +describe('component: TransactionsList', () => { + beforeEach(() => { + component = renderer.create( + + + + ); + }); + + it('it renders', () => { + const tree = component?.toJSON(); + expect(tree).toMatchSnapshot(); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); +}); diff --git a/apps/web-cheqd/src/components/transactions_list/index.tsx b/apps/web-cheqd/src/components/transactions_list/index.tsx new file mode 100644 index 0000000000..100343c484 --- /dev/null +++ b/apps/web-cheqd/src/components/transactions_list/index.tsx @@ -0,0 +1,43 @@ +import { FC } from 'react'; +import Loading from '@/components/loading'; +import Desktop from '@/components/transactions_list/components/desktop'; +import Mobile from '@/components/transactions_list/components/mobile'; +import type { TransactionsListState } from '@/components/transactions_list/types'; +import { useDisplayStyles } from '@/styles/useSharedStyles'; + +const TransactionsList: FC = (props) => { + // setting fallback values + const { + hasNextPage = false, + isNextPageLoading = false, + loadNextPage = () => null, + loadMoreItems = () => null, + isItemLoaded = () => true, + itemCount, + transactions, + } = props; + const display = useDisplayStyles().classes; + + const formatProps: TransactionsListState = { + hasNextPage, + isNextPageLoading, + isItemLoaded, + loadNextPage, + loadMoreItems, + itemCount, + transactions, + }; + + if (!itemCount) { + if (isNextPageLoading) return ; + } + + return ( + <> + + + + ); +}; + +export default TransactionsList; diff --git a/apps/web-cheqd/src/components/transactions_list/types.ts b/apps/web-cheqd/src/components/transactions_list/types.ts new file mode 100644 index 0000000000..4551f17bcb --- /dev/null +++ b/apps/web-cheqd/src/components/transactions_list/types.ts @@ -0,0 +1,11 @@ +export interface TransactionsListState { + ['@type']?: string; + className?: string; + hasNextPage?: boolean; + isNextPageLoading?: boolean; + loadNextPage?: (...args: unknown[]) => void; + loadMoreItems?: (...args: unknown[]) => void; + isItemLoaded?: (index: number) => boolean; + itemCount: number; + transactions: Transactions[]; +} diff --git a/apps/web-cheqd/src/components/transactions_list_details/__snapshots__/index.test.tsx.snap b/apps/web-cheqd/src/components/transactions_list_details/__snapshots__/index.test.tsx.snap new file mode 100644 index 0000000000..15bfb8215a --- /dev/null +++ b/apps/web-cheqd/src/components/transactions_list_details/__snapshots__/index.test.tsx.snap @@ -0,0 +1,46 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`component: TransactionsListDetails handles empty data correctly 1`] = ` +
+`; + +exports[`component: TransactionsListDetails matches snapshot 1`] = ` +
+`; diff --git a/apps/web-cheqd/src/components/transactions_list_details/components/index.ts b/apps/web-cheqd/src/components/transactions_list_details/components/index.ts new file mode 100644 index 0000000000..21b88fa19d --- /dev/null +++ b/apps/web-cheqd/src/components/transactions_list_details/components/index.ts @@ -0,0 +1,3 @@ +import List from '@/components/transactions_list_details/components/list'; + +export { List }; diff --git a/apps/web-cheqd/src/components/transactions_list_details/components/list/__snapshots__/index.test.tsx.snap b/apps/web-cheqd/src/components/transactions_list_details/components/list/__snapshots__/index.test.tsx.snap new file mode 100644 index 0000000000..0cf57ad256 --- /dev/null +++ b/apps/web-cheqd/src/components/transactions_list_details/components/list/__snapshots__/index.test.tsx.snap @@ -0,0 +1,21 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`component: TransactionsListDetails/TransactionList matches snapshot 1`] = ` +.emotion-0 { + height: 100%; +} + +
+
+
+`; diff --git a/apps/web-cheqd/src/components/transactions_list_details/components/list/components/index.ts b/apps/web-cheqd/src/components/transactions_list_details/components/list/components/index.ts new file mode 100644 index 0000000000..47bfe720c8 --- /dev/null +++ b/apps/web-cheqd/src/components/transactions_list_details/components/list/components/index.ts @@ -0,0 +1,3 @@ +import SingleTransaction from '@/components/transactions_list_details/components/list/components/single_transaction'; + +export { SingleTransaction }; diff --git a/apps/web-cheqd/src/components/transactions_list_details/components/list/components/single_transaction/__snapshots__/index.test.tsx.snap b/apps/web-cheqd/src/components/transactions_list_details/components/list/components/single_transaction/__snapshots__/index.test.tsx.snap new file mode 100644 index 0000000000..46c608972c --- /dev/null +++ b/apps/web-cheqd/src/components/transactions_list_details/components/list/components/single_transaction/__snapshots__/index.test.tsx.snap @@ -0,0 +1,213 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`components: SingleTransaction matches snapshot 1`] = ` +.emotion-0 { + margin-bottom: 16px; + margin-top: 16px; +} + +.emotion-1 { + background: #F8F8F8; + padding: 8px 16px; + color: #414141; +} + +.emotion-2 { + margin: 0; + font-size: 1rem; + white-space: pre-wrap; + letter-spacing: 0.5px; + font-family: "Roboto","Helvetica","Arial",sans-serif; + font-weight: 400; + line-height: 1.5; +} + +.emotion-3 { + padding: 16px 16px 8px; +} + +.emotion-4 { + display: grid; + grid-template-columns: repeat(2, 1fr); +} + +@media (min-width:1280px) { + .emotion-4 { + grid-template-columns: repeat(4, 1fr); + } +} + +.emotion-5 { + grid-column: 1/3; + margin-bottom: 16px; +} + +.emotion-5.messages, +.emotion-5.result { + grid-column: auto/span 1; +} + +.emotion-5 .label { + margin-bottom: 8px; + color: #777777; +} + +.emotion-5 p.value { + color: #414141; +} + +.emotion-5 a { + color: #4092CD; +} + +@media (min-width:768px) { + .emotion-5 { + grid-column: auto/span 1; + } +} + +.emotion-6 { + margin: 0; + font-size: 1rem; + letter-spacing: 0.15px; + font-family: "Roboto","Helvetica","Arial",sans-serif; + font-weight: 400; + line-height: 1.235; +} + +.emotion-17 { + margin: 0; + -webkit-flex-shrink: 0; + -ms-flex-negative: 0; + flex-shrink: 0; + border-width: 0; + border-style: solid; + border-color: #E8E8E8; + border-bottom-width: thin; +} + +.emotion-19 { + margin-top: 24px; +} + +.emotion-20:not(:last-child) { + margin-bottom: 32px; +} + +.emotion-21 { + margin-bottom: 16px; +} + +
+
+

+

+ hash +
+

+
+
+
+
+

+ block +

+
+ block +
+
+
+

+ type +

+
+ type +
+
+
+

+ time +

+

+ 2021-07-13T08:00:00 +

+
+
+

+ messages +

+

+ 1,000 +

+
+
+

+ result +

+
+ result +
+
+
+
+
+
+
+
+
+ type +
+
+
+ message +
+
+
+
+
+
+`; diff --git a/apps/web-cheqd/src/components/transactions_list_details/components/list/components/single_transaction/index.test.tsx b/apps/web-cheqd/src/components/transactions_list_details/components/list/components/single_transaction/index.test.tsx new file mode 100644 index 0000000000..29f38dd430 --- /dev/null +++ b/apps/web-cheqd/src/components/transactions_list_details/components/list/components/single_transaction/index.test.tsx @@ -0,0 +1,35 @@ +import renderer from 'react-test-renderer'; +import SingleTransaction from '@/components/transactions_list_details/components/list/components/single_transaction'; +import MockTheme from '@/tests/mocks/MockTheme'; + +// ================================== +// unit tests +// ================================== +describe('components: SingleTransaction', () => { + it('matches snapshot', () => { + const component = renderer.create( + + block
} + hash={
hash
} + time="2021-07-13T08:00:00" + type={
type
} + messageCount="1,000" + messages={[ + { + type:
type
, + message:
message
, + }, + ]} + result={
result
} + /> + + ); + const tree = component?.toJSON(); + expect(tree).toMatchSnapshot(); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); +}); diff --git a/apps/web-cheqd/src/components/transactions_list_details/components/list/components/single_transaction/index.tsx b/apps/web-cheqd/src/components/transactions_list_details/components/list/components/single_transaction/index.tsx new file mode 100644 index 0000000000..e13876252e --- /dev/null +++ b/apps/web-cheqd/src/components/transactions_list_details/components/list/components/single_transaction/index.tsx @@ -0,0 +1,97 @@ +import Divider from '@mui/material/Divider'; +import Typography from '@mui/material/Typography'; +import useAppTranslation from '@/hooks/useAppTranslation'; +import { FC, ReactNode } from 'react'; +import useStyles from '@/components/transactions_list_details/components/list/components/single_transaction/styles'; + +export type SingleTransactionProps = { + className?: string; + block: ReactNode; + hash: ReactNode; + type: ReactNode; + time: string; + messageCount: string; + messages: Array< + { + type: ReactNode; + message: ReactNode; + } & JSX.IntrinsicAttributes + >; + result?: ReactNode; +}; + +const SingleTransaction: FC = ({ + className, + block, + hash, + type, + time, + messages, + result, + messageCount, +}) => { + const { t } = useAppTranslation('transactions'); + const { classes, cx } = useStyles(); + + return ( +
+
+ + {hash} + +
+
+
+
+ + {t('block')} + + {block} +
+
+ + {t('type')} + + {type} +
+
+ + {t('time')} + + + {time} + +
+
+ + {t('messages')} + + + {messageCount} + +
+
+ + {t('result')} + + {result} +
+
+ +
+
+ {messages.map((x, i) => ( + // eslint-disable-next-line react/no-array-index-key +
+
{x.type}
+ {x.message} +
+ ))} +
+
+
+
+ ); +}; + +export default SingleTransaction; diff --git a/apps/web-cheqd/src/components/transactions_list_details/components/list/components/single_transaction/styles.ts b/apps/web-cheqd/src/components/transactions_list_details/components/list/components/single_transaction/styles.ts new file mode 100644 index 0000000000..fcf3fafe67 --- /dev/null +++ b/apps/web-cheqd/src/components/transactions_list_details/components/list/components/single_transaction/styles.ts @@ -0,0 +1,61 @@ +import { makeStyles } from 'tss-react/mui'; + +const useStyles = makeStyles()((theme) => ({ + root: { + marginBottom: theme.spacing(2), + marginTop: theme.spacing(2), + }, + timeContainer: { + background: theme.palette.custom.general.surfaceTwo, + padding: theme.spacing(1, 2), + color: theme.palette.custom.fonts.fontTwo, + }, + itemContainer: { + padding: theme.spacing(2, 2, 1), + }, + itemPrimaryDetailsContainer: { + display: 'grid', + gridTemplateColumns: 'repeat(2, 1fr)', + [theme.breakpoints.up('lg')]: { + gridTemplateColumns: 'repeat(4, 1fr)', + }, + }, + item: { + gridColumn: '1/3', + marginBottom: theme.spacing(2), + '&.messages, &.result': { + gridColumn: 'auto / span 1', + }, + '& .label': { + marginBottom: theme.spacing(1), + color: theme.palette.custom.fonts.fontThree, + }, + '& p.value': { + color: theme.palette.custom.fonts.fontTwo, + }, + '& a': { + color: theme.palette.custom.fonts.highlight, + }, + [theme.breakpoints.up('md')]: { + '&.block, &.time': { + gridColumn: 'auto / span 1', + }, + }, + [theme.breakpoints.up('md')]: { + gridColumn: 'auto / span 1', + }, + }, + msgListContainer: { + marginTop: theme.spacing(3), + }, + msg: { + '&:not(:last-child)': { + marginBottom: theme.spacing(4), + }, + }, + tags: { + marginBottom: theme.spacing(2), + }, +})); + +export default useStyles; diff --git a/apps/web-cheqd/src/components/transactions_list_details/components/list/index.test.tsx b/apps/web-cheqd/src/components/transactions_list_details/components/list/index.test.tsx new file mode 100644 index 0000000000..364d929510 --- /dev/null +++ b/apps/web-cheqd/src/components/transactions_list_details/components/list/index.test.tsx @@ -0,0 +1,86 @@ +import renderer from 'react-test-renderer'; +import TransactionList from '@/components/transactions_list_details/components/list'; +import MockTheme from '@/tests/mocks/MockTheme'; + +// ================================== +// mocks +// ================================== + +jest.mock('react-window-infinite-loader', () => ({ + InfiniteLoader: (props: JSX.IntrinsicElements['div']) =>
, +})); + +jest.mock('@/components/loading', () => (props: JSX.IntrinsicElements['div']) => ( +
+)); + +jest.mock( + '@/components/transactions_list_details/components/list/components/single_transaction', + () => (props: JSX.IntrinsicElements['div']) =>
+); + +// ================================== +// unit tests +// ================================== +describe('component: TransactionsListDetails/TransactionList', () => { + it('matches snapshot', () => { + const txs = [ + { + height: 1212131, + hash: 'D0243447726B8BD7AE94BF4F98E536A647959194E870AB8566CB833A3CC847F6', + type: [''], + success: true, + timestamp: '2021-05-24T05:28:05.839448', + messages: { + count: 2, + items: [ + { + '@type': '/cosmos.distribution.v1beta1.MsgWithdrawDelegatorReward', + delegator_address: 'desmos1r574p4zzhe0rq7w4acjwdqrejszjjz2lc4an7a', + validator_address: 'desmosvaloper1r574p4zzhe0rq7w4acjwdqrejszjjz2lxc4850', + }, + { + '@type': '/cosmos.distribution.v1beta1.MsgWithdrawValidatorCommission', + validator_address: 'desmosvaloper1r574p4zzhe0rq7w4acjwdqrejszjjz2lxc4850', + }, + ], + }, + }, + { + height: 1212132, + hash: 'W0333447726B8BD7AE94BF4F98E536A647959194E870AB8566CB833A3CC847F6', + type: [''], + success: true, + timestamp: '2021-05-24T05:28:11.839448', + messages: { + count: 1, + items: [ + { + '@type': '/cosmos.distribution.v1beta1.MsgWithdrawDelegatorReward', + delegator_address: 'desmos1r574p4zzhe0rq7w4acjwdqrejszjjz2lc4an7a', + validator_address: 'desmosvaloper1r574p4zzhe0rq7w4acjwdqrejszjjz2lxc4850', + }, + ], + }, + }, + ]; + + const txProps = { + loadMoreItems: () => jest.fn(), + isItemLoaded: () => false, + itemCount: 2, + transactions: txs, + }; + const component = renderer.create( + + + + ); + const tree = component?.toJSON(); + expect(tree).toMatchSnapshot(); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); +}); diff --git a/apps/web-cheqd/src/components/transactions_list_details/components/list/index.tsx b/apps/web-cheqd/src/components/transactions_list_details/components/list/index.tsx new file mode 100644 index 0000000000..3403974ac7 --- /dev/null +++ b/apps/web-cheqd/src/components/transactions_list_details/components/list/index.tsx @@ -0,0 +1,138 @@ +import useAppTranslation from '@/hooks/useAppTranslation'; +import Link from 'next/link'; +import numeral from 'numeral'; +import { FC } from 'react'; +import AutoSizer from 'react-virtualized-auto-sizer'; +import { ListChildComponentProps, VariableSizeList as List } from 'react-window'; +import InfiniteLoader from 'react-window-infinite-loader'; +import { useRecoilValue } from 'recoil'; +import Loading from '@/components/loading'; +import { getMessageByType } from '@/components/msg/utils'; +import Result from '@/components/result'; +import Tag from '@/components/tag'; +import SingleTransaction from '@/components/transactions_list_details/components/list/components/single_transaction'; +import useStyles from '@/components/transactions_list_details/components/list/styles'; +import type { TransactionsListDetailsState } from '@/components/transactions_list_details/types'; +import { useList, useListRow } from '@/hooks/use_react_window'; +import { readDate, readTimeFormat } from '@/recoil/settings'; +import { useDisplayStyles } from '@/styles/useSharedStyles'; +import dayjs, { formatDayJs } from '@/utils/dayjs'; +import { getMiddleEllipsis } from '@/utils/get_middle_ellipsis'; +import { BLOCK_DETAILS, TRANSACTION_DETAILS } from '@/utils/go_to_page'; +import { mergeRefs } from '@/utils/merge_refs'; + +type ListItemProps = Pick & { + setRowHeight: Parameters[1]; + isItemLoaded: TransactionsListDetailsState['isItemLoaded']; + transaction: TransactionsListDetailsState['transactions'][number]; +}; + +const ListItem: FC = ({ index, style, setRowHeight, isItemLoaded, transaction }) => { + const { rowRef } = useListRow(index, setRowHeight); + const display = useDisplayStyles().classes; + const { t } = useAppTranslation('transactions'); + const dateFormat = useRecoilValue(readDate); + const timeFormat = useRecoilValue(readTimeFormat); + if (!isItemLoaded?.(index)) { + return ( +
+
+ +
+
+ ); + } + + const item = { + key: transaction.hash, + block: ( + + {numeral(transaction.height).format('0,0')} + + ), + hash: ( + + {transaction.hash} + + {getMiddleEllipsis(transaction.hash, { + beginning: 15, + ending: 5, + })} + + + ), + type: ( +
+ + {transaction.messages.count > 1 && ' +'} +
+ ), + result: , + time: formatDayJs(dayjs.utc(transaction.timestamp), dateFormat, timeFormat), + messageCount: numeral(transaction.messages.count).format('0,0'), + messages: transaction.messages.items.map((message) => getMessageByType(message, false, t)), + }; + return ( +
+
+ +
+
+ ); +}; + +const TransactionList: FC = ({ + className, + itemCount, + loadMoreItems, + isItemLoaded, + transactions, +}) => { + const { classes, cx } = useStyles(); + + const { listRef, getRowHeight, setRowHeight } = useList(); + + return ( +
+ + {({ height, width }) => ( + true)} + itemCount={itemCount} + loadMoreItems={ + loadMoreItems ?? + (() => { + // do nothing + }) + } + > + {({ onItemsRendered, ref }) => ( + + {({ index, style }) => ( + + )} + + )} + + )} + +
+ ); +}; + +export default TransactionList; diff --git a/apps/web-cheqd/src/components/transactions_list_details/components/list/styles.ts b/apps/web-cheqd/src/components/transactions_list_details/components/list/styles.ts new file mode 100644 index 0000000000..920476b7cf --- /dev/null +++ b/apps/web-cheqd/src/components/transactions_list_details/components/list/styles.ts @@ -0,0 +1,9 @@ +import { makeStyles } from 'tss-react/mui'; + +const useStyles = makeStyles()(() => ({ + root: { + height: '100%', + }, +})); + +export default useStyles; diff --git a/apps/web-cheqd/src/components/transactions_list_details/index.test.tsx b/apps/web-cheqd/src/components/transactions_list_details/index.test.tsx new file mode 100644 index 0000000000..513a3cc288 --- /dev/null +++ b/apps/web-cheqd/src/components/transactions_list_details/index.test.tsx @@ -0,0 +1,93 @@ +import renderer from 'react-test-renderer'; +import TransactionsListDetails from '@/components/transactions_list_details'; +import MockTheme from '@/tests/mocks/MockTheme'; + +// ================================== +// global setup +// ================================== +let component: renderer.ReactTestRenderer; + +// ================================== +// mocks +// ================================== +jest.mock('@/components/no_data', () => (props: JSX.IntrinsicElements['div']) => ( +
+)); + +jest.mock( + '@/components/transactions_list_details/components/list', + () => (props: JSX.IntrinsicElements['div']) =>
+); + +const txs = [ + { + height: 1212131, + hash: 'D0243447726B8BD7AE94BF4F98E536A647959194E870AB8566CB833A3CC847F6', + type: ['cosmos.distribution.v1beta1.MsgWithdrawDelegatorReward'], + success: true, + timestamp: '2021-05-24T05:28:05.839448', + messages: { + count: 2, + items: [ + { + '@type': '/cosmos.distribution.v1beta1.MsgWithdrawDelegatorReward', + delegator_address: 'desmos1r574p4zzhe0rq7w4acjwdqrejszjjz2lc4an7a', + validator_address: 'desmosvaloper1r574p4zzhe0rq7w4acjwdqrejszjjz2lxc4850', + }, + { + '@type': '/cosmos.distribution.v1beta1.MsgWithdrawValidatorCommission', + validator_address: 'desmosvaloper1r574p4zzhe0rq7w4acjwdqrejszjjz2lxc4850', + }, + ], + }, + }, +]; + +const props = { + hasNextPage: false, + isNextPageLoading: false, + loadNextPage: () => jest.fn(), + loadMoreItems: () => jest.fn(), + isItemLoaded: () => false, + itemCount: 1, + transactions: txs, +}; + +const emptyProps = { + hasNextPage: true, + isNextPageLoading: false, + loadNextPage: () => jest.fn(), + loadMoreItems: () => jest.fn(), + isItemLoaded: () => true, + itemCount: 0, + transactions: [], +}; + +// ================================== +// unit tests +// ================================== +describe('component: TransactionsListDetails', () => { + it('matches snapshot', () => { + component = renderer.create( + + + + ); + const tree = component?.toJSON(); + expect(tree).toMatchSnapshot(); + }); + + it('handles empty data correctly', () => { + component = renderer.create( + + + + ); + const tree = component?.toJSON(); + expect(tree).toMatchSnapshot(); + }); + + afterEach(() => { + jest.clearAllMocks(); + }); +}); diff --git a/apps/web-cheqd/src/components/transactions_list_details/index.tsx b/apps/web-cheqd/src/components/transactions_list_details/index.tsx new file mode 100644 index 0000000000..66ee826c71 --- /dev/null +++ b/apps/web-cheqd/src/components/transactions_list_details/index.tsx @@ -0,0 +1,35 @@ +import { FC } from 'react'; +import NoData from '@/components/no_data'; +import List from '@/components/transactions_list_details/components/list'; +import type { TransactionsListDetailsState } from '@/components/transactions_list_details/types'; + +const TransactionsListDetails: FC = (props) => { + // setting fallback values + const { + hasNextPage = false, + isNextPageLoading = false, + loadNextPage = () => null, + loadMoreItems = () => null, + isItemLoaded = () => true, + itemCount, + transactions, + } = props; + + const formatProps: TransactionsListDetailsState = { + hasNextPage, + isNextPageLoading, + isItemLoaded, + loadNextPage, + loadMoreItems, + itemCount, + transactions, + }; + + if (!itemCount) { + return ; + } + + return ; +}; + +export default TransactionsListDetails; diff --git a/apps/web-cheqd/src/components/transactions_list_details/types.ts b/apps/web-cheqd/src/components/transactions_list_details/types.ts new file mode 100644 index 0000000000..8124b2a164 --- /dev/null +++ b/apps/web-cheqd/src/components/transactions_list_details/types.ts @@ -0,0 +1,10 @@ +export interface TransactionsListDetailsState { + className?: string; + hasNextPage?: boolean; + isNextPageLoading?: boolean; + loadNextPage?: (...args: unknown[]) => void; + loadMoreItems?: (...args: unknown[]) => void; + isItemLoaded?: (index: number) => boolean; + itemCount: number; + transactions: Transactions[]; +} diff --git a/apps/web-cheqd/src/graphql/types/general_types.ts b/apps/web-cheqd/src/graphql/types/general_types.ts index cf3b508908..60b7f4cea3 100644 --- a/apps/web-cheqd/src/graphql/types/general_types.ts +++ b/apps/web-cheqd/src/graphql/types/general_types.ts @@ -18,10 +18,10 @@ export type Scalars = { ActionPagination: any; ActionRedelegation: any; ActionUnbondingDelegation: any; + _coin: any; + _dec_coin: any; _text: any; bigint: any; - coin: any; - dec_coin: any; jsonb: any; numeric: any; smallint: any; @@ -93,23 +93,6 @@ export type Int_Comparison_Exp = { _nin?: InputMaybe>; }; -/** Boolean expression to compare columns of type "String". All fields are combined with logical 'AND'. */ -export type String_Array_Comparison_Exp = { - /** is the array contained in the given array value */ - _contained_in?: InputMaybe>; - /** does the array contain the given value */ - _contains?: InputMaybe>; - _eq?: InputMaybe>; - _gt?: InputMaybe>; - _gte?: InputMaybe>; - _in?: InputMaybe>>; - _is_null?: InputMaybe; - _lt?: InputMaybe>; - _lte?: InputMaybe>; - _neq?: InputMaybe>; - _nin?: InputMaybe>>; -}; - /** Boolean expression to compare columns of type "String". All fields are combined with logical 'AND'. */ export type String_Comparison_Exp = { _eq?: InputMaybe; @@ -143,6 +126,45 @@ export type String_Comparison_Exp = { _similar?: InputMaybe; }; +/** Boolean expression to compare columns of type "_coin". All fields are combined with logical 'AND'. */ +export type _Coin_Comparison_Exp = { + _eq?: InputMaybe; + _gt?: InputMaybe; + _gte?: InputMaybe; + _in?: InputMaybe>; + _is_null?: InputMaybe; + _lt?: InputMaybe; + _lte?: InputMaybe; + _neq?: InputMaybe; + _nin?: InputMaybe>; +}; + +/** Boolean expression to compare columns of type "_dec_coin". All fields are combined with logical 'AND'. */ +export type _Dec_Coin_Comparison_Exp = { + _eq?: InputMaybe; + _gt?: InputMaybe; + _gte?: InputMaybe; + _in?: InputMaybe>; + _is_null?: InputMaybe; + _lt?: InputMaybe; + _lte?: InputMaybe; + _neq?: InputMaybe; + _nin?: InputMaybe>; +}; + +/** Boolean expression to compare columns of type "_text". All fields are combined with logical 'AND'. */ +export type _Text_Comparison_Exp = { + _eq?: InputMaybe; + _gt?: InputMaybe; + _gte?: InputMaybe; + _in?: InputMaybe>; + _is_null?: InputMaybe; + _lt?: InputMaybe; + _lte?: InputMaybe; + _neq?: InputMaybe; + _nin?: InputMaybe>; +}; + /** columns and relationships of "account" */ export type Account = { __typename?: 'account'; @@ -720,27 +742,10 @@ export type Block_Variance_Order_By = { total_gas?: InputMaybe; }; -/** Boolean expression to compare columns of type "coin". All fields are combined with logical 'AND'. */ -export type Coin_Array_Comparison_Exp = { - /** is the array contained in the given array value */ - _contained_in?: InputMaybe>; - /** does the array contain the given value */ - _contains?: InputMaybe>; - _eq?: InputMaybe>; - _gt?: InputMaybe>; - _gte?: InputMaybe>; - _in?: InputMaybe>>; - _is_null?: InputMaybe; - _lt?: InputMaybe>; - _lte?: InputMaybe>; - _neq?: InputMaybe>; - _nin?: InputMaybe>>; -}; - /** columns and relationships of "community_pool" */ export type Community_Pool = { __typename?: 'community_pool'; - coins: Array; + coins: Scalars['_dec_coin']; height: Scalars['bigint']; }; @@ -749,7 +754,7 @@ export type Community_Pool_Bool_Exp = { _and?: InputMaybe>; _not?: InputMaybe; _or?: InputMaybe>; - coins?: InputMaybe; + coins?: InputMaybe<_Dec_Coin_Comparison_Exp>; height?: InputMaybe; }; @@ -777,7 +782,7 @@ export type Community_Pool_Stream_Cursor_Input = { /** Initial value of the column from where the streaming should start */ export type Community_Pool_Stream_Cursor_Value_Input = { - coins?: InputMaybe>; + coins?: InputMaybe; height?: InputMaybe; }; @@ -789,37 +794,20 @@ export enum Cursor_Ordering { Desc = 'DESC' } -/** Boolean expression to compare columns of type "dec_coin". All fields are combined with logical 'AND'. */ -export type Dec_Coin_Array_Comparison_Exp = { - /** is the array contained in the given array value */ - _contained_in?: InputMaybe>; - /** does the array contain the given value */ - _contains?: InputMaybe>; - _eq?: InputMaybe>; - _gt?: InputMaybe>; - _gte?: InputMaybe>; - _in?: InputMaybe>>; - _is_null?: InputMaybe; - _lt?: InputMaybe>; - _lte?: InputMaybe>; - _neq?: InputMaybe>; - _nin?: InputMaybe>>; -}; - /** columns and relationships of "did_doc" */ export type Did_Doc = { __typename?: 'did_doc'; - also_known_as?: Maybe>; - assertion_method?: Maybe>; - authentication?: Maybe>; - capability_delegation?: Maybe>; - capability_invocation?: Maybe>; - context?: Maybe>; - controller?: Maybe>; + also_known_as?: Maybe; + assertion_method?: Maybe; + authentication?: Maybe; + capability_delegation?: Maybe; + capability_invocation?: Maybe; + context?: Maybe; + controller?: Maybe; from_address?: Maybe; height: Scalars['bigint']; id: Scalars['String']; - key_agreement?: Maybe>; + key_agreement?: Maybe; service?: Maybe; verification_method?: Maybe; version_id?: Maybe; @@ -842,17 +830,17 @@ export type Did_Doc_Bool_Exp = { _and?: InputMaybe>; _not?: InputMaybe; _or?: InputMaybe>; - also_known_as?: InputMaybe; - assertion_method?: InputMaybe; - authentication?: InputMaybe; - capability_delegation?: InputMaybe; - capability_invocation?: InputMaybe; - context?: InputMaybe; - controller?: InputMaybe; + also_known_as?: InputMaybe<_Text_Comparison_Exp>; + assertion_method?: InputMaybe<_Text_Comparison_Exp>; + authentication?: InputMaybe<_Text_Comparison_Exp>; + capability_delegation?: InputMaybe<_Text_Comparison_Exp>; + capability_invocation?: InputMaybe<_Text_Comparison_Exp>; + context?: InputMaybe<_Text_Comparison_Exp>; + controller?: InputMaybe<_Text_Comparison_Exp>; from_address?: InputMaybe; height?: InputMaybe; id?: InputMaybe; - key_agreement?: InputMaybe; + key_agreement?: InputMaybe<_Text_Comparison_Exp>; service?: InputMaybe; verification_method?: InputMaybe; version_id?: InputMaybe; @@ -918,17 +906,17 @@ export type Did_Doc_Stream_Cursor_Input = { /** Initial value of the column from where the streaming should start */ export type Did_Doc_Stream_Cursor_Value_Input = { - also_known_as?: InputMaybe>; - assertion_method?: InputMaybe>; - authentication?: InputMaybe>; - capability_delegation?: InputMaybe>; - capability_invocation?: InputMaybe>; - context?: InputMaybe>; - controller?: InputMaybe>; + also_known_as?: InputMaybe; + assertion_method?: InputMaybe; + authentication?: InputMaybe; + capability_delegation?: InputMaybe; + capability_invocation?: InputMaybe; + context?: InputMaybe; + controller?: InputMaybe; from_address?: InputMaybe; height?: InputMaybe; id?: InputMaybe; - key_agreement?: InputMaybe>; + key_agreement?: InputMaybe; service?: InputMaybe; verification_method?: InputMaybe; version_id?: InputMaybe; @@ -1587,7 +1575,7 @@ export type Message = { __typename?: 'message'; height: Scalars['bigint']; index: Scalars['bigint']; - involved_accounts_addresses: Array; + involved_accounts_addresses: Scalars['_text']; /** An object relationship */ transaction?: Maybe; transaction_hash: Scalars['String']; @@ -1608,7 +1596,7 @@ export type Message_Bool_Exp = { _or?: InputMaybe>; height?: InputMaybe; index?: InputMaybe; - involved_accounts_addresses?: InputMaybe; + involved_accounts_addresses?: InputMaybe<_Text_Comparison_Exp>; transaction?: InputMaybe; transaction_hash?: InputMaybe; type?: InputMaybe; @@ -1654,12 +1642,68 @@ export type Message_Stream_Cursor_Input = { export type Message_Stream_Cursor_Value_Input = { height?: InputMaybe; index?: InputMaybe; - involved_accounts_addresses?: InputMaybe>; + involved_accounts_addresses?: InputMaybe; transaction_hash?: InputMaybe; type?: InputMaybe; value?: InputMaybe; }; +/** columns and relationships of "message_type" */ +export type Message_Type = { + __typename?: 'message_type'; + height: Scalars['bigint']; + label: Scalars['String']; + module: Scalars['String']; + type: Scalars['String']; +}; + +/** Boolean expression to filter rows from the table "message_type". All fields are combined with a logical 'AND'. */ +export type Message_Type_Bool_Exp = { + _and?: InputMaybe>; + _not?: InputMaybe; + _or?: InputMaybe>; + height?: InputMaybe; + label?: InputMaybe; + module?: InputMaybe; + type?: InputMaybe; +}; + +/** Ordering options when selecting data from "message_type". */ +export type Message_Type_Order_By = { + height?: InputMaybe; + label?: InputMaybe; + module?: InputMaybe; + type?: InputMaybe; +}; + +/** select columns of table "message_type" */ +export enum Message_Type_Select_Column { + /** column name */ + Height = 'height', + /** column name */ + Label = 'label', + /** column name */ + Module = 'module', + /** column name */ + Type = 'type' +} + +/** Streaming cursor of the table "message_type" */ +export type Message_Type_Stream_Cursor_Input = { + /** Stream column input with initial value */ + initial_value: Message_Type_Stream_Cursor_Value_Input; + /** cursor ordering */ + ordering?: InputMaybe; +}; + +/** Initial value of the column from where the streaming should start */ +export type Message_Type_Stream_Cursor_Value_Input = { + height?: InputMaybe; + label?: InputMaybe; + module?: InputMaybe; + type?: InputMaybe; +}; + export type Messages_By_Address_Args = { addresses?: InputMaybe; limit?: InputMaybe; @@ -1673,6 +1717,12 @@ export type Messages_By_Type_Args = { types?: InputMaybe; }; +export type Messages_Types_By_Address_Args = { + addresses?: InputMaybe; + limit?: InputMaybe; + offset?: InputMaybe; +}; + /** columns and relationships of "mint_params" */ export type Mint_Params = { __typename?: 'mint_params'; @@ -2241,7 +2291,7 @@ export type Proposal_Bool_Exp = { /** columns and relationships of "proposal_deposit" */ export type Proposal_Deposit = { __typename?: 'proposal_deposit'; - amount?: Maybe>; + amount?: Maybe; /** An object relationship */ block?: Maybe; /** An object relationship */ @@ -2280,7 +2330,7 @@ export type Proposal_Deposit_Bool_Exp = { _and?: InputMaybe>; _not?: InputMaybe; _or?: InputMaybe>; - amount?: InputMaybe; + amount?: InputMaybe<_Coin_Comparison_Exp>; block?: InputMaybe; depositor?: InputMaybe; depositor_address?: InputMaybe; @@ -2292,7 +2342,6 @@ export type Proposal_Deposit_Bool_Exp = { /** order by max() on columns of table "proposal_deposit" */ export type Proposal_Deposit_Max_Order_By = { - amount?: InputMaybe; depositor_address?: InputMaybe; height?: InputMaybe; proposal_id?: InputMaybe; @@ -2301,7 +2350,6 @@ export type Proposal_Deposit_Max_Order_By = { /** order by min() on columns of table "proposal_deposit" */ export type Proposal_Deposit_Min_Order_By = { - amount?: InputMaybe; depositor_address?: InputMaybe; height?: InputMaybe; proposal_id?: InputMaybe; @@ -2362,7 +2410,7 @@ export type Proposal_Deposit_Stream_Cursor_Input = { /** Initial value of the column from where the streaming should start */ export type Proposal_Deposit_Stream_Cursor_Value_Input = { - amount?: InputMaybe>; + amount?: InputMaybe; depositor_address?: InputMaybe; height?: InputMaybe; proposal_id?: InputMaybe; @@ -2793,7 +2841,7 @@ export type Proposal_Validator_Status_Snapshot = { /** An object relationship */ validator: Validator; validator_address: Scalars['String']; - voting_power: Scalars['String']; + voting_power: Scalars['bigint']; }; /** order by aggregate values of table "proposal_validator_status_snapshot" */ @@ -2816,6 +2864,7 @@ export type Proposal_Validator_Status_Snapshot_Avg_Order_By = { height?: InputMaybe; proposal_id?: InputMaybe; status?: InputMaybe; + voting_power?: InputMaybe; }; /** Boolean expression to filter rows from the table "proposal_validator_status_snapshot". All fields are combined with a logical 'AND'. */ @@ -2830,7 +2879,7 @@ export type Proposal_Validator_Status_Snapshot_Bool_Exp = { status?: InputMaybe; validator?: InputMaybe; validator_address?: InputMaybe; - voting_power?: InputMaybe; + voting_power?: InputMaybe; }; /** order by max() on columns of table "proposal_validator_status_snapshot" */ @@ -2884,6 +2933,7 @@ export type Proposal_Validator_Status_Snapshot_Stddev_Order_By = { height?: InputMaybe; proposal_id?: InputMaybe; status?: InputMaybe; + voting_power?: InputMaybe; }; /** order by stddev_pop() on columns of table "proposal_validator_status_snapshot" */ @@ -2891,6 +2941,7 @@ export type Proposal_Validator_Status_Snapshot_Stddev_Pop_Order_By = { height?: InputMaybe; proposal_id?: InputMaybe; status?: InputMaybe; + voting_power?: InputMaybe; }; /** order by stddev_samp() on columns of table "proposal_validator_status_snapshot" */ @@ -2898,24 +2949,7 @@ export type Proposal_Validator_Status_Snapshot_Stddev_Samp_Order_By = { height?: InputMaybe; proposal_id?: InputMaybe; status?: InputMaybe; -}; - -/** Streaming cursor of the table "proposal_validator_status_snapshot" */ -export type Proposal_Validator_Status_Snapshot_Stream_Cursor_Input = { - /** Stream column input with initial value */ - initial_value: Proposal_Validator_Status_Snapshot_Stream_Cursor_Value_Input; - /** cursor ordering */ - ordering?: InputMaybe; -}; - -/** Initial value of the column from where the streaming should start */ -export type Proposal_Validator_Status_Snapshot_Stream_Cursor_Value_Input = { - height?: InputMaybe; - jailed?: InputMaybe; - proposal_id?: InputMaybe; - status?: InputMaybe; - validator_address?: InputMaybe; - voting_power?: InputMaybe; + voting_power?: InputMaybe; }; /** Streaming cursor of the table "proposal_validator_status_snapshot" */ @@ -2941,6 +2975,7 @@ export type Proposal_Validator_Status_Snapshot_Sum_Order_By = { height?: InputMaybe; proposal_id?: InputMaybe; status?: InputMaybe; + voting_power?: InputMaybe; }; /** order by var_pop() on columns of table "proposal_validator_status_snapshot" */ @@ -2948,6 +2983,7 @@ export type Proposal_Validator_Status_Snapshot_Var_Pop_Order_By = { height?: InputMaybe; proposal_id?: InputMaybe; status?: InputMaybe; + voting_power?: InputMaybe; }; /** order by var_samp() on columns of table "proposal_validator_status_snapshot" */ @@ -2955,6 +2991,7 @@ export type Proposal_Validator_Status_Snapshot_Var_Samp_Order_By = { height?: InputMaybe; proposal_id?: InputMaybe; status?: InputMaybe; + voting_power?: InputMaybe; }; /** order by variance() on columns of table "proposal_validator_status_snapshot" */ @@ -2962,6 +2999,7 @@ export type Proposal_Validator_Status_Snapshot_Variance_Order_By = { height?: InputMaybe; proposal_id?: InputMaybe; status?: InputMaybe; + voting_power?: InputMaybe; }; /** aggregate var_pop on columns */ @@ -3220,6 +3258,8 @@ export type Query_Root = { messages_by_address: Array; /** execute function "messages_by_type" which returns "message" */ messages_by_type: Array; + /** execute function "messages_types_by_address" which returns "message" */ + messages_types_by_address: Array; /** fetch data from the table: "mint_params" */ mint_params: Array; /** fetch data from the table: "modules" */ @@ -3602,6 +3642,16 @@ export type Query_RootMessages_By_TypeArgs = { }; +export type Query_RootMessages_Types_By_AddressArgs = { + args: Messages_Types_By_Address_Args; + distinct_on?: InputMaybe>; + limit?: InputMaybe; + offset?: InputMaybe; + order_by?: InputMaybe>; + where?: InputMaybe; +}; + + export type Query_RootMint_ParamsArgs = { distinct_on?: InputMaybe>; limit?: InputMaybe; @@ -4522,10 +4572,16 @@ export type Subscription_Root = { message: Array; /** fetch data from the table in a streaming manner: "message" */ message_stream: Array; + /** fetch data from the table: "message_type" */ + message_type: Array; + /** fetch data from the table in a streaming manner: "message_type" */ + message_type_stream: Array; /** execute function "messages_by_address" which returns "message" */ messages_by_address: Array; /** execute function "messages_by_type" which returns "message" */ messages_by_type: Array; + /** execute function "messages_types_by_address" which returns "message" */ + messages_types_by_address: Array; /** fetch data from the table: "mint_params" */ mint_params: Array; /** fetch data from the table in a streaming manner: "mint_params" */ @@ -4967,6 +5023,22 @@ export type Subscription_RootMessage_StreamArgs = { }; +export type Subscription_RootMessage_TypeArgs = { + distinct_on?: InputMaybe>; + limit?: InputMaybe; + offset?: InputMaybe; + order_by?: InputMaybe>; + where?: InputMaybe; +}; + + +export type Subscription_RootMessage_Type_StreamArgs = { + batch_size: Scalars['Int']; + cursor: Array>; + where?: InputMaybe; +}; + + export type Subscription_RootMessages_By_AddressArgs = { args: Messages_By_Address_Args; distinct_on?: InputMaybe>; @@ -4987,6 +5059,16 @@ export type Subscription_RootMessages_By_TypeArgs = { }; +export type Subscription_RootMessages_Types_By_AddressArgs = { + args: Messages_Types_By_Address_Args; + distinct_on?: InputMaybe>; + limit?: InputMaybe; + offset?: InputMaybe; + order_by?: InputMaybe>; + where?: InputMaybe; +}; + + export type Subscription_RootMint_ParamsArgs = { distinct_on?: InputMaybe>; limit?: InputMaybe; @@ -5600,7 +5682,7 @@ export type Subscription_RootVesting_Period_StreamArgs = { /** columns and relationships of "supply" */ export type Supply = { __typename?: 'supply'; - coins: Array; + coins: Scalars['_coin']; height: Scalars['bigint']; }; @@ -5609,7 +5691,7 @@ export type Supply_Bool_Exp = { _and?: InputMaybe>; _not?: InputMaybe; _or?: InputMaybe>; - coins?: InputMaybe; + coins?: InputMaybe<_Coin_Comparison_Exp>; height?: InputMaybe; }; @@ -5637,7 +5719,7 @@ export type Supply_Stream_Cursor_Input = { /** Initial value of the column from where the streaming should start */ export type Supply_Stream_Cursor_Value_Input = { - coins?: InputMaybe>; + coins?: InputMaybe; height?: InputMaybe; }; @@ -6003,7 +6085,7 @@ export type Token_Stream_Cursor_Value_Input = { /** columns and relationships of "token_unit" */ export type Token_Unit = { __typename?: 'token_unit'; - aliases?: Maybe>; + aliases?: Maybe; denom: Scalars['String']; exponent: Scalars['Int']; price_id?: Maybe; @@ -6063,7 +6145,7 @@ export type Token_Unit_Bool_Exp = { _and?: InputMaybe>; _not?: InputMaybe; _or?: InputMaybe>; - aliases?: InputMaybe; + aliases?: InputMaybe<_Text_Comparison_Exp>; denom?: InputMaybe; exponent?: InputMaybe; price_id?: InputMaybe; @@ -6076,7 +6158,6 @@ export type Token_Unit_Bool_Exp = { /** order by max() on columns of table "token_unit" */ export type Token_Unit_Max_Order_By = { - aliases?: InputMaybe; denom?: InputMaybe; exponent?: InputMaybe; price_id?: InputMaybe; @@ -6085,7 +6166,6 @@ export type Token_Unit_Max_Order_By = { /** order by min() on columns of table "token_unit" */ export type Token_Unit_Min_Order_By = { - aliases?: InputMaybe; denom?: InputMaybe; exponent?: InputMaybe; price_id?: InputMaybe; @@ -6144,7 +6224,7 @@ export type Token_Unit_Stream_Cursor_Input = { /** Initial value of the column from where the streaming should start */ export type Token_Unit_Stream_Cursor_Value_Input = { - aliases?: InputMaybe>; + aliases?: InputMaybe; denom?: InputMaybe; exponent?: InputMaybe; price_id?: InputMaybe; @@ -6313,7 +6393,7 @@ export type Transaction = { memo?: Maybe; messages: Scalars['jsonb']; raw_log?: Maybe; - signatures: Array; + signatures: Scalars['_text']; signer_infos: Scalars['jsonb']; success: Scalars['Boolean']; }; @@ -6379,7 +6459,7 @@ export type Transaction_Bool_Exp = { memo?: InputMaybe; messages?: InputMaybe; raw_log?: InputMaybe; - signatures?: InputMaybe; + signatures?: InputMaybe<_Text_Comparison_Exp>; signer_infos?: InputMaybe; success?: InputMaybe; }; @@ -6392,7 +6472,6 @@ export type Transaction_Max_Order_By = { height?: InputMaybe; memo?: InputMaybe; raw_log?: InputMaybe; - signatures?: InputMaybe; }; /** order by min() on columns of table "transaction" */ @@ -6403,7 +6482,6 @@ export type Transaction_Min_Order_By = { height?: InputMaybe; memo?: InputMaybe; raw_log?: InputMaybe; - signatures?: InputMaybe; }; /** Ordering options when selecting data from "transaction". */ @@ -6491,7 +6569,7 @@ export type Transaction_Stream_Cursor_Value_Input = { memo?: InputMaybe; messages?: InputMaybe; raw_log?: InputMaybe; - signatures?: InputMaybe>; + signatures?: InputMaybe; signer_infos?: InputMaybe; success?: InputMaybe; }; @@ -7364,33 +7442,6 @@ export type Validator_Status_Aggregate_Bool_Exp_Count = { predicate: Int_Comparison_Exp; }; -export type Validator_Status_Aggregate_Bool_Exp = { - bool_and?: InputMaybe; - bool_or?: InputMaybe; - count?: InputMaybe; -}; - -export type Validator_Status_Aggregate_Bool_Exp_Bool_And = { - arguments: Validator_Status_Select_Column_Validator_Status_Aggregate_Bool_Exp_Bool_And_Arguments_Columns; - distinct?: InputMaybe; - filter?: InputMaybe; - predicate: Boolean_Comparison_Exp; -}; - -export type Validator_Status_Aggregate_Bool_Exp_Bool_Or = { - arguments: Validator_Status_Select_Column_Validator_Status_Aggregate_Bool_Exp_Bool_Or_Arguments_Columns; - distinct?: InputMaybe; - filter?: InputMaybe; - predicate: Boolean_Comparison_Exp; -}; - -export type Validator_Status_Aggregate_Bool_Exp_Count = { - arguments?: InputMaybe>; - distinct?: InputMaybe; - filter?: InputMaybe; - predicate: Int_Comparison_Exp; -}; - /** aggregate fields of "validator_status" */ export type Validator_Status_Aggregate_Fields = { __typename?: 'validator_status_aggregate_fields'; @@ -7647,7 +7698,7 @@ export type Validator_Voting_Power = { /** An object relationship */ validator: Validator; validator_address: Scalars['String']; - voting_power: Scalars['String']; + voting_power: Scalars['bigint']; }; /** aggregated selection of "validator_voting_power" */ @@ -7710,11 +7761,13 @@ export type Validator_Voting_Power_Aggregate_Order_By = { export type Validator_Voting_Power_Avg_Fields = { __typename?: 'validator_voting_power_avg_fields'; height?: Maybe; + voting_power?: Maybe; }; /** order by avg() on columns of table "validator_voting_power" */ export type Validator_Voting_Power_Avg_Order_By = { height?: InputMaybe; + voting_power?: InputMaybe; }; /** Boolean expression to filter rows from the table "validator_voting_power". All fields are combined with a logical 'AND'. */ @@ -7726,7 +7779,7 @@ export type Validator_Voting_Power_Bool_Exp = { height?: InputMaybe; validator?: InputMaybe; validator_address?: InputMaybe; - voting_power?: InputMaybe; + voting_power?: InputMaybe; }; /** aggregate max on columns */ @@ -7734,7 +7787,7 @@ export type Validator_Voting_Power_Max_Fields = { __typename?: 'validator_voting_power_max_fields'; height?: Maybe; validator_address?: Maybe; - voting_power?: Maybe; + voting_power?: Maybe; }; /** order by max() on columns of table "validator_voting_power" */ @@ -7749,7 +7802,7 @@ export type Validator_Voting_Power_Min_Fields = { __typename?: 'validator_voting_power_min_fields'; height?: Maybe; validator_address?: Maybe; - voting_power?: Maybe; + voting_power?: Maybe; }; /** order by min() on columns of table "validator_voting_power" */ @@ -7782,48 +7835,39 @@ export enum Validator_Voting_Power_Select_Column { export type Validator_Voting_Power_Stddev_Fields = { __typename?: 'validator_voting_power_stddev_fields'; height?: Maybe; + voting_power?: Maybe; }; /** order by stddev() on columns of table "validator_voting_power" */ export type Validator_Voting_Power_Stddev_Order_By = { height?: InputMaybe; + voting_power?: InputMaybe; }; /** aggregate stddev_pop on columns */ export type Validator_Voting_Power_Stddev_Pop_Fields = { __typename?: 'validator_voting_power_stddev_pop_fields'; height?: Maybe; + voting_power?: Maybe; }; /** order by stddev_pop() on columns of table "validator_voting_power" */ export type Validator_Voting_Power_Stddev_Pop_Order_By = { height?: InputMaybe; + voting_power?: InputMaybe; }; /** aggregate stddev_samp on columns */ export type Validator_Voting_Power_Stddev_Samp_Fields = { __typename?: 'validator_voting_power_stddev_samp_fields'; height?: Maybe; + voting_power?: Maybe; }; /** order by stddev_samp() on columns of table "validator_voting_power" */ export type Validator_Voting_Power_Stddev_Samp_Order_By = { height?: InputMaybe; -}; - -/** Streaming cursor of the table "validator_voting_power" */ -export type Validator_Voting_Power_Stream_Cursor_Input = { - /** Stream column input with initial value */ - initial_value: Validator_Voting_Power_Stream_Cursor_Value_Input; - /** cursor ordering */ - ordering?: InputMaybe; -}; - -/** Initial value of the column from where the streaming should start */ -export type Validator_Voting_Power_Stream_Cursor_Value_Input = { - height?: InputMaybe; - validator_address?: InputMaybe; - voting_power?: InputMaybe; + voting_power?: InputMaybe; }; /** Streaming cursor of the table "validator_voting_power" */ @@ -7845,44 +7889,52 @@ export type Validator_Voting_Power_Stream_Cursor_Value_Input = { export type Validator_Voting_Power_Sum_Fields = { __typename?: 'validator_voting_power_sum_fields'; height?: Maybe; + voting_power?: Maybe; }; /** order by sum() on columns of table "validator_voting_power" */ export type Validator_Voting_Power_Sum_Order_By = { height?: InputMaybe; + voting_power?: InputMaybe; }; /** aggregate var_pop on columns */ export type Validator_Voting_Power_Var_Pop_Fields = { __typename?: 'validator_voting_power_var_pop_fields'; height?: Maybe; + voting_power?: Maybe; }; /** order by var_pop() on columns of table "validator_voting_power" */ export type Validator_Voting_Power_Var_Pop_Order_By = { height?: InputMaybe; + voting_power?: InputMaybe; }; /** aggregate var_samp on columns */ export type Validator_Voting_Power_Var_Samp_Fields = { __typename?: 'validator_voting_power_var_samp_fields'; height?: Maybe; + voting_power?: Maybe; }; /** order by var_samp() on columns of table "validator_voting_power" */ export type Validator_Voting_Power_Var_Samp_Order_By = { height?: InputMaybe; + voting_power?: InputMaybe; }; /** aggregate variance on columns */ export type Validator_Voting_Power_Variance_Fields = { __typename?: 'validator_voting_power_variance_fields'; height?: Maybe; + voting_power?: Maybe; }; /** order by variance() on columns of table "validator_voting_power" */ export type Validator_Voting_Power_Variance_Order_By = { height?: InputMaybe; + voting_power?: InputMaybe; }; /** columns and relationships of "vesting_account" */ @@ -7892,7 +7944,7 @@ export type Vesting_Account = { account: Account; address: Scalars['String']; end_time: Scalars['timestamp']; - original_vesting: Array; + original_vesting: Scalars['_coin']; start_time?: Maybe; type: Scalars['String']; /** An array relationship */ @@ -7924,7 +7976,7 @@ export type Vesting_Account_Bool_Exp = { account?: InputMaybe; address?: InputMaybe; end_time?: InputMaybe; - original_vesting?: InputMaybe; + original_vesting?: InputMaybe<_Coin_Comparison_Exp>; start_time?: InputMaybe; type?: InputMaybe; vesting_periods?: InputMaybe; @@ -7934,7 +7986,6 @@ export type Vesting_Account_Bool_Exp = { export type Vesting_Account_Max_Order_By = { address?: InputMaybe; end_time?: InputMaybe; - original_vesting?: InputMaybe; start_time?: InputMaybe; type?: InputMaybe; }; @@ -7943,7 +7994,6 @@ export type Vesting_Account_Max_Order_By = { export type Vesting_Account_Min_Order_By = { address?: InputMaybe; end_time?: InputMaybe; - original_vesting?: InputMaybe; start_time?: InputMaybe; type?: InputMaybe; }; @@ -7985,7 +8035,7 @@ export type Vesting_Account_Stream_Cursor_Input = { export type Vesting_Account_Stream_Cursor_Value_Input = { address?: InputMaybe; end_time?: InputMaybe; - original_vesting?: InputMaybe>; + original_vesting?: InputMaybe; start_time?: InputMaybe; type?: InputMaybe; }; @@ -7993,7 +8043,7 @@ export type Vesting_Account_Stream_Cursor_Value_Input = { /** columns and relationships of "vesting_period" */ export type Vesting_Period = { __typename?: 'vesting_period'; - amount: Array; + amount: Scalars['_coin']; length: Scalars['bigint']; period_order: Scalars['bigint']; /** An object relationship */ @@ -8026,7 +8076,7 @@ export type Vesting_Period_Bool_Exp = { _and?: InputMaybe>; _not?: InputMaybe; _or?: InputMaybe>; - amount?: InputMaybe; + amount?: InputMaybe<_Coin_Comparison_Exp>; length?: InputMaybe; period_order?: InputMaybe; vesting_account?: InputMaybe; @@ -8034,14 +8084,12 @@ export type Vesting_Period_Bool_Exp = { /** order by max() on columns of table "vesting_period" */ export type Vesting_Period_Max_Order_By = { - amount?: InputMaybe; length?: InputMaybe; period_order?: InputMaybe; }; /** order by min() on columns of table "vesting_period" */ export type Vesting_Period_Min_Order_By = { - amount?: InputMaybe; length?: InputMaybe; period_order?: InputMaybe; }; @@ -8092,7 +8140,7 @@ export type Vesting_Period_Stream_Cursor_Input = { /** Initial value of the column from where the streaming should start */ export type Vesting_Period_Stream_Cursor_Value_Input = { - amount?: InputMaybe>; + amount?: InputMaybe; length?: InputMaybe; period_order?: InputMaybe; }; @@ -8251,7 +8299,7 @@ export type MarketDataQueryVariables = Exact<{ }>; -export type MarketDataQuery = { communityPool: Array<{ __typename?: 'community_pool', coins: Array }>, inflation: Array<{ __typename?: 'inflation', value: any }>, tokenPrice: Array<{ __typename?: 'token_price', price: any, marketCap: any }>, supply: Array<{ __typename?: 'supply', coins: Array }>, bondedTokens: Array<{ __typename?: 'staking_pool', bonded_tokens: string }>, distributionParams: Array<{ __typename?: 'distribution_params', params: any }> }; +export type MarketDataQuery = { communityPool: Array<{ __typename?: 'community_pool', coins: any }>, inflation: Array<{ __typename?: 'inflation', value: any }>, tokenPrice: Array<{ __typename?: 'token_price', price: any, marketCap: any }>, supply: Array<{ __typename?: 'supply', coins: any }>, bondedTokens: Array<{ __typename?: 'staking_pool', bonded_tokens: string }>, distributionParams: Array<{ __typename?: 'distribution_params', params: any }> }; export type MessageTypesQueryVariables = Exact<{ [key: string]: never; }>; @@ -8315,14 +8363,14 @@ export type ProposalDetailsTallyQueryVariables = Exact<{ }>; -export type ProposalDetailsTallyQuery = { proposalTallyResult: Array<{ __typename?: 'proposal_tally_result', yes: string, no: string, abstain: string, noWithVeto: string }>, stakingPool: Array<{ __typename?: 'proposal_staking_pool_snapshot', bondedTokens: string }> }; +export type ProposalDetailsTallyQuery = { proposalTallyResult: Array<{ __typename?: 'proposal_tally_result', yes: string, no: string, abstain: string, noWithVeto: string }>, stakingPool: Array<{ __typename?: 'proposal_staking_pool_snapshot', bondedTokens: string }>, quorum: Array<{ __typename?: 'gov_params', tallyParams: any }> }; export type ProposalDetailsDepositsQueryVariables = Exact<{ proposalId?: InputMaybe; }>; -export type ProposalDetailsDepositsQuery = { proposalDeposit: Array<{ __typename?: 'proposal_deposit', amount?: Array | null, depositorAddress?: string | null, block?: { __typename?: 'block', timestamp: any } | null }> }; +export type ProposalDetailsDepositsQuery = { proposalDeposit: Array<{ __typename?: 'proposal_deposit', amount?: any | null, depositorAddress?: string | null, block?: { __typename?: 'block', timestamp: any } | null }> }; export type ProposalDetailsVotesQueryVariables = Exact<{ proposalId?: InputMaybe; @@ -8357,7 +8405,7 @@ export type TokenPriceHistoryQuery = { tokenPrice: Array<{ __typename?: 'token_p export type TokenomicsQueryVariables = Exact<{ [key: string]: never; }>; -export type TokenomicsQuery = { stakingParams: Array<{ __typename?: 'staking_params', params: any }>, stakingPool: Array<{ __typename?: 'staking_pool', bonded: string, unbonded: string }>, supply: Array<{ __typename?: 'supply', coins: Array }> }; +export type TokenomicsQuery = { stakingParams: Array<{ __typename?: 'staking_params', params: any }>, stakingPool: Array<{ __typename?: 'staking_pool', bonded: string, unbonded: string }>, supply: Array<{ __typename?: 'supply', coins: any }> }; export type TopAccountsQueryVariables = Exact<{ limit?: InputMaybe; diff --git a/apps/web-cheqd/src/screens/transactions/hooks.ts b/apps/web-cheqd/src/screens/transactions/hooks.ts deleted file mode 100644 index 72c3fe7565..0000000000 --- a/apps/web-cheqd/src/screens/transactions/hooks.ts +++ /dev/null @@ -1,143 +0,0 @@ -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, - }; -};