From 7a4d7198db9fb6b3cd186d5e90387fb96d9f9147 Mon Sep 17 00:00:00 2001 From: alexmf91 Date: Thu, 5 Sep 2024 00:11:30 +0200 Subject: [PATCH 01/40] refactor: Update InfiniteScroll component to improve performance --- src/components/ui/InfiniteScroll.tsx | 42 +++++++++++++++------------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/src/components/ui/InfiniteScroll.tsx b/src/components/ui/InfiniteScroll.tsx index 9666165..370709f 100644 --- a/src/components/ui/InfiniteScroll.tsx +++ b/src/components/ui/InfiniteScroll.tsx @@ -3,8 +3,8 @@ import { useCallback, useEffect, useRef, useState } from 'react' import { ArrowUpIcon } from '@app/assets/icons' import { Alert } from '@app/components/ui' +import { DotsLoader } from '@app/components/ui/loaders' import { clsxTwMerge } from '@app/utils' -import { DotsLoader } from './loaders' interface InfiniteScrollProps { items: T[] // Array of items to display @@ -36,8 +36,8 @@ export default function InfiniteScroll({ const [internalError, setInternalError] = useState(null) const [showScrollToTop, setShowScrollToTop] = useState(false) - const isLoading = externalIsLoading !== undefined ? externalIsLoading : internalIsLoading - const error = externalError !== undefined ? externalError : internalError + const isLoading = externalIsLoading ?? internalIsLoading + const error = externalError ?? internalError const handleLoadMore = useCallback(async () => { try { @@ -69,22 +69,29 @@ export default function InfiniteScroll({ [handleLoadMore, hasMore, isLoading, threshold] ) - const handleScrollToTop = () => { + const handleScrollToTop = useCallback(() => { window.scrollTo({ top: 0, behavior: 'smooth' }) - } + }, []) - useEffect(() => { - const handleScroll = () => { - if (window.scrollY > window.innerHeight) { - setShowScrollToTop(true) - } else { - setShowScrollToTop(false) - } + const handleScroll = useCallback(() => { + if (window.scrollY > window.innerHeight) { + setShowScrollToTop(true) + } else { + setShowScrollToTop(false) } + }, []) + useEffect(() => { window.addEventListener('scroll', handleScroll) return () => window.removeEventListener('scroll', handleScroll) - }, []) + }, [handleScroll]) + + const renderStatus = useCallback(() => { + if (error) return {error} + if (isLoading) return loader + if (!hasMore && endMessage) return endMessage + return null + }, [error, isLoading, loader, hasMore, endMessage]) return (
@@ -98,13 +105,8 @@ export default function InfiniteScroll({ ))} - {isLoading && loader} - {!hasMore && !isLoading && endMessage} - {error && ( - - {error} - - )} + + {renderStatus()} {showScrollToTop && ( From c586adf4d7215938c5f33e2f821f0b0da4345ad7 Mon Sep 17 00:00:00 2001 From: alexmf91 Date: Fri, 6 Sep 2024 10:01:40 +0200 Subject: [PATCH 05/40] refactor: Fix typo in TxItem component prop name --- src/pages/network/components/TxItem/TxItem.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/pages/network/components/TxItem/TxItem.tsx b/src/pages/network/components/TxItem/TxItem.tsx index 44f37cb..b9c109c 100644 --- a/src/pages/network/components/TxItem/TxItem.tsx +++ b/src/pages/network/components/TxItem/TxItem.tsx @@ -15,7 +15,7 @@ import type { TxItemVariant } from './TxItem.types' type Props = { tx: Omit - identify?: string + identity?: string nonExecutedTxIds: string[] variant?: TxItemVariant isHistoricalTx?: boolean @@ -23,7 +23,7 @@ type Props = { function TxItem({ tx: { txId, sourceId, tickNumber, destId, inputType, amount, inputHex }, - identify, + identity, nonExecutedTxIds, variant = 'primary', isHistoricalTx = false @@ -82,16 +82,16 @@ function TxItem({ isTransferTx={isTransferTransaction} />
- {identify ? ( + {identity ? (
- {identify === sourceId ? ( + {identity === sourceId ? ( ) : ( )} From 401286a80717c254b3e1180d8900f5dbde009b9c Mon Sep 17 00:00:00 2001 From: alexmf91 Date: Fri, 6 Sep 2024 16:59:12 +0200 Subject: [PATCH 06/40] feat: Add useLatestTransactions hook for fetching latest transactions --- src/pages/network/address/hooks/index.ts | 2 + .../address/hooks/useLatestTransactions.ts | 129 ++++++++++++++++++ 2 files changed, 131 insertions(+) create mode 100644 src/pages/network/address/hooks/index.ts create mode 100644 src/pages/network/address/hooks/useLatestTransactions.ts diff --git a/src/pages/network/address/hooks/index.ts b/src/pages/network/address/hooks/index.ts new file mode 100644 index 0000000..7a2af43 --- /dev/null +++ b/src/pages/network/address/hooks/index.ts @@ -0,0 +1,2 @@ +// eslint-disable-next-line +export { default as useLatestTransactions } from './useLatestTransactions' diff --git a/src/pages/network/address/hooks/useLatestTransactions.ts b/src/pages/network/address/hooks/useLatestTransactions.ts new file mode 100644 index 0000000..900afd0 --- /dev/null +++ b/src/pages/network/address/hooks/useLatestTransactions.ts @@ -0,0 +1,129 @@ +import { useLazyGetIndentityTransfersQuery } from '@app/store/apis/archiver-v2.api' +import type { TransactionV2 } from '@app/store/apis/archiver-v2.types' +import type { Address } from '@app/store/network/addressSlice' +import { useCallback, useEffect, useState } from 'react' + +const BATCH_SIZE = 50 +const TICK_SIZE = 200_000 + +export interface UseLatestTransactionsResult { + transactions: TransactionV2[] + loadMoreTransactions: () => Promise + hasMore: boolean + isLoading: boolean + error: string | null +} + +export default function useLatestTransactions( + addressId: string, + addressEndTick: Address['endTick'] = 0 +): UseLatestTransactionsResult { + const [startTick, setStartTick] = useState(Math.max(0, addressEndTick - TICK_SIZE)) + const [transactions, setTransactions] = useState([]) + const [txsList, setTxsList] = useState([]) + const [isLoading, setIsLoading] = useState(false) + const [getIdentityTransfersQuery, { isFetching, error }] = useLazyGetIndentityTransfersQuery({}) + + const hasMore = startTick > 0 + + const fetchTransfers = useCallback( + async (start: number, end: number) => { + const result = await getIdentityTransfersQuery({ + addressId, + startTick: start, + endTick: end + }).unwrap() + + return result || [] + }, + [getIdentityTransfersQuery, addressId] + ) + + const fetchRecursive = useCallback( + async (start: number, end: number, accumulatedData: TransactionV2[] = []) => { + const newTxs = await fetchTransfers(start, end) + const combinedData = [...new Set(accumulatedData.concat(newTxs))] + + if (combinedData.length < BATCH_SIZE && start > 0) { + const newEndTick = Math.max(0, start - 1) + const newStartTick = Math.max(0, start - 1 - TICK_SIZE) + return fetchRecursive(newStartTick, newEndTick, combinedData) + } + + return { + newTxs: combinedData.sort((a, b) => b.transaction.tickNumber - a.transaction.tickNumber), + lastStartTick: start + } + }, + [fetchTransfers] + ) + + const loadMoreTransactions = useCallback(async () => { + if (isLoading || isFetching || !hasMore) return + + setIsLoading(true) + try { + if (txsList.length < BATCH_SIZE) { + const newStartTick = Math.max(0, startTick - 1 - TICK_SIZE) + const newEndTick = Math.max(0, startTick - 1) + const { newTxs, lastStartTick } = await fetchRecursive(newStartTick, newEndTick) + // Since there could be some txs in txsList already, we need to merge them and then slice it + const updatedTxList = [...txsList, ...newTxs] + // Adding the new transactions to the list to be displayed + setTransactions((prev) => [...prev, ...updatedTxList.slice(0, BATCH_SIZE)]) + // Updating the list of remaining transactions + setTxsList(updatedTxList.slice(BATCH_SIZE, updatedTxList.length)) + // Updating the start and end tick + setStartTick(lastStartTick) + } else { + setTransactions((prev) => [...prev, ...txsList.slice(0, BATCH_SIZE)]) + setTxsList((prevTxsList) => prevTxsList.slice(BATCH_SIZE, prevTxsList.length)) + } + } finally { + setIsLoading(false) + } + }, [startTick, fetchRecursive, isLoading, isFetching, hasMore, txsList]) + + useEffect(() => { + let isMounted = true + + const initialFetch = async () => { + setIsLoading(true) + const initialStartTick = Math.max(0, addressEndTick - TICK_SIZE) + const { newTxs, lastStartTick } = await fetchRecursive(initialStartTick, addressEndTick) + + if (isMounted) { + setTransactions(newTxs.slice(0, BATCH_SIZE)) + setTxsList(newTxs.slice(BATCH_SIZE, newTxs.length)) + setStartTick(lastStartTick) + setIsLoading(false) + } + } + + if (transactions.length === 0 && addressEndTick) { + initialFetch() + } + + return () => { + isMounted = false + } + }, [fetchRecursive, transactions.length, addressEndTick]) + + useEffect(() => { + return () => { + if (addressId) { + setTransactions([]) + setTxsList([]) + setStartTick(0) + } + } + }, [addressId]) + + return { + transactions, + loadMoreTransactions, + hasMore, + isLoading, + error: error ? String(error) : null + } +} From ef2d35f28e2249bfc14d00faecaf1f440f025452 Mon Sep 17 00:00:00 2001 From: alexmf91 Date: Fri, 6 Sep 2024 16:59:38 +0200 Subject: [PATCH 07/40] refactor: Update Transactions component to use transactions prop instead of address and fetch data using useLatestTransactions hook --- .../address/components/Transactions.tsx | 126 ++---------------- 1 file changed, 10 insertions(+), 116 deletions(-) diff --git a/src/pages/network/address/components/Transactions.tsx b/src/pages/network/address/components/Transactions.tsx index b746f53..1d5c29b 100644 --- a/src/pages/network/address/components/Transactions.tsx +++ b/src/pages/network/address/components/Transactions.tsx @@ -1,132 +1,26 @@ -import { memo, useCallback, useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' import { InfiniteScroll } from '@app/components/ui' import { DotsLoader } from '@app/components/ui/loaders' -import { useLazyGetIndentityTransfersQuery } from '@app/store/apis/archiver-v2.api' import type { TransactionV2 } from '@app/store/apis/archiver-v2.types' -import type { Address } from '@app/store/network/addressSlice' +import { memo } from 'react' import { TxItem } from '../../components' type Props = { addressId: string - address: Address + transactions: TransactionV2[] + loadMore: () => Promise + hasMore: boolean + isLoading: boolean + error: string | null } -export const BATCH_SIZE = 50 - -export const TICK_SIZE = 200_000 - -function Transactions({ addressId, address }: Props) { +function Transactions({ addressId, transactions, loadMore, hasMore, isLoading, error }: Props) { const { t } = useTranslation('network-page') - const [startTick, setStartTick] = useState(address.endTick - TICK_SIZE) - const [endTick, setEndTick] = useState(address.endTick) - const [getIdentityTransfersQuery, { isFetching, error }] = useLazyGetIndentityTransfersQuery({}) - const [displayTransferTxs, setDisplayTransferTxs] = useState([]) - const [txsList, setTxsList] = useState([]) - - const [isLoading, setIsLoading] = useState(false) - - const hasMore = startTick > 0 - - const fetchTransfers = useCallback( - async (start: number, end: number) => { - const result = await getIdentityTransfersQuery({ - addressId, - startTick: start, - endTick: end - }).unwrap() - - return result || [] - }, - [getIdentityTransfersQuery, addressId] - ) - - const fetchRecursive = useCallback( - async (start: number, end: number, accumulatedData: TransactionV2[] = []) => { - const newTxs = await fetchTransfers(start, end) - - console.log('🚀 ~ fetchRecursive ~ newTxs:', newTxs.length) - - const combinedData = [...new Set(accumulatedData.concat(newTxs))] - - console.log('🚀 ~ fetchRecursive ~ combinedData:', combinedData.length) - - if (combinedData.length < BATCH_SIZE && start > 0) { - const newEndTick = Math.max(0, start - 1) - const newStartTick = Math.max(0, start - 1 - TICK_SIZE) - console.log('ticks in recursive', { newStartTick, newEndTick }, newStartTick - newEndTick) - return fetchRecursive(newStartTick, newEndTick, combinedData) - } - - return { - newTxs: combinedData.sort((a, b) => b.transaction.tickNumber - a.transaction.tickNumber), - lastStartTick: start, - lastEndTick: end - } - }, - [fetchTransfers] - ) - - const loadMore = useCallback(async () => { - if (isLoading || isFetching || !hasMore) return - - setIsLoading(true) - - if (txsList.length < BATCH_SIZE) { - const newStartTick = Math.max(0, startTick - 1 - TICK_SIZE) - const newEndTick = Math.max(0, startTick - 1) - console.log('ticks in loadMore', { newStartTick, newEndTick }, newStartTick - newEndTick) - const { newTxs, lastStartTick, lastEndTick } = await fetchRecursive(newStartTick, newEndTick) - - // since there could be some txs in txsList already, we need to merge them and then slice - // setTxsList((prev) => [...prev, ...newTxs]) - const updatedTxList = [...txsList, ...newTxs] - // - setDisplayTransferTxs((prev) => [...prev, ...updatedTxList.slice(0, BATCH_SIZE)]) - // Removing the txs that we just added to displayTransferTxs - setTxsList(updatedTxList.slice(BATCH_SIZE, updatedTxList.length)) - // setTxsList((prevTxsList) => prevTxsList.slice(BATCH_SIZE, prevTxsList.length)) - setStartTick(lastStartTick) - setEndTick(lastEndTick) - } else { - setDisplayTransferTxs((prev) => [...prev, ...txsList.slice(0, BATCH_SIZE)]) - setTxsList((prevTxsList) => prevTxsList.slice(BATCH_SIZE, prevTxsList.length)) - } - setIsLoading(false) - }, [startTick, fetchRecursive, isLoading, isFetching, hasMore, txsList]) - - useEffect(() => { - let isMounted = true - - const initialFetch = async () => { - setIsLoading(true) - const { newTxs, lastStartTick, lastEndTick } = await fetchRecursive(startTick, endTick) - - if (isMounted) { - setDisplayTransferTxs(newTxs.slice(0, BATCH_SIZE)) - setTxsList(newTxs.slice(BATCH_SIZE, newTxs.length)) - setStartTick(lastStartTick) - setEndTick(lastEndTick) - setIsLoading(false) - } - } - - if (displayTransferTxs.length === 0 && endTick) { - initialFetch() - } - - return () => { - isMounted = false - } - }, [startTick, endTick, fetchRecursive, displayTransferTxs.length]) - - console.log('🚀 ~ txsList:', txsList.length) - console.log('🚀 ~ displayTransferTxs:', displayTransferTxs.length) return ( - {displayTransferTxs.length === 0 ? t('noTransactions') : t('allTransactionsLoaded')} + {transactions.length === 0 ? t('noTransactions') : t('allTransactionsLoaded')}

} renderItem={({ transaction, moneyFlew }: TransactionV2) => ( From 0a561d501f8088af92b58056eb6a86b149da2861 Mon Sep 17 00:00:00 2001 From: alexmf91 Date: Fri, 6 Sep 2024 19:41:47 +0200 Subject: [PATCH 08/40] refactor: Update HistoricalTxs component to render TransactionWithStatus using renderTxItem callback --- .../address/components/HistoricalTxs.tsx | 26 ++++++++++++------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/src/pages/network/address/components/HistoricalTxs.tsx b/src/pages/network/address/components/HistoricalTxs.tsx index 92fd6d1..ab73228 100644 --- a/src/pages/network/address/components/HistoricalTxs.tsx +++ b/src/pages/network/address/components/HistoricalTxs.tsx @@ -3,6 +3,7 @@ import { InfiniteScroll } from '@app/components/ui' import { DotsLoader } from '@app/components/ui/loaders' import { useAppDispatch, useAppSelector } from '@app/hooks/redux' import { getHistoricalTxs, selectHistoricalTxs } from '@app/store/network/addressSlice' +import type { TransactionWithStatus } from '@app/store/network/txSlice' import { useCallback, useEffect } from 'react' import { useTranslation } from 'react-i18next' import { TxItem } from '../../components' @@ -20,6 +21,20 @@ export default function HistoricalTxs({ addressId }: Props) { dispatch(getHistoricalTxs(addressId)) }, [dispatch, addressId]) + const renderTxItem = useCallback( + ({ tx, status }: TransactionWithStatus) => ( + + ), + [addressId] + ) + useEffect(() => { if (historicalTxs.length === 0) { loadMoreTxs() @@ -47,16 +62,7 @@ export default function HistoricalTxs({ addressId }: Props) { {historicalTxs.length === 0 ? t('noTransactions') : t('allTransactionsLoaded')}

} - renderItem={({ tx, status }) => ( - - )} + renderItem={renderTxItem} />
) From 951c9ccc51f938bb5da807746e9b599a8f4d710f Mon Sep 17 00:00:00 2001 From: alexmf91 Date: Fri, 6 Sep 2024 19:42:28 +0200 Subject: [PATCH 09/40] refactor: Update Transactions component to use transactions prop instead of address and fetch data using useLatestTransactions hook --- ...ransactions.tsx => LatestTransactions.tsx} | 38 ++++++++------ .../components/TransactionsOverview.tsx | 51 +++++++++++++++++++ 2 files changed, 74 insertions(+), 15 deletions(-) rename src/pages/network/address/components/{Transactions.tsx => LatestTransactions.tsx} (64%) create mode 100644 src/pages/network/address/components/TransactionsOverview.tsx diff --git a/src/pages/network/address/components/Transactions.tsx b/src/pages/network/address/components/LatestTransactions.tsx similarity index 64% rename from src/pages/network/address/components/Transactions.tsx rename to src/pages/network/address/components/LatestTransactions.tsx index 1d5c29b..3c82837 100644 --- a/src/pages/network/address/components/Transactions.tsx +++ b/src/pages/network/address/components/LatestTransactions.tsx @@ -3,7 +3,7 @@ import { useTranslation } from 'react-i18next' import { InfiniteScroll } from '@app/components/ui' import { DotsLoader } from '@app/components/ui/loaders' import type { TransactionV2 } from '@app/store/apis/archiver-v2.types' -import { memo } from 'react' +import { useCallback } from 'react' import { TxItem } from '../../components' type Props = { @@ -15,9 +15,29 @@ type Props = { error: string | null } -function Transactions({ addressId, transactions, loadMore, hasMore, isLoading, error }: Props) { +export default function LatestTransactions({ + addressId, + transactions, + loadMore, + hasMore, + isLoading, + error +}: Props) { const { t } = useTranslation('network-page') + const renderTxItem = useCallback( + ({ transaction, moneyFlew }: TransactionV2) => ( + + ), + [addressId] + ) + return ( } - renderItem={({ transaction, moneyFlew }: TransactionV2) => ( - - )} + renderItem={renderTxItem} /> ) } - -const MemoizedTransactions = memo(Transactions) - -export default MemoizedTransactions diff --git a/src/pages/network/address/components/TransactionsOverview.tsx b/src/pages/network/address/components/TransactionsOverview.tsx new file mode 100644 index 0000000..e170aa4 --- /dev/null +++ b/src/pages/network/address/components/TransactionsOverview.tsx @@ -0,0 +1,51 @@ +import { useTranslation } from 'react-i18next' + +import { Tabs } from '@app/components/ui' +import type { Address } from '@app/store/network/addressSlice' +import { memo } from 'react' +import { useLatestTransactions } from '../hooks' +import HistoricalTxs from './HistoricalTxs' +import LatestTransactions from './LatestTransactions' + +type Props = { + address: Address + addressId: string +} + +function TransactionsOverview({ address, addressId }: Props) { + const { t } = useTranslation('network-page') + const { transactions, loadMoreTransactions, hasMore, isLoading, error } = useLatestTransactions( + addressId, + address.endTick + ) + + return ( +
+

{t('transactions')}

+ + + {t('latest')} + {t('historical')} + + + + + + + + + + +
+ ) +} + +const MemoizedTransactionsOverview = memo(TransactionsOverview) +export default MemoizedTransactionsOverview From 6cb69705e8352cabd0f86bc1017e16afd6e5073f Mon Sep 17 00:00:00 2001 From: alexmf91 Date: Fri, 6 Sep 2024 19:42:46 +0200 Subject: [PATCH 10/40] refactor: Update AddressPage to use TransactionsOverview component for displaying transactions --- src/pages/network/address/AddressPage.tsx | 22 +++------------------- 1 file changed, 3 insertions(+), 19 deletions(-) diff --git a/src/pages/network/address/AddressPage.tsx b/src/pages/network/address/AddressPage.tsx index ffa223e..9b93488 100644 --- a/src/pages/network/address/AddressPage.tsx +++ b/src/pages/network/address/AddressPage.tsx @@ -2,7 +2,7 @@ import { useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' import { useParams } from 'react-router-dom' -import { Alert, Breadcrumbs, Tabs } from '@app/components/ui' +import { Alert, Breadcrumbs } from '@app/components/ui' import { ChevronToggleButton, CopyTextButton } from '@app/components/ui/buttons' import { LinearProgress } from '@app/components/ui/loaders' import { useAppDispatch, useAppSelector } from '@app/hooks/redux' @@ -10,7 +10,7 @@ import { getAddress, resetState, selectAddress } from '@app/store/network/addres import { getOverview, selectOverview } from '@app/store/network/overviewSlice' import { formatEllipsis, formatString } from '@app/utils' import { CardItem, HomeLink, TickLink } from '../components' -import { HistoricalTxs, Transactions } from './components' +import { TransactionsOverview } from './components' export default function AddressPage() { const { t } = useTranslation('network-page') @@ -116,23 +116,7 @@ export default function AddressPage() {
)}
-
-

{t('transactions')}

- - - {t('latest')} - {t('historical')} - - - - - - - - - - -
+ ) } From e719461a106a35ab7231c824428b8f2c7ff84355 Mon Sep 17 00:00:00 2001 From: alexmf91 Date: Fri, 6 Sep 2024 19:43:04 +0200 Subject: [PATCH 11/40] refactor: Update useLatestTransactions hook to fix addressEndTick default value --- src/pages/network/address/hooks/useLatestTransactions.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/network/address/hooks/useLatestTransactions.ts b/src/pages/network/address/hooks/useLatestTransactions.ts index 900afd0..d0f5fb3 100644 --- a/src/pages/network/address/hooks/useLatestTransactions.ts +++ b/src/pages/network/address/hooks/useLatestTransactions.ts @@ -16,7 +16,7 @@ export interface UseLatestTransactionsResult { export default function useLatestTransactions( addressId: string, - addressEndTick: Address['endTick'] = 0 + addressEndTick: Address['endTick'] ): UseLatestTransactionsResult { const [startTick, setStartTick] = useState(Math.max(0, addressEndTick - TICK_SIZE)) const [transactions, setTransactions] = useState([]) From ae7c49f4ead358d0db1cd7ef94ece70d7d6f6bab Mon Sep 17 00:00:00 2001 From: alexmf91 Date: Fri, 6 Sep 2024 19:52:17 +0200 Subject: [PATCH 12/40] refactor: Update component imports to match new file structure --- .../components/{ => TransactionsOverview}/HistoricalTxs.tsx | 2 +- .../{ => TransactionsOverview}/LatestTransactions.tsx | 2 +- .../{ => TransactionsOverview}/TransactionsOverview.tsx | 2 +- src/pages/network/address/components/index.ts | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) rename src/pages/network/address/components/{ => TransactionsOverview}/HistoricalTxs.tsx (97%) rename src/pages/network/address/components/{ => TransactionsOverview}/LatestTransactions.tsx (96%) rename src/pages/network/address/components/{ => TransactionsOverview}/TransactionsOverview.tsx (96%) diff --git a/src/pages/network/address/components/HistoricalTxs.tsx b/src/pages/network/address/components/TransactionsOverview/HistoricalTxs.tsx similarity index 97% rename from src/pages/network/address/components/HistoricalTxs.tsx rename to src/pages/network/address/components/TransactionsOverview/HistoricalTxs.tsx index ab73228..2fac7dc 100644 --- a/src/pages/network/address/components/HistoricalTxs.tsx +++ b/src/pages/network/address/components/TransactionsOverview/HistoricalTxs.tsx @@ -6,7 +6,7 @@ import { getHistoricalTxs, selectHistoricalTxs } from '@app/store/network/addres import type { TransactionWithStatus } from '@app/store/network/txSlice' import { useCallback, useEffect } from 'react' import { useTranslation } from 'react-i18next' -import { TxItem } from '../../components' +import { TxItem } from '../../../components' type Props = { addressId: string diff --git a/src/pages/network/address/components/LatestTransactions.tsx b/src/pages/network/address/components/TransactionsOverview/LatestTransactions.tsx similarity index 96% rename from src/pages/network/address/components/LatestTransactions.tsx rename to src/pages/network/address/components/TransactionsOverview/LatestTransactions.tsx index 3c82837..b71c224 100644 --- a/src/pages/network/address/components/LatestTransactions.tsx +++ b/src/pages/network/address/components/TransactionsOverview/LatestTransactions.tsx @@ -4,7 +4,7 @@ import { InfiniteScroll } from '@app/components/ui' import { DotsLoader } from '@app/components/ui/loaders' import type { TransactionV2 } from '@app/store/apis/archiver-v2.types' import { useCallback } from 'react' -import { TxItem } from '../../components' +import { TxItem } from '../../../components' type Props = { addressId: string diff --git a/src/pages/network/address/components/TransactionsOverview.tsx b/src/pages/network/address/components/TransactionsOverview/TransactionsOverview.tsx similarity index 96% rename from src/pages/network/address/components/TransactionsOverview.tsx rename to src/pages/network/address/components/TransactionsOverview/TransactionsOverview.tsx index e170aa4..fde35d6 100644 --- a/src/pages/network/address/components/TransactionsOverview.tsx +++ b/src/pages/network/address/components/TransactionsOverview/TransactionsOverview.tsx @@ -3,7 +3,7 @@ import { useTranslation } from 'react-i18next' import { Tabs } from '@app/components/ui' import type { Address } from '@app/store/network/addressSlice' import { memo } from 'react' -import { useLatestTransactions } from '../hooks' +import { useLatestTransactions } from '../../hooks' import HistoricalTxs from './HistoricalTxs' import LatestTransactions from './LatestTransactions' diff --git a/src/pages/network/address/components/index.ts b/src/pages/network/address/components/index.ts index a1ef3bd..4411351 100644 --- a/src/pages/network/address/components/index.ts +++ b/src/pages/network/address/components/index.ts @@ -1,2 +1,2 @@ -export { default as HistoricalTxs } from './HistoricalTxs' -export { default as Transactions } from './Transactions' +export { default as AddressDetails } from './AddressDetails' +export { default as TransactionsOverview } from './TransactionsOverview/TransactionsOverview' From 10a062d60d1782f95719edd3eea45d6b2ea09eb0 Mon Sep 17 00:00:00 2001 From: alexmf91 Date: Fri, 6 Sep 2024 19:52:37 +0200 Subject: [PATCH 13/40] feat: Add AddressDetails component --- .../address/components/AddressDetails.tsx | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 src/pages/network/address/components/AddressDetails.tsx diff --git a/src/pages/network/address/components/AddressDetails.tsx b/src/pages/network/address/components/AddressDetails.tsx new file mode 100644 index 0000000..bda8645 --- /dev/null +++ b/src/pages/network/address/components/AddressDetails.tsx @@ -0,0 +1,53 @@ +import type { Address } from '@app/store/network/addressSlice' +import { formatString } from '@app/utils' +import { useTranslation } from 'react-i18next' +import { CardItem, TickLink } from '../../components' + +type Props = { + address: Address +} + +export default function AddressDetails({ address }: Props) { + const { t } = useTranslation('network-page') + + return ( +
+ {(Object.entries(address.reportedValues) || []).map(([ip, details]) => ( + +
+
+

{t('value')}

+

+ {formatString(details.incomingAmount - details.outgoingAmount)}{' '} + QUBIC +

+
+

{ip}

+
+
+

+ {t('incoming')}: + {details.numberOfIncomingTransfers} ( + {t('latest')}:{' '} + + ) +

+

+ {t('outgoing')}: + {details.numberOfOutgoingTransfers} ( + {t('latest')}:{' '} + + ) +

+
+
+ ))} +
+ ) +} From 9e490afb3000ef211d0b71bb65b9e68c14e4d8c7 Mon Sep 17 00:00:00 2001 From: AndyQus Date: Sun, 8 Sep 2024 20:57:26 +0200 Subject: [PATCH 14/40] created new branch release/1.5.1 --- src/components/ui/layouts/Footer.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/ui/layouts/Footer.tsx b/src/components/ui/layouts/Footer.tsx index 3a870f6..53ddfd3 100644 --- a/src/components/ui/layouts/Footer.tsx +++ b/src/components/ui/layouts/Footer.tsx @@ -51,7 +51,7 @@ function Footer() { ))} -

Version 1.5

+

Version 1.5.1

) } From a1e7b63b2ddfe0691cd5e24f0f383bfe6ebc39d2 Mon Sep 17 00:00:00 2001 From: alexmf91 Date: Sun, 22 Sep 2024 20:55:08 +0200 Subject: [PATCH 15/40] refactor: Rearrange CSS classes for SearchBar component --- src/components/ui/SearchBar/SearchBar.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/ui/SearchBar/SearchBar.tsx b/src/components/ui/SearchBar/SearchBar.tsx index 5273230..e690111 100644 --- a/src/components/ui/SearchBar/SearchBar.tsx +++ b/src/components/ui/SearchBar/SearchBar.tsx @@ -90,7 +90,7 @@ export default function SearchBar() { closeOnOutsideClick onClose={handleCloseCallback} > -
+
{isLoading && (
From 576ee01117924755a332e79af93266544dabf1a3 Mon Sep 17 00:00:00 2001 From: alexmf91 Date: Sun, 22 Sep 2024 21:14:39 +0200 Subject: [PATCH 16/40] refactor: Memoize Breadcrumbs component for performance improvement --- src/components/ui/Breadcrumbs.tsx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/components/ui/Breadcrumbs.tsx b/src/components/ui/Breadcrumbs.tsx index 6b5844c..970760d 100644 --- a/src/components/ui/Breadcrumbs.tsx +++ b/src/components/ui/Breadcrumbs.tsx @@ -1,10 +1,10 @@ -import { Children, Fragment } from 'react' +import { Children, Fragment, memo } from 'react' type Props = { children?: React.ReactNode | React.ReactNode[] } -export default function Breadcrumbs({ children }: Props) { +function Breadcrumbs({ children }: Props) { return ( ) } + +const MemoizedBreadcrumbs = memo(Breadcrumbs) + +export default MemoizedBreadcrumbs From 2153e25ebda10b3a58918a709bf5ea48fb327ef3 Mon Sep 17 00:00:00 2001 From: alexmf91 Date: Sun, 22 Sep 2024 21:19:20 +0200 Subject: [PATCH 17/40] refactor: Update formatDate utility function for formatting dates and split --- src/utils/date.ts | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 src/utils/date.ts diff --git a/src/utils/date.ts b/src/utils/date.ts new file mode 100644 index 0000000..c12daa7 --- /dev/null +++ b/src/utils/date.ts @@ -0,0 +1,37 @@ +// eslint-disable-next-line import/prefer-default-export -- Remove this comment when adding more functions +export function formatDate( + dateString: string | undefined, + options?: { split?: T } +): T extends true ? { date: string; time: string } : string { + const defaultResult = (options?.split ? { date: '', time: '' } : '') as T extends true + ? { date: string; time: string } + : string + const formatDateTime = (date: Date, dateTimeFormatOptions: Intl.DateTimeFormatOptions) => + new Intl.DateTimeFormat('en-US', dateTimeFormatOptions).format(date) + + if (!dateString) return defaultResult + + const dateOptions: Intl.DateTimeFormatOptions = { + year: 'numeric', + month: 'long', + day: 'numeric' + } + + const timeOptions: Intl.DateTimeFormatOptions = { + hour: 'numeric', + minute: 'numeric', + second: 'numeric', + timeZoneName: 'short', + hour12: true + } + + const date = new Date(dateString.includes('T') ? dateString : parseInt(dateString, 10)) + + if (Number.isNaN(date.getTime())) return defaultResult + + return ( + options?.split + ? { date: formatDateTime(date, dateOptions), time: formatDateTime(date, timeOptions) } + : formatDateTime(date, { ...dateOptions, ...timeOptions }) + ) as T extends true ? { date: string; time: string } : string +} From d6dcee40251fdb29739e63a04df4642c615c3e35 Mon Sep 17 00:00:00 2001 From: alexmf91 Date: Sun, 22 Sep 2024 21:19:34 +0200 Subject: [PATCH 18/40] feat: Add getTxType function to handle transaction type determination --- src/utils/transactions.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 src/utils/transactions.ts diff --git a/src/utils/transactions.ts b/src/utils/transactions.ts new file mode 100644 index 0000000..50c0ec1 --- /dev/null +++ b/src/utils/transactions.ts @@ -0,0 +1,11 @@ +import type { TxType } from '@app/types' +import { TxTypeEnum } from '@app/types' +import { isTransferTx } from './qubic-ts' + +// eslint-disable-next-line import/prefer-default-export -- Remove this comment when adding more functions +export const getTxType = (tx: { + sourceId: string + destId: string + amount: string | number +}): TxType => + isTransferTx(tx.sourceId, tx.destId, tx.amount) ? TxTypeEnum.TRANSFER : TxTypeEnum.PROTOCOL From 23e7782fab6149da829d58983e40d5c1a66c333f Mon Sep 17 00:00:00 2001 From: alexmf91 Date: Sun, 22 Sep 2024 21:19:47 +0200 Subject: [PATCH 19/40] refactor: Remove unused formatDate utility function and update imports in utils/index.ts --- src/utils/index.ts | 29 +++-------------------------- 1 file changed, 3 insertions(+), 26 deletions(-) diff --git a/src/utils/index.ts b/src/utils/index.ts index 92b2693..a7289fc 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -9,31 +9,6 @@ const formatString = (string: string | number | undefined | null) => { return String(string) } -const formatDate = (dateString: string | undefined) => { - const options = { - year: 'numeric', - month: 'long', - day: 'numeric', - hour: 'numeric', - minute: 'numeric', - second: 'numeric', - timeZoneName: 'short', - hour12: true - } as const - - if (dateString) { - let date - if (dateString.includes('T')) { - date = new Date(dateString) - } else { - const timestamp = parseInt(dateString, 10) // Include the radix parameter - date = new Date(timestamp) - } - return new Intl.DateTimeFormat('en-US', options).format(date) // Format date - } - return '' -} - function formatEllipsis(str = '') { if (str.length > 10) { return `${str.slice(0, 5)}...${str.slice(-5)}` @@ -67,5 +42,7 @@ function copyText(textToCopy: string) { } } +export * from './date' export * from './styles' -export { copyText, formatBase64, formatDate, formatEllipsis, formatString } +export * from './transactions' +export { copyText, formatBase64, formatEllipsis, formatString } From 28fffc0a63b51e0f9bb6254071f8b59de968aa1e Mon Sep 17 00:00:00 2001 From: alexmf91 Date: Sun, 22 Sep 2024 21:21:57 +0200 Subject: [PATCH 20/40] refactor: Update dev-proxy.config.ts to use createProxyConfig function for proxy configurations --- dev-proxy.config.ts | 110 ++++++++++++++++++-------------------------- 1 file changed, 46 insertions(+), 64 deletions(-) diff --git a/dev-proxy.config.ts b/dev-proxy.config.ts index 988b1f4..4a79f8c 100644 --- a/dev-proxy.config.ts +++ b/dev-proxy.config.ts @@ -1,69 +1,51 @@ import type { HttpProxy, ProxyOptions } from 'vite' -export const qliApiProxy: ProxyOptions = { - target: 'https://api.qubic.li', - changeOrigin: true, - rewrite: (path: string) => path.replace(/^\/dev-proxy-qli-api/, ''), - configure: (proxy: HttpProxy.Server, options: ProxyOptions) => { - proxy.on('proxyReq', (proxyReq, req, res) => { - res.setHeader('Access-Control-Allow-Origin', '*') - res.setHeader('Access-Control-Allow-Methods', 'GET, PUT, PATCH, POST, DELETE') - res.setHeader( - 'Access-Control-Allow-Headers', - req.headers['access-control-request-headers'] || '' - ) - - if (req.method === 'OPTIONS') { - res.writeHead(200) - res.end() - return - } - - // eslint-disable-next-line no-console - console.log(`[QLI-API-DEV-PROXY] - API CALL - [${req.method}] ${options.target}${req.url}`) - proxyReq.setHeader('Authorization', req.headers.authorization || '') - }) - - proxy.on('error', (err, _req, res) => { - // eslint-disable-next-line no-console - console.error(`Proxy error: ${err.message}`) - res.writeHead(500, { 'Content-Type': 'application/json' }) - res.end(JSON.stringify({ error: 'Proxy error', details: err.message })) - }) +export const createProxyConfig = ( + target: string, + rewritePath: string, + label = 'PROXY' +): ProxyOptions => { + return { + target, + changeOrigin: true, + rewrite: (path: string) => path.replace(rewritePath, ''), + configure: (proxy: HttpProxy.Server, options: ProxyOptions) => { + proxy.on('proxyReq', (proxyReq, req, res) => { + res.setHeader('Access-Control-Allow-Origin', '*') + res.setHeader('Access-Control-Allow-Methods', 'GET, PUT, PATCH, POST, DELETE') + res.setHeader( + 'Access-Control-Allow-Headers', + req.headers['access-control-request-headers'] || '' + ) + + if (req.method === 'OPTIONS') { + res.writeHead(200) + res.end() + return + } + + // eslint-disable-next-line no-console + console.log(`[${label}] - API CALL - [${req.method}] ${options.target}${req.url}`) + proxyReq.setHeader('Authorization', req.headers.authorization || '') + }) + + proxy.on('error', (err, _req, res) => { + // eslint-disable-next-line no-console + console.error(`Proxy error: ${err.message}`) + res.writeHead(500, { 'Content-Type': 'application/json' }) + res.end(JSON.stringify({ error: 'Proxy error', details: err.message })) + }) + } } } -export const archiverApiProxy: ProxyOptions = { - target: 'https://rpc.qubic.org/v1', - changeOrigin: true, - rewrite: (path: string) => path.replace(/^\/dev-proxy-archiver-api/, ''), - configure: (proxy: HttpProxy.Server, options: ProxyOptions) => { - proxy.on('proxyReq', (proxyReq, req, res) => { - res.setHeader('Access-Control-Allow-Origin', '*') - res.setHeader('Access-Control-Allow-Methods', 'GET, PUT, PATCH, POST, DELETE') - res.setHeader( - 'Access-Control-Allow-Headers', - req.headers['access-control-request-headers'] || '' - ) - - if (req.method === 'OPTIONS') { - res.writeHead(200) - res.end() - return - } - - // eslint-disable-next-line no-console - console.log( - `[ARCHIVER-API-DEV-PROXY] - API CALL - [${req.method}] ${options.target}${req.url}` - ) - proxyReq.setHeader('Authorization', req.headers.authorization || '') - }) - - proxy.on('error', (err, _req, res) => { - // eslint-disable-next-line no-console - console.error(`Proxy error: ${err.message}`) - res.writeHead(500, { 'Content-Type': 'application/json' }) - res.end(JSON.stringify({ error: 'Proxy error', details: err.message })) - }) - } -} +export const qliApiProxy = createProxyConfig( + 'https://api.qubic.li', + '/dev-proxy-qli-api', + 'QLI-API-DEV-PROXY' +) +export const archiverApiProxy = createProxyConfig( + 'https://rpc.qubic.org', + '/dev-proxy-archiver-api', + 'ARCHIVER-API-DEV-PROXY' +) From 0b1c1dd091eacd09c2900894921482c01578c339 Mon Sep 17 00:00:00 2001 From: alexmf91 Date: Sun, 22 Sep 2024 21:22:17 +0200 Subject: [PATCH 21/40] feat: Add TransactionWithStatus type --- src/types/index.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/types/index.ts b/src/types/index.ts index a19692f..e10aa1c 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -1,3 +1,5 @@ +import type { Transaction, TransactionStatus } from '@app/services/archiver' + export type Language = { id: string label: string @@ -11,3 +13,9 @@ export enum TxTypeEnum { } export type TxType = keyof typeof TxTypeEnum + +export type TransactionWithStatus = { + tx: Transaction + status: TransactionStatus & { txType: TxType } + timestamp?: string +} From 964fcfdfe194eaf3aedd6f8bf68d22882fd02fc0 Mon Sep 17 00:00:00 2001 From: alexmf91 Date: Sun, 22 Sep 2024 21:22:45 +0200 Subject: [PATCH 22/40] feat: Add archiver-v2.api.ts for Archiver V2 API integration --- src/store/apis/archiver-v2.api.ts | 34 +++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 src/store/apis/archiver-v2.api.ts diff --git a/src/store/apis/archiver-v2.api.ts b/src/store/apis/archiver-v2.api.ts new file mode 100644 index 0000000..462fe27 --- /dev/null +++ b/src/store/apis/archiver-v2.api.ts @@ -0,0 +1,34 @@ +import { envConfig } from '@app/configs' +import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react' +import type { + GetIdentityTransfersArgs, + GetIdentityTransfersResponse, + GetTransactionResponse +} from './archiver-v2.types' + +const BASE_URL = `${envConfig.ARCHIVER_API_URL}/v2` + +export const archiverV2Api = createApi({ + reducerPath: 'archiverV2Api', + baseQuery: fetchBaseQuery({ baseUrl: BASE_URL }), + endpoints: (builder) => ({ + getTransaction: builder.query({ + query: (txId) => `transactions/${txId}` + }), + getIndentityTransfers: builder.query< + GetIdentityTransfersResponse['transactions'][0]['transactions'], + GetIdentityTransfersArgs + >({ + query: ({ addressId, startTick, endTick }) => + `identities/${addressId}/transfers?startTick=${startTick}&endTick=${endTick}`, + transformResponse: (response: GetIdentityTransfersResponse) => + response.transactions.flatMap(({ transactions }) => transactions) + }) + }) +}) + +export const { + useGetTransactionQuery, + useGetIndentityTransfersQuery, + useLazyGetIndentityTransfersQuery +} = archiverV2Api From fd9693cf6c6fdda234fb56173f5ced1a237b7259 Mon Sep 17 00:00:00 2001 From: alexmf91 Date: Sun, 22 Sep 2024 21:22:56 +0200 Subject: [PATCH 23/40] feat: Add archiver-v2.types.ts for Archiver V2 types --- src/store/apis/archiver-v2.types.ts | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 src/store/apis/archiver-v2.types.ts diff --git a/src/store/apis/archiver-v2.types.ts b/src/store/apis/archiver-v2.types.ts new file mode 100644 index 0000000..816e311 --- /dev/null +++ b/src/store/apis/archiver-v2.types.ts @@ -0,0 +1,23 @@ +import type { Transaction } from '@app/services/archiver' + +export interface TransactionV2 { + transaction: Transaction + timestamp: string + moneyFlew: boolean +} + +export type GetTransactionResponse = TransactionV2 + +export interface GetIdentityTransfersArgs { + addressId: string + startTick: number + endTick: number +} + +export interface GetIdentityTransfersResponse { + transactions: { + identity: string + tickNumber: number + transactions: TransactionV2[] + }[] +} From 94bf5cf487bfeb7573e3a2bf0820cfe7d405d5d2 Mon Sep 17 00:00:00 2001 From: alexmf91 Date: Sun, 22 Sep 2024 21:23:22 +0200 Subject: [PATCH 24/40] refactor: Update convertHistoricalTxToTxWithStatus function --- .../adapters/convertHistoricalTxToTxWithStatus.ts | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/store/network/adapters/convertHistoricalTxToTxWithStatus.ts b/src/store/network/adapters/convertHistoricalTxToTxWithStatus.ts index 3e0aff9..cd47019 100644 --- a/src/store/network/adapters/convertHistoricalTxToTxWithStatus.ts +++ b/src/store/network/adapters/convertHistoricalTxToTxWithStatus.ts @@ -1,7 +1,6 @@ import type { HistoricalTx } from '@app/services/qli' -import { TxTypeEnum } from '@app/types' -import { isTransferTx } from '@app/utils/qubic-ts' -import type { TransactionWithStatus } from '../txSlice' +import { type TransactionWithStatus } from '@app/types' +import { getTxType } from '@app/utils' import convertHistoricalTxToLatestTx from './convertHistoricalTxToLatestTx' export default function convertHistoricalTxToTxWithStatus( @@ -12,9 +11,7 @@ export default function convertHistoricalTxToTxWithStatus( status: { txId: historicalTx.id, moneyFlew: historicalTx.moneyFlew, - txType: isTransferTx(historicalTx.sourceId, historicalTx.destId, historicalTx.amount) - ? TxTypeEnum.TRANSFER - : TxTypeEnum.PROTOCOL + txType: getTxType(historicalTx) } } } From b1e58ed8f2ae432b9197b7dd59b5dc746e202eb3 Mon Sep 17 00:00:00 2001 From: alexmf91 Date: Sun, 22 Sep 2024 21:23:35 +0200 Subject: [PATCH 25/40] feat: Add convertTxV2ToTxWithStatus adapter function --- .../network/adapters/convertTxV2ToTxWithStatus.ts | 15 +++++++++++++++ src/store/network/adapters/index.ts | 1 + 2 files changed, 16 insertions(+) create mode 100644 src/store/network/adapters/convertTxV2ToTxWithStatus.ts diff --git a/src/store/network/adapters/convertTxV2ToTxWithStatus.ts b/src/store/network/adapters/convertTxV2ToTxWithStatus.ts new file mode 100644 index 0000000..5106d4e --- /dev/null +++ b/src/store/network/adapters/convertTxV2ToTxWithStatus.ts @@ -0,0 +1,15 @@ +import type { TransactionV2 } from '@app/store/apis/archiver-v2.types' +import type { TransactionWithStatus } from '@app/types' +import { getTxType } from '@app/utils' + +export default function convertTxV2ToTxWithStatus(tx: TransactionV2): TransactionWithStatus { + return { + tx: tx.transaction, + status: { + txId: tx.transaction.txId, + moneyFlew: tx.moneyFlew, + txType: getTxType(tx.transaction) + }, + timestamp: tx.timestamp + } +} diff --git a/src/store/network/adapters/index.ts b/src/store/network/adapters/index.ts index 7c588ce..88df7cb 100644 --- a/src/store/network/adapters/index.ts +++ b/src/store/network/adapters/index.ts @@ -1,2 +1,3 @@ export { default as convertHistoricalTxToLatestTx } from './convertHistoricalTxToLatestTx' export { default as convertHistoricalTxToTxWithStatus } from './convertHistoricalTxToTxWithStatus' +export { default as convertTxV2ToTxWithStatus } from './convertTxV2ToTxWithStatus' From 09f90e6d92831bb31b52cf8793486697a9c139ae Mon Sep 17 00:00:00 2001 From: alexmf91 Date: Sun, 22 Sep 2024 21:24:04 +0200 Subject: [PATCH 26/40] refactor: Add timestamp prop to TxItem component --- src/pages/network/components/TxItem/TxItem.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/pages/network/components/TxItem/TxItem.tsx b/src/pages/network/components/TxItem/TxItem.tsx index b9c109c..8ee33b7 100644 --- a/src/pages/network/components/TxItem/TxItem.tsx +++ b/src/pages/network/components/TxItem/TxItem.tsx @@ -19,6 +19,7 @@ type Props = { nonExecutedTxIds: string[] variant?: TxItemVariant isHistoricalTx?: boolean + timestamp?: string } function TxItem({ @@ -26,7 +27,8 @@ function TxItem({ identity, nonExecutedTxIds, variant = 'primary', - isHistoricalTx = false + isHistoricalTx = false, + timestamp }: Props) { const [entries, setEntries] = useState([]) const [detailsOpen, setDetailsOpen] = useState(false) @@ -69,6 +71,7 @@ function TxItem({ isHistoricalTx={isHistoricalTx} variant={variant} entries={entries} + timestamp={timestamp} /> ) @@ -121,6 +124,7 @@ function TxItem({ isHistoricalTx={isHistoricalTx} variant={variant} entries={entries} + timestamp={timestamp} /> )} From 170ec79b5f198504cd8a2401e6abc84b5e74fffb Mon Sep 17 00:00:00 2001 From: alexmf91 Date: Sun, 22 Sep 2024 21:27:02 +0200 Subject: [PATCH 27/40] refactor: Update addressSlice: - Update getAddress to include addressId - Remove transfers impl since after archiverv2 implementation --- src/store/network/addressSlice.ts | 121 +----------------------------- 1 file changed, 4 insertions(+), 117 deletions(-) diff --git a/src/store/network/addressSlice.ts b/src/store/network/addressSlice.ts index 408a1e7..8948b65 100644 --- a/src/store/network/addressSlice.ts +++ b/src/store/network/addressSlice.ts @@ -1,21 +1,13 @@ import { createAsyncThunk, createSlice } from '@reduxjs/toolkit' -import { BATCH_SIZE, TICK_SIZE } from '@app/pages/network/address/components/Transactions' -import type { Balance, Transaction } from '@app/services/archiver' +import type { Balance } from '@app/services/archiver' import { archiverApiService } from '@app/services/archiver' import type { ReportedValues } from '@app/services/qli' import { qliApiService } from '@app/services/qli' import type { RootState } from '@app/store' -import { TxTypeEnum } from '@app/types' +import type { TransactionWithStatus } from '@app/types' import { handleThunkError } from '@app/utils/error-handlers' -import { isTransferTx } from '@app/utils/qubic-ts' import { convertHistoricalTxToTxWithStatus } from './adapters' -import type { TransactionWithStatus } from './txSlice' - -export type TransactionWithMoneyFlew = Transaction & { - moneyFlew: boolean | null - txType: TxTypeEnum -} export const getAddress = createAsyncThunk( 'network/address', @@ -27,6 +19,7 @@ export const getAddress = createAsyncThunk( archiverApiService.getBalance(addressId) ]) return { + addressId, reportedValues, endTick: lastProcessedTick.tickNumber, balance @@ -37,76 +30,6 @@ export const getAddress = createAsyncThunk( } ) -export const getTransferTxs = createAsyncThunk< - { - data: TransactionWithMoneyFlew[] - lastStartTick: number - lastEndTick: number - }, - { addressId: string; startTick: number; endTick: number }, - { - state: RootState - } ->( - 'network/getTransferTxs', - async ({ addressId, startTick, endTick }, { rejectWithValue }) => { - try { - let data: Transaction[] = [] - let lastStartTick = startTick - let lastEndTick = endTick - - const getTransfers = async (start: number, end: number) => { - const { transferTransactionsPerTick } = - await archiverApiService.getAddressTransferTransactions(addressId, start, end) - return transferTransactionsPerTick.flatMap(({ transactions }) => transactions) || [] - } - const fetchRecursive = async (start: number, end: number) => { - const transfers = await getTransfers(start, end) - data = [...new Set(data.concat(transfers))] - - if (start === 0 && transfers.length === 0) { - return { data: data.sort((a, b) => b.tickNumber - a.tickNumber) } - } - - if (data.length < BATCH_SIZE) { - lastEndTick = Math.max(0, start - 1) - lastStartTick = Math.max(0, lastEndTick - TICK_SIZE) - - return fetchRecursive(lastStartTick, lastEndTick) - } - return { data: data.sort((a, b) => b.tickNumber - a.tickNumber) } - } - - const finalResult = await fetchRecursive(startTick, endTick) - - const txsWithMoneyFlew = await Promise.all( - finalResult.data.map(async (tx) => { - if (!isTransferTx(tx.sourceId, tx.destId, tx.amount)) { - return { ...tx, moneyFlew: true, txType: TxTypeEnum.PROTOCOL } - } - try { - const { transactionStatus } = await archiverApiService.getTransactionStatus(tx.txId) - return { ...tx, moneyFlew: transactionStatus.moneyFlew, txType: TxTypeEnum.TRANSFER } - } catch (error) { - return { ...tx, moneyFlew: null, txType: TxTypeEnum.TRANSFER } - } - }) - ) - - return { data: txsWithMoneyFlew, lastStartTick, lastEndTick } - } catch (error) { - return rejectWithValue(handleThunkError(error)) - } - }, - // Conditionally fetch historical transactions to prevent issues from React.StrictMode - https://redux.js.org/tutorials/essentials/part-5-async-logic#avoiding-duplicate-fetches - { - condition: (_, { getState }) => { - const { isLoading, hasMore, error } = getState().network.address.transferTxs - return !isLoading && hasMore && !error - } - } -) - export const getHistoricalTxs = createAsyncThunk< TransactionWithStatus[], string, @@ -134,6 +57,7 @@ export const getHistoricalTxs = createAsyncThunk< ) export type Address = { + addressId: string reportedValues: ReportedValues endTick: number balance: Balance @@ -143,14 +67,6 @@ export interface AddressState { address: Address | null isLoading: boolean error: string | null - transferTxs: { - data: TransactionWithMoneyFlew[] - isLoading: boolean - error: string | null - hasMore: boolean - lastStartTick: number - lastEndTick: number - } historicalTxs: { data: TransactionWithStatus[] isLoading: boolean @@ -164,14 +80,6 @@ const initialState: AddressState = { address: null, isLoading: false, error: null, - transferTxs: { - data: [], - isLoading: false, - error: null, - hasMore: true, - lastStartTick: 0, - lastEndTick: 0 - }, historicalTxs: { data: [], isLoading: false, @@ -204,26 +112,6 @@ const addressSlice = createSlice({ state.isLoading = false state.error = action.error.message ?? 'Unknown error' }) - // getTransferTxs - .addCase(getTransferTxs.pending, (state) => { - state.transferTxs.isLoading = true - state.transferTxs.error = null - }) - .addCase(getTransferTxs.fulfilled, (state, action) => { - state.transferTxs.isLoading = false - const newTxs = action.payload - if (newTxs.data.length === 0) { - state.transferTxs.hasMore = false - } else { - state.transferTxs.data.push(...newTxs.data) - } - state.transferTxs.lastStartTick = newTxs.lastStartTick - state.transferTxs.lastEndTick = newTxs.lastEndTick - }) - .addCase(getTransferTxs.rejected, (state, action) => { - state.transferTxs.isLoading = false - state.transferTxs.error = action.error.message ?? 'Unknown error' - }) // getHistoricalTxs .addCase(getHistoricalTxs.pending, (state) => { state.historicalTxs.isLoading = true @@ -249,7 +137,6 @@ const addressSlice = createSlice({ // Selectors export const selectAddress = (state: RootState) => state.network.address -export const selectTransferTxs = (state: RootState) => state.network.address.transferTxs export const selectHistoricalTxs = (state: RootState) => state.network.address.historicalTxs // actions From 6c1fa4cea91d66c33f2edc1697e3858ae567c658 Mon Sep 17 00:00:00 2001 From: alexmf91 Date: Sun, 22 Sep 2024 21:27:53 +0200 Subject: [PATCH 28/40] refactor: Update TransactionDetails component to include timestamp --- .../components/TxItem/TransactionDetails.tsx | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/src/pages/network/components/TxItem/TransactionDetails.tsx b/src/pages/network/components/TxItem/TransactionDetails.tsx index 93816d2..6829aa3 100644 --- a/src/pages/network/components/TxItem/TransactionDetails.tsx +++ b/src/pages/network/components/TxItem/TransactionDetails.tsx @@ -1,8 +1,9 @@ import { useTranslation } from 'react-i18next' import type { Transaction } from '@app/services/archiver' -import { formatString } from '@app/utils' +import { formatDate, formatString } from '@app/utils' import type { Transfer } from '@app/utils/qubic-ts' +import { useMemo } from 'react' import AddressLink from '../AddressLink' import SubCardItem from '../SubCardItem' import TickLink from '../TickLink' @@ -15,6 +16,7 @@ type Props = { entries: Transfer[] isHistoricalTx?: boolean variant?: TxItemVariant + timestamp?: string } function TransactionDetailsWrapper({ @@ -37,11 +39,13 @@ export default function TransactionDetails({ txDetails: { txId, sourceId, tickNumber, destId, inputType, amount }, entries, isHistoricalTx = false, + timestamp, variant = 'primary' }: Props) { const { t } = useTranslation('network-page') const isSecondaryVariant = variant === 'secondary' + const { date, time } = useMemo(() => formatDate(timestamp, { split: true }), [timestamp]) return ( @@ -105,6 +109,19 @@ export default function TransactionDetails({ /> )} + {timestamp && ( + + {date}{' '} + {time} +

+ } + /> + )} +
) From 0f1d70029de8ae8f7b571c6e8c94a377fb418890 Mon Sep 17 00:00:00 2001 From: alexmf91 Date: Sun, 22 Sep 2024 21:28:14 +0200 Subject: [PATCH 29/40] refactor: Update txSlice to remove unused code and dependencies --- src/store/network/txSlice.ts | 26 +++----------------------- 1 file changed, 3 insertions(+), 23 deletions(-) diff --git a/src/store/network/txSlice.ts b/src/store/network/txSlice.ts index c690dd5..d11d868 100644 --- a/src/store/network/txSlice.ts +++ b/src/store/network/txSlice.ts @@ -1,18 +1,10 @@ import { createAsyncThunk, createSlice } from '@reduxjs/toolkit' -import type { Transaction, TransactionStatus } from '@app/services/archiver' -import { archiverApiService } from '@app/services/archiver' import { qliApiService } from '@app/services/qli' import type { RootState } from '@app/store' -import { TxTypeEnum, type TxEra, type TxType } from '@app/types' -import { isTransferTx } from '@app/utils/qubic-ts' +import type { TransactionWithStatus, TxEra } from '@app/types' import { convertHistoricalTxToTxWithStatus } from './adapters' -export type TransactionWithStatus = { - tx: Transaction - status: TransactionStatus & { txType: TxType } -} - export interface TxState { txWithStatus: TransactionWithStatus | null isLoading: boolean @@ -31,7 +23,7 @@ type GetTxArgs = { } export const getTx = createAsyncThunk< - TransactionWithStatus, + TransactionWithStatus | null, GetTxArgs, { state: RootState @@ -54,19 +46,7 @@ export const getTx = createAsyncThunk< return historicalTx } - const { transaction } = await archiverApiService.getTransaction(txId) - const { transactionStatus } = isTransferTx( - transaction.sourceId, - transaction.destId, - transaction.amount - ) - ? await archiverApiService.getTransactionStatus(txId) - : { transactionStatus: { txId, moneyFlew: false, txType: TxTypeEnum.PROTOCOL } } - - return { - tx: transaction, - status: { ...transactionStatus, txType: TxTypeEnum.TRANSFER } - } + return null }) const txSlice = createSlice({ From aad3b9c0960268acaee2de181e392226062edeec Mon Sep 17 00:00:00 2001 From: alexmf91 Date: Sun, 22 Sep 2024 21:28:28 +0200 Subject: [PATCH 30/40] refactor: Update TickPage component button styles --- src/pages/network/TickPage.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/network/TickPage.tsx b/src/pages/network/TickPage.tsx index d99a2e7..07ac543 100644 --- a/src/pages/network/TickPage.tsx +++ b/src/pages/network/TickPage.tsx @@ -94,7 +94,7 @@ export default function TickPage() {
From 950cd5b036e5c545d3e6667b0aef529493d148e4 Mon Sep 17 00:00:00 2001 From: alexmf91 Date: Sun, 22 Sep 2024 21:30:20 +0200 Subject: [PATCH 34/40] refactor: Import TransactionWithStatus type from correct location in HistoricalTxs.tsx --- .../address/components/TransactionsOverview/HistoricalTxs.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/network/address/components/TransactionsOverview/HistoricalTxs.tsx b/src/pages/network/address/components/TransactionsOverview/HistoricalTxs.tsx index 2fac7dc..ec48c26 100644 --- a/src/pages/network/address/components/TransactionsOverview/HistoricalTxs.tsx +++ b/src/pages/network/address/components/TransactionsOverview/HistoricalTxs.tsx @@ -3,7 +3,7 @@ import { InfiniteScroll } from '@app/components/ui' import { DotsLoader } from '@app/components/ui/loaders' import { useAppDispatch, useAppSelector } from '@app/hooks/redux' import { getHistoricalTxs, selectHistoricalTxs } from '@app/store/network/addressSlice' -import type { TransactionWithStatus } from '@app/store/network/txSlice' +import type { TransactionWithStatus } from '@app/types' import { useCallback, useEffect } from 'react' import { useTranslation } from 'react-i18next' import { TxItem } from '../../../components' From 38079bb4dd075f97187bc576d9249c35ba4f34ef Mon Sep 17 00:00:00 2001 From: alexmf91 Date: Sun, 22 Sep 2024 21:30:56 +0200 Subject: [PATCH 35/40] refactor: Update LatestTransactions component to include timestamp prop --- .../components/TransactionsOverview/LatestTransactions.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/pages/network/address/components/TransactionsOverview/LatestTransactions.tsx b/src/pages/network/address/components/TransactionsOverview/LatestTransactions.tsx index b71c224..ec776a3 100644 --- a/src/pages/network/address/components/TransactionsOverview/LatestTransactions.tsx +++ b/src/pages/network/address/components/TransactionsOverview/LatestTransactions.tsx @@ -26,13 +26,14 @@ export default function LatestTransactions({ const { t } = useTranslation('network-page') const renderTxItem = useCallback( - ({ transaction, moneyFlew }: TransactionV2) => ( + ({ transaction, moneyFlew, timestamp }: TransactionV2) => ( ), [addressId] From 6b81126f9e1e8d7ff4170e0b0066a7d1d7280434 Mon Sep 17 00:00:00 2001 From: alexmf91 Date: Sun, 22 Sep 2024 21:33:00 +0200 Subject: [PATCH 36/40] refactor: Update TxPage component: - Add new impl to get latest txs from v2 api --- src/pages/network/TxPage.tsx | 75 ++++++++++++++++++++---------------- 1 file changed, 42 insertions(+), 33 deletions(-) diff --git a/src/pages/network/TxPage.tsx b/src/pages/network/TxPage.tsx index da33311..1eb5637 100644 --- a/src/pages/network/TxPage.tsx +++ b/src/pages/network/TxPage.tsx @@ -1,4 +1,4 @@ -import { useEffect } from 'react' +import { useCallback, useEffect } from 'react' import { useTranslation } from 'react-i18next' import { useParams } from 'react-router-dom' @@ -6,6 +6,8 @@ import { Alert, Breadcrumbs } from '@app/components/ui' import { LinearProgress } from '@app/components/ui/loaders' import { useAppDispatch, useAppSelector } from '@app/hooks/redux' import type { TransactionStatus } from '@app/services/archiver' +import { useGetTransactionQuery } from '@app/store/apis/archiver-v2.api' +import { convertTxV2ToTxWithStatus } from '@app/store/network/adapters' import { getTx, selectTx } from '@app/store/network/txSlice' import { formatEllipsis } from '@app/utils' import { HomeLink, TickLink, TxItem } from './components' @@ -17,51 +19,58 @@ export default function TxPage() { const { txWithStatus, isLoading } = useAppSelector(selectTx) const { txId } = useParams() const txEra = useValidatedTxEra() + const { data, isFetching } = useGetTransactionQuery(txId ?? '', { + skip: !txId || txEra === 'historical' + }) - const getNonExecutedTxIds = (status: TransactionStatus) => { - return status?.moneyFlew ? [] : [status?.txId] - } + const getNonExecutedTxIds = useCallback( + (status: TransactionStatus) => (status?.moneyFlew ? [] : [status?.txId]), + [] + ) + + const transaction = txWithStatus || (data && convertTxV2ToTxWithStatus(data)) useEffect(() => { dispatch(getTx({ txId, txEra })) }, [txId, txEra, dispatch]) - if (isLoading) { + if (isLoading || isFetching) { return } + if (!transaction) { + return ( +

+ {t('transactionNotFound')} +

+ ) + } + return (
- {txWithStatus?.tx ? ( - <> - {txEra === 'historical' && ( - - {t('historicalDataWarning')} - - )} - - -

- {t('tick')}{' '} - -

-

- {formatEllipsis(txWithStatus.tx.txId)} -

-
-

{t('transactionPreview')}

- - - ) : ( -

- {t('transactionNotFound')} -

+ {txEra === 'historical' && ( + + {t('historicalDataWarning')} + )} + + +

+ {t('tick')}{' '} + +

+

+ {formatEllipsis(transaction.tx.txId)} +

+
+

{t('transactionPreview')}

+
) From 6d853c9795eda90ca07efa62ea3fcb2d3469f274 Mon Sep 17 00:00:00 2001 From: alexmf91 Date: Sun, 22 Sep 2024 21:39:55 +0200 Subject: [PATCH 37/40] refactor: Update VITE_ENABLE_PROXY in .env.example and vite-env.d.ts --- .env.example | 1 + src/vite-env.d.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/.env.example b/.env.example index 9dc7eb7..a1d86ab 100644 --- a/.env.example +++ b/.env.example @@ -1,2 +1,3 @@ +VITE_ENABLE_PROXY= VITE_QLI_API_URL= VITE_ARCHIVER_API_URL= diff --git a/src/vite-env.d.ts b/src/vite-env.d.ts index 228cb6e..43902ea 100644 --- a/src/vite-env.d.ts +++ b/src/vite-env.d.ts @@ -2,6 +2,7 @@ /// interface ImportMetaEnv { + readonly VITE_ENABLE_PROXY: string readonly VITE_QLI_API_URL: string readonly VITE_ARCHIVER_API_URL: string } From 553efc5b7bfc421603c865e403fbe5160ffe5e68 Mon Sep 17 00:00:00 2001 From: alexmf91 Date: Sun, 22 Sep 2024 21:40:13 +0200 Subject: [PATCH 38/40] refactor: Update README.md --- README.md | 19 ++----------------- 1 file changed, 2 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 0f3cc75..e4a693b 100644 --- a/README.md +++ b/README.md @@ -16,8 +16,7 @@ cd ### Step 2: Configure Environment Variables -Before running the project, you must configure environment-specific variables for development and -production environments. +Before running the project, you must configure environment-specific variables for development environment. - **Development Environment**: @@ -30,25 +29,11 @@ production environments. Open this file and add the development API URL: ``` + VITE_ENABLE_PROXY=true VITE_QLI_API_URL=/dev-proxy-qli-api VITE_ARCHIVER_API_URL=/dev-proxy-archiver-api ``` -- **Production Environment**: - - Copy the `.env.example` file, renaming it to `.env.production.local`: - - ``` - cp .env.production.local.example .env.production.local - ``` - - Then, set the production API URL: - - ``` - VITE_QLI_API_URL=https://api.qubic.li - VITE_ARCHIVER_API_URL=https://rpc.qubic.org/v1 - ``` - Ensure these files are not committed to the repository to protect sensitive information. ### Step 3: Install Dependencies From 85b19ab9b83d5205a698fe43d70f0802db7e460d Mon Sep 17 00:00:00 2001 From: alexmf91 Date: Sun, 22 Sep 2024 22:14:54 +0200 Subject: [PATCH 39/40] refactor: Update OverviewCardItem component --- src/pages/network/components/OverviewCardItem.tsx | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/pages/network/components/OverviewCardItem.tsx b/src/pages/network/components/OverviewCardItem.tsx index 96d21cb..42aa7c8 100644 --- a/src/pages/network/components/OverviewCardItem.tsx +++ b/src/pages/network/components/OverviewCardItem.tsx @@ -14,18 +14,20 @@ export default function OverviewCardItem({ value: string variant?: 'normal' | 'small' }) { + const LabelTag = typeof label === 'string' ? 'p' : 'div' + return (
- +
-

{label}

-

{value}

+ {label} +

{value}

From 918b64a63e6ad25690c65188392e71337a8e8e46 Mon Sep 17 00:00:00 2001 From: alexmf91 Date: Sun, 22 Sep 2024 22:17:21 +0200 Subject: [PATCH 40/40] refactor: Update network page translations - Add "timestamp" translation for multiple locales --- public/locales/ar/network-page.json | 3 ++- public/locales/de/network-page.json | 3 ++- public/locales/en/network-page.json | 3 ++- public/locales/es/network-page.json | 3 ++- public/locales/fr/network-page.json | 3 ++- public/locales/ja/network-page.json | 3 ++- public/locales/nl/network-page.json | 3 ++- public/locales/pt/network-page.json | 3 ++- public/locales/ru/network-page.json | 3 ++- public/locales/tr/network-page.json | 3 ++- public/locales/zh/network-page.json | 3 ++- 11 files changed, 22 insertions(+), 11 deletions(-) diff --git a/public/locales/ar/network-page.json b/public/locales/ar/network-page.json index bca729d..75b5a53 100644 --- a/public/locales/ar/network-page.json +++ b/public/locales/ar/network-page.json @@ -53,5 +53,6 @@ "rank": "الترتيب", "addressID": "معرّف العنوان", "richListLoadFailed": "خطأ: فشل تحميل قائمة الأثرياء. يرجى تحديث الصفحة أو المحاولة مرة أخرى لاحقًا.", - "richListWarning": "يتم تحديث بيانات قائمة الأثرياء في بداية كل فترة" + "richListWarning": "يتم تحديث بيانات قائمة الأثرياء في بداية كل فترة", + "timestamp": "الطابع الزمني" } diff --git a/public/locales/de/network-page.json b/public/locales/de/network-page.json index 4456ada..60e3328 100644 --- a/public/locales/de/network-page.json +++ b/public/locales/de/network-page.json @@ -53,5 +53,6 @@ "rank": "Rang", "addressID": "Adress-ID", "richListLoadFailed": "Fehler: Reichenliste konnte nicht geladen werden. Bitte aktualisieren Sie die Seite oder versuchen Sie es später erneut.", - "richListWarning": "Die Daten der Reichenliste werden zu Beginn jeder Epoche aktualisiert" + "richListWarning": "Die Daten der Reichenliste werden zu Beginn jeder Epoche aktualisiert", + "timestamp": "Zeitstempel" } diff --git a/public/locales/en/network-page.json b/public/locales/en/network-page.json index 4fa14ed..01e43d9 100644 --- a/public/locales/en/network-page.json +++ b/public/locales/en/network-page.json @@ -53,5 +53,6 @@ "rank": "Rank", "addressID": "Address ID", "richListLoadFailed": "Error: Failed to load rich list. Please refresh the page or try again later.", - "richListWarning": "Rich list data is updated at the beginning of each epoch" + "richListWarning": "Rich list data is updated at the beginning of each epoch", + "timestamp": "Timestamp" } diff --git a/public/locales/es/network-page.json b/public/locales/es/network-page.json index d7dcf4b..f3118cd 100644 --- a/public/locales/es/network-page.json +++ b/public/locales/es/network-page.json @@ -53,5 +53,6 @@ "rank": "Rango", "addressID": "ID de Dirección", "richListLoadFailed": "Error: No se pudo cargar la lista de ricos. Por favor, actualice la página o intente nuevamente más tarde.", - "richListWarning": "Los datos de la lista de ricos se actualizan al comienzo de cada época" + "richListWarning": "Los datos de la lista de ricos se actualizan al comienzo de cada época", + "timestamp": "Fecha" } diff --git a/public/locales/fr/network-page.json b/public/locales/fr/network-page.json index 6a12071..6d4fe76 100644 --- a/public/locales/fr/network-page.json +++ b/public/locales/fr/network-page.json @@ -53,5 +53,6 @@ "rank": "Rang", "addressID": "ID d'adresse", "richListLoadFailed": "Erreur : Impossible de charger la liste des riches. Veuillez actualiser la page ou réessayer plus tard.", - "richListWarning": "Les données de la liste des riches sont mises à jour au début de chaque époque" + "richListWarning": "Les données de la liste des riches sont mises à jour au début de chaque époque", + "timestamp": "Horodatage" } diff --git a/public/locales/ja/network-page.json b/public/locales/ja/network-page.json index 1cb3dd9..dc1a32f 100644 --- a/public/locales/ja/network-page.json +++ b/public/locales/ja/network-page.json @@ -53,5 +53,6 @@ "rank": "ランク", "addressID": "アドレスID", "richListLoadFailed": "エラー:リッチリストの読み込みに失敗しました。ページを更新するか、後でもう一度お試しください。", - "richListWarning": "リッチリストのデータは各エポックの開始時に更新されます" + "richListWarning": "リッチリストのデータは各エポックの開始時に更新されます", + "timestamp": "タイムスタンプ" } diff --git a/public/locales/nl/network-page.json b/public/locales/nl/network-page.json index 6e7afca..5e088cd 100644 --- a/public/locales/nl/network-page.json +++ b/public/locales/nl/network-page.json @@ -53,5 +53,6 @@ "rank": "Rang", "addressID": "Adres-ID", "richListLoadFailed": "Fout: Het laden van de rijkelijst is mislukt. Ververs de pagina of probeer het later opnieuw.", - "richListWarning": "Rijkelijstgegevens worden aan het begin van elke epoche bijgewerkt" + "richListWarning": "Rijkelijstgegevens worden aan het begin van elke epoche bijgewerkt", + "timestamp": "Tijdstempel" } diff --git a/public/locales/pt/network-page.json b/public/locales/pt/network-page.json index 12aa391..d6814c7 100644 --- a/public/locales/pt/network-page.json +++ b/public/locales/pt/network-page.json @@ -53,5 +53,6 @@ "rank": "Classificação", "addressID": "ID do Endereço", "richListLoadFailed": "Erro: Falha ao carregar a lista de ricos. Por favor, atualize a página ou tente novamente mais tarde.", - "richListWarning": "Os dados da lista de ricos são atualizados no início de cada época" + "richListWarning": "Os dados da lista de ricos são atualizados no início de cada época", + "timestamp": "Carimbo de tempo" } diff --git a/public/locales/ru/network-page.json b/public/locales/ru/network-page.json index 912ff45..8fca17d 100644 --- a/public/locales/ru/network-page.json +++ b/public/locales/ru/network-page.json @@ -53,5 +53,6 @@ "rank": "Ранг", "addressID": "ID адреса", "richListLoadFailed": "Ошибка: Не удалось загрузить список богатых. Пожалуйста, обновите страницу или попробуйте позже.", - "richListWarning": "Данные списка богатых обновляются в начале каждой эпохи" + "richListWarning": "Данные списка богатых обновляются в начале каждой эпохи", + "timestamp": "Метка времени" } diff --git a/public/locales/tr/network-page.json b/public/locales/tr/network-page.json index e84cfcd..5c631c4 100644 --- a/public/locales/tr/network-page.json +++ b/public/locales/tr/network-page.json @@ -53,5 +53,6 @@ "rank": "Rütbe", "addressID": "Adres Kimliği", "richListLoadFailed": "Hata: Zenginler listesi yüklenemedi. Lütfen sayfayı yenileyin veya daha sonra tekrar deneyin.", - "richListWarning": "Zenginler listesi verileri her dönemin başında güncellenir" + "richListWarning": "Zenginler listesi verileri her dönemin başında güncellenir", + "timestamp": "Zaman damgası" } diff --git a/public/locales/zh/network-page.json b/public/locales/zh/network-page.json index 72117fb..f95533b 100644 --- a/public/locales/zh/network-page.json +++ b/public/locales/zh/network-page.json @@ -53,5 +53,6 @@ "rank": "排名", "addressID": "地址标识", "richListLoadFailed": "错误:无法加载富豪榜。请刷新页面或稍后再试。", - "richListWarning": "富豪榜数据会在每个纪元开始时更新" + "richListWarning": "富豪榜数据会在每个纪元开始时更新", + "timestamp": "时间戳" }