Skip to content

Commit

Permalink
feat: nakamoto homepage blocks view
Browse files Browse the repository at this point in the history
  • Loading branch information
He1DAr committed Mar 25, 2024
1 parent 4200b51 commit aa8d5e0
Show file tree
Hide file tree
Showing 3 changed files with 243 additions and 4 deletions.
3 changes: 2 additions & 1 deletion src/app/PageClient.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { useGlobalContext } from '../common/context/useAppContext';
import { TxListTabs } from '../features/txs-list/tabs/TxListTabs';
import { Grid } from '../ui/Grid';
import { SkeletonBlockList } from './_components/BlockList/SkeletonBlockList';
import { UpdatedBlocksList } from './_components/BlockList/UpdatedBlockList';

Check warning on line 10 in src/app/PageClient.tsx

View check run for this annotation

Codecov / codecov/patch

src/app/PageClient.tsx#L10

Added line #L10 was not covered by tests
import { PageTitle } from './_components/PageTitle';
import { Stats } from './_components/Stats/Stats';

Expand Down Expand Up @@ -40,7 +41,7 @@ export default function Home() {
<TxListTabs limit={DEFAULT_LIST_LIMIT_SMALL} />

{activeNetworkKey.indexOf('naka') !== -1 ? (
<NonPaginatedBlockListLayoutA />
<UpdatedBlocksList limit={DEFAULT_BLOCKS_LIST_LIMIT} />
) : (
<BlocksList limit={DEFAULT_BLOCKS_LIST_LIMIT} />
)}
Expand Down
7 changes: 4 additions & 3 deletions src/app/_components/BlockList/LayoutA/BurnBlock.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,19 @@ import { Timestamp } from '../../../../common/components/Timestamp';
import { useGlobalContext } from '../../../../common/context/useAppContext';
import { truncateMiddle } from '../../../../common/utils/utils';
import { Box } from '../../../../ui/Box';
import { Flex } from '../../../../ui/Flex';
import { Flex, FlexProps } from '../../../../ui/Flex';
import { HStack } from '../../../../ui/HStack';
import { Icon } from '../../../../ui/Icon';
import { Text } from '../../../../ui/Text';
import { TextLink } from '../../../../ui/TextLink';
import { BitcoinIcon } from '../../../../ui/icons';

interface ListItemProps {
interface ListItemProps extends FlexProps {
height: number | string;
hash: string;
timestamp?: number;
}
export const BurnBlock = memo(function ({ timestamp, height, hash }: ListItemProps) {
export const BurnBlock = memo(function ({ timestamp, height, hash, ...flexProps }: ListItemProps) {
const { btcBlockBaseUrl } = useGlobalContext().activeNetwork;
const bgColor = useColorModeValue('slate.150', 'slate.900');
const textColor = useColorModeValue('slate.700', 'slate.500');
Expand All @@ -36,6 +36,7 @@ export const BurnBlock = memo(function ({ timestamp, height, hash }: ListItemPro
mr={'-8'}
ml={'-10'}
color={textColor}
{...flexProps}
>
<HStack gap={1.5}>
<Icon
Expand Down
237 changes: 237 additions & 0 deletions src/app/_components/BlockList/UpdatedBlockList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,237 @@
'use client';

import { useColorModeValue } from '@chakra-ui/react';
import { useQueryClient } from '@tanstack/react-query';

Check warning on line 4 in src/app/_components/BlockList/UpdatedBlockList.tsx

View check run for this annotation

Codecov / codecov/patch

src/app/_components/BlockList/UpdatedBlockList.tsx#L3-L4

Added lines #L3 - L4 were not covered by tests
import pluralize from 'pluralize';
import React, { FC, useCallback, useEffect, useMemo, useState } from 'react';

Check warning on line 6 in src/app/_components/BlockList/UpdatedBlockList.tsx

View check run for this annotation

Codecov / codecov/patch

src/app/_components/BlockList/UpdatedBlockList.tsx#L6

Added line #L6 was not covered by tests

import { connectWebSocketClient } from '@stacks/blockchain-api-client';

Check warning on line 8 in src/app/_components/BlockList/UpdatedBlockList.tsx

View check run for this annotation

Codecov / codecov/patch

src/app/_components/BlockList/UpdatedBlockList.tsx#L8

Added line #L8 was not covered by tests
import { Block } from '@stacks/stacks-blockchain-api-types';

import { BtcStxBlockLinks } from '../../../common/components/BtcStxBlockLinks';
import { ListFooter } from '../../../common/components/ListFooter';
import { Section } from '../../../common/components/Section';

Check warning on line 13 in src/app/_components/BlockList/UpdatedBlockList.tsx

View check run for this annotation

Codecov / codecov/patch

src/app/_components/BlockList/UpdatedBlockList.tsx#L12-L13

Added lines #L12 - L13 were not covered by tests
import { TwoColsListItem } from '../../../common/components/TwoColumnsListItem';
import { SkeletonBlockList } from '../../../common/components/loaders/skeleton-text';
import { DEFAULT_LIST_LIMIT } from '../../../common/constants/constants';
import { useGlobalContext } from '../../../common/context/useAppContext';
import { useSuspenseInfiniteQueryResult } from '../../../common/hooks/useInfiniteQueryResult';
import { useSuspenseBlockListInfinite } from '../../../common/queries/useBlockListInfinite';

Check warning on line 19 in src/app/_components/BlockList/UpdatedBlockList.tsx

View check run for this annotation

Codecov / codecov/patch

src/app/_components/BlockList/UpdatedBlockList.tsx#L15-L19

Added lines #L15 - L19 were not covered by tests
import { addSepBetweenStrings, toRelativeTime, truncateMiddle } from '../../../common/utils/utils';
import { Accordion } from '../../../ui/Accordion';
import { Box } from '../../../ui/Box';
import { Collapse } from '../../../ui/Collapse';

Check warning on line 23 in src/app/_components/BlockList/UpdatedBlockList.tsx

View check run for this annotation

Codecov / codecov/patch

src/app/_components/BlockList/UpdatedBlockList.tsx#L22-L23

Added lines #L22 - L23 were not covered by tests
import { Flex, FlexProps } from '../../../ui/Flex';
import { FormControl } from '../../../ui/FormControl';
import { FormLabel } from '../../../ui/FormLabel';
import { Icon } from '../../../ui/Icon';
import { Stack } from '../../../ui/Stack';
import { Switch } from '../../../ui/Switch';
import { StxIcon } from '../../../ui/icons';

Check warning on line 30 in src/app/_components/BlockList/UpdatedBlockList.tsx

View check run for this annotation

Codecov / codecov/patch

src/app/_components/BlockList/UpdatedBlockList.tsx#L25-L30

Added lines #L25 - L30 were not covered by tests
import { Caption } from '../../../ui/typography';
import { ExplorerErrorBoundary } from '../ErrorBoundary';
import { BurnBlock } from './LayoutA/BurnBlock';
import { StxBlock } from './LayoutA/StxBlock';

Check warning on line 34 in src/app/_components/BlockList/UpdatedBlockList.tsx

View check run for this annotation

Codecov / codecov/patch

src/app/_components/BlockList/UpdatedBlockList.tsx#L32-L34

Added lines #L32 - L34 were not covered by tests
import { EnhancedBlock } from './types';

export const animationDuration = 0.8;

Check warning on line 37 in src/app/_components/BlockList/UpdatedBlockList.tsx

View check run for this annotation

Codecov / codecov/patch

src/app/_components/BlockList/UpdatedBlockList.tsx#L37

Added line #L37 was not covered by tests

export const BlockListItem: React.FC<{ block: Block } & FlexProps> = React.memo(
({ block, ...rest }) => {

Check warning on line 40 in src/app/_components/BlockList/UpdatedBlockList.tsx

View check run for this annotation

Codecov / codecov/patch

src/app/_components/BlockList/UpdatedBlockList.tsx#L39-L40

Added lines #L39 - L40 were not covered by tests
return (
<>
<StxBlock
key={block.hash}
hash={block.hash}
height={block.height}
timestamp={block.burn_block_time}
txsCount={block.txs.length}
icon={<Icon as={StxIcon} size={2.5} color={'white'} />}
/>
<BurnBlock
mr={'-6'}
ml={'-6'}
key={block.hash}
hash={block.hash}
height={block.height}
timestamp={block.burn_block_time}
/>
</>
);
}
);

export const AnimatedBlockAndMicroblocksItem: FC<{

Check warning on line 64 in src/app/_components/BlockList/UpdatedBlockList.tsx

View check run for this annotation

Codecov / codecov/patch

src/app/_components/BlockList/UpdatedBlockList.tsx#L64

Added line #L64 was not covered by tests
block: EnhancedBlock;
onAnimationExit?: () => void;
}> = ({ block, onAnimationExit }) => {
const [show, setShow] = useState(!block.animate);
useEffect(() => {

Check warning on line 69 in src/app/_components/BlockList/UpdatedBlockList.tsx

View check run for this annotation

Codecov / codecov/patch

src/app/_components/BlockList/UpdatedBlockList.tsx#L67-L69

Added lines #L67 - L69 were not covered by tests
if (block.animate) {
setTimeout(() => {
setShow(true);

Check warning on line 72 in src/app/_components/BlockList/UpdatedBlockList.tsx

View check run for this annotation

Codecov / codecov/patch

src/app/_components/BlockList/UpdatedBlockList.tsx#L71-L72

Added lines #L71 - L72 were not covered by tests
}, 100);
}
}, [block.animate]);
useEffect(() => {

Check warning on line 76 in src/app/_components/BlockList/UpdatedBlockList.tsx

View check run for this annotation

Codecov / codecov/patch

src/app/_components/BlockList/UpdatedBlockList.tsx#L76

Added line #L76 was not covered by tests
if (block.destroy) {
setShow(false);

Check warning on line 78 in src/app/_components/BlockList/UpdatedBlockList.tsx

View check run for this annotation

Codecov / codecov/patch

src/app/_components/BlockList/UpdatedBlockList.tsx#L78

Added line #L78 was not covered by tests
}
}, [block.destroy]);

return (
<Collapse
in={show}
animateOpacity
transition={{
enter: { duration: animationDuration },
exit: { duration: animationDuration },
}}
onAnimationComplete={state => {

Check warning on line 90 in src/app/_components/BlockList/UpdatedBlockList.tsx

View check run for this annotation

Codecov / codecov/patch

src/app/_components/BlockList/UpdatedBlockList.tsx#L90

Added line #L90 was not covered by tests
if (state === 'exit') {
onAnimationExit?.();

Check warning on line 92 in src/app/_components/BlockList/UpdatedBlockList.tsx

View check run for this annotation

Codecov / codecov/patch

src/app/_components/BlockList/UpdatedBlockList.tsx#L92

Added line #L92 was not covered by tests
}
}}
data-testid={`block-item-${block.hash}`}
style={{
overflow: 'unset',
}}
>
<BlockAndMicroblocksItem block={block} />
</Collapse>
);
};

export const BlockAndMicroblocksItem: React.FC<{ block: Block }> = ({ block }) => {

Check warning on line 105 in src/app/_components/BlockList/UpdatedBlockList.tsx

View check run for this annotation

Codecov / codecov/patch

src/app/_components/BlockList/UpdatedBlockList.tsx#L105

Added line #L105 was not covered by tests
return <BlockListItem block={block} data-test={`block-${block.hash}`} />;
};

function UpdatedBlocksListBase({
limit,
}: {
limit?: number;
} & FlexProps) {
const [isLive, setIsLive] = React.useState(false);
const [initialBlocks, setInitialBlocks] = useState<EnhancedBlock[]>([]);
const [latestBlocks, setLatestBlocks] = useState<EnhancedBlock[]>([]);
const activeNetwork = useGlobalContext().activeNetwork;
const response = useSuspenseBlockListInfinite();
const { isFetchingNextPage, fetchNextPage, hasNextPage } = response;
const queryClient = useQueryClient();

Check warning on line 120 in src/app/_components/BlockList/UpdatedBlockList.tsx

View check run for this annotation

Codecov / codecov/patch

src/app/_components/BlockList/UpdatedBlockList.tsx#L113-L120

Added lines #L113 - L120 were not covered by tests

const blocks = useSuspenseInfiniteQueryResult<Block>(response, limit);

Check warning on line 122 in src/app/_components/BlockList/UpdatedBlockList.tsx

View check run for this annotation

Codecov / codecov/patch

src/app/_components/BlockList/UpdatedBlockList.tsx#L122

Added line #L122 was not covered by tests

const labelColor = useColorModeValue('slate.600', 'slate.400');

Check warning on line 124 in src/app/_components/BlockList/UpdatedBlockList.tsx

View check run for this annotation

Codecov / codecov/patch

src/app/_components/BlockList/UpdatedBlockList.tsx#L124

Added line #L124 was not covered by tests

useEffect(() => {
setInitialBlocks(blocks);

Check warning on line 127 in src/app/_components/BlockList/UpdatedBlockList.tsx

View check run for this annotation

Codecov / codecov/patch

src/app/_components/BlockList/UpdatedBlockList.tsx#L126-L127

Added lines #L126 - L127 were not covered by tests
}, [blocks]);

useEffect(() => {

Check warning on line 130 in src/app/_components/BlockList/UpdatedBlockList.tsx

View check run for this annotation

Codecov / codecov/patch

src/app/_components/BlockList/UpdatedBlockList.tsx#L130

Added line #L130 was not covered by tests
if (!isLive) return;
void queryClient.invalidateQueries({ queryKey: ['blockListInfinite'] });

Check warning on line 132 in src/app/_components/BlockList/UpdatedBlockList.tsx

View check run for this annotation

Codecov / codecov/patch

src/app/_components/BlockList/UpdatedBlockList.tsx#L132

Added line #L132 was not covered by tests
let sub: {
unsubscribe?: () => Promise<void>;
};
const subscribe = async () => {
const client = await connectWebSocketClient(activeNetwork.url.replace('https://', 'wss://'));
sub = await client.subscribeBlocks((block: any) => {
setLatestBlocks(prevLatestBlocks => [

Check warning on line 139 in src/app/_components/BlockList/UpdatedBlockList.tsx

View check run for this annotation

Codecov / codecov/patch

src/app/_components/BlockList/UpdatedBlockList.tsx#L136-L139

Added lines #L136 - L139 were not covered by tests
{ ...block, microblock_tx_count: {}, animate: true },
...prevLatestBlocks,
]);
});
};
void subscribe();
return () => {

Check warning on line 146 in src/app/_components/BlockList/UpdatedBlockList.tsx

View check run for this annotation

Codecov / codecov/patch

src/app/_components/BlockList/UpdatedBlockList.tsx#L145-L146

Added lines #L145 - L146 were not covered by tests
if (sub?.unsubscribe) {
void sub.unsubscribe();

Check warning on line 148 in src/app/_components/BlockList/UpdatedBlockList.tsx

View check run for this annotation

Codecov / codecov/patch

src/app/_components/BlockList/UpdatedBlockList.tsx#L148

Added line #L148 was not covered by tests
}
};
}, [activeNetwork.url, isLive, queryClient]);

const allBlocks = useMemo(() => {
return [...latestBlocks, ...initialBlocks]

Check warning on line 154 in src/app/_components/BlockList/UpdatedBlockList.tsx

View check run for this annotation

Codecov / codecov/patch

src/app/_components/BlockList/UpdatedBlockList.tsx#L153-L154

Added lines #L153 - L154 were not covered by tests
.sort((a, b) => (b.height || 0) - (a.height || 0))
.reduce((acc: EnhancedBlock[], block, index) => {

Check warning on line 156 in src/app/_components/BlockList/UpdatedBlockList.tsx

View check run for this annotation

Codecov / codecov/patch

src/app/_components/BlockList/UpdatedBlockList.tsx#L156

Added line #L156 was not covered by tests
if (!acc.some(b => b.height === block.height)) {
acc.push({ ...block, destroy: index >= (limit || DEFAULT_LIST_LIMIT) });
}
return acc;

Check warning on line 160 in src/app/_components/BlockList/UpdatedBlockList.tsx

View check run for this annotation

Codecov / codecov/patch

src/app/_components/BlockList/UpdatedBlockList.tsx#L160

Added line #L160 was not covered by tests
}, []);
}, [initialBlocks, latestBlocks, limit]);

const removeOldBlock = useCallback((block: EnhancedBlock) => {
setInitialBlocks(prevBlocks => prevBlocks.filter(b => b.height !== block.height));
setLatestBlocks(prevBlocks => prevBlocks.filter(b => b.height !== block.height));

Check warning on line 166 in src/app/_components/BlockList/UpdatedBlockList.tsx

View check run for this annotation

Codecov / codecov/patch

src/app/_components/BlockList/UpdatedBlockList.tsx#L164-L166

Added lines #L164 - L166 were not covered by tests
}, []);

if (!allBlocks?.length) return <SkeletonBlockList />;

return (
<Section
title="Recent Blocks"
gridColumnStart={['1', '1', '1', '2']}
gridColumnEnd={['2', '2', '2', '3']}
minWidth={0}
flexGrow={0}
flexShrink={1}
topRight={
<FormControl display="flex" alignItems="center">
<FormLabel htmlFor="blocks-live-view-switch" mb="0" color={labelColor}>
live view
</FormLabel>
<Switch
id="blocks-live-view-switch"
isChecked={isLive}
onChange={() => setIsLive(!isLive)}

Check warning on line 187 in src/app/_components/BlockList/UpdatedBlockList.tsx

View check run for this annotation

Codecov / codecov/patch

src/app/_components/BlockList/UpdatedBlockList.tsx#L187

Added line #L187 was not covered by tests
/>
</FormControl>
}
>
<Stack pb={6} gap={5}>
<Box>
{allBlocks?.map(block =>
isLive ? (
<AnimatedBlockAndMicroblocksItem

Check warning on line 196 in src/app/_components/BlockList/UpdatedBlockList.tsx

View check run for this annotation

Codecov / codecov/patch

src/app/_components/BlockList/UpdatedBlockList.tsx#L196

Added line #L196 was not covered by tests
block={block}
key={block.hash}
onAnimationExit={() => removeOldBlock(block)}

Check warning on line 199 in src/app/_components/BlockList/UpdatedBlockList.tsx

View check run for this annotation

Codecov / codecov/patch

src/app/_components/BlockList/UpdatedBlockList.tsx#L199

Added line #L199 was not covered by tests
/>
) : (
<BlockAndMicroblocksItem block={block} key={block.hash} />

Check warning on line 202 in src/app/_components/BlockList/UpdatedBlockList.tsx

View check run for this annotation

Codecov / codecov/patch

src/app/_components/BlockList/UpdatedBlockList.tsx#L202

Added line #L202 was not covered by tests
)
)}
</Box>
<Box>
{!isLive && (
<ListFooter
isLoading={isFetchingNextPage}
hasNextPage={hasNextPage}
href={limit ? '/blocks' : undefined}
fetchNextPage={limit ? undefined : fetchNextPage}
label={'blocks'}
/>
)}
</Box>
</Stack>
</Section>
);
}

export function UpdatedBlocksList({ limit }: { limit?: number }) {

Check warning on line 222 in src/app/_components/BlockList/UpdatedBlockList.tsx

View check run for this annotation

Codecov / codecov/patch

src/app/_components/BlockList/UpdatedBlockList.tsx#L222

Added line #L222 was not covered by tests
return (
<ExplorerErrorBoundary
Wrapper={Section}
wrapperProps={{
title: 'Recent Blocks',
gridColumnStart: ['1', '1', '2'],
gridColumnEnd: ['2', '2', '3'],
minWidth: 0,
}}
tryAgainButton
>
<UpdatedBlocksListBase limit={limit} />
</ExplorerErrorBoundary>
);
}

0 comments on commit aa8d5e0

Please sign in to comment.