Feat/122 next previous buttons (#131)
* fix use fetch hook * add next/previous buttons * disable the button if the block is 1 * prevent slow fetches from overriding data * move splash loader into UI toolkit * remove splash loader * remove splash * remove pointless component * add tests for blocks page * fix jump to party * merge updates * address PR comments
This commit is contained in:
parent
47e703c558
commit
c5788fa1cf
@ -1 +0,0 @@
|
|||||||
export * from "./splash-loader";
|
|
@ -1,22 +0,0 @@
|
|||||||
@import "../../styles/colors";
|
|
||||||
|
|
||||||
.loading {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
&__animation {
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
width: 50px;
|
|
||||||
height: 50px;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
|
|
||||||
div {
|
|
||||||
width: 10px;
|
|
||||||
height: 10px;
|
|
||||||
background: white;
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,32 +0,0 @@
|
|||||||
import './splash-loader.scss';
|
|
||||||
|
|
||||||
import React from 'react';
|
|
||||||
|
|
||||||
export const SplashLoader = ({ text = 'Loading' }: { text?: string }) => {
|
|
||||||
const [, forceRender] = React.useState(false);
|
|
||||||
React.useEffect(() => {
|
|
||||||
const interval = setInterval(() => {
|
|
||||||
forceRender((x) => !x);
|
|
||||||
}, 100);
|
|
||||||
|
|
||||||
return () => clearInterval(interval);
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="loading" data-testid="splash-loader">
|
|
||||||
<div className="loading__animation">
|
|
||||||
{new Array(25).fill(null).map((_, i) => {
|
|
||||||
return (
|
|
||||||
<div
|
|
||||||
key={i}
|
|
||||||
style={{
|
|
||||||
opacity: Math.random() > 0.75 ? 1 : 0,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</div>
|
|
||||||
<div>{text}</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
@ -1 +0,0 @@
|
|||||||
export * from "./splash-screen";
|
|
@ -1,12 +0,0 @@
|
|||||||
@import "../../styles/colors";
|
|
||||||
|
|
||||||
.splash-screen {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
text-align: center;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
font-size: 20px;
|
|
||||||
color: $white;
|
|
||||||
}
|
|
@ -1,7 +0,0 @@
|
|||||||
import './splash-screen.scss';
|
|
||||||
|
|
||||||
import React from 'react';
|
|
||||||
|
|
||||||
export const SplashScreen = ({ children }: { children: React.ReactNode }) => {
|
|
||||||
return <div className="splash-screen">{children}</div>;
|
|
||||||
};
|
|
@ -1,10 +1,9 @@
|
|||||||
import React, { useState } from "react";
|
import React, { useState } from 'react';
|
||||||
import useWebSocket from "react-use-websocket";
|
import useWebSocket from 'react-use-websocket';
|
||||||
|
|
||||||
import { SplashLoader } from "../../components/splash-loader";
|
import { Loader, Splash } from '@vegaprotocol/ui-toolkit';
|
||||||
import { SplashScreen } from "../../components/splash-screen";
|
import { DATA_SOURCES } from '../../config';
|
||||||
import { DATA_SOURCES } from "../../config";
|
import { TendermintWebsocketContext } from './tendermint-websocket-context';
|
||||||
import { TendermintWebsocketContext } from "./tendermint-websocket-context";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provides a single, shared, websocket instance to the entire app to prevent recreation on every render
|
* Provides a single, shared, websocket instance to the entire app to prevent recreation on every render
|
||||||
@ -19,9 +18,9 @@ export const TendermintWebsocketProvider = ({
|
|||||||
|
|
||||||
if (!contextShape) {
|
if (!contextShape) {
|
||||||
return (
|
return (
|
||||||
<SplashScreen>
|
<Splash>
|
||||||
<SplashLoader />
|
<Loader />
|
||||||
</SplashScreen>
|
</Splash>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,12 +18,12 @@ type Action<T> =
|
|||||||
| { type: ActionType.FETCHED; payload: T }
|
| { type: ActionType.FETCHED; payload: T }
|
||||||
| { type: ActionType.ERROR; error: Error };
|
| { type: ActionType.ERROR; error: Error };
|
||||||
|
|
||||||
function useFetch<T = unknown>(
|
function useFetch<T>(
|
||||||
url?: string,
|
url: string,
|
||||||
options?: RequestInit
|
options?: RequestInit
|
||||||
): { state: State<T>; refetch: () => void } {
|
): { state: State<T>; refetch: () => void } {
|
||||||
// Used to prevent state update if the component is unmounted
|
// Used to prevent state update if the component is unmounted
|
||||||
const cancelRequest = useRef<boolean>(false);
|
const cancelRequest = useRef<{ [key: string]: boolean }>({ [url]: false });
|
||||||
|
|
||||||
const initialState: State<T> = {
|
const initialState: State<T> = {
|
||||||
error: undefined,
|
error: undefined,
|
||||||
@ -61,11 +61,11 @@ function useFetch<T = unknown>(
|
|||||||
// @ts-ignore - data.error
|
// @ts-ignore - data.error
|
||||||
throw new Error(data.error);
|
throw new Error(data.error);
|
||||||
}
|
}
|
||||||
if (cancelRequest.current) return;
|
if (cancelRequest.current[url]) return;
|
||||||
|
|
||||||
dispatch({ type: ActionType.FETCHED, payload: data });
|
dispatch({ type: ActionType.FETCHED, payload: data });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (cancelRequest.current) return;
|
if (cancelRequest.current[url]) return;
|
||||||
|
|
||||||
dispatch({ type: ActionType.ERROR, error: error as Error });
|
dispatch({ type: ActionType.ERROR, error: error as Error });
|
||||||
}
|
}
|
||||||
@ -78,13 +78,15 @@ function useFetch<T = unknown>(
|
|||||||
}, [url]);
|
}, [url]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
const cancel = cancelRequest.current;
|
||||||
|
cancel[url] = false;
|
||||||
fetchCallback();
|
fetchCallback();
|
||||||
// Use the cleanup function for avoiding a possibly...
|
// Use the cleanup function for avoiding a possibly...
|
||||||
// ...state update after the component was unmounted
|
// ...state update after the component was unmounted
|
||||||
return () => {
|
return () => {
|
||||||
cancelRequest.current = true;
|
cancel[url] = true;
|
||||||
};
|
};
|
||||||
}, [fetchCallback]);
|
}, [fetchCallback, url]);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
state,
|
state,
|
||||||
|
181
apps/explorer/src/app/routes/blocks/id/block.spec.tsx
Normal file
181
apps/explorer/src/app/routes/blocks/id/block.spec.tsx
Normal file
@ -0,0 +1,181 @@
|
|||||||
|
import { Block } from './block';
|
||||||
|
import {
|
||||||
|
fireEvent,
|
||||||
|
render,
|
||||||
|
screen,
|
||||||
|
waitFor,
|
||||||
|
act,
|
||||||
|
} from '@testing-library/react';
|
||||||
|
import { MemoryRouter, Route, Routes } from 'react-router-dom';
|
||||||
|
import { Routes as RouteNames } from '../../router-config';
|
||||||
|
|
||||||
|
const blockId = 1085890;
|
||||||
|
|
||||||
|
const createBlockResponse = () => {
|
||||||
|
return {
|
||||||
|
jsonrpc: '2.0',
|
||||||
|
id: -1,
|
||||||
|
result: {
|
||||||
|
block_id: {
|
||||||
|
hash: '26D92E4B0C892AC6EF33A281185E612671976AD9C629F6C8182C1D92C2CE2F6F',
|
||||||
|
parts: {
|
||||||
|
total: 1,
|
||||||
|
hash: 'D18AEBADEAADA2C701FFFAD5A16EE8C493C5D1B54589FD1587EA5C61B7179AAC',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
block: {
|
||||||
|
header: {
|
||||||
|
version: {
|
||||||
|
block: '11',
|
||||||
|
app: '1',
|
||||||
|
},
|
||||||
|
chain_id: 'testnet-12cd7b',
|
||||||
|
height: '1085891',
|
||||||
|
time: '2022-03-24T11:03:40.014303953Z',
|
||||||
|
last_block_id: {
|
||||||
|
hash: 'C50CA169545AC1280220433D7971C50D941F675E9B0FFF358ABE8F3A7F74AE0E',
|
||||||
|
parts: {
|
||||||
|
total: 1,
|
||||||
|
hash: '86974C6359B39084235EE31C1389DEA052E01E552CD1D113B3222A63A8DF390C',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
last_commit_hash:
|
||||||
|
'D8FBE7DEB393D740B22EF8E91DA426494E2535902A6FB89B1D754F0DAF74DB37',
|
||||||
|
data_hash:
|
||||||
|
'E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855',
|
||||||
|
validators_hash:
|
||||||
|
'2BC96D9FD4A7663A270909F7E604C24E1F8C87605F913F6DA55AF2DDE023BAC9',
|
||||||
|
next_validators_hash:
|
||||||
|
'2BC96D9FD4A7663A270909F7E604C24E1F8C87605F913F6DA55AF2DDE023BAC9',
|
||||||
|
consensus_hash:
|
||||||
|
'048091BC7DDC283F77BFBF91D73C44DA58C3DF8A9CBC867405D8B7F3DAADA22F',
|
||||||
|
app_hash:
|
||||||
|
'B04B71A61C9970A132631FFBA50E36B9C5A8A490983E803F6295133C255D3FCE',
|
||||||
|
last_results_hash:
|
||||||
|
'E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855',
|
||||||
|
evidence_hash:
|
||||||
|
'E3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855',
|
||||||
|
proposer_address: '1C9B6E2708F8217F8D5BFC8D8734ED9A5BC19B21',
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
txs: [],
|
||||||
|
},
|
||||||
|
evidence: {
|
||||||
|
evidence: [],
|
||||||
|
},
|
||||||
|
last_commit: {
|
||||||
|
height: blockId.toString(),
|
||||||
|
round: 0,
|
||||||
|
block_id: {
|
||||||
|
hash: 'C50CA169545AC1280220433D7971C50D941F675E9B0FFF358ABE8F3A7F74AE0E',
|
||||||
|
parts: {
|
||||||
|
total: 1,
|
||||||
|
hash: '86974C6359B39084235EE31C1389DEA052E01E552CD1D113B3222A63A8DF390C',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
signatures: [
|
||||||
|
{
|
||||||
|
block_id_flag: 2,
|
||||||
|
validator_address: '1C9B6E2708F8217F8D5BFC8D8734ED9A5BC19B21',
|
||||||
|
timestamp: '2022-03-24T11:03:40.026173466Z',
|
||||||
|
signature:
|
||||||
|
'/BbNDfNflmhL5eNmpijxjjuLV8WJ1SkoesIThcpvxSjUhf+8tjZ+mIUkXig7xD5JB/7X23l6eEsbrBLxG6ppBA==',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
block_id_flag: 2,
|
||||||
|
validator_address: '31D6EBD2A8E40524142613A241CA1D2056159EF4',
|
||||||
|
timestamp: '2022-03-24T11:03:40.014303953Z',
|
||||||
|
signature:
|
||||||
|
'zJ717hzAyUN0qdfjtXHHQP05oKeGPSL5HOZ8syU6M0kj3C5fuP+IG6PdVHj26ZKthTyRhEyHcMBJ/FHu2s5MBw==',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
block_id_flag: 2,
|
||||||
|
validator_address: '6DB7E2A705ABF86C6B4A4817E778669D45421166',
|
||||||
|
timestamp: '2022-03-24T11:03:39.991116117Z',
|
||||||
|
signature:
|
||||||
|
'lRwyqUnIBqyyL9XHfgTdfABVT3B3T9aIb7HP656TcqOf1d1hmnZ8oZGXeKc5SNpssJSlHl9V/F9k2LZtHChKBg==',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
block_id_flag: 2,
|
||||||
|
validator_address: 'A5429AF24A820AFD9C3D21507C8642F27F5DD308',
|
||||||
|
timestamp: '2022-03-24T11:03:39.988302733Z',
|
||||||
|
signature:
|
||||||
|
'ARjFOJger/wlBwMap3DaMhYKe9ywkQg/rxVCLZ0MMwdhAkviC8gvZRwoDajbKuYgbsgG1MwsGk/mEib5O5cBBA==',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
block_id_flag: 2,
|
||||||
|
validator_address: 'AE5B9A8193AEFC405C159C930ED2BBF40A806785',
|
||||||
|
timestamp: '2022-03-24T11:03:40.020448546Z',
|
||||||
|
signature:
|
||||||
|
'o2z4gdBiNUskFQ4m/yb+uM0/jaOf1p6jpGlKoEhebn2ExreaayN/JJR8F98uWk1M4S0zK9trI9oWDgwmxo5CAg==',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderComponent = () => {
|
||||||
|
return (
|
||||||
|
<MemoryRouter initialEntries={[`/${RouteNames.BLOCKS}/${blockId}`]}>
|
||||||
|
<Routes>
|
||||||
|
<Route path={`/${RouteNames.BLOCKS}/:block`} element={<Block />} />
|
||||||
|
</Routes>
|
||||||
|
</MemoryRouter>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
global.fetch = jest.fn(() =>
|
||||||
|
Promise.resolve({
|
||||||
|
ok: true,
|
||||||
|
json: () => Promise.resolve(createBlockResponse()),
|
||||||
|
})
|
||||||
|
) as jest.Mock;
|
||||||
|
jest.useFakeTimers().setSystemTime(1648123348642);
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
jest.useRealTimers();
|
||||||
|
jest.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Block', () => {
|
||||||
|
it('should render title, proposer address and time mined', async () => {
|
||||||
|
global.fetch = jest.fn(() =>
|
||||||
|
Promise.resolve({
|
||||||
|
ok: true,
|
||||||
|
json: () => Promise.resolve(createBlockResponse()),
|
||||||
|
})
|
||||||
|
) as jest.Mock;
|
||||||
|
render(renderComponent());
|
||||||
|
await waitFor(() => screen.getByTestId('block-header'));
|
||||||
|
|
||||||
|
expect(screen.getByTestId('block-header')).toHaveTextContent(
|
||||||
|
`BLOCK ${blockId}`
|
||||||
|
);
|
||||||
|
const proposer = screen.getByTestId('block-validator');
|
||||||
|
expect(proposer).toHaveTextContent(
|
||||||
|
'1C9B6E2708F8217F8D5BFC8D8734ED9A5BC19B21'
|
||||||
|
);
|
||||||
|
expect(proposer).toHaveAttribute('href', `/${RouteNames.VALIDATORS}`);
|
||||||
|
expect(screen.getByTestId('block-time')).toHaveTextContent(
|
||||||
|
'3528 seconds ago'
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders next and previous buttons', async () => {
|
||||||
|
render(renderComponent());
|
||||||
|
await waitFor(() => screen.getByTestId('block-header'));
|
||||||
|
|
||||||
|
expect(screen.getByTestId('previous-block')).toHaveAttribute(
|
||||||
|
'href',
|
||||||
|
`/${RouteNames.BLOCKS}/${blockId - 1}`
|
||||||
|
);
|
||||||
|
expect(screen.getByTestId('next-block')).toHaveAttribute(
|
||||||
|
'href',
|
||||||
|
`/${RouteNames.BLOCKS}/${blockId + 1}`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
88
apps/explorer/src/app/routes/blocks/id/block.tsx
Normal file
88
apps/explorer/src/app/routes/blocks/id/block.tsx
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { Link, useParams } from 'react-router-dom';
|
||||||
|
import { DATA_SOURCES } from '../../../config';
|
||||||
|
import useFetch from '../../../hooks/use-fetch';
|
||||||
|
import { TendermintBlocksResponse } from '../tendermint-blocks-response';
|
||||||
|
import { RouteTitle } from '../../../components/route-title';
|
||||||
|
import { SecondsAgo } from '../../../components/seconds-ago';
|
||||||
|
import {
|
||||||
|
Table,
|
||||||
|
TableRow,
|
||||||
|
TableHeader,
|
||||||
|
TableCell,
|
||||||
|
} from '../../../components/table';
|
||||||
|
import { TxsPerBlock } from '../../../components/txs/txs-per-block';
|
||||||
|
import { Button } from '@vegaprotocol/ui-toolkit';
|
||||||
|
import { Routes } from '../../router-config';
|
||||||
|
import { RenderFetched } from '../../../components/render-fetched';
|
||||||
|
|
||||||
|
const Block = () => {
|
||||||
|
const { block } = useParams<{ block: string }>();
|
||||||
|
const {
|
||||||
|
state: { data: blockData, loading, error },
|
||||||
|
} = useFetch<TendermintBlocksResponse>(
|
||||||
|
`${DATA_SOURCES.tendermintUrl}/block?height=${block}`
|
||||||
|
);
|
||||||
|
|
||||||
|
const header = blockData?.result.block.header;
|
||||||
|
if (!header) {
|
||||||
|
return <p>Could not get block data</p>;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section>
|
||||||
|
<RouteTitle data-testid="block-header">BLOCK {block}</RouteTitle>
|
||||||
|
<RenderFetched error={error} loading={loading}>
|
||||||
|
<>
|
||||||
|
<div className="grid grid-cols-2 gap-16">
|
||||||
|
<Link
|
||||||
|
data-testid="previous-block"
|
||||||
|
to={`/${Routes.BLOCKS}/${Number(block) - 1}`}
|
||||||
|
>
|
||||||
|
<Button
|
||||||
|
className="w-full"
|
||||||
|
disabled={Number(block) === 1}
|
||||||
|
variant="secondary"
|
||||||
|
>
|
||||||
|
Previous
|
||||||
|
</Button>
|
||||||
|
</Link>
|
||||||
|
<Link
|
||||||
|
data-testid="next-block"
|
||||||
|
to={`/${Routes.BLOCKS}/${Number(block) + 1}`}
|
||||||
|
>
|
||||||
|
<Button className="w-full" variant="secondary">
|
||||||
|
Next
|
||||||
|
</Button>
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
<Table className="mb-28">
|
||||||
|
<TableRow modifier="bordered">
|
||||||
|
<TableHeader scope="row">Mined by</TableHeader>
|
||||||
|
<TableCell modifier="bordered">
|
||||||
|
<Link
|
||||||
|
data-testid="block-validator"
|
||||||
|
className="text-vega-yellow font-mono"
|
||||||
|
to={`/${Routes.VALIDATORS}`}
|
||||||
|
>
|
||||||
|
{header.proposer_address}
|
||||||
|
</Link>
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
<TableRow modifier="bordered">
|
||||||
|
<TableHeader scope="row">Time</TableHeader>
|
||||||
|
<TableCell modifier="bordered">
|
||||||
|
<SecondsAgo data-testid="block-time" date={header.time} />
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
</Table>
|
||||||
|
{blockData && blockData.result.block.data.txs.length > 0 ? (
|
||||||
|
<TxsPerBlock blockHeight={block} />
|
||||||
|
) : null}
|
||||||
|
</>
|
||||||
|
</RenderFetched>
|
||||||
|
</section>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export { Block };
|
@ -1,60 +1 @@
|
|||||||
import React from 'react';
|
export * from './block';
|
||||||
import { Link, useParams } from 'react-router-dom';
|
|
||||||
import { DATA_SOURCES } from '../../../config';
|
|
||||||
import useFetch from '../../../hooks/use-fetch';
|
|
||||||
import { TendermintBlocksResponse } from '../tendermint-blocks-response';
|
|
||||||
import { RouteTitle } from '../../../components/route-title';
|
|
||||||
import { SecondsAgo } from '../../../components/seconds-ago';
|
|
||||||
import {
|
|
||||||
Table,
|
|
||||||
TableRow,
|
|
||||||
TableHeader,
|
|
||||||
TableCell,
|
|
||||||
} from '../../../components/table';
|
|
||||||
import { TxsPerBlock } from '../../../components/txs/txs-per-block';
|
|
||||||
|
|
||||||
const Block = () => {
|
|
||||||
const { block } = useParams<{ block: string }>();
|
|
||||||
const {
|
|
||||||
state: { data: blockData },
|
|
||||||
} = useFetch<TendermintBlocksResponse>(
|
|
||||||
`${DATA_SOURCES.tendermintUrl}/block?height=${block}`
|
|
||||||
);
|
|
||||||
|
|
||||||
const header = blockData?.result.block.header;
|
|
||||||
|
|
||||||
if (!header) {
|
|
||||||
return <>Could not get block data</>;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<section>
|
|
||||||
<RouteTitle>BLOCK {block}</RouteTitle>
|
|
||||||
<Table className="mb-28">
|
|
||||||
<TableRow modifier="bordered">
|
|
||||||
<TableHeader scope="row">Mined by</TableHeader>
|
|
||||||
<TableCell modifier="bordered">
|
|
||||||
<Link
|
|
||||||
data-testid="block-validator"
|
|
||||||
className="text-vega-yellow font-mono"
|
|
||||||
to={'/validators'}
|
|
||||||
>
|
|
||||||
{header.proposer_address}
|
|
||||||
</Link>
|
|
||||||
</TableCell>
|
|
||||||
</TableRow>
|
|
||||||
<TableRow modifier="bordered">
|
|
||||||
<TableHeader scope="row">Time</TableHeader>
|
|
||||||
<TableCell modifier="bordered">
|
|
||||||
<SecondsAgo data-testid="block-time" date={header.time} />
|
|
||||||
</TableCell>
|
|
||||||
</TableRow>
|
|
||||||
</Table>
|
|
||||||
{blockData?.result.block.data.txs.length > 0 && (
|
|
||||||
<TxsPerBlock blockHeight={block} />
|
|
||||||
)}
|
|
||||||
</section>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export { Block };
|
|
||||||
|
@ -2,9 +2,8 @@ import React from 'react';
|
|||||||
import { useRoutes } from 'react-router-dom';
|
import { useRoutes } from 'react-router-dom';
|
||||||
import { RouteErrorBoundary } from '../components/router-error-boundary';
|
import { RouteErrorBoundary } from '../components/router-error-boundary';
|
||||||
|
|
||||||
import { SplashLoader } from '../components/splash-loader';
|
|
||||||
import { SplashScreen } from '../components/splash-screen';
|
|
||||||
import routerConfig from './router-config';
|
import routerConfig from './router-config';
|
||||||
|
import { Loader, Splash } from '@vegaprotocol/ui-toolkit';
|
||||||
|
|
||||||
export interface RouteChildProps {
|
export interface RouteChildProps {
|
||||||
name: string;
|
name: string;
|
||||||
@ -14,9 +13,9 @@ export const AppRouter = () => {
|
|||||||
const routes = useRoutes(routerConfig);
|
const routes = useRoutes(routerConfig);
|
||||||
|
|
||||||
const splashLoading = (
|
const splashLoading = (
|
||||||
<SplashScreen>
|
<Splash>
|
||||||
<SplashLoader />
|
<Loader />
|
||||||
</SplashScreen>
|
</Splash>
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -7,23 +7,19 @@ import { Routes } from '../../router-config';
|
|||||||
export const JumpToParty = () => {
|
export const JumpToParty = () => {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
|
|
||||||
const handleSubmit = React.useCallback(
|
const handleSubmit = (e: React.SyntheticEvent) => {
|
||||||
() => (e: React.SyntheticEvent) => {
|
e.preventDefault();
|
||||||
e.preventDefault();
|
|
||||||
|
|
||||||
const target = e.target as typeof e.target & {
|
const target = e.target as typeof e.target & {
|
||||||
partyId: { value: number };
|
partyId: { value: number };
|
||||||
};
|
};
|
||||||
|
|
||||||
const partyId = target.partyId.value;
|
const partyId = target.partyId.value;
|
||||||
|
|
||||||
if (partyId) {
|
|
||||||
navigate(`/${Routes.PARTIES}/${partyId}`);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[navigate]
|
|
||||||
);
|
|
||||||
|
|
||||||
|
if (partyId) {
|
||||||
|
navigate(`/${Routes.PARTIES}/${partyId}`);
|
||||||
|
}
|
||||||
|
};
|
||||||
return (
|
return (
|
||||||
<form onSubmit={handleSubmit}>
|
<form onSubmit={handleSubmit}>
|
||||||
<label
|
<label
|
||||||
|
@ -12,18 +12,20 @@ export const Loader = () => {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<span className="flex flex-wrap w-[15px] h-[15px]">
|
<div className="flex flex-col items-center" data-testid="splash-loader">
|
||||||
{new Array(9).fill(null).map((_, i) => {
|
<div className="w-64 h-64 flex flex-wrap">
|
||||||
return (
|
{new Array(16).fill(null).map((_, i) => {
|
||||||
<span
|
return (
|
||||||
key={i}
|
<div
|
||||||
className="block w-[5px] h-[5px] bg-black dark:bg-white"
|
className="w-16 h-16 dark:bg-white bg-black"
|
||||||
style={{
|
key={i}
|
||||||
opacity: Math.random() > 0.5 ? 1 : 0,
|
style={{
|
||||||
}}
|
opacity: Math.random() > 0.75 ? 1 : 0,
|
||||||
/>
|
}}
|
||||||
);
|
/>
|
||||||
})}
|
);
|
||||||
</span>
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user