diff --git a/src/app/_components/BlockList/BlocksPage/BlocksPageBlockList.tsx b/src/app/_components/BlockList/BlocksPage/BlocksPageBlockList.tsx index 62e9196b5..509ecfada 100644 --- a/src/app/_components/BlockList/BlocksPage/BlocksPageBlockList.tsx +++ b/src/app/_components/BlockList/BlocksPage/BlocksPageBlockList.tsx @@ -1,6 +1,6 @@ 'use client'; -import { useCallback, useRef } from 'react'; +import { ReactNode, useCallback, useRef } from 'react'; import { Section } from '../../../../common/components/Section'; import { Stack } from '../../../../ui/Stack'; @@ -11,41 +11,66 @@ import { Controls } from '../Controls'; import { BlocksPageBlockListGrouped } from './BlocksPageBlockListGrouped'; import { BlocksPageBlockListUngrouped } from './BlocksPageBlockListUngrouped'; +export function BlocksPageBlockListLayout({ children }: { children: ReactNode }) { + return
{children}
; +} + +export function BlocksPageControlsLayout({ + liveUpdates, + children, +}: { + liveUpdates?: boolean; + children: ReactNode; +}) { + return ( + + {children} + + ); +} + function BlocksPageBlockListBase() { const { groupedByBtc, setGroupedByBtc, liveUpdates, setLiveUpdates } = useBlockListContext(); const lastClickTimeRef = useRef(0); - const toggleLiveUpdates = useCallback(() => { - const now = Date.now(); - if (now - lastClickTimeRef.current > 2000) { - lastClickTimeRef.current = now; - setLiveUpdates(!liveUpdates); - } - }, [liveUpdates, setLiveUpdates]); + const toggleLiveUpdates = useCallback( + (immediately?: boolean) => { + const now = Date.now(); + if (immediately || now - lastClickTimeRef.current > 2000) { + lastClickTimeRef.current = now; + setLiveUpdates(!liveUpdates); + } + }, + [liveUpdates, setLiveUpdates] + ); return ( -
- + + { setGroupedByBtc(!groupedByBtc); + if (liveUpdates) { + toggleLiveUpdates(true); + } }, isChecked: groupedByBtc, }} liveUpdates={{ - onChange: toggleLiveUpdates, + onChange: () => toggleLiveUpdates(), isChecked: liveUpdates, }} horizontal={true} /> - + {groupedByBtc ? : } -
+ ); } diff --git a/src/app/_components/BlockList/BlocksPage/BlocksPageBlockListGrouped.tsx b/src/app/_components/BlockList/BlocksPage/BlocksPageBlockListGrouped.tsx index 1856a83db..a71cc7ffa 100644 --- a/src/app/_components/BlockList/BlocksPage/BlocksPageBlockListGrouped.tsx +++ b/src/app/_components/BlockList/BlocksPage/BlocksPageBlockListGrouped.tsx @@ -1,6 +1,6 @@ 'use client'; -import { Suspense } from 'react'; +import { Suspense, useMemo } from 'react'; import { ListFooter } from '../../../../common/components/ListFooter'; import { Section } from '../../../../common/components/Section'; @@ -17,22 +17,26 @@ function BlocksPageBlockListGroupedBase() { const { liveUpdates } = useBlockListContext(); const { blockList, updateBlockList, isFetchingNextPage, hasNextPage, fetchNextPage } = useBlocksPageBlockListGrouped(); + const latestBlock = useMemo(() => blockList?.[0], [blockList]); return ( <> - {!liveUpdates && } + {!liveUpdates && } - + - {!liveUpdates && ( - - )} + ); diff --git a/src/app/_components/BlockList/BlocksPage/BlocksPageBlockListUngrouped.tsx b/src/app/_components/BlockList/BlocksPage/BlocksPageBlockListUngrouped.tsx index f9a7a1d72..62e385044 100644 --- a/src/app/_components/BlockList/BlocksPage/BlocksPageBlockListUngrouped.tsx +++ b/src/app/_components/BlockList/BlocksPage/BlocksPageBlockListUngrouped.tsx @@ -1,6 +1,6 @@ 'use client'; -import { Suspense } from 'react'; +import { Suspense, useMemo } from 'react'; import { ListFooter } from '../../../../common/components/ListFooter'; import { Section } from '../../../../common/components/Section'; @@ -17,20 +17,19 @@ function BlocksPageBlockListUngroupedBase() { const { blockList, hasNextPage, fetchNextPage, isFetchingNextPage, updateBlockList } = useBlocksPageBlockListUngrouped(); + const latestBlock = useMemo(() => blockList?.[0], [blockList]); return ( - {!liveUpdates && } - + {!liveUpdates && } + - {!liveUpdates && ( - - )} + ); diff --git a/src/app/_components/BlockList/Controls.tsx b/src/app/_components/BlockList/Controls.tsx index f94c4dea7..229129d25 100644 --- a/src/app/_components/BlockList/Controls.tsx +++ b/src/app/_components/BlockList/Controls.tsx @@ -21,7 +21,7 @@ export function ControlsLayout({ children: ReactNode; } & FlexProps) { return ( - + {children} ); @@ -35,6 +35,7 @@ export function Controls({ groupByBtc, liveUpdates, horizontal, ...rest }: Contr - #{stxBlock.height} + #{stxBlock.height} isLast: {isLast?.toString()} - ∙} gap={1} whiteSpace="nowrap" gridColumn="3 / 4"> + ∙} + gap={1} + whiteSpace="nowrap" + gridColumn="3 / 4" + justifyContent="flex-end" + > {truncateMiddle(stxBlock.hash, 3)} @@ -176,7 +182,7 @@ export function BurnBlockGroupGrid({ stxBlock={stxBlock} minimized={minimized} isFirst={i === 0} - isLast={i === stxBlocks.length - 1 && numStxBlocksNotDisplayed === 0} + isLast={i === stxBlocks.length - 1 && numStxBlocksNotDisplayed <= 0} /> {i < stxBlocks.length - 1 && ( @@ -206,35 +212,52 @@ function BitcoinHeader({ px={PADDING} borderBottom={minimized ? '1px solid var(--stacks-colors-borderPrimary)' : 'none'} flexWrap={'wrap'} + // height={5} > - + {isFirst ? ( - - {btcBlock.height} - + + + Next Bitcoin block + + ) : ( - - #{btcBlock.height} - + + + #{btcBlock.height} + + )} - ∙} gap={1} flexWrap={'wrap'}> + {isFirst ? ( - - {truncateMiddle(btcBlock.hash, 6)} - + + + + Unconfirmed + + ) : ( - + ∙} gap={1} flexWrap={'wrap'}> + + {truncateMiddle(btcBlock.hash, 6)} + + + )} - - + ); } @@ -266,12 +289,14 @@ 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 unaccountedTxs = useMemo( @@ -293,7 +318,14 @@ export function BurnBlockGroup({ ? btcBlock.blockCount + unaccountedStxBlocks : btcBlock.blockCount : undefined; - const numStxBlocksNotDisplayed = blocksCount ? blocksCount - (stxBlocksLimit || 0) : 0; + const numStxBlocksNotDisplayed = + onlyShowStxBlocksForFirstBtcBlock && !isFirst + ? blocksCount + ? blocksCount + : 0 + : blocksCount + ? blocksCount - (stxBlocksLimit || 0) + : 0; const displayedStxBlocks = useMemo( () => (stxBlocksLimit ? stxBlocks.slice(0, stxBlocksLimit) : stxBlocks), [stxBlocks, stxBlocksLimit] @@ -301,13 +333,15 @@ export function BurnBlockGroup({ return ( - - - + {onlyShowStxBlocksForFirstBtcBlock && !isFirst ? null : ( + + + + )} {numStxBlocksNotDisplayed > 0 ? ( @@ -349,6 +385,7 @@ 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 c5efa05ae..f20e955c8 100644 --- a/src/app/_components/BlockList/Grouped/skeleton.tsx +++ b/src/app/_components/BlockList/Grouped/skeleton.tsx @@ -1,7 +1,6 @@ import { useColorModeValue } from '@chakra-ui/react'; import { Circle } from '../../../../common/components/Circle'; -import { Section } from '../../../../common/components/Section'; import { Box } from '../../../../ui/Box'; import { Button } from '../../../../ui/Button'; import { Flex } from '../../../../ui/Flex'; @@ -9,8 +8,13 @@ import { SkeletonItem } from '../../../../ui/SkeletonItem'; import { SkeletonText } from '../../../../ui/SkeletonText'; import { Stack } from '../../../../ui/Stack'; import { Text } from '../../../../ui/Text'; +import { + BlocksPageBlockListLayout, + BlocksPageControlsLayout, +} from '../BlocksPage/BlocksPageBlockList'; import { BlocksPageHeaderLayout } from '../BlocksPage/BlocksPageHeaders'; import { ControlsLayout } from '../Controls'; +import { HomePageBlockListLayout, HomePageControlsLayout } from '../HomePage/HomePageBlockList'; import { BlockListRowSkeleton } from '../Ungrouped/skeleton'; import { UpdateBarLayout } from '../UpdateBar'; import { BurnBlockGroupGridLayout } from './BlockListGrouped'; @@ -194,12 +198,26 @@ export function BlockPageHeadersSkeleton() { ); } -function ControlsSkeleton({ horizontal }: { horizontal?: boolean }) { +function HomePageControlsSkeleton({ horizontal }: { horizontal?: boolean }) { return ( - - - - + + + + + + + + ); +} + +function BlocksPageControlsSkeleton({ horizontal }: { horizontal?: boolean }) { + return ( + + + + + + ); } @@ -216,20 +234,20 @@ export function UpdateBarSkeleton() { export function BlocksPageBlockListSkeleton() { return ( -
- + + -
+ ); } export function HomePageBlockListSkeleton() { return ( -
- + + -
+ ); } diff --git a/src/app/_components/BlockList/HomePage/HomePageBlockList.tsx b/src/app/_components/BlockList/HomePage/HomePageBlockList.tsx index ee702b209..cb3848a43 100644 --- a/src/app/_components/BlockList/HomePage/HomePageBlockList.tsx +++ b/src/app/_components/BlockList/HomePage/HomePageBlockList.tsx @@ -1,7 +1,7 @@ 'use client'; import dynamic from 'next/dynamic'; -import { useCallback, useRef } from 'react'; +import { ReactNode, useCallback, useRef } from 'react'; import { Section } from '../../../../common/components/Section'; import { Stack } from '../../../../ui/Stack'; @@ -29,40 +29,67 @@ const HomePageBlockListUngroupedDynamic = dynamic( } ); +export function HomePageBlockListLayout({ children }: { children: ReactNode }) { + return ( +
+ {children} +
+ ); +} + +export function HomePageControlsLayout({ + liveUpdates, + children, +}: { + liveUpdates?: boolean; + children: ReactNode; +}) { + return ( + + {children} + + ); +} + function HomePageBlockListBase() { const { groupedByBtc, setGroupedByBtc, liveUpdates, setLiveUpdates } = useBlockListContext(); const lastClickTimeRef = useRef(0); - const toggleLiveUpdates = useCallback(() => { - const now = Date.now(); - if (now - lastClickTimeRef.current > 2000) { - lastClickTimeRef.current = now; - setLiveUpdates(!liveUpdates); - } - }, [liveUpdates, setLiveUpdates]); + const toggleLiveUpdates = useCallback( + (immediately?: boolean) => { + const now = Date.now(); + if (immediately || now - lastClickTimeRef.current > 2000) { + lastClickTimeRef.current = now; + setLiveUpdates(!liveUpdates); + } + }, + [liveUpdates, setLiveUpdates] + ); return ( -
- + + Recent Blocks { setGroupedByBtc(!groupedByBtc); + if (liveUpdates) { + toggleLiveUpdates(true); + } }, isChecked: groupedByBtc, }} liveUpdates={{ - onChange: toggleLiveUpdates, + onChange: () => toggleLiveUpdates(), isChecked: liveUpdates, }} padding={0} - gap={3} border="none" /> - + {groupedByBtc ? : } -
+ ); } diff --git a/src/app/_components/BlockList/HomePage/HomePageBlockListGrouped.tsx b/src/app/_components/BlockList/HomePage/HomePageBlockListGrouped.tsx index 138d822bd..e7b3c96af 100644 --- a/src/app/_components/BlockList/HomePage/HomePageBlockListGrouped.tsx +++ b/src/app/_components/BlockList/HomePage/HomePageBlockListGrouped.tsx @@ -1,6 +1,6 @@ 'use client'; -import { Suspense } from 'react'; +import { Suspense, useMemo } from 'react'; import { ListFooter } from '../../../../common/components/ListFooter'; import { Flex } from '../../../../ui/Flex'; @@ -13,12 +13,13 @@ import { useHomePageBlockList } from '../data/useHomePageBlockList'; function HomePageBlockListGroupedBase() { const { liveUpdates } = useBlockListContext(); const { blockList, updateBlockList } = useHomePageBlockList(); + const latestBlock = useMemo(() => blockList?.[0], [blockList]); return ( <> - {!liveUpdates && } + {!liveUpdates && } - {!liveUpdates && } + ); diff --git a/src/app/_components/BlockList/HomePage/HomePageBlockListUngrouped.tsx b/src/app/_components/BlockList/HomePage/HomePageBlockListUngrouped.tsx index ee76f3855..0530623cd 100644 --- a/src/app/_components/BlockList/HomePage/HomePageBlockListUngrouped.tsx +++ b/src/app/_components/BlockList/HomePage/HomePageBlockListUngrouped.tsx @@ -1,6 +1,6 @@ 'use client'; -import { Suspense } from 'react'; +import { Suspense, useMemo } from 'react'; import { ListFooter } from '../../../../common/components/ListFooter'; import { Flex } from '../../../../ui/Flex'; @@ -13,12 +13,13 @@ import { useHomePageBlockList } from '../data/useHomePageBlockList'; function HomePageBlockListUngroupedBase() { const { liveUpdates } = useBlockListContext(); const { blockList, updateBlockList } = useHomePageBlockList(); + const latestBlock = useMemo(() => blockList?.[0], [blockList]); return ( <> - {!liveUpdates && } + {!liveUpdates && } - {!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 2883b0df4..b9b437d71 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 @@ -21,7 +21,7 @@ exports[`BlockListWithControls renders correctly 1`] = ` class="css-1gdm77d" >