diff --git a/src/app/_components/BlockList/BlockCount.tsx b/src/app/_components/BlockList/BlockCount.tsx
index 625d792e3..1c3cc17eb 100644
--- a/src/app/_components/BlockList/BlockCount.tsx
+++ b/src/app/_components/BlockList/BlockCount.tsx
@@ -1,68 +1,65 @@
import { useColorModeValue } from '@chakra-ui/react';
-import { ArrowUpRight } from '@phosphor-icons/react';
+import { CaretDown } from '@phosphor-icons/react';
import pluralize from 'pluralize';
-import { memo, useMemo } from 'react';
+import { memo } from 'react';
-import { Circle } from '../../../common/components/Circle';
-import { ExplorerLink } from '../../../common/components/ExplorerLinks';
-import { Flex } from '../../../ui/Flex';
+import { Button } from '../../../ui/Button';
import { Icon } from '../../../ui/Icon';
import { Text } from '../../../ui/Text';
export const BlockCount = memo(function ({
count,
- btcBlockHash,
isFirst,
+ loadMoreStxBlocksHandler,
+ minimized = false,
}: {
count: number;
- btcBlockHash?: string;
isFirst?: boolean;
+ loadMoreStxBlocksHandler?: () => void;
+ minimized?: boolean;
}) {
- // TODO: remove. use theme
const bgColor = useColorModeValue('purple.100', 'slate.900');
const bgColorHover = useColorModeValue('purple.200', 'slate.850');
const textColor = useColorModeValue('purple.600', 'purple.400');
const iconColor = useColorModeValue('purple.600', 'purple.200');
- const content = useMemo(() => {
- return (
+
+ return (
+
);
});
diff --git a/src/app/_components/BlockList/BlocksPage/BlocksPageBlockListGrouped.tsx b/src/app/_components/BlockList/BlocksPage/BlocksPageBlockListGrouped.tsx
index a71cc7ffa..1bc58cd70 100644
--- a/src/app/_components/BlockList/BlocksPage/BlocksPageBlockListGrouped.tsx
+++ b/src/app/_components/BlockList/BlocksPage/BlocksPageBlockListGrouped.tsx
@@ -23,12 +23,7 @@ function BlocksPageBlockListGroupedBase() {
<>
{!liveUpdates && }
-
+
{!liveUpdates && }
-
+
(response);
+ const btcBlock = burnBlocks[0];
return (
@@ -84,7 +87,7 @@ function LastConfirmedBitcoinBlockCard() {
- 214
+ {btcBlock.stacks_blocks.length}
Stacks blocks
@@ -92,7 +95,7 @@ function LastConfirmedBitcoinBlockCard() {
- 4354
+ {btcBlock.total_tx_count}
Stacks transactions
diff --git a/src/app/_components/BlockList/Grouped/BlockListGrouped.tsx b/src/app/_components/BlockList/Grouped/BlockListGrouped.tsx
index d618eb522..b72a47dab 100644
--- a/src/app/_components/BlockList/Grouped/BlockListGrouped.tsx
+++ b/src/app/_components/BlockList/Grouped/BlockListGrouped.tsx
@@ -1,8 +1,10 @@
import { ArrowElbowLeftDown, Clock } from '@phosphor-icons/react';
-import React, { ReactNode, useMemo } from 'react';
+import React, { ReactNode, useCallback, useMemo, useState } from 'react';
import { BlockLink, ExplorerLink } from '../../../../common/components/ExplorerLinks';
import { Timestamp } from '../../../../common/components/Timestamp';
+import { useInfiniteQueryResult } from '../../../../common/hooks/useInfiniteQueryResult';
+import { useBlocksByBurnBlock } from '../../../../common/queries/useBlocksByBurnBlock';
import { truncateMiddle } from '../../../../common/utils/utils';
import { Box } from '../../../../ui/Box';
import { Flex } from '../../../../ui/Flex';
@@ -21,7 +23,7 @@ import { LineAndNode } from '../LineAndNode';
import { ScrollableBox } from '../ScrollableDiv';
import { getFadeAnimationStyle, mobileBorderCss } from '../consts';
import { BlockListBtcBlock, BlockListStxBlock } from '../types';
-import { BlockListData } from '../utils';
+import { BlockListData, createBlockListStxBlock } from '../utils';
const PADDING = 4;
@@ -158,6 +160,7 @@ export function BurnBlockGroupGridLayout({
templateColumns={minimized ? 'auto 1fr auto' : 'repeat(4, 1fr)'}
width={'full'}
columnGap={minimized ? 0 : 4}
+ mb="1px"
>
{children}
@@ -165,31 +168,68 @@ export function BurnBlockGroupGridLayout({
}
export function BurnBlockGroupGrid({
+ blocksCount,
stxBlocks,
+ stxBlocksLimit,
+ lastStxBlock,
minimized,
- numStxBlocksNotDisplayed,
+ isFirst,
+ loadMoreStxBlocksHandler,
}: {
stxBlocks: BlockListStxBlock[];
- minimized: boolean;
- numStxBlocksNotDisplayed: number;
+ lastStxBlock: BlockListStxBlock | null;
+ stxBlocksLimit?: number;
+ blocksCount: number;
+ isFirst: boolean;
+ minimized?: boolean;
+ loadMoreStxBlocksHandler?: () => void;
}) {
+ const stxBlocksToDisplay = useMemo(
+ () => (stxBlocksLimit ? stxBlocks.slice(0, stxBlocksLimit) : stxBlocks),
+ [stxBlocks, stxBlocksLimit]
+ );
+ const numStxBlocksNotDisplayed = blocksCount - stxBlocksToDisplay.length;
+ const showLastStxBlock = lastStxBlock && numStxBlocksNotDisplayed > 1;
+
return (
-
- {minimized || stxBlocks.length === 0 ? null : }
- {stxBlocks.map((stxBlock, i) => (
-
+
+
+ {minimized || stxBlocksToDisplay.length === 0 ? null : }
+ {stxBlocksToDisplay.map((stxBlock, i) => (
+
+
+ {i < stxBlocksToDisplay.length - 1 && (
+
+ )}
+
+ ))}
+
+ {numStxBlocksNotDisplayed > 0 ? (
+
+
+
+ ) : null}
+ {showLastStxBlock ? (
+
- {i < stxBlocks.length - 1 && (
-
- )}
-
- ))}
-
+
+ ) : null}
+
);
}
@@ -212,7 +252,6 @@ function BitcoinHeader({
px={PADDING}
borderBottom={minimized ? '1px solid var(--stacks-colors-borderPrimary)' : 'none'}
flexWrap={'wrap'}
- // height={5}
>
@@ -289,66 +328,89 @@ export function BurnBlockGroup({
isFirst,
stxBlocksLimit,
minimized = false,
- onlyShowStxBlocksForFirstBtcBlock,
}: {
btcBlock: BlockListBtcBlock;
stxBlocks: BlockListStxBlock[];
isFirst: boolean;
stxBlocksLimit?: number;
minimized?: boolean;
- onlyShowStxBlocksForFirstBtcBlock?: boolean;
}) {
- const unaccountedStxBlocks = btcBlock.blockCount ? stxBlocks.length - btcBlock.blockCount : 0;
+ const [enabled, setEnabled] = useState(false);
+
+ const response = useBlocksByBurnBlock(
+ btcBlock.height,
+ stxBlocksLimit,
+ {
+ offset: stxBlocks.length,
+ enabled,
+ },
+ 'additional-stx-blocks-loaded'
+ );
+ const { fetchNextPage, hasNextPage } = response;
+ const additionalStxBlocksLoaded = useInfiniteQueryResult(response);
+
+ const handleLoadMoreStxBlocks = useCallback(() => {
+ setEnabled(true);
+ if (hasNextPage) {
+ fetchNextPage();
+ }
+ }, [fetchNextPage, hasNextPage]);
+
+ const allStxBlocks = useMemo(() => {
+ return stxBlocks.concat(additionalStxBlocksLoaded.map(createBlockListStxBlock));
+ }, [stxBlocks, additionalStxBlocksLoaded]);
+
+ // Live updates cause unaccounted blocks and txs
+ const unaccountedStxBlocks = useMemo(
+ () => allStxBlocks.length - btcBlock.blockCount,
+ [allStxBlocks, btcBlock.blockCount]
+ );
const unaccountedTxs = useMemo(
() =>
unaccountedStxBlocks > 0
- ? stxBlocks
+ ? allStxBlocks
.slice(0, unaccountedStxBlocks)
.reduce((acc, block) => acc + (block.txsCount || 0), 0)
: 0,
- [stxBlocks, unaccountedStxBlocks]
+ [allStxBlocks, unaccountedStxBlocks]
);
- const txsCount = btcBlock.txsCount
- ? isFirst
- ? btcBlock.txsCount + unaccountedTxs
- : btcBlock.txsCount
- : undefined;
- const blocksCount = btcBlock.blockCount
- ? isFirst
- ? btcBlock.blockCount + unaccountedStxBlocks
- : btcBlock.blockCount
- : undefined;
- const numStxBlocksNotDisplayed =
- onlyShowStxBlocksForFirstBtcBlock && !isFirst
- ? blocksCount
- ? blocksCount
- : 0
- : blocksCount
- ? blocksCount - (stxBlocksLimit || 0)
- : 0;
- const displayedStxBlocks = useMemo(
- () => (stxBlocksLimit ? stxBlocks.slice(0, stxBlocksLimit) : stxBlocks),
- [stxBlocks, stxBlocksLimit]
+
+ const txsCount = isFirst
+ ? btcBlock.txsCount + unaccountedTxs // For the first btc block, if live is on, then the btc block tx count can be stale and we need to account for the unaccounted txs
+ : btcBlock.txsCount;
+ const blocksCount = useMemo(
+ () => (isFirst ? btcBlock.blockCount + unaccountedStxBlocks : btcBlock.blockCount),
+ [btcBlock, unaccountedStxBlocks, isFirst]
+ );
+
+ const queryForLastStxBlockRequired = useMemo(
+ () => allStxBlocks.length < blocksCount,
+ [allStxBlocks, blocksCount]
+ );
+ const lastStxBlockResponse = useBlocksByBurnBlock(btcBlock.height, 1, {
+ offset: blocksCount - 1,
+ enabled: queryForLastStxBlockRequired,
+ });
+ const lastStxBlock = useInfiniteQueryResult(lastStxBlockResponse)[0];
+ const lastStxBlockFormatted = useMemo(
+ () => (lastStxBlock ? createBlockListStxBlock(lastStxBlock) : null),
+ [lastStxBlock]
);
+
return (
- {onlyShowStxBlocksForFirstBtcBlock && !isFirst ? null : (
-
-
-
- )}
- {numStxBlocksNotDisplayed > 0 ? (
-
+
- ) : null}
+
);
@@ -368,12 +430,10 @@ export function BlockListGrouped({
blockList,
minimized,
stxBlocksLimit,
- onlyShowStxBlocksForFirstBtcBlock,
}: {
blockList: BlockListData[];
minimized: boolean;
stxBlocksLimit?: number;
- onlyShowStxBlocksForFirstBtcBlock?: boolean;
}) {
return (
@@ -385,7 +445,6 @@ export function BlockListGrouped({
minimized={minimized}
stxBlocksLimit={stxBlocksLimit}
isFirst={i === 0}
- onlyShowStxBlocksForFirstBtcBlock={onlyShowStxBlocksForFirstBtcBlock}
/>
))}
diff --git a/src/app/_components/BlockList/Grouped/skeleton.tsx b/src/app/_components/BlockList/Grouped/skeleton.tsx
index f20e955c8..69f2b56e4 100644
--- a/src/app/_components/BlockList/Grouped/skeleton.tsx
+++ b/src/app/_components/BlockList/Grouped/skeleton.tsx
@@ -165,9 +165,9 @@ export function HomePageBlockListGroupedSkeleton() {
export function BlocksPageBlockListGroupedSkeleton() {
return (
);
}
diff --git a/src/app/_components/BlockList/HomePage/HomePageBlockListUngrouped.tsx b/src/app/_components/BlockList/HomePage/HomePageBlockListUngrouped.tsx
index 0530623cd..66584f95f 100644
--- a/src/app/_components/BlockList/HomePage/HomePageBlockListUngrouped.tsx
+++ b/src/app/_components/BlockList/HomePage/HomePageBlockListUngrouped.tsx
@@ -18,7 +18,7 @@ function HomePageBlockListUngroupedBase() {
<>
{!liveUpdates && }
-
+
>
diff --git a/src/app/_components/BlockList/LayoutA/__tests__/__snapshots__/BlockListWithControls.test.tsx.snap b/src/app/_components/BlockList/LayoutA/__tests__/__snapshots__/BlockListWithControls.test.tsx.snap
index b9b437d71..a0dff80e4 100644
--- a/src/app/_components/BlockList/LayoutA/__tests__/__snapshots__/BlockListWithControls.test.tsx.snap
+++ b/src/app/_components/BlockList/LayoutA/__tests__/__snapshots__/BlockListWithControls.test.tsx.snap
@@ -366,37 +366,29 @@ exports[`BlockListWithControls renders correctly 1`] = `
-
+
+
+
+
) : (
diff --git a/src/app/_components/BlockList/Ungrouped/BlockListUngrouped.tsx b/src/app/_components/BlockList/Ungrouped/BlockListUngrouped.tsx
index 44f651fca..271dc3cdd 100644
--- a/src/app/_components/BlockList/Ungrouped/BlockListUngrouped.tsx
+++ b/src/app/_components/BlockList/Ungrouped/BlockListUngrouped.tsx
@@ -1,9 +1,11 @@
import { useColorModeValue } from '@chakra-ui/react';
import { ArrowBendDownLeft, Clock } from '@phosphor-icons/react';
-import React, { ReactNode, useMemo } from 'react';
+import React, { ReactNode, useCallback, useMemo, useState } from 'react';
import { BlockLink, ExplorerLink } from '../../../../common/components/ExplorerLinks';
import { Timestamp } from '../../../../common/components/Timestamp';
+import { useInfiniteQueryResult } from '../../../../common/hooks/useInfiniteQueryResult';
+import { useBlocksByBurnBlock } from '../../../../common/queries/useBlocksByBurnBlock';
import { truncateMiddle } from '../../../../common/utils/utils';
import { Box } from '../../../../ui/Box';
import { Flex, FlexProps } from '../../../../ui/Flex';
@@ -21,7 +23,7 @@ import { LineAndNode } from '../LineAndNode';
import { ScrollableBox } from '../ScrollableDiv';
import { getFadeAnimationStyle, mobileBorderCss } from '../consts';
import { BlockListStxBlock } from '../types';
-import { BlockListData } from '../utils';
+import { BlockListData, createBlockListStxBlock } from '../utils';
interface BtcBlockRowProps {
height: number | string;
@@ -257,34 +259,73 @@ export function StxBlocksGridLayout({
}
function StxBlocksGrid({
+ blocksCount,
stxBlocks,
+ lastStxBlock,
+ stxBlocksLimit,
minimized = false,
- numStxBlocksNotDisplayed,
+ isFirst,
+ loadMoreStxBlocksHandler,
}: {
stxBlocks: BlockListStxBlock[];
+ lastStxBlock: BlockListStxBlock | null;
+ stxBlocksLimit?: number;
+ blocksCount: number;
minimized?: boolean;
- numStxBlocksNotDisplayed: number;
+ isFirst: boolean;
+ loadMoreStxBlocksHandler?: () => void;
}) {
+ const stxBlocksToDisplay = useMemo(
+ () => (stxBlocksLimit ? stxBlocks.slice(0, stxBlocksLimit) : stxBlocks),
+ [stxBlocks, stxBlocksLimit]
+ );
+ const numStxBlocksNotDisplayed = blocksCount - stxBlocksToDisplay.length;
+ const showLastStxBlock = lastStxBlock && numStxBlocksNotDisplayed > 1;
+
return (
-
- {minimized ? null : }
- {stxBlocks.map((stxBlock, i) => (
-
+
+
+ {minimized ? null : }
+ {stxBlocksToDisplay.map((stxBlock, i) => (
+
+
+ {i < stxBlocksToDisplay.length - 1 && (
+
+ )}
+
+ ))}
+
+ {numStxBlocksNotDisplayed > 0 ? (
+
+
+
+ ) : null}
+ {showLastStxBlock ? (
+
- {i < stxBlocks.length - 1 && (
-
- )}
-
- ))}
-
+
+ ) : null}
+
);
}
@@ -301,34 +342,69 @@ function StxBlocksGroupedByBtcBlock({
}) {
const btcBlock = blockList.btcBlock;
const stxBlocks = blockList.stxBlocks;
- const unaccountedStxBlocks = btcBlock.blockCount ? stxBlocks.length - btcBlock.blockCount : 0;
- const blocksCount = btcBlock.blockCount
- ? isFirst
- ? btcBlock.blockCount + unaccountedStxBlocks
- : btcBlock.blockCount
- : undefined;
- const numStxBlocksNotDisplayed = blocksCount ? blocksCount - (stxBlocksLimit || 0) : 0;
- const displayedStxBlocks = useMemo(
- () => (stxBlocksLimit ? blockList.stxBlocks.slice(0, stxBlocksLimit) : blockList.stxBlocks),
- [blockList.stxBlocks, stxBlocksLimit]
+
+ const [enabled, setEnabled] = useState(false);
+
+ const response = useBlocksByBurnBlock(
+ btcBlock.height,
+ stxBlocksLimit,
+ {
+ offset: stxBlocks.length,
+ enabled,
+ },
+ 'additional-stx-blocks-loaded'
+ );
+ const { fetchNextPage, hasNextPage } = response;
+ const additionalStxBlocks = useInfiniteQueryResult(response);
+
+ const handleLoadMoreStxBlocks = useCallback(() => {
+ setEnabled(true);
+ if (hasNextPage) {
+ fetchNextPage();
+ }
+ }, [fetchNextPage, hasNextPage]);
+
+ const allStxBlocks = useMemo(() => {
+ return stxBlocks.concat(additionalStxBlocks.map(createBlockListStxBlock));
+ }, [stxBlocks, additionalStxBlocks]);
+
+ // Live updates cause unaccounted blocks and txs
+ const unaccountedStxBlocks = useMemo(
+ () => allStxBlocks.length - btcBlock.blockCount,
+ [allStxBlocks, btcBlock.blockCount]
+ );
+ const blocksCount = useMemo(
+ () => (isFirst ? btcBlock.blockCount + unaccountedStxBlocks : btcBlock.blockCount),
+ [btcBlock, unaccountedStxBlocks, isFirst]
+ );
+
+ const queryForLastStxBlockRequired = useMemo(
+ () => allStxBlocks.length < blocksCount,
+ [allStxBlocks, blocksCount]
+ );
+ const lastStxBlockResponse = useBlocksByBurnBlock(btcBlock.height, 1, {
+ offset: blocksCount - 1,
+ enabled: queryForLastStxBlockRequired,
+ });
+ const lastStxBlock = useInfiniteQueryResult(lastStxBlockResponse)[0];
+ const lastStxBlockFormatted = useMemo(
+ () => (lastStxBlock ? createBlockListStxBlock(lastStxBlock) : null),
+ [lastStxBlock]
);
return (
-
- {numStxBlocksNotDisplayed > 0 ? (
-
- ) : null}
+
{
- const accumulatedStxBlocksRef = useRef>(new Set());
- const btcBlocksHashesRef = useRef>(new Set());
- const btcBlocksRef = useRef([] as BurnBlock[]);
-
- useEffect(() => {
- if (latestStxBlocks.length > 0) {
- latestStxBlocks.forEach(block => {
- if (!accumulatedStxBlocksRef.current.has(block.burn_block_hash)) {
- accumulatedStxBlocksRef.current.add(block.burn_block_hash);
- }
- });
- }
- }, [latestStxBlocks]);
-
- const queryClient = useQueryClient();
- const getQuery = useGetBurnBlockQuery();
- const btcBlockQueries = useMemo(() => {
- const hashes = Array.from(accumulatedStxBlocksRef.current).filter(
- hash => !btcBlocksHashesRef.current.has(hash)
- );
- return {
- queries: hashes.map(hash => {
- return getQuery(hash);
- }),
- combine: (response: UseQueryResult[]) => response.map(r => r.data),
- };
- }, [accumulatedStxBlocksRef, getQuery]);
- const btcBlocks = useQueries(btcBlockQueries, queryClient);
- const filteredBtcBlocks = btcBlocks.filter(block => !!block) as BurnBlock[];
- filteredBtcBlocks.forEach(block => {
- btcBlocksHashesRef.current.add(block.burn_block_hash);
- });
- btcBlocksRef.current = [...filteredBtcBlocks, ...btcBlocksRef.current];
-
- return { btcBlocks: btcBlocksRef.current };
-};
diff --git a/src/app/_components/BlockList/data/useBlocksPageBlockListGrouped.tsx b/src/app/_components/BlockList/data/useBlocksPageBlockListGrouped.tsx
index fc1ebd0a5..7b9eb4043 100644
--- a/src/app/_components/BlockList/data/useBlocksPageBlockListGrouped.tsx
+++ b/src/app/_components/BlockList/data/useBlocksPageBlockListGrouped.tsx
@@ -1,13 +1,13 @@
import { useCallback, useEffect, useRef, useState } from 'react';
+import { useFetchMultipleBurnBlocks } from '../../../../common/queries/useBurnBlock';
import { useBlockListContext } from '../BlockListContext';
import { useBlockListWebSocket } from '../Sockets/useBlockListWebSocket';
import { BlockListData, generateBlockList, mergeBlockLists, waitForFadeAnimation } from '../utils';
-import { useBlockListBtcBlocks } from './useBlockListBtcBlocks';
import { useBlocksGroupedInitialBlockList } from './useBlocksPageGroupedInitialBlockList';
import { generateBtcBlocksMap } from './utils';
-export function useBlocksPageBlockListGrouped(btcBlockLimit: number = 10) {
+export function useBlocksPageBlockListGrouped(btcBlockLimit: number = 3) {
const { setBlockListLoading, liveUpdates } = useBlockListContext();
const {
@@ -41,14 +41,17 @@ export function useBlocksPageBlockListGrouped(btcBlockLimit: number = 10) {
[blockList]
);
- const { btcBlocks: latestBtcBlocks } = useBlockListBtcBlocks(latestStxBlocksFromWebSocket);
+ const fetchBurnBlocks = useFetchMultipleBurnBlocks();
const showLatestStxBlocksFromWebSocket = useCallback(() => {
setBlockListLoading(true);
- waitForFadeAnimation(() => {
+ waitForFadeAnimation(async () => {
+ const btcBlocks = await fetchBurnBlocks(
+ latestStxBlocksFromWebSocket.map(block => block.burn_block_hash)
+ );
const websocketBlockList = generateBlockList(
latestStxBlocksFromWebSocket,
- generateBtcBlocksMap(latestBtcBlocks)
+ generateBtcBlocksMap(btcBlocks)
);
updateBlockListManually(websocketBlockList);
clearLatestStxBlocksFromWebSocket();
@@ -59,7 +62,7 @@ export function useBlocksPageBlockListGrouped(btcBlockLimit: number = 10) {
updateBlockListManually,
setBlockListLoading,
clearLatestStxBlocksFromWebSocket,
- latestBtcBlocks,
+ fetchBurnBlocks,
]);
const updateBlockListWithQuery = useCallback(
diff --git a/src/app/_components/BlockList/data/useBlocksPageBlockListUngrouped.ts b/src/app/_components/BlockList/data/useBlocksPageBlockListUngrouped.ts
index a9b038ebf..5c902a70f 100644
--- a/src/app/_components/BlockList/data/useBlocksPageBlockListUngrouped.ts
+++ b/src/app/_components/BlockList/data/useBlocksPageBlockListUngrouped.ts
@@ -2,10 +2,10 @@
import { useCallback, useEffect, useRef, useState } from 'react';
+import { useFetchMultipleBurnBlocks } from '../../../../common/queries/useBurnBlock';
import { useBlockListContext } from '../BlockListContext';
import { useBlockListWebSocket } from '../Sockets/useBlockListWebSocket';
import { BlockListData, generateBlockList, mergeBlockLists, waitForFadeAnimation } from '../utils';
-import { useBlockListBtcBlocks } from './useBlockListBtcBlocks';
import { useBlocksPageUngroupedInitialBlockList } from './useBlocksPageUngroupedInitialBlockList';
import { generateBtcBlocksMap } from './utils';
@@ -34,7 +34,7 @@ export function useBlocksPageBlockListUngrouped(btcBlockLimit: number = 3) {
clearLatestStxBlocks: clearLatestStxBlocksFromWebSocket,
} = useBlockListWebSocket(liveUpdates, initialStxBlocksHashes);
- const { btcBlocks: latestBtcBlocks } = useBlockListBtcBlocks(latestStxBlocksFromWebSocket);
+ const fetchBurnBlocks = useFetchMultipleBurnBlocks();
// manually update the block list with block list updates from the websocket
const updateBlockListManually = useCallback(
@@ -47,10 +47,13 @@ export function useBlocksPageBlockListUngrouped(btcBlockLimit: number = 3) {
const showLatestStxBlocksFromWebSocket = useCallback(() => {
setBlockListLoading(true);
- waitForFadeAnimation(() => {
+ waitForFadeAnimation(async () => {
+ const btcBlocks = await fetchBurnBlocks(
+ latestStxBlocksFromWebSocket.map(block => block.burn_block_hash)
+ );
const websocketBlockList = generateBlockList(
latestStxBlocksFromWebSocket,
- generateBtcBlocksMap(latestBtcBlocks)
+ generateBtcBlocksMap(btcBlocks)
);
updateBlockListManually(websocketBlockList);
clearLatestStxBlocksFromWebSocket();
@@ -61,7 +64,7 @@ export function useBlocksPageBlockListUngrouped(btcBlockLimit: number = 3) {
updateBlockListManually,
setBlockListLoading,
clearLatestStxBlocksFromWebSocket,
- latestBtcBlocks,
+ fetchBurnBlocks,
]);
const updateBlockListWithQuery = useCallback(
diff --git a/src/app/_components/BlockList/data/useBlocksPageGroupedInitialBlockList.ts b/src/app/_components/BlockList/data/useBlocksPageGroupedInitialBlockList.ts
index f60a413f9..c302bd191 100644
--- a/src/app/_components/BlockList/data/useBlocksPageGroupedInitialBlockList.ts
+++ b/src/app/_components/BlockList/data/useBlocksPageGroupedInitialBlockList.ts
@@ -1,17 +1,17 @@
+import { UseQueryResult, useQueries, useQueryClient } from '@tanstack/react-query';
import { useMemo } from 'react';
-import { BurnBlock } from '@stacks/blockchain-api-client';
+import { BurnBlock, NakamotoBlockListResponse } from '@stacks/blockchain-api-client';
import { useSuspenseInfiniteQueryResult } from '../../../../common/hooks/useInfiniteQueryResult';
import {
GET_BLOCKS_BY_BURN_BLOCK_QUERY_KEY,
- useSuspenseBlocksByBurnBlock,
+ useGetStxBlocksByBurnBlockQuery,
} from '../../../../common/queries/useBlocksByBurnBlock';
import {
BURN_BLOCKS_QUERY_KEY,
useSuspenseBurnBlocks,
} from '../../../../common/queries/useBurnBlocksInfinite';
-import { BlockListBtcBlock } from '../types';
import { generateBlockList } from '../utils';
import { useBtcBlocksMap, useRefetchInitialBlockList } from './utils';
@@ -22,7 +22,7 @@ export function useBlocksGroupedInitialBlockList(blockListLimit: number) {
const { isFetchingNextPage, fetchNextPage, hasNextPage } = response;
const btcBlocks = useSuspenseInfiniteQueryResult(response);
- // Remove duplicates
+ // Remove duplicates. Duplicates happen sometimes because the chaintip moves and this causes the offset to be off
const uniqueBtcBlocks = useMemo(() => {
const blockMap = new Map();
btcBlocks.forEach(block => {
@@ -31,39 +31,61 @@ export function useBlocksGroupedInitialBlockList(blockListLimit: number) {
return Array.from(blockMap.values());
}, [btcBlocks]);
- const latestBurnBlock = useMemo(() => btcBlocks[0], [btcBlocks]);
+ const queryClient = useQueryClient();
+ const getQuery = useGetStxBlocksByBurnBlockQuery();
+ const stxBlockQueries = useMemo(() => {
+ return {
+ queries: uniqueBtcBlocks.map(btcBlock => {
+ return getQuery(btcBlock.burn_block_height, 10);
+ }),
+ combine: (response: UseQueryResult[]) => {
+ const result = response.flatMap(data => data.data?.results || []);
+ return result;
+ },
+ };
+ }, [uniqueBtcBlocks, getQuery]);
+ const initialStxBlocks = useQueries(stxBlockQueries, queryClient);
+
+ // const latestBurnBlock = useMemo(() => btcBlocks[0], [btcBlocks]);
const btcBlocksMap = useBtcBlocksMap(btcBlocks);
- const latestBurnBlockStxBlocks = useSuspenseInfiniteQueryResult(
- useSuspenseBlocksByBurnBlock(latestBurnBlock.burn_block_height, 10, {}, 'blocks-page')
- );
+ // const latestBurnBlockStxBlocks = useSuspenseInfiniteQueryResult(
+ // useSuspenseBlocksByBurnBlock(latestBurnBlock.burn_block_height, undefined, {}, 'blocks-page')
+ // );
+ // const initialStxBlockHashes = useMemo(() => {
+ // return new Set([...latestBurnBlockStxBlocks.map(block => block.hash)]);
+ // }, [latestBurnBlockStxBlocks]);
const initialStxBlockHashes = useMemo(() => {
- return new Set([...latestBurnBlockStxBlocks.map(block => block.hash)]);
- }, [latestBurnBlockStxBlocks]);
+ return new Set(initialStxBlocks.map(block => block.hash));
+ }, [initialStxBlocks]);
+
+ const initialBlockList = useMemo(() => {
+ return generateBlockList(initialStxBlocks, btcBlocksMap);
+ }, [initialStxBlocks, btcBlocksMap]);
const refetchInitialBlockList = useRefetchInitialBlockList([
- [GET_BLOCKS_BY_BURN_BLOCK_QUERY_KEY],
[BURN_BLOCKS_QUERY_KEY],
+ [GET_BLOCKS_BY_BURN_BLOCK_QUERY_KEY],
]);
- const initialBlockList = useMemo(() => {
- const startOfBlockList = generateBlockList(latestBurnBlockStxBlocks, btcBlocksMap);
- const restOfBlockList = uniqueBtcBlocks.slice(1).map(block => ({
- btcBlock: {
- type: 'btc_block',
- height: block.burn_block_height,
- hash: block.burn_block_hash,
- timestamp: block.burn_block_time,
- txsCount: block.stacks_blocks.length,
- blockCount: block.stacks_blocks.length,
- avgBlockTime: block.avg_block_time,
- } as BlockListBtcBlock,
- stxBlocks: [],
- }));
- return [...startOfBlockList, ...restOfBlockList];
- }, [latestBurnBlockStxBlocks, btcBlocksMap, uniqueBtcBlocks]);
+ // const initialBlockList = useMemo(() => {
+ // const startOfBlockList = generateBlockList(latestBurnBlockStxBlocks, btcBlocksMap);
+ // const restOfBlockList = uniqueBtcBlocks.slice(1).map(block => ({
+ // btcBlock: {
+ // type: 'btc_block',
+ // height: block.burn_block_height,
+ // hash: block.burn_block_hash,
+ // timestamp: block.burn_block_time,
+ // txsCount: block.stacks_blocks.length,
+ // blockCount: block.stacks_blocks.length,
+ // avgBlockTime: block.avg_block_time,
+ // } as BlockListBtcBlock,
+ // stxBlocks: [],
+ // }));
+ // return [...startOfBlockList, ...restOfBlockList];
+ // }, [latestBurnBlockStxBlocks, btcBlocksMap, uniqueBtcBlocks]);
return {
initialStxBlockHashes,
diff --git a/src/app/_components/BlockList/data/useHomePageBlockList.ts b/src/app/_components/BlockList/data/useHomePageBlockList.ts
index cc9a7c754..1939b8486 100644
--- a/src/app/_components/BlockList/data/useHomePageBlockList.ts
+++ b/src/app/_components/BlockList/data/useHomePageBlockList.ts
@@ -1,9 +1,9 @@
import { useCallback, useEffect, useRef, useState } from 'react';
+import { useFetchMultipleBurnBlocks } from '../../../../common/queries/useBurnBlock';
import { useBlockListContext } from '../BlockListContext';
import { useBlockListWebSocket } from '../Sockets/useBlockListWebSocket';
import { BlockListData, generateBlockList, mergeBlockLists, waitForFadeAnimation } from '../utils';
-import { useBlockListBtcBlocks } from './useBlockListBtcBlocks';
import { useHomePageInitialBlockList } from './useHomePageInitialBlockList';
import { generateBtcBlocksMap } from './utils';
@@ -35,14 +35,17 @@ export function useHomePageBlockList(btcBlockLimit: number = 3) {
clearLatestStxBlocks: clearLatestStxBlocksFromWebSocket,
} = useBlockListWebSocket(liveUpdates, initialStxBlocksHashes);
- const { btcBlocks: latestBtcBlocks } = useBlockListBtcBlocks(latestStxBlocksFromWebSocket);
+ const fetchBurnBlocks = useFetchMultipleBurnBlocks();
const showLatestStxBlocksFromWebSocket = useCallback(() => {
setBlockListLoading(true);
- waitForFadeAnimation(() => {
+ waitForFadeAnimation(async () => {
+ const btcBlocks = await fetchBurnBlocks(
+ latestStxBlocksFromWebSocket.map(block => block.burn_block_hash)
+ );
const websocketBlockList = generateBlockList(
latestStxBlocksFromWebSocket,
- generateBtcBlocksMap(latestBtcBlocks)
+ generateBtcBlocksMap(btcBlocks)
);
updateBlockListManually(websocketBlockList);
clearLatestStxBlocksFromWebSocket();
@@ -53,7 +56,7 @@ export function useHomePageBlockList(btcBlockLimit: number = 3) {
updateBlockListManually,
setBlockListLoading,
clearLatestStxBlocksFromWebSocket,
- latestBtcBlocks,
+ fetchBurnBlocks,
]);
const updateBlockListWithQuery = useCallback(
diff --git a/src/app/_components/BlockList/data/useHomePageInitialBlockList.ts b/src/app/_components/BlockList/data/useHomePageInitialBlockList.ts
index 6bd9d2cb9..4dc8edce2 100644
--- a/src/app/_components/BlockList/data/useHomePageInitialBlockList.ts
+++ b/src/app/_components/BlockList/data/useHomePageInitialBlockList.ts
@@ -27,13 +27,13 @@ export function useHomePageInitialBlockList(blockListLimit: number = 3) {
const btcBlocksMap = useBtcBlocksMap(btcBlocks);
const latestBurnBlockStxBlocks = useSuspenseInfiniteQueryResult(
- useSuspenseBlocksByBurnBlock(latestBurnBlock.burn_block_height)
+ useSuspenseBlocksByBurnBlock(latestBurnBlock.burn_block_height, 3)
);
const secondLatestBurnBlockStxBlocks = useSuspenseInfiniteQueryResult(
- useSuspenseBlocksByBurnBlock(secondLatestBurnBlock.burn_block_height)
+ useSuspenseBlocksByBurnBlock(secondLatestBurnBlock.burn_block_height, 3)
);
const thirdLatestBurnBlockStxBlocks = useSuspenseInfiniteQueryResult(
- useSuspenseBlocksByBurnBlock(thirdLatestBurnBlock.burn_block_height)
+ useSuspenseBlocksByBurnBlock(thirdLatestBurnBlock.burn_block_height, 3)
);
const refetchInitialBlockList = useRefetchInitialBlockList([
diff --git a/src/app/_components/BlockList/types.ts b/src/app/_components/BlockList/types.ts
index a1e219e63..40c06881b 100644
--- a/src/app/_components/BlockList/types.ts
+++ b/src/app/_components/BlockList/types.ts
@@ -1,4 +1,4 @@
-import { Block, BurnBlock } from '@stacks/blockchain-api-client';
+import { Block } from '@stacks/blockchain-api-client';
export type EnhancedBlock = Block & { destroy?: boolean; animate?: boolean };
@@ -28,9 +28,9 @@ export interface BlockListBtcBlock {
height: number | string;
hash: string;
timestamp: number;
- txsCount: number | undefined;
- blockCount: number | undefined;
- avgBlockTime: number | undefined;
+ txsCount: number;
+ blockCount: number;
+ avgBlockTime: number;
}
export interface BlockListStxBlock {
diff --git a/src/app/_components/BlockList/utils.ts b/src/app/_components/BlockList/utils.ts
index f33b0fb49..a14de88e0 100644
--- a/src/app/_components/BlockList/utils.ts
+++ b/src/app/_components/BlockList/utils.ts
@@ -41,9 +41,9 @@ export function createBlockListBtcBlockFromStxBlock(
height: stxBlock.burn_block_height,
hash: stxBlock.burn_block_hash,
timestamp: stxBlock.burn_block_time,
- txsCount: undefined,
- blockCount: undefined,
- avgBlockTime: undefined,
+ txsCount: 0,
+ blockCount: 0,
+ avgBlockTime: 0,
};
}
@@ -119,3 +119,20 @@ export function mergeBlockLists(newblockList: BlockListData[], initialBlockList:
return [...newblockList, ...initialBlockList];
}
}
+
+// export function mergeStxBlockLists(newblockList: BlockListStxBlock[], initialBlockList: BlockListStxBlock[]) {
+// if (newblockList.length === 0) return initialBlockList;
+// const earliestStxBlock = newblockList[newblockList.length - 1];
+// const latestStxBlock = initialBlockList[0];
+// if (earliestStxBlock.hash === latestStxBlock.hash) {
+// const btcBlock = earliestStxBlock.btcBlock || latestStxBlock.btcBlock;
+// const stxBlocks = [...earliestStxBlock.stxBlocks, ...latestStxBlock.stxBlocks];
+// return [
+// ...newblockList.slice(0, newblockList.length - 1),
+// { btcBlock, stxBlocks },
+// ...initialBlockList.slice(1),
+// ];
+// } else {
+// return [...newblockList, ...initialBlockList];
+// }
+// }
diff --git a/src/app/btcblock/[hash]/PageClient.tsx b/src/app/btcblock/[hash]/PageClient.tsx
index 14f51675a..7163db104 100644
--- a/src/app/btcblock/[hash]/PageClient.tsx
+++ b/src/app/btcblock/[hash]/PageClient.tsx
@@ -4,7 +4,10 @@ import { NakamotoBlock } from '@stacks/blockchain-api-client';
import { BurnBlockGroupGrid } from '../../../app/_components/BlockList/Grouped/BlockListGrouped';
import { ScrollableBox } from '../../../app/_components/BlockList/ScrollableDiv';
-import { createBlockListStxBlock } from '../../../app/_components/BlockList/utils';
+import {
+ createBlockListBtcBlock,
+ createBlockListStxBlock,
+} from '../../../app/_components/BlockList/utils';
import { NavBlock, NavDirection } from '../../../app/btcblock/[hash]/NavBlock';
import { ListFooter } from '../../../common/components/ListFooter';
import { Section } from '../../../common/components/Section';
@@ -50,9 +53,11 @@ export default function BitcoinBlockPage({ params: { hash } }: any) {
>> {
const api = useApi();
return useInfiniteQuery({
- queryKey: [GET_BLOCKS_BY_BURN_BLOCK_QUERY_KEY, heightOrHash],
+ queryKey: [GET_BLOCKS_BY_BURN_BLOCK_QUERY_KEY, heightOrHash, queryKeyExtension],
queryFn: ({ pageParam }: { pageParam: number }) =>
api.blocksApi.getBlocksByBurnBlock({
heightOrHash,
@@ -51,7 +52,7 @@ export function useBlocksByBurnBlock(
offset: pageParam,
}),
getNextPageParam,
- initialPageParam: 0,
+ initialPageParam: options.offset ?? 0,
staleTime: heightOrHash === 'latest' ? ONE_SECOND * 5 : TWO_MINUTES,
...options,
});
@@ -73,8 +74,9 @@ export function useSuspenseBlocksByBurnBlock(
offset: pageParam,
}),
getNextPageParam,
- initialPageParam: 0,
+ initialPageParam: options.offset ?? 0,
staleTime: heightOrHash === 'latest' ? ONE_SECOND * 5 : TWO_MINUTES,
+ enabled: false,
...options,
});
}
diff --git a/src/common/queries/useBurnBlock.ts b/src/common/queries/useBurnBlock.ts
index f49bf19df..a15a1e0cb 100644
--- a/src/common/queries/useBurnBlock.ts
+++ b/src/common/queries/useBurnBlock.ts
@@ -2,6 +2,7 @@ import {
UseQueryResult,
UseSuspenseQueryResult,
useQuery,
+ useQueryClient,
useSuspenseQuery,
} from '@tanstack/react-query';
@@ -11,6 +12,43 @@ import { useApi } from '../api/useApi';
export const BURN_BLOCKS_QUERY_KEY = 'burnBlocks';
+export function useFetchBurnBlock(): (heightOrHash: string | number) => Promise {
+ const api = useApi();
+ const queryClient = useQueryClient();
+
+ return async (heightOrHash: string | number) => {
+ const queryKey = [BURN_BLOCKS_QUERY_KEY, heightOrHash];
+
+ const cachedData = queryClient.getQueryData(queryKey);
+ if (cachedData) {
+ return cachedData;
+ }
+
+ // Fetch the data and update the cache
+ const fetchBurnBlock = async (): Promise => {
+ return api.burnBlocksApi.getBurnBlock({ heightOrHash });
+ };
+
+ return queryClient.fetchQuery({
+ queryKey,
+ queryFn: fetchBurnBlock,
+ staleTime: Infinity,
+ });
+ };
+}
+
+export function useFetchMultipleBurnBlocks(): (
+ heightOrHashes: (string | number)[]
+) => Promise {
+ const fetchBurnBlock = useFetchBurnBlock();
+
+ return async (heightOrHashes: (string | number)[]) => {
+ const burnBlockPromises = heightOrHashes.map(heightOrHash => fetchBurnBlock(heightOrHash));
+
+ return await Promise.all(burnBlockPromises);
+ };
+}
+
export function useGetBurnBlockQuery() {
const api = useApi();
return (heightOrHash: string | number) => ({