Skip to content

Commit

Permalink
Feat/blocklist (#1569)
Browse files Browse the repository at this point in the history
* feat: nakamoto epoch 3.0 blocklist
  • Loading branch information
BLuEScioN authored Apr 25, 2024
1 parent df08a57 commit 8f1591a
Show file tree
Hide file tree
Showing 111 changed files with 4,319 additions and 601 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
"@react-stately/toggle": "3.6.1",
"@reduxjs/toolkit": "1.9.7",
"@stacks/auth": "6.9.0",
"@stacks/blockchain-api-client": "7.9.0-beta.1",
"@stacks/blockchain-api-client": "7.10.0",
"@stacks/common": "6.8.1",
"@stacks/connect": "7.4.0",
"@stacks/connect-react": "22.2.0",
Expand Down
8 changes: 4 additions & 4 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

41 changes: 26 additions & 15 deletions src/app/PageClient.tsx
Original file line number Diff line number Diff line change
@@ -1,34 +1,40 @@
'use client';

import { NextPage } from 'next';
import dynamic from 'next/dynamic';

import { DEFAULT_BLOCKS_LIST_LIMIT, DEFAULT_LIST_LIMIT_SMALL } from '../common/constants/constants';
import { useGlobalContext } from '../common/context/useAppContext';
import { NetworkModes } from '../common/types/network';
import { TxListTabs } from '../features/txs-list/tabs/TxListTabs';
import { Grid } from '../ui/Grid';
import { HomePageBlockListSkeleton } from './_components/BlockList/Grouped/skeleton';
import { SkeletonBlockList } from './_components/BlockList/SkeletonBlockList';
import { UpdatedBlocksList } from './_components/BlockList/UpdatedBlockList';
import { PageTitle } from './_components/PageTitle';
import { Stats } from './_components/Stats/Stats';

const NonPaginatedBlockListLayoutA = dynamic(
() =>
import('./_components/BlockList/LayoutA/NonPaginated').then(
mod => mod.NonPaginatedBlockListLayoutA
),
const UpdatedBlockListDynamic = dynamic(
() => import('./_components/BlockList/UpdatedBlockList').then(mod => mod.UpdatedBlocksList),
{
loading: () => <SkeletonBlockList />,
ssr: false,
}
);

const BlocksList = dynamic(() => import('./_components/BlockList').then(mod => mod.BlocksList), {
loading: () => <SkeletonBlockList />,
ssr: false,
});
const HomePageBlockListDynamic = dynamic(
() =>
import('./_components/BlockList/HomePage/HomePageBlockList').then(mod => mod.HomePageBlockList),
{
loading: () => <HomePageBlockListSkeleton />,
ssr: false,
}
);

export default function Home() {
const { activeNetwork, activeNetworkKey } = useGlobalContext();
const Home: NextPage = () => {
const { activeNetworkKey, activeNetwork } = useGlobalContext();
const chain = activeNetwork.mode;
const isNaka1Testnet =
chain === NetworkModes.Testnet && activeNetworkKey.indexOf('nakamoto-1') !== -1;
return (
<>
<PageTitle data-test="homepage-title">Stacks Explorer</PageTitle>
Expand All @@ -39,9 +45,14 @@ export default function Home() {
gridTemplateColumns={['100%', '100%', '100%', 'minmax(0, 0.6fr) minmax(0, 0.4fr)']}
>
<TxListTabs limit={DEFAULT_LIST_LIMIT_SMALL} showFilterButton={false} />

<UpdatedBlocksList limit={DEFAULT_BLOCKS_LIST_LIMIT} />
{isNaka1Testnet ? (
<HomePageBlockListDynamic />
) : (
<UpdatedBlockListDynamic limit={DEFAULT_BLOCKS_LIST_LIMIT} />
)}
</Grid>
</>
);
}
};

export default Home;
Original file line number Diff line number Diff line change
@@ -1,17 +1,13 @@
'use client';

import { ScaleFade, SlideFade } from '@chakra-ui/react';
import { FC, memo, useEffect, useState } from 'react';
import { FC, useEffect, useState } from 'react';

import { Box } from '../../../ui/Box';
import { Button } from '../../../ui/Button';
import { Collapse } from '../../../ui/Collapse';
import { useDisclosure } from '../../../ui/hooks/useDisclosure';
import { BlockAndMicroblocksItem } from './BlockAndMicroblocksItem';
import { EnhancedBlock } from './types';

export const animationDuration = 0.8;

export const AnimatedBlockAndMicroblocksItem: FC<{
block: EnhancedBlock;
onAnimationExit?: () => void;
Expand Down
2 changes: 1 addition & 1 deletion src/app/_components/BlockList/BlockAndMicroblocksItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export const BlockAndMicroblocksItem: React.FC<{ block: Block }> = ({ block }) =
return (
<AccordionItem
border={'none'}
borderBottom={'1px solid var(--stacks-colors-border)'}
borderBottom={'1px solid var(--stacks-colors-borderPrimary)'}
_last={{ border: 'none' }}
>
<Flex gap={'6px'}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,27 @@ import pluralize from 'pluralize';
import { memo } from 'react';
import { PiArrowUpRight } from 'react-icons/pi';

import { Circle } from '../../../../common/components/Circle';
import { ExplorerLink } from '../../../../common/components/ExplorerLinks';
import { Flex } from '../../../../ui/Flex';
import { Icon } from '../../../../ui/Icon';
import { Text } from '../../../../ui/Text';
import { Circle } from '../../../common/components/Circle';
import { ExplorerLink } from '../../../common/components/ExplorerLinks';
import { Flex } from '../../../ui/Flex';
import { Icon } from '../../../ui/Icon';
import { Text } from '../../../ui/Text';

export const BlockCount = memo(function ({ count }: { count: number }) {
export const BlockCount = memo(function ({
count,
btcBlockHash,
}: {
count: number;
btcBlockHash?: string;
}) {
// 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 circleColor = useColorModeValue('white', 'black');
return (
<Flex ml={-3} pb={4} pt={1}>
<ExplorerLink href={'/blocks'}>
<Flex py={3}>
<ExplorerLink href={btcBlockHash ? `btcblock/${btcBlockHash}` : '/blocks'}>
<Text
display={'flex'}
color={textColor}
Expand All @@ -36,7 +42,7 @@ export const BlockCount = memo(function ({ count }: { count: number }) {
}}
>
+{count} {pluralize('block', count)}
<Circle size={4.5} bg={circleColor}>
<Circle size={4.5} bg="surface">
<Icon as={PiArrowUpRight} size={2.5} color={iconColor} />
</Circle>
</Text>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import { Dispatch, SetStateAction, createContext, useContext } from 'react';

interface BlockListContextType {
isUpdateListLoading: boolean;
setIsUpdateListLoading: Dispatch<SetStateAction<boolean>>;
isBlockListLoading: boolean;
setBlockListLoading: Dispatch<SetStateAction<boolean>>;
groupedByBtc: boolean;
setGroupedByBtc: Dispatch<SetStateAction<boolean>>;
liveUpdates: boolean;
Expand Down
4 changes: 2 additions & 2 deletions src/app/_components/BlockList/BlockListItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { BtcStxBlockLinks } from '../../../common/components/BtcStxBlockLinks';
import { TwoColsListItem } from '../../../common/components/TwoColumnsListItem';
import { addSepBetweenStrings, toRelativeTime, truncateMiddle } from '../../../common/utils/utils';
import { Flex, FlexProps } from '../../../ui/Flex';
import { Caption, Text } from '../../../ui/typography';
import { Caption } from '../../../ui/typography';

export const BlockListItem: React.FC<{ block: Block } & FlexProps> = React.memo(
({ block, ...rest }) => {
Expand All @@ -30,7 +30,7 @@ export const BlockListItem: React.FC<{ block: Block } & FlexProps> = React.memo(
</Flex>
),
subtitle: (
<Caption display="block" color={'secondaryText'}>
<Caption display="block" color={'textSubdued'}>
{addSepBetweenStrings([
`${block?.microblocks_accepted?.length || 0} ${pluralize(
'microblock',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { ReactNode, useState } from 'react';

import { BlockListContext } from './context';
import { BlockListContext } from './BlockListContext';

export function BlockListProvider({ children }: { children: ReactNode }) {
const [isUpdateListLoading, setIsUpdateListLoading] = useState(false);
Expand All @@ -10,8 +10,8 @@ export function BlockListProvider({ children }: { children: ReactNode }) {
return (
<BlockListContext.Provider
value={{
isUpdateListLoading,
setIsUpdateListLoading,
isBlockListLoading: isUpdateListLoading,
setBlockListLoading: setIsUpdateListLoading,
groupedByBtc,
setGroupedByBtc,
liveUpdates,
Expand Down
68 changes: 68 additions & 0 deletions src/app/_components/BlockList/BlocksPage/BlocksPageBlockList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
'use client';

import { useCallback, useRef } from 'react';

import { Section } from '../../../../common/components/Section';
import { Stack } from '../../../../ui/Stack';
import { ExplorerErrorBoundary } from '../../ErrorBoundary';
import { useBlockListContext } from '../BlockListContext';
import { BlockListProvider } from '../BlockListProvider';
import { Controls } from '../Controls';
import { BlocksPageBlockListGrouped } from './BlocksPageBlockListGrouped';
import { BlocksPageBlockListUngrouped } from './BlocksPageBlockListUngrouped';

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]);

return (
<Section>
<Stack
marginX={-6}
px={6}
borderBottom={liveUpdates ? '1px solid var(--stacks-colors-borderPrimary)' : 'none'}
>
<Controls
groupByBtc={{
onChange: () => {
setGroupedByBtc(!groupedByBtc);
},
isChecked: groupedByBtc,
}}
liveUpdates={{
onChange: toggleLiveUpdates,
isChecked: liveUpdates,
}}
horizontal={true}
/>
</Stack>
{groupedByBtc ? <BlocksPageBlockListGrouped /> : <BlocksPageBlockListUngrouped />}
</Section>
);
}

export function BlocksPageBlockList() {
return (
<ExplorerErrorBoundary
Wrapper={Section}
wrapperProps={{
gridColumnStart: ['1', '1', '2'],
gridColumnEnd: ['2', '2', '3'],
minWidth: 0,
}}
tryAgainButton
>
<BlockListProvider>
<BlocksPageBlockListBase />
</BlockListProvider>
</ExplorerErrorBoundary>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
'use client';

import { Suspense } from 'react';

import { ListFooter } from '../../../../common/components/ListFooter';
import { Section } from '../../../../common/components/Section';
import { Box } from '../../../../ui/Box';
import { Flex } from '../../../../ui/Flex';
import { ExplorerErrorBoundary } from '../../ErrorBoundary';
import { useBlockListContext } from '../BlockListContext';
import { BlockListGrouped } from '../Grouped/BlockListGrouped';
import { BlocksPageBlockListGroupedSkeleton } from '../Grouped/skeleton';
import { UpdateBar } from '../UpdateBar';
import { useBlocksPageBlockListGrouped } from '../data/useBlocksPageBlockListGrouped';

function BlocksPageBlockListGroupedBase() {
const { liveUpdates } = useBlockListContext();
const { blockList, updateBlockList, isFetchingNextPage, hasNextPage, fetchNextPage } =
useBlocksPageBlockListGrouped();

return (
<>
{!liveUpdates && <UpdateBar blockList={blockList} onClick={updateBlockList} />}
<Flex flexDirection="column" gap={4} pt={4}>
<BlockListGrouped blockList={blockList} minimized={false} stxBlocksLimit={10} />
</Flex>
<Box pt={5} pb={5}>
{!liveUpdates && (
<ListFooter
isLoading={isFetchingNextPage}
hasNextPage={hasNextPage}
fetchNextPage={fetchNextPage}
label={'blocks'}
/>
)}
</Box>
</>
);
}

export function BlocksPageBlockListGrouped() {
return (
<ExplorerErrorBoundary
Wrapper={Section}
wrapperProps={{
gridColumnStart: ['1', '1', '2'],
gridColumnEnd: ['2', '2', '3'],
minWidth: 0,
}}
tryAgainButton
>
<Suspense fallback={<BlocksPageBlockListGroupedSkeleton />}>
<BlocksPageBlockListGroupedBase />
</Suspense>
</ExplorerErrorBoundary>
);
}
Loading

0 comments on commit 8f1591a

Please sign in to comment.