From 5eaceef95408ab734d55598338a5d13d697e93cc Mon Sep 17 00:00:00 2001 From: hemanthghs Date: Tue, 3 Sep 2024 14:10:32 +0530 Subject: [PATCH] chore: transaction history iterations --- .../history/SearchTransaction.tsx | 5 +- .../history/[network]/[hash]/loading.tsx | 4 +- .../history/[network]/[hash]/page.tsx | 43 +++++++++-- .../[network]/components/Transaction.tsx | 29 ++++++- .../TransactionHistoryDashboard.tsx | 2 + .../history/loaders/TransactionLoading.tsx | 77 +++++++++++++++++++ frontend/src/app/globals.css | 10 ++- .../src/custom-hooks/useGetTransactions.ts | 32 ++++---- .../src/custom-hooks/useInitTransactions.tsx | 24 ++++++ .../recentTransactionsSlice.tsx | 63 +++++++-------- 10 files changed, 225 insertions(+), 64 deletions(-) create mode 100644 frontend/src/app/(routes)/transactions/history/loaders/TransactionLoading.tsx create mode 100644 frontend/src/custom-hooks/useInitTransactions.tsx diff --git a/frontend/src/app/(routes)/transactions/history/SearchTransaction.tsx b/frontend/src/app/(routes)/transactions/history/SearchTransaction.tsx index a026ed911..109d1bb36 100644 --- a/frontend/src/app/(routes)/transactions/history/SearchTransaction.tsx +++ b/frontend/src/app/(routes)/transactions/history/SearchTransaction.tsx @@ -33,6 +33,9 @@ const SearchTransaction = () => { const txnResult = useAppSelector( (state) => state.recentTransactions.txn?.data?.[0] ); + const txnStatus = useAppSelector( + (state) => state.recentTransactions.txn.status + ); const { txHash = '' } = txnResult ? parseTxnData(txnResult) : {}; const [chainID, setChainID] = useState(''); @@ -76,7 +79,7 @@ const SearchTransaction = () => { ) : null} )} - + {txnStatus === TxStatus.REJECTED ? : null} ); }; diff --git a/frontend/src/app/(routes)/transactions/history/[network]/[hash]/loading.tsx b/frontend/src/app/(routes)/transactions/history/[network]/[hash]/loading.tsx index f4c8108df..81e6fd2ed 100644 --- a/frontend/src/app/(routes)/transactions/history/[network]/[hash]/loading.tsx +++ b/frontend/src/app/(routes)/transactions/history/[network]/[hash]/loading.tsx @@ -1,10 +1,10 @@ 'use client'; -import PageLoading from '@/components/common/PageLoading'; import React from 'react'; +import TransactionLoading from '../../loaders/TransactionLoading'; const loading = () => { - return ; + return ; }; export default loading; diff --git a/frontend/src/app/(routes)/transactions/history/[network]/[hash]/page.tsx b/frontend/src/app/(routes)/transactions/history/[network]/[hash]/page.tsx index 5d1a96510..0f4d8f990 100644 --- a/frontend/src/app/(routes)/transactions/history/[network]/[hash]/page.tsx +++ b/frontend/src/app/(routes)/transactions/history/[network]/[hash]/page.tsx @@ -6,6 +6,8 @@ import Transaction from '../components/Transaction'; import useGetTransactions from '@/custom-hooks/useGetTransactions'; import { useAppSelector } from '@/custom-hooks/StateHooks'; import { RootState } from '@/store/store'; +import { TxStatus } from '@/types/enums'; +import TransactionLoading from '../../loaders/TransactionLoading'; const Page = () => { const params = useParams(); @@ -25,6 +27,12 @@ const Page = () => { const chainID = nameToChainsIDs[arrChainNames[0]]; const txnHistory = useGetTransactions({ chainID }); + const txnStatus = useAppSelector( + (state) => state.recentTransactions.txn.status + ); + const txnResult = useAppSelector( + (state) => state.recentTransactions.txn?.data?.[0] + ); useEffect(() => { txnHistory.fetchTransaction(paramTxHash[0]); @@ -32,15 +40,38 @@ const Page = () => { return (
- {chainID && ( - + {txnStatus === TxStatus.PENDING ? ( + + ) : ( + <> + {txnResult && chainID && ( + + )} + )} + {txnStatus === TxStatus.REJECTED ? : null}
); }; export default Page; + +const TransactionNotFound = () => { + const txSearchError = useAppSelector( + (state) => state.recentTransactions.txn.error + ); + + if (txSearchError) + return ( +
+

+ Sorry, the transaction you're looking for is not found. +

+
+ ); + return <>; +}; diff --git a/frontend/src/app/(routes)/transactions/history/[network]/components/Transaction.tsx b/frontend/src/app/(routes)/transactions/history/[network]/components/Transaction.tsx index 1b7d341e9..df0701ec5 100644 --- a/frontend/src/app/(routes)/transactions/history/[network]/components/Transaction.tsx +++ b/frontend/src/app/(routes)/transactions/history/[network]/components/Transaction.tsx @@ -13,6 +13,7 @@ import { buildMessages } from '@/utils/transaction'; import { txRepeatTransaction } from '@/store/features/recent-transactions/recentTransactionsSlice'; import DialogLoader from '@/components/common/DialogLoader'; import { TxStatus } from '@/types/enums'; +import CustomDialog from '@/components/common/CustomDialog'; const Transaction = ({ chainName, @@ -44,6 +45,8 @@ const Transaction = ({ const formattedMessages = messages ? buildMessages(messages) : []; const disableAction = formattedMessages.length === 0 || !success; + const [viewRawOpen, setViewRawOpen] = useState(false); + const onRepeatTxn = () => { dispatch( txRepeatTransaction({ @@ -63,7 +66,7 @@ const Transaction = ({ }; }; - const [expandedIndex, setExpandedIndex] = useState(null); + const [expandedIndex, setExpandedIndex] = useState(0); const toggleExpand = (index: number) => { setExpandedIndex(expandedIndex === index ? null : index); @@ -117,6 +120,17 @@ const Transaction = ({ +
+
Messages: {msgs?.length}
+
+ +
+
{msgs?.map((msg, mIndex) => ( + setViewRawOpen(false)} + title="Raw Transaction" + > +
+ {txnResult ? ( +
{JSON.stringify(txnResult, undefined, 2)}
+ ) : ( +
- No Data -
+ )} +
+
); }; diff --git a/frontend/src/app/(routes)/transactions/history/[network]/components/TransactionHistoryDashboard.tsx b/frontend/src/app/(routes)/transactions/history/[network]/components/TransactionHistoryDashboard.tsx index 86808ceb7..4791ea016 100644 --- a/frontend/src/app/(routes)/transactions/history/[network]/components/TransactionHistoryDashboard.tsx +++ b/frontend/src/app/(routes)/transactions/history/[network]/components/TransactionHistoryDashboard.tsx @@ -10,10 +10,12 @@ import { TxStatus } from '@/types/enums'; import { Pagination } from '@mui/material'; import { paginationComponentStyles } from '@/utils/commonStyles'; import TxnsLoading from '../../loaders/TxnsLoading'; +import useInitTransactions from '@/custom-hooks/useInitTransactions'; const ITEMS_PER_PAGE = 5; const TransactionHistoryDashboard = ({ chainID }: { chainID: string }) => { + useInitTransactions({chainID}) const { getChainInfo, getDenomInfo } = useGetChainInfo(); const { displayDenom, decimals, minimalDenom } = getDenomInfo(chainID); const basicChainInfo = getChainInfo(chainID); diff --git a/frontend/src/app/(routes)/transactions/history/loaders/TransactionLoading.tsx b/frontend/src/app/(routes)/transactions/history/loaders/TransactionLoading.tsx new file mode 100644 index 000000000..7dc77e601 --- /dev/null +++ b/frontend/src/app/(routes)/transactions/history/loaders/TransactionLoading.tsx @@ -0,0 +1,77 @@ +import React from 'react'; + +const TransactionLoading = () => { + return ( +
+ +
+
+
+
+
+
+

+
+
+
+
+
+

+
+
+
+
+ {Array(4) + .fill(null) + .map((index) => ( +
+
+

+ +
+
+

+
+
+
+ ))} +
+
+ +
+ {Array(3) + .fill(null) + .map((index) => ( +
+
+

+
+
+
+ ))} +
+
+
+ ); +}; + +export default TransactionLoading; + +const TransactionHeader = () => { + return ( +
+
+
+
+
+
+
+
+ ); +}; diff --git a/frontend/src/app/globals.css b/frontend/src/app/globals.css index bae0a3dd2..0ee05c5f3 100644 --- a/frontend/src/app/globals.css +++ b/frontend/src/app/globals.css @@ -213,7 +213,8 @@ input[type='checkbox'] { @apply flex flex-col items-start gap-4 w-full p-10; background: rgba(255, 255, 255, 0.02); } -.profile-grid, .txn-data { +.profile-grid, +.txn-data { @apply flex flex-col justify-center items-center gap-2 p-4 rounded-2xl; background: rgba(255, 255, 255, 0.02); } @@ -242,6 +243,13 @@ input[type='checkbox'] { @apply flex justify-center items-center gap-4 px-4 py-2 rounded-lg border-[0.25px] hover:bg-[#ffffff14] hover:border-transparent; } +.shimmer { + @apply bg-[#252525] animate-pulse; +} + +.shimmer-line { + @apply bg-[#252525] animate-pulse rounded h-5; +} /* TransactionHistory */ .search-Txn-input { diff --git a/frontend/src/custom-hooks/useGetTransactions.ts b/frontend/src/custom-hooks/useGetTransactions.ts index 4a934cd6b..2f425dd95 100644 --- a/frontend/src/custom-hooks/useGetTransactions.ts +++ b/frontend/src/custom-hooks/useGetTransactions.ts @@ -1,6 +1,8 @@ -import { useEffect } from 'react'; import { useAppDispatch, useAppSelector } from './StateHooks'; -import { getAllTransactions, getTransaction } from '@/store/features/recent-transactions/recentTransactionsSlice'; +import { + getAllTransactions, + getTransaction, +} from '@/store/features/recent-transactions/recentTransactionsSlice'; import useGetChainInfo from './useGetChainInfo'; const useGetTransactions = ({ chainID }: { chainID: string }) => { @@ -8,16 +10,6 @@ const useGetTransactions = ({ chainID }: { chainID: string }) => { const { getChainInfo } = useGetChainInfo(); const { address } = getChainInfo(chainID); const txns = useAppSelector((state) => state.recentTransactions.txns.data); - useEffect(() => { - dispatch( - getAllTransactions({ - address, - chainID, - limit: 5, - offset: 0, - }) - ); - }, []); const getTransactions = () => { return { txns, @@ -36,13 +28,15 @@ const useGetTransactions = ({ chainID }: { chainID: string }) => { }; const fetchTransaction = (txhash: string) => { - dispatch( - getTransaction({ - address, - chainID, - txhash, - }) - ); + if (chainID) { + dispatch( + getTransaction({ + address, + chainID, + txhash, + }) + ); + } }; return { getTransactions, fetchTransactions, fetchTransaction }; diff --git a/frontend/src/custom-hooks/useInitTransactions.tsx b/frontend/src/custom-hooks/useInitTransactions.tsx new file mode 100644 index 000000000..1932bc806 --- /dev/null +++ b/frontend/src/custom-hooks/useInitTransactions.tsx @@ -0,0 +1,24 @@ +import { useEffect } from 'react'; +import { useAppDispatch } from './StateHooks'; +import { getAllTransactions } from '@/store/features/recent-transactions/recentTransactionsSlice'; +import useGetChainInfo from './useGetChainInfo'; + +const useInitTransactions = ({ chainID }: { chainID: string }) => { + const dispatch = useAppDispatch(); + const { getChainInfo } = useGetChainInfo(); + const { address } = getChainInfo(chainID); + useEffect(() => { + if (chainID) { + dispatch( + getAllTransactions({ + address, + chainID, + limit: 5, + offset: 0, + }) + ); + } + }, [chainID]); +}; + +export default useInitTransactions; diff --git a/frontend/src/store/features/recent-transactions/recentTransactionsSlice.tsx b/frontend/src/store/features/recent-transactions/recentTransactionsSlice.tsx index b694a3d0b..f18ec274b 100644 --- a/frontend/src/store/features/recent-transactions/recentTransactionsSlice.tsx +++ b/frontend/src/store/features/recent-transactions/recentTransactionsSlice.tsx @@ -184,45 +184,41 @@ export const getTransaction = createAsyncThunk( let txns: ParsedTransaction[] = []; const txnsData = response?.data?.data?.data; const { txhash, messages } = txnsData; - if (messages[0]?.['@type'] === IBC_SEND_TYPE_URL) { - const ibcTx = getIBCTxn(txhash); - if (ibcTx) { - txns = [...txns, ibcTx]; - if (ibcTx?.isIBCPending) { - dispatch( - trackTx({ - chainID: ibcTx.chain_id, - txHash: ibcTx.txhash, - }) - ); + if (txnsData) { + if (messages[0]?.['@type'] === IBC_SEND_TYPE_URL) { + const ibcTx = getIBCTxn(txhash); + if (ibcTx) { + txns = [...txns, ibcTx]; + if (ibcTx?.isIBCPending) { + dispatch( + trackTx({ + chainID: ibcTx.chain_id, + txHash: ibcTx.txhash, + }) + ); + } + } else { + let formattedTxn = txnsData; + formattedTxn = { ...formattedTxn, isIBCPending: false }; + formattedTxn = { ...formattedTxn, isIBCTxn: true }; + txns = [...txns, formattedTxn]; } } else { let formattedTxn = txnsData; formattedTxn = { ...formattedTxn, isIBCPending: false }; - formattedTxn = { ...formattedTxn, isIBCTxn: true }; + formattedTxn = { ...formattedTxn, isIBCTxn: false }; + txns = [...txns, formattedTxn]; } } else { - let formattedTxn = txnsData; - formattedTxn = { ...formattedTxn, isIBCPending: false }; - formattedTxn = { ...formattedTxn, isIBCTxn: false }; - - txns = [...txns, formattedTxn]; + throw new Error(ERR_TXN_NOT_FOUND); } - // }); - // return { - // data: { - // data: txns, - // total: response?.data?.data?.total, - // }, - // }; + return { data: txns }; - } catch (error) { - if (error instanceof AxiosError) - return rejectWithValue({ - message: error?.response?.data?.message || ERR_UNKNOWN, - }); - return rejectWithValue({ message: ERR_UNKNOWN }); + /* eslint-disable @typescript-eslint/no-explicit-any */ + } catch (error: any) { + const errMsg = error?.message || ERR_TXN_NOT_FOUND; + return rejectWithValue(errMsg); } } ); @@ -387,14 +383,13 @@ export const recentTransactionsSlice = createSlice({ }) .addCase(getTransaction.fulfilled, (state, action) => { state.txn.status = TxStatus.IDLE; - state.txn.data = action?.payload?.data || {}; + state.txn.data = action?.payload?.data || []; state.txns.error = ''; }) .addCase(getTransaction.rejected, (state, action) => { state.txn.status = TxStatus.REJECTED; - - const payload = action.payload as { message: string }; - state.txns.error = payload.message || ''; + state.txn.data = []; + state.txn.error = action.error.message || 'Failed to fetch transaction'; }); builder .addCase(getAnyChainTransaction.pending, (state) => {