(url, {}, true);
useEffect(() => {
- if (data && isNumber(data?.transactions?.length)) {
- setTxsState((prev) => ({
- txsData: [...prev.txsData, ...data.transactions],
- hasMoreTxs: data.transactions.length > 0,
- lastCursor:
- data.transactions[data.transactions.length - 1]?.cursor || '',
- }));
+ if (!loading && data && isNumber(data.transactions.length)) {
+ setTxsState((prev) => {
+ return {
+ ...prev,
+ txsData: data.transactions,
+ hasMoreTxs: data.transactions.length >= limit,
+ cursor: data?.transactions.at(-1)?.cursor || '',
+ };
+ });
}
- }, [setTxsState, data]);
+ }, [loading, setTxsState, data, limit]);
- useEffect(() => {
- setTxsState((prev) => ({
- txsData: [],
- hasMoreTxs: true,
- lastCursor: '',
- }));
- }, [filters]);
+ const nextPage = useCallback(() => {
+ const c = data?.transactions.at(0)?.cursor;
+ const newPreviousCursors = c ? [...previousCursors, c] : previousCursors;
- const loadTxs = useCallback(() => {
- return refetch({
- limit: limit,
- before: lastCursor,
- });
- }, [lastCursor, limit, refetch]);
-
- const refreshTxs = useCallback(async () => {
setTxsState((prev) => ({
...prev,
- lastCursor: '',
- hasMoreTxs: true,
- txsData: [],
+ hasPreviousPage: true,
+ previousCursors: newPreviousCursors,
}));
- }, [setTxsState]);
+
+ return refetch({
+ limit,
+ before: cursor,
+ });
+ }, [data, previousCursors, cursor, limit, refetch]);
+
+ const previousPage = useCallback(() => {
+ const previousCursor = [...previousCursors].pop();
+ const newPreviousCursors = previousCursors.slice(0, -1);
+ setTxsState((prev) => ({
+ ...prev,
+ hasPreviousPage: newPreviousCursors.length > 0,
+ previousCursors: newPreviousCursors,
+ }));
+ return refetch({
+ limit,
+ before: previousCursor,
+ });
+ }, [previousCursors, limit, refetch]);
+
+ const refreshTxs = useCallback(async () => {
+ setTxsState(() => ({
+ txsData: [],
+ cursor: '',
+ previousCursors: [],
+ hasMoreTxs: false,
+ hasPreviousPage: false,
+ }));
+
+ refetch({ limit });
+ }, [setTxsState, limit, refetch, filters]); // eslint-disable-line react-hooks/exhaustive-deps
return {
- data,
+ txsData,
loading,
error,
- txsData,
hasMoreTxs,
- lastCursor,
+ hasPreviousPage,
+ previousCursors,
+ cursor,
refreshTxs,
- loadTxs,
+ nextPage,
+ previousPage,
};
};
diff --git a/apps/explorer/src/app/routes/layout.tsx b/apps/explorer/src/app/routes/layout.tsx
index c11cca676..139795b2d 100644
--- a/apps/explorer/src/app/routes/layout.tsx
+++ b/apps/explorer/src/app/routes/layout.tsx
@@ -21,6 +21,7 @@ import {
import { Footer } from '../components/footer/footer';
import { Header } from '../components/header';
import { Routes } from './route-names';
+import { useExplorerNodeNamesLazyQuery } from './validators/__generated__/NodeNames';
const DialogsContainer = () => {
const { isOpen, id, trigger, asJson, setOpen } = useAssetDetailsDialogStore();
@@ -39,6 +40,7 @@ export const Layout = () => {
const isHome = Boolean(useMatch(Routes.HOME));
const { ANNOUNCEMENTS_CONFIG_URL } = useEnvironment();
const fixedWidthClasses = 'w-full max-w-[1500px] mx-auto';
+ useExplorerNodeNamesLazyQuery();
return (
<>
@@ -49,7 +51,7 @@ export const Layout = () => {
'grid grid-rows-[auto_1fr_auto] grid-cols-1',
'border-vega-light-200 dark:border-vega-dark-200',
'antialiased text-black dark:text-white',
- 'overflow-hidden relative'
+ 'relative'
)}
>
diff --git a/apps/explorer/src/app/routes/oracles/components/oracle-signers.spec.tsx b/apps/explorer/src/app/routes/oracles/components/oracle-signers.spec.tsx
index 6bd3b7f3a..7fc693214 100644
--- a/apps/explorer/src/app/routes/oracles/components/oracle-signers.spec.tsx
+++ b/apps/explorer/src/app/routes/oracles/components/oracle-signers.spec.tsx
@@ -2,12 +2,15 @@ import { render } from '@testing-library/react';
import { MemoryRouter } from 'react-router-dom';
import type { SourceType } from './oracle';
import { OracleSigners } from './oracle-signers';
+import { MockedProvider } from '@apollo/client/testing';
function renderComponent(sourceType: SourceType) {
return (
-
-
-
+
+
+
+
+
);
}
diff --git a/apps/explorer/src/app/routes/parties/id/index.tsx b/apps/explorer/src/app/routes/parties/id/index.tsx
index 9f2b1e638..80cd3d501 100644
--- a/apps/explorer/src/app/routes/parties/id/index.tsx
+++ b/apps/explorer/src/app/routes/parties/id/index.tsx
@@ -1,6 +1,6 @@
import { t } from '@vegaprotocol/i18n';
import { useScreenDimensions } from '@vegaprotocol/react-helpers';
-import { useMemo } from 'react';
+import { useMemo, useState } from 'react';
import { useNavigate, useParams } from 'react-router-dom';
import { SubHeading } from '../../../components/sub-heading';
import { toNonHex } from '../../../components/search/detect-search';
@@ -14,8 +14,11 @@ import { PartyBlockStake } from './components/party-block-stake';
import { PartyBlockAccounts } from './components/party-block-accounts';
import { isValidPartyId } from './components/party-id-error';
import { useDataProvider } from '@vegaprotocol/data-provider';
+import { TxsListNavigation } from '../../../components/txs/tx-list-navigation';
+import { AllFilterOptions, TxsFilter } from '../../../components/txs/tx-filter';
const Party = () => {
+ const [filters, setFilters] = useState(new Set(AllFilterOptions));
const { party } = useParams<{ party: string }>();
useDocumentTitle(['Public keys', party || '-']);
@@ -24,10 +27,24 @@ const Party = () => {
const partyId = toNonHex(party ? party : '');
const { isMobile } = useScreenDimensions();
const visibleChars = useMemo(() => (isMobile ? 10 : 14), [isMobile]);
- const filters = `filters[tx.submitter]=${partyId}`;
- const { hasMoreTxs, loadTxs, error, txsData, loading } = useTxsData({
- limit: 10,
- filters,
+ const baseFilters = `filters[tx.submitter]=${partyId}`;
+ const f =
+ filters && filters.size === 1
+ ? `${baseFilters}&filters[cmd.type]=${Array.from(filters)[0]}`
+ : baseFilters;
+
+ const {
+ hasMoreTxs,
+ nextPage,
+ previousPage,
+ error,
+ refreshTxs,
+ loading,
+ txsData,
+ hasPreviousPage,
+ } = useTxsData({
+ limit: 25,
+ filters: f,
});
const variables = useMemo(() => ({ partyId }), [partyId]);
@@ -81,14 +98,24 @@ const Party = () => {
{t('Transactions')}
+
+
+
{!error && txsData ? (
) : (
diff --git a/apps/explorer/src/app/routes/txs/home/index.tsx b/apps/explorer/src/app/routes/txs/home/index.tsx
index d24283de4..3816cbb00 100644
--- a/apps/explorer/src/app/routes/txs/home/index.tsx
+++ b/apps/explorer/src/app/routes/txs/home/index.tsx
@@ -1,14 +1,14 @@
import { t } from '@vegaprotocol/i18n';
import { RouteTitle } from '../../../components/route-title';
-import { BlocksRefetch } from '../../../components/blocks';
import { TxsInfiniteList } from '../../../components/txs';
import { useTxsData } from '../../../hooks/use-txs-data';
import { useDocumentTitle } from '../../../hooks/use-document-title';
import { useState } from 'react';
import { AllFilterOptions, TxsFilter } from '../../../components/txs/tx-filter';
+import { TxsListNavigation } from '../../../components/txs/tx-list-navigation';
-const BE_TXS_PER_REQUEST = 15;
+const BE_TXS_PER_REQUEST = 25;
export const TxsList = () => {
useDocumentTitle(['Transactions']);
@@ -21,6 +21,11 @@ export const TxsList = () => {
);
};
+/**
+ * Displays a list of transactions with filters and controls to navigate through the list.
+ *
+ * @returns {JSX.Element} Transaction List and controls
+ */
export const TxsListFiltered = () => {
const [filters, setFilters] = useState(new Set(AllFilterOptions));
@@ -29,26 +34,40 @@ export const TxsListFiltered = () => {
? `filters[cmd.type]=${Array.from(filters)[0]}`
: '';
- const { hasMoreTxs, loadTxs, error, txsData, refreshTxs, loading } =
- useTxsData({
- limit: BE_TXS_PER_REQUEST,
- filters: f,
- });
+ const {
+ hasMoreTxs,
+ nextPage,
+ previousPage,
+ error,
+ refreshTxs,
+ loading,
+ txsData,
+ hasPreviousPage,
+ } = useTxsData({
+ limit: BE_TXS_PER_REQUEST,
+ filters: f,
+ });
return (
<>
-
-
+
0}
hasMoreTxs={hasMoreTxs}
areTxsLoading={loading}
txs={txsData}
- loadMoreTxs={loadTxs}
+ loadMoreTxs={nextPage}
error={error}
- className="mb-28"
+ className="mb-28 w-full min-w-[400px]"
/>
>
);
diff --git a/apps/explorer/src/app/routes/txs/id/tx-details.spec.tsx b/apps/explorer/src/app/routes/txs/id/tx-details.spec.tsx
index c7f18b1d8..7b58423bb 100644
--- a/apps/explorer/src/app/routes/txs/id/tx-details.spec.tsx
+++ b/apps/explorer/src/app/routes/txs/id/tx-details.spec.tsx
@@ -1,10 +1,11 @@
-import { BrowserRouter as Router } from 'react-router-dom';
import { render, screen } from '@testing-library/react';
import { TxDetails } from './tx-details';
import type {
BlockExplorerTransactionResult,
ValidatorHeartbeat,
} from '../../../routes/types/block-explorer-response';
+import { MemoryRouter } from 'react-router-dom';
+import { MockedProvider } from '@apollo/client/testing';
// Note: Long enough that there is a truncated output and a full output
const pubKey =
@@ -27,9 +28,11 @@ const txData: BlockExplorerTransactionResult = {
};
const renderComponent = (txData: BlockExplorerTransactionResult) => (
-
-
-
+
+
+
+
+
);
describe('Transaction details', () => {
diff --git a/apps/explorer/src/app/routes/validators/NodeNames.graphql b/apps/explorer/src/app/routes/validators/NodeNames.graphql
new file mode 100644
index 000000000..984aaff57
--- /dev/null
+++ b/apps/explorer/src/app/routes/validators/NodeNames.graphql
@@ -0,0 +1,13 @@
+query ExplorerNodeNames {
+ nodesConnection {
+ edges {
+ node {
+ id
+ name
+ pubkey
+ tmPubkey
+ ethereumAddress
+ }
+ }
+ }
+}
diff --git a/apps/explorer/src/app/routes/validators/__generated__/NodeNames.ts b/apps/explorer/src/app/routes/validators/__generated__/NodeNames.ts
new file mode 100644
index 000000000..0f47cb165
--- /dev/null
+++ b/apps/explorer/src/app/routes/validators/__generated__/NodeNames.ts
@@ -0,0 +1,53 @@
+import * as Types from '@vegaprotocol/types';
+
+import { gql } from '@apollo/client';
+import * as Apollo from '@apollo/client';
+const defaultOptions = {} as const;
+export type ExplorerNodeNamesQueryVariables = Types.Exact<{ [key: string]: never; }>;
+
+
+export type ExplorerNodeNamesQuery = { __typename?: 'Query', nodesConnection: { __typename?: 'NodesConnection', edges?: Array<{ __typename?: 'NodeEdge', node: { __typename?: 'Node', id: string, name: string, pubkey: string, tmPubkey: string, ethereumAddress: string } } | null> | null } };
+
+
+export const ExplorerNodeNamesDocument = gql`
+ query ExplorerNodeNames {
+ nodesConnection {
+ edges {
+ node {
+ id
+ name
+ pubkey
+ tmPubkey
+ ethereumAddress
+ }
+ }
+ }
+}
+ `;
+
+/**
+ * __useExplorerNodeNamesQuery__
+ *
+ * To run a query within a React component, call `useExplorerNodeNamesQuery` and pass it any options that fit your needs.
+ * When your component renders, `useExplorerNodeNamesQuery` returns an object from Apollo Client that contains loading, error, and data properties
+ * you can use to render your UI.
+ *
+ * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options;
+ *
+ * @example
+ * const { data, loading, error } = useExplorerNodeNamesQuery({
+ * variables: {
+ * },
+ * });
+ */
+export function useExplorerNodeNamesQuery(baseOptions?: Apollo.QueryHookOptions) {
+ const options = {...defaultOptions, ...baseOptions}
+ return Apollo.useQuery(ExplorerNodeNamesDocument, options);
+ }
+export function useExplorerNodeNamesLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions) {
+ const options = {...defaultOptions, ...baseOptions}
+ return Apollo.useLazyQuery(ExplorerNodeNamesDocument, options);
+ }
+export type ExplorerNodeNamesQueryHookResult = ReturnType;
+export type ExplorerNodeNamesLazyQueryHookResult = ReturnType;
+export type ExplorerNodeNamesQueryResult = Apollo.QueryResult;
\ No newline at end of file
diff --git a/libs/ui-toolkit/src/components/viewing-as-user/index.tsx b/libs/ui-toolkit/src/components/viewing-as-user/index.tsx
index deb69c510..da7e042b5 100644
--- a/libs/ui-toolkit/src/components/viewing-as-user/index.tsx
+++ b/libs/ui-toolkit/src/components/viewing-as-user/index.tsx
@@ -1,12 +1,12 @@
import { Button } from '../button';
import { t } from '@vegaprotocol/i18n';
-export function truncateMiddle(address: string) {
+export function truncateMiddle(address: string, start = 6, end = 4) {
if (address.length < 11) return address;
return (
- address.slice(0, 6) +
+ address.slice(0, start) +
'\u2026' +
- address.slice(address.length - 4, address.length)
+ address.slice(address.length - end, address.length)
);
}
diff --git a/libs/utils/src/lib/format/strings.spec.ts b/libs/utils/src/lib/format/strings.spec.ts
index d864b1310..9d38117db 100644
--- a/libs/utils/src/lib/format/strings.spec.ts
+++ b/libs/utils/src/lib/format/strings.spec.ts
@@ -16,7 +16,8 @@ describe('truncateByChars', () => {
},
{ i: '12345678901234567890', s: 0, e: 10, o: `${ELLIPSIS}1234567890` },
{ i: '123', s: 0, e: 4, o: '123' },
- ])('should truncate given string by specific chars', ({ i, s, e, o }) => {
+ { i: '12345678901234567890', s: 3, e: 0, o: `123${ELLIPSIS}` },
+ ])('should truncate given string by specific chars: %s', ({ i, s, e, o }) => {
expect(truncateByChars(i, s, e)).toStrictEqual(o);
});
});
@@ -28,7 +29,7 @@ describe('shorten', () => {
{ i: '12345678901234567890', l: 10, o: `123456789${ELLIPSIS}` },
{ i: '12345678901234567890', l: 20, o: `1234567890123456789${ELLIPSIS}` },
{ i: '12345678901234567890', l: 30, o: `12345678901234567890` },
- ])('should shorten given string by specific limit', ({ i, l, o }) => {
+ ])('should shorten given string by specific limit: %s', ({ i, l, o }) => {
const output = shorten(i, l);
expect(output).toStrictEqual(o);
});
@@ -51,7 +52,7 @@ describe('titlefy', () => {
words: ['VEGAUSD', '123.22'],
o: 'VEGAUSD - 123.22 - Vega',
},
- ])('should convert to title-like string', ({ words, o }) => {
+ ])('should convert to title-like string: %s', ({ words, o }) => {
expect(titlefy(words)).toEqual(o);
});
});
diff --git a/libs/utils/src/lib/format/strings.ts b/libs/utils/src/lib/format/strings.ts
index 4f455e9ee..a10dbfb4b 100644
--- a/libs/utils/src/lib/format/strings.ts
+++ b/libs/utils/src/lib/format/strings.ts
@@ -6,7 +6,10 @@ export function truncateByChars(input: string, start = 6, end = 6) {
if (input.length <= start + end + 1) {
return input;
}
- return input.slice(0, start) + ELLIPSIS + input.slice(-end);
+
+ const s = input.slice(0, start);
+ const e = end !== 0 ? input.slice(-end) : '';
+ return `${s}${ELLIPSIS}${e}`;
}
export function shorten(input: string, limit?: number) {