From 6f35e27fa77388fc5d660a585050a51c9f2d130e Mon Sep 17 00:00:00 2001 From: Nicholas Barnett Date: Tue, 23 Jul 2024 17:02:09 -0500 Subject: [PATCH] chore: addr block list feedback. purple circles + load functionality + last block --- src/app/_components/BlockList/BlockCount.tsx | 75 ++++--- .../BlocksPage/BlocksPageBlockListGrouped.tsx | 7 +- .../BlocksPageBlockListUngrouped.tsx | 2 +- .../BlocksPage/BlocksPageHeaders.tsx | 7 +- .../BlockList/Grouped/BlockListGrouped.tsx | 183 ++++++++++++------ .../BlockList/Grouped/skeleton.tsx | 4 +- .../HomePage/HomePageBlockListUngrouped.tsx | 2 +- src/app/_components/BlockList/LineAndNode.tsx | 10 +- .../Ungrouped/BlockListUngrouped.tsx | 154 +++++++++++---- .../BlockList/data/useBlockListBtcBlocks.ts | 45 ----- .../data/useBlocksPageBlockListGrouped.tsx | 15 +- .../data/useBlocksPageBlockListUngrouped.ts | 13 +- .../useBlocksPageGroupedInitialBlockList.ts | 76 +++++--- .../BlockList/data/useHomePageBlockList.ts | 13 +- .../data/useHomePageInitialBlockList.ts | 6 +- src/app/_components/BlockList/types.ts | 8 +- src/app/_components/BlockList/utils.ts | 17 ++ src/app/btcblock/[hash]/PageClient.tsx | 9 +- src/common/queries/useBlocksByBurnBlock.ts | 10 +- src/common/queries/useBurnBlock.ts | 38 ++++ 20 files changed, 436 insertions(+), 258 deletions(-) delete mode 100644 src/app/_components/BlockList/data/useBlockListBtcBlocks.ts 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} +