diff --git a/public/locales/en.json b/public/locales/en.json
index d6bdb35..47ddc2a 100644
--- a/public/locales/en.json
+++ b/public/locales/en.json
@@ -671,6 +671,11 @@
"columnDifficulty": "Difficulty",
"columnTime": "Time",
+ "mobileHeight": "Block #{{height, number}}",
+ "mobileMiner": "<0>Miner: 0> <1 />",
+ "mobileHash": "<0>Hash: 0> <1 />",
+ "mobileDifficulty": "<0>Difficulty: 0> <1 />",
+
"tableTotal": "{{count, number}} block",
"tableTotal_plural": "{{count, number}} blocks",
"tableTotalEmpty": "No blocks"
@@ -1160,7 +1165,13 @@
"namesARecord": "A Record",
"namesUnpaid": "Unpaid Blocks",
"namesRegistered": "Registered Time",
- "namesUpdated": "Updated Time"
+ "namesUpdated": "Updated Time",
+
+ "blocksMiner": "Miner",
+ "blocksHash": "Hash",
+ "blocksValue": "Value",
+ "blocksDifficulty": "Difficulty",
+ "blocksTime": "Time"
}
}
}
diff --git a/src/pages/blocks/BlockMobileItem.tsx b/src/pages/blocks/BlockMobileItem.tsx
new file mode 100644
index 0000000..e84049f
--- /dev/null
+++ b/src/pages/blocks/BlockMobileItem.tsx
@@ -0,0 +1,80 @@
+// Copyright (c) 2020-2021 Drew Lemmy
+// This file is part of KristWeb 2 under AGPL-3.0.
+// Full details: https://github.com/tmpim/KristWeb2/blob/master/LICENSE.txt
+import { useCallback } from "react";
+
+import { Trans } from "react-i18next";
+import { useTFns } from "@utils/i18n";
+
+import { KristBlock } from "@api/types";
+
+import { KristValue } from "@comp/krist/KristValue";
+import { ContextualAddress } from "@comp/addresses/ContextualAddress";
+import { DateTime } from "@comp/DateTime";
+
+interface Props {
+ block: KristBlock;
+}
+
+export function BlockMobileItem({ block }: Props): JSX.Element {
+ const { t, tKey } = useTFns("blocks.");
+
+ const Hash = useCallback(() => (
+
+ {block.hash?.substr(0, 12) || ""}
+
+ ), [block.hash]);
+
+ const Difficulty = useCallback(() => (
+
+ {block.difficulty.toLocaleString()}
+
+ ), [block.difficulty]);
+
+ return
+ {/* Block value */}
+
+
+
+
+ {/* Block height */}
+
+ {t(tKey("mobileHeight"), { height: block.height })}
+
+
+ {/* Miner */}
+
+
+ Miner:
+
+
+
+
+
+ {/* Hash */}
+ {block.hash && <>
+
+
+ Hash:
+
+
+
+
+ >}
+
+ {/* Difficulty */}
+
+
+ Difficulty:
+
+
+
+
+
+
+ {/* Mined time */}
+
+
+
+
;
+}
diff --git a/src/pages/blocks/BlocksPage.less b/src/pages/blocks/BlocksPage.less
new file mode 100644
index 0000000..21ad869
--- /dev/null
+++ b/src/pages/blocks/BlocksPage.less
@@ -0,0 +1,51 @@
+// Copyright (c) 2020-2021 Drew Lemmy
+// This file is part of KristWeb 2 under AGPL-3.0.
+// Full details: https://github.com/tmpim/KristWeb2/blob/master/LICENSE.txt
+@import (reference) "../../App.less";
+@import "../../style/table.less";
+
+.blocks-page .table-mobile-list-view {
+ .block-mobile-item {
+ .block-height {
+ display: block;
+ font-size: 120%;
+ }
+
+ .block-value {
+ float: right;
+ font-size: 120%;
+ }
+
+ .block-field {
+ font-weight: bold;
+ white-space: nowrap;
+ color: @text-color-secondary;
+ }
+
+ .block-technical-row {
+ display: block;
+
+ .block-mobile-hash, .block-difficulty {
+ font-size: 90%;
+ }
+
+ .block-mobile-hash-value {
+ color: @text-color;
+ font-family: monospace;
+ }
+
+ .sep:before {
+ content: "\2013";
+
+ display: inline-block;
+ margin: 0 @padding-xs;
+ color: @text-color-secondary;
+ }
+ }
+
+ .block-mined {
+ color: @text-color-secondary;
+ font-size: @font-size-sm;
+ }
+ }
+}
diff --git a/src/pages/blocks/BlocksPage.tsx b/src/pages/blocks/BlocksPage.tsx
index 0a860ee..cceaa27 100644
--- a/src/pages/blocks/BlocksPage.tsx
+++ b/src/pages/blocks/BlocksPage.tsx
@@ -1,7 +1,7 @@
// Copyright (c) 2020-2021 Drew Lemmy
// This file is part of KristWeb 2 under AGPL-3.0.
// Full details: https://github.com/tmpim/KristWeb2/blob/master/LICENSE.txt
-import { useState, useMemo } from "react";
+import { useState, useMemo, useEffect } from "react";
import { useSelector } from "react-redux";
import { RootState } from "@store";
@@ -12,6 +12,9 @@
import { useBooleanSetting } from "@utils/settings";
import { useLinkedPagination } from "@utils/table/table";
+import { useTopMenuOptions } from "@layout/nav/TopMenu";
+
+import "./BlocksPage.less";
interface Props {
lowest?: boolean;
@@ -30,6 +33,9 @@
// If auto-refresh is disabled, use a static refresh ID
const usedRefreshID = shouldAutoRefresh ? lastBlockID : 0;
+ const [,, unset, setOpenSortModal] = useTopMenuOptions();
+ useEffect(() => unset, [unset]);
+
// Memoise the table so that it only updates the props (thus triggering a
// re-fetch of the blocks) when something relevant changes
const memoTable = useMemo(() => (
@@ -38,8 +44,9 @@
lowest={lowest}
setError={setError}
setPagination={setPagination}
+ setOpenSortModal={setOpenSortModal}
/>
- ), [usedRefreshID, lowest, setError, setPagination]);
+ ), [usedRefreshID, lowest, setError, setPagination, setOpenSortModal]);
return >;
- setPagination?: Dispatch>;
-}
-
function getColumns(
tStr: TStrFn,
dateColumnWidth: number,
@@ -111,28 +107,48 @@
];
}
-export function BlocksTable({ refreshingID, lowest, setError, setPagination }: Props): JSX.Element {
- const { tStr, tKey } = useTFns("blocks.");
+const sortOptions: SortOptions = [
+ { sortKey: "address", i18nKey: "blocksMiner" },
+ { sortKey: "hash", i18nKey: "blocksHash" },
+ { sortKey: "value", i18nKey: "blocksValue" },
+ { sortKey: "difficulty", i18nKey: "blocksDifficulty" },
+ { sortKey: "time", i18nKey: "blocksTime" }
+];
+
+interface Props {
+ // Number used to trigger a refresh of the blocks listing
+ refreshingID?: number;
+ lowest?: boolean;
+
+ setError?: Dispatch>;
+ setPagination?: Dispatch>;
+ setOpenSortModal?: SetOpenSortModalFn;
+}
+
+export function BlocksTable({
+ refreshingID,
+ lowest,
+ setError,
+ setPagination,
+ setOpenSortModal
+}: Props): JSX.Element {
+ const { tKey } = useTFns("blocks.");
+
+ const defaultOrderBy = lowest ? "hash" : "time";
+ const defaultOrder = lowest ? "ASC" : "DESC";
const [loading, setLoading] = useState(true);
const [res, setRes] = useState();
const { options, setOptions } = useTableHistory({
- orderBy: lowest ? "hash" : "height",
- order: lowest ? "ASC" : "DESC"
+ orderBy: defaultOrderBy, order: defaultOrder
});
- const { paginationTableProps, hotkeys } = useMalleablePagination(
+ const { paginationTableProps, paginationChange, hotkeys } = useMalleablePagination(
res, res?.blocks,
tKey("tableTotal"),
options, setOptions, setPagination
);
- const dateColumnWidth = useDateColumnWidth();
-
- const columns = useMemo(() => getColumns(
- tStr, dateColumnWidth, lowest
- ), [tStr, dateColumnWidth, lowest]);
-
// Fetch the blocks from the API, mapping the table options
useEffect(() => {
debug("looking up blocks");
@@ -146,7 +162,56 @@
debug("results? %b res.blocks.length: %d res.count: %d res.total: %d", !!res, res?.blocks?.length, res?.count, res?.total);
- const tbl =
+ const renderMobileItem: RenderItem = useCallback(block => (
+
+ ), []);
+
+ const { isMobile, list } = useMobileList(
+ loading, res?.blocks || [], "height",
+ paginationTableProps.pagination, paginationChange,
+ sortOptions, defaultOrderBy, defaultOrder,
+ options, setOptions, setOpenSortModal,
+ renderMobileItem
+ );
+
+ return <>
+ {isMobile && list
+ ? list
+ : }
+ {hotkeys}
+ >;
+}
+
+interface DesktopViewProps {
+ loading: boolean;
+ res?: LookupBlocksResponse;
+
+ lowest?: boolean;
+
+ paginationTableProps: PaginationTableProps;
+}
+
+function DesktopView({
+ loading, res,
+ lowest,
+ paginationTableProps,
+}: DesktopViewProps): JSX.Element {
+ const { tStr } = useTFns("blocks.");
+
+ const dateColumnWidth = useDateColumnWidth();
+
+ const columns = useMemo(() => getColumns(
+ tStr, dateColumnWidth, lowest
+ ), [tStr, dateColumnWidth, lowest]);
+
+ return
className="blocks-table"
size="small"
scroll={{ x: true }}
@@ -159,9 +224,4 @@
columns={columns}
/>;
-
- return <>
- {tbl}
- {hotkeys}
- >;
}
diff --git a/src/pages/names/NamesTable.tsx b/src/pages/names/NamesTable.tsx
index a8f6149..70d1b2a 100644
--- a/src/pages/names/NamesTable.tsx
+++ b/src/pages/names/NamesTable.tsx
@@ -263,7 +263,7 @@
loading: boolean;
res?: LookupNamesResponse;
- sortNew: boolean | undefined;
+ sortNew?: boolean;
paginationTableProps: PaginationTableProps;