Skip to content

Commit 69f7f8d

Browse files
committed
miners: Add table sort
1 parent fbbf60c commit 69f7f8d

3 files changed

Lines changed: 119 additions & 29 deletions

File tree

common/data.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import config from "../config/config.ts";
2-
import { IBlock, IMinerData } from "./interfaces.ts";
2+
import { IBlock, IMinerData, IMiners } from "./interfaces.ts";
33

44
export function computeStats(blocks: IBlock[]) {
55
const threshold = config.fork.threshold;
@@ -42,7 +42,7 @@ export function computeStats(blocks: IBlock[]) {
4242
};
4343
}
4444

45-
export function computeMiners(blocks: IBlock[]) {
45+
export function computeMiners(blocks: IBlock[]): [string, IMinerData][] {
4646
// We have to reverse the array as we have to check
4747
// for the latest block by a miner to decide whether they
4848
// are signalling or not.
@@ -69,7 +69,7 @@ export function computeMiners(blocks: IBlock[]) {
6969
prev[key].numSignallingBlocks++;
7070
}
7171
return prev;
72-
}, {} as IMinerData);
72+
}, {} as IMiners);
7373

7474
// Sort the miners by share
7575
return Object.entries(miners).sort(([_, a], [_2, b]) => {

common/interfaces.ts

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,13 @@ export interface IBlock {
66
}
77

88
export interface IMinerData {
9-
[key: string]: {
10-
name: string;
11-
signals: boolean;
12-
website: string | undefined;
13-
numBlocks: number;
14-
numSignallingBlocks: number;
15-
};
9+
name: string;
10+
signals: boolean;
11+
website: string | undefined;
12+
numBlocks: number;
13+
numSignallingBlocks: number;
14+
}
15+
16+
export interface IMiners {
17+
[key: string]: IMinerData;
1618
}

frontend/pages/miners.tsx

Lines changed: 107 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { useMemo } from "https://esm.sh/react@17.0.2";
1+
import React, { useMemo, useState } from "https://esm.sh/react@17.0.2";
22
import styled from "https://esm.sh/styled-components";
33
import Anchor from "https://deno.land/x/aleph@v0.3.0-alpha.32/framework/react/components/Anchor.ts";
44

@@ -13,6 +13,7 @@ import SiteMenu from "../components/SiteMenu.tsx";
1313
import { Donation } from "../components/Donation.tsx";
1414
import ContactTwitter from "../components/ContactTwitter.tsx";
1515
import CommonHeader from "../components/CommonHeader.ts";
16+
import { IMinerData, IMiners } from "../back/common/interfaces.ts";
1617

1718
const Table = styled.table`
1819
width: 100%;
@@ -51,12 +52,24 @@ const TableHeader = styled.th`
5152
padding: 9px;
5253
`;
5354

55+
export const TableHeaderLink = styled.a`
56+
color: #efefef;
57+
cursor: pointer;
58+
text-decoration: none;
59+
display: inline-block;
60+
`;
61+
62+
export const TableHeaderSortContainer = styled.span`
63+
position: absolute;
64+
margin-left: 3px;
65+
`;
66+
5467
const Cell = styled.td`
5568
color: #f0f0f0;
5669
> a {
5770
color: #f0f0f0;
5871
}
59-
padding: 17px;
72+
padding: 16px;
6073
`;
6174

6275
const SignallingCell = styled.td`
@@ -85,6 +98,15 @@ const TotalsPotential = styled(CommonHeader)`
8598
text-underline-position: under;
8699
`;
87100

101+
type SortKey = "name" | "share" | "blocks" | "signallingStatus";
102+
interface TableRow {
103+
name: string;
104+
share: string;
105+
blocks: string;
106+
signallingStatus: boolean;
107+
website: string | undefined;
108+
}
109+
88110
export default function Miners() {
89111
const blocks = useStoreState((store) => store.blocks);
90112
const forkName = config.fork.name;
@@ -96,6 +118,47 @@ export default function Miners() {
96118
const totalSignallingPotentialRatio = miners
97119
.filter(([_, m]) => m.numSignallingBlocks > 0)
98120
.reduce((sum, [_, m]) => sum + m.numBlocks / currentNumberOfBlocks, 0);
121+
const [sortKey, setSortKey] = useState<SortKey>("share");
122+
const [sortDirection, setSortDirection] = useState<"ASC" | "DESC">("DESC");
123+
124+
const fixTable = (miners: [string, IMinerData][], key: SortKey): TableRow[] => {
125+
let data = miners
126+
.map(([_, miner]) => {
127+
const r: TableRow = {
128+
name: miner.name,
129+
share: `${((miner.numBlocks / currentNumberOfBlocks) * 100).toFixed(2)}%`,
130+
blocks: `${miner.numSignallingBlocks}/${miner.numBlocks + " "}`,
131+
signallingStatus: miner.signals,
132+
website: miner.website,
133+
};
134+
return r;
135+
})
136+
.sort((a, b) => {
137+
if (key === "blocks") {
138+
return Number.parseInt(b["blocks"].split("/")[0]) - Number.parseInt(a["blocks"].split("/")[0]);
139+
} else if (key === "share") {
140+
return Number.parseFloat(b["share"].split("%")[0]) - Number.parseFloat(a["share"].split("%")[0]);
141+
}
142+
return `${b[key]}`.localeCompare(`${a[key]}`);
143+
});
144+
145+
if (sortDirection === "ASC") {
146+
data = data.slice().reverse();
147+
}
148+
return data;
149+
};
150+
151+
const onClickSort = (e: React.MouseEvent<HTMLAnchorElement, MouseEvent>, key: SortKey) => {
152+
e.preventDefault();
153+
if (sortKey !== key) {
154+
setSortKey(key);
155+
setSortDirection("DESC");
156+
} else {
157+
setSortDirection(sortDirection === "DESC" ? "ASC" : "DESC");
158+
}
159+
};
160+
161+
const tableData = fixTable(miners, sortKey);
99162

100163
return (
101164
<Container>
@@ -116,34 +179,59 @@ export default function Miners() {
116179
<Table>
117180
<TableHead>
118181
<TableRow>
119-
<TableHeader>Mining pool</TableHeader>
120-
<TableHeader>Share</TableHeader>
121-
<TableHeader>Blocks</TableHeader>
122-
<TableHeader>Signals</TableHeader>
182+
<TableHeader>
183+
<TableHeaderLink href="#" onClick={(e) => onClickSort(e, "name")}>
184+
Mining pool{" "}
185+
<TableHeaderSortContainer>
186+
{sortKey === "name" && (sortDirection === "ASC" ? "▴" : "▾")}
187+
</TableHeaderSortContainer>
188+
</TableHeaderLink>
189+
</TableHeader>
190+
<TableHeader>
191+
<TableHeaderLink href="#" onClick={(e) => onClickSort(e, "share")}>
192+
Share{" "}
193+
<TableHeaderSortContainer>
194+
{sortKey === "share" && (sortDirection === "ASC" ? "▴" : "▾")}
195+
</TableHeaderSortContainer>
196+
</TableHeaderLink>
197+
</TableHeader>
198+
<TableHeader>
199+
<TableHeaderLink href="#" onClick={(e) => onClickSort(e, "blocks")}>
200+
Blocks{" "}
201+
<TableHeaderSortContainer>
202+
{sortKey === "blocks" && (sortDirection === "ASC" ? "▴" : "▾")}
203+
</TableHeaderSortContainer>
204+
</TableHeaderLink>
205+
</TableHeader>
206+
<TableHeader>
207+
<TableHeaderLink href="#" onClick={(e) => onClickSort(e, "signallingStatus")}>
208+
Signals{" "}
209+
<TableHeaderSortContainer>
210+
{sortKey === "signallingStatus" && (sortDirection === "ASC" ? "▴" : "▾")}
211+
</TableHeaderSortContainer>
212+
</TableHeaderLink>
213+
</TableHeader>
123214
</TableRow>
124215
</TableHead>
125-
126216
<TableBody>
127-
{miners.map(([key, miner]) => {
217+
{tableData.map((row) => {
128218
return (
129-
<TableRow key={key}>
219+
<TableRow key={row.name}>
130220
<Cell>
131-
{miner.website && (
132-
<a href={miner.website} target="_blank">
133-
{miner.name}
221+
{row.website && (
222+
<a href={row.website} target="_blank">
223+
{row.name}
134224
</a>
135225
)}
136-
{!miner.website && miner.name}
226+
{!row.website && row.name}
137227
</Cell>
138-
<Cell>{((miner.numBlocks / currentNumberOfBlocks) * 100).toFixed(2)}%</Cell>
228+
<Cell>{row.share}</Cell>
139229
<SignallingCell>
140-
<Anchor href={`/miner/${miner.name}`}>
141-
{miner.numSignallingBlocks}/{miner.numBlocks + " "}
142-
</Anchor>
230+
<Anchor href={`/miner/${row.name}`}>{row.blocks}</Anchor>
143231
</SignallingCell>
144232
<SignallingCell>
145-
{miner.signals && <></>}
146-
{!miner.signals && <>🚫</>}
233+
{row.signallingStatus && <></>}
234+
{!row.signallingStatus && <>🚫</>}
147235
</SignallingCell>
148236
</TableRow>
149237
);

0 commit comments

Comments
 (0)