diff --git a/packages/ui/src/components/transaction_type_filter/hooks.ts b/packages/ui/src/components/transaction_type_filter/hooks.ts index a9e19c6ca9..fc9b8346b6 100644 --- a/packages/ui/src/components/transaction_type_filter/hooks.ts +++ b/packages/ui/src/components/transaction_type_filter/hooks.ts @@ -1,9 +1,11 @@ // useTransactionTypeFilter hook import { useEffect, useMemo, useState, ChangeEvent, useCallback } from 'react'; -import { useMessageTypesQuery } from '@/graphql/types/general_types'; +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; @@ -11,16 +13,56 @@ export type MessageType = { label: string; }; +export type MessageTypes = { + message_type: MessageType; +}; + export const useTransactionTypeFilter = () => { - const { data, error, loading, refetch } = useMessageTypesQuery(); + 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); - // db message query filter + + // Recoil state for managing filters and dialog state const [, setFilter] = useRecoilState(writeFilter) as [string, SetterOrUpdater]; - // checkbox message type filter const [, setSelectedMsgs] = useRecoilState(writeSelectedMsgTypes) as [ string[], SetterOrUpdater @@ -30,11 +72,12 @@ export const useTransactionTypeFilter = () => { SetterOrUpdater ]; - // Fetch data if there's an error + // Fetch data again if there's an error useEffect(() => { if (error) refetch(); }, [error, refetch]); + // Open and cancel dialog functions const handleOpen = () => { setOpenDialog(true); }; @@ -43,9 +86,12 @@ export const useTransactionTypeFilter = () => { setOpenDialog(false); }; - const mergeMessagesByLabel = (messages: MessageType[] | undefined): MessageType[] => { + // 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; @@ -54,8 +100,10 @@ export const useTransactionTypeFilter = () => { } }); + // 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', @@ -68,18 +116,57 @@ export const useTransactionTypeFilter = () => { 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: MessageType[] | null | undefined): { module: string; msgTypes: MessageType[] }[] => { + ( + messages: MessageTypes[] | MessageType[] | null | undefined + ): { module: string; msgTypes: MessageType[] }[] => { if (!messages) { return []; } + const msgs = [...messages]; - let updatedMessages = [...messages]; - - updatedMessages = mergeMessagesByLabel(updatedMessages); + // 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] = []; @@ -94,18 +181,20 @@ export const useTransactionTypeFilter = () => { 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(); // Close dialog after filtering + handleCancel(); }; + // Handle selection of transaction types const handleTxTypeSelection = (event: ChangeEvent) => { const { checked, value } = event.target; if (checked) { @@ -118,6 +207,7 @@ export const useTransactionTypeFilter = () => { } }; + // Handle selection of all transaction types const handleSelectAllTxTypes = (event: ChangeEvent) => { const { checked } = event.target; setSelectAllChecked(checked); @@ -131,21 +221,36 @@ export const useTransactionTypeFilter = () => { } }; + // Memoized computation of message type list const msgTypeList = useMemo(() => { - const typesList = formatTypes(data?.msgTypes as MessageType[]); + const typesList = formatTypes( + isAccountsPage || isValidatorDetailsPage + ? (data?.msgTypes as MessageTypes[]) + : (data?.msgTypes as MessageType[]) + ); typesList.sort((a, b) => a.module.localeCompare(b.module)); setFilteredTypes(typesList); - }, [data?.msgTypes, formatTypes]); + 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(data?.msgTypes as MessageType[]); + 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(data?.msgTypes as MessageType[]); + 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 }[] }) => @@ -154,8 +259,9 @@ export const useTransactionTypeFilter = () => { setFilteredTypes(types); } }, - [data?.msgTypes, formatTypes] + [data?.msgTypes, formatTypes, isAccountsPage, isValidatorDetailsPage] ); + return { data, loading, diff --git a/packages/ui/src/components/transaction_type_filter/index.tsx b/packages/ui/src/components/transaction_type_filter/index.tsx index 729d1b9d63..25d2f6e7a0 100644 --- a/packages/ui/src/components/transaction_type_filter/index.tsx +++ b/packages/ui/src/components/transaction_type_filter/index.tsx @@ -12,12 +12,14 @@ import TxTypeSearch from '@/components/transaction_type_filter/components/transa 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, @@ -63,74 +65,80 @@ const FilterTxsByType: FC = () => { - {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(' ') - : ''} - - -
- ))} -
+ {!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(' ') + : ''} + + +
+ ))} +
+
+ )) + ) : ( +
+
- ))} + )}