Add per-worker stats page with cumulative work and luck
Expose ckpool's per-worker cumulative shares via patch 0004, add a WorkerDetailPage with hashrate, status, best share with per-worker luck (bestever/shares*100), and total work with pool share percentage. Worker names are now clickable in MinersTable and UserDetailPage. Also centers numeric columns below their headers.
This commit is contained in:
@@ -1,30 +1,38 @@
|
||||
// Simple hash-based routing for user pages. The URL hash is the
|
||||
// Simple hash-based routing for detail pages. The URL hash is the
|
||||
// source of truth:
|
||||
// #/ -> dashboard
|
||||
// #/user/<address> -> per-user page
|
||||
// #/ -> dashboard
|
||||
// #/user/<address> -> per-user page
|
||||
// #/worker/<workername> -> per-worker page
|
||||
//
|
||||
// selectUser / clearSelection just mutate the hash; a hashchange
|
||||
// listener reads it back into reactive state so any component in the
|
||||
// tree re-renders when the route changes.
|
||||
// selectUser / selectWorker / clearSelection just mutate the hash;
|
||||
// a hashchange listener reads it back into reactive state so any
|
||||
// component in the tree re-renders when the route changes.
|
||||
|
||||
const USER_PREFIX = "#/user/";
|
||||
const WORKER_PREFIX = "#/worker/";
|
||||
|
||||
export const selection = $state<{ user: string | null }>({
|
||||
user: readHash(),
|
||||
});
|
||||
type Selection = { user: string | null; worker: string | null };
|
||||
|
||||
function readHash(): string | null {
|
||||
if (typeof window === "undefined") return null;
|
||||
export const selection = $state<Selection>(readHash());
|
||||
|
||||
function readHash(): Selection {
|
||||
if (typeof window === "undefined") return { user: null, worker: null };
|
||||
const h = window.location.hash;
|
||||
if (h.startsWith(USER_PREFIX)) {
|
||||
const addr = decodeURIComponent(h.slice(USER_PREFIX.length));
|
||||
return addr || null;
|
||||
if (h.startsWith(WORKER_PREFIX)) {
|
||||
const w = decodeURIComponent(h.slice(WORKER_PREFIX.length));
|
||||
return { user: null, worker: w || null };
|
||||
}
|
||||
return null;
|
||||
if (h.startsWith(USER_PREFIX)) {
|
||||
const u = decodeURIComponent(h.slice(USER_PREFIX.length));
|
||||
return { user: u || null, worker: null };
|
||||
}
|
||||
return { user: null, worker: null };
|
||||
}
|
||||
|
||||
function syncFromHash(): void {
|
||||
selection.user = readHash();
|
||||
const parsed = readHash();
|
||||
selection.user = parsed.user;
|
||||
selection.worker = parsed.worker;
|
||||
}
|
||||
|
||||
if (typeof window !== "undefined") {
|
||||
@@ -32,16 +40,19 @@ if (typeof window !== "undefined") {
|
||||
}
|
||||
|
||||
export function selectUser(address: string): void {
|
||||
// Set the hash; the hashchange listener updates selection.user so
|
||||
// routing-style state stays in one place.
|
||||
window.location.hash = USER_PREFIX + encodeURIComponent(address);
|
||||
}
|
||||
|
||||
export function selectWorker(workername: string): void {
|
||||
window.location.hash = WORKER_PREFIX + encodeURIComponent(workername);
|
||||
}
|
||||
|
||||
export function clearSelection(): void {
|
||||
// Prefer history.back when we arrived here via selectUser so the
|
||||
// browser back arrow does the obvious thing; otherwise just drop
|
||||
// the hash so a fresh page load starts on the dashboard.
|
||||
if (window.history.length > 1 && window.location.hash.startsWith(USER_PREFIX)) {
|
||||
if (
|
||||
window.history.length > 1 &&
|
||||
(window.location.hash.startsWith(USER_PREFIX) ||
|
||||
window.location.hash.startsWith(WORKER_PREFIX))
|
||||
) {
|
||||
window.history.back();
|
||||
} else {
|
||||
window.location.hash = "";
|
||||
|
||||
Reference in New Issue
Block a user