2024-10-31 19:18:21 +01:00
|
|
|
|
import {
|
2024-11-01 15:07:30 +01:00
|
|
|
|
TabSortState,
|
2024-10-31 19:18:21 +01:00
|
|
|
|
Row,
|
2024-11-01 15:07:30 +01:00
|
|
|
|
Cell,
|
2024-10-31 19:18:21 +01:00
|
|
|
|
Table,
|
|
|
|
|
|
} from "@codex-storage/marketplace-ui-components";
|
2024-10-10 10:34:59 +02:00
|
|
|
|
import DottedMap from "dotted-map/without-countries";
|
2024-11-01 15:07:30 +01:00
|
|
|
|
import { useRef, useState, useCallback } from "react";
|
|
|
|
|
|
import { ErrorCircleIcon } from "../ErrorCircleIcon/ErrorCircleIcon";
|
|
|
|
|
|
import { PeersIcon } from "../Menu/PeersIcon";
|
|
|
|
|
|
import { PeerCountryCell } from "./PeerCountryCell";
|
|
|
|
|
|
import { SuccessCheckIcon } from "../SuccessCheckIcon/SuccessCheckIcon";
|
|
|
|
|
|
import { useDebug } from "../../hooks/useDebug";
|
|
|
|
|
|
import { getMapJSON } from "dotted-map";
|
|
|
|
|
|
import "./Peers.css";
|
|
|
|
|
|
import { PeerPin, PeerSortFn, PeerUtils } from "./peers.util";
|
2024-10-10 10:34:59 +02:00
|
|
|
|
|
|
|
|
|
|
// This function accepts the same arguments as DottedMap in the example above.
|
|
|
|
|
|
const mapJsonString = getMapJSON({ height: 60, grid: "diagonal" });
|
|
|
|
|
|
|
2024-10-30 19:29:14 +01:00
|
|
|
|
type CustomCSSProperties = React.CSSProperties & {
|
2024-11-01 15:07:30 +01:00
|
|
|
|
"--codex-peers-degrees": number;
|
2024-10-31 19:18:21 +01:00
|
|
|
|
};
|
|
|
|
|
|
|
2024-11-01 15:07:30 +01:00
|
|
|
|
const throwOnError = true;
|
2024-10-31 19:18:21 +01:00
|
|
|
|
|
2024-11-01 15:07:30 +01:00
|
|
|
|
export const Peers = () => {
|
2024-10-31 19:18:21 +01:00
|
|
|
|
const ips = useRef<Record<string, string>>({});
|
2024-10-11 17:58:13 +02:00
|
|
|
|
const [pins, setPins] = useState<[PeerPin, number][]>([]);
|
2024-11-01 15:07:30 +01:00
|
|
|
|
const [sortFn, setSortFn] = useState<PeerSortFn | null>(() =>
|
|
|
|
|
|
PeerUtils.sortByBoolean("desc")
|
2024-10-31 19:18:21 +01:00
|
|
|
|
);
|
2024-11-01 15:07:30 +01:00
|
|
|
|
const { data } = useDebug(throwOnError);
|
2024-10-11 17:58:13 +02:00
|
|
|
|
|
2024-10-31 19:18:21 +01:00
|
|
|
|
const onPinAdd = useCallback(
|
|
|
|
|
|
({
|
|
|
|
|
|
countryIso,
|
|
|
|
|
|
ip,
|
|
|
|
|
|
...pin
|
|
|
|
|
|
}: PeerPin & { countryIso: string; ip: string }) => {
|
2024-11-01 15:07:30 +01:00
|
|
|
|
setPins((val) => PeerUtils.incPin(val, pin));
|
2024-10-31 19:18:21 +01:00
|
|
|
|
ips.current[ip] = countryIso;
|
|
|
|
|
|
},
|
|
|
|
|
|
[]
|
|
|
|
|
|
);
|
2024-10-11 17:58:13 +02:00
|
|
|
|
|
|
|
|
|
|
// It’s safe to re-create the map at each render, because of the
|
|
|
|
|
|
// pre-computation it’s super fast ⚡️
|
|
|
|
|
|
const map = new DottedMap({ map: JSON.parse(mapJsonString) });
|
|
|
|
|
|
|
|
|
|
|
|
pins.map(([pin, quantity]) =>
|
|
|
|
|
|
map.addPin({
|
|
|
|
|
|
lat: pin.lat,
|
|
|
|
|
|
lng: pin.lng,
|
|
|
|
|
|
svgOptions: { color: "#d6ff79", radius: 0.8 * quantity },
|
|
|
|
|
|
})
|
|
|
|
|
|
);
|
2024-10-10 10:34:59 +02:00
|
|
|
|
|
2024-10-11 17:58:13 +02:00
|
|
|
|
const svgMap = map.getSVG({
|
2024-10-30 19:29:14 +01:00
|
|
|
|
radius: 0.32,
|
|
|
|
|
|
color: "#969696",
|
2024-10-11 17:58:13 +02:00
|
|
|
|
shape: "circle",
|
2024-10-30 19:29:14 +01:00
|
|
|
|
backgroundColor: "#141414",
|
2024-10-11 17:58:13 +02:00
|
|
|
|
});
|
|
|
|
|
|
|
2024-10-31 19:18:21 +01:00
|
|
|
|
const onSortByCountry = (state: TabSortState) => {
|
|
|
|
|
|
if (!state) {
|
|
|
|
|
|
setSortFn(null);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-11-01 15:07:30 +01:00
|
|
|
|
setSortFn(() => PeerUtils.sortByCountry(state, ips.current));
|
2024-10-31 19:18:21 +01:00
|
|
|
|
};
|
2024-10-11 17:58:13 +02:00
|
|
|
|
|
2024-10-31 19:18:21 +01:00
|
|
|
|
const onSortActive = (state: TabSortState) => {
|
|
|
|
|
|
if (!state) {
|
|
|
|
|
|
setSortFn(null);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-11-01 15:07:30 +01:00
|
|
|
|
setSortFn(() => PeerUtils.sortByBoolean(state));
|
2024-10-31 19:18:21 +01:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const headers = [
|
|
|
|
|
|
["Country", onSortByCountry],
|
|
|
|
|
|
["PeerId"],
|
|
|
|
|
|
["Active", onSortActive],
|
|
|
|
|
|
] satisfies [string, ((state: TabSortState) => void)?][];
|
|
|
|
|
|
|
|
|
|
|
|
const nodes = data?.table?.nodes || [];
|
|
|
|
|
|
const sorted = sortFn ? nodes.slice().sort(sortFn) : nodes;
|
|
|
|
|
|
|
|
|
|
|
|
const rows = sorted.map((node) => (
|
|
|
|
|
|
<Row
|
|
|
|
|
|
cells={[
|
|
|
|
|
|
<PeerCountryCell
|
|
|
|
|
|
onPinAdd={onPinAdd}
|
|
|
|
|
|
address={node.address}></PeerCountryCell>,
|
|
|
|
|
|
<Cell>{node.peerId}</Cell>,
|
|
|
|
|
|
<Cell>
|
|
|
|
|
|
{node.seen ? (
|
|
|
|
|
|
<div className="status--active">
|
|
|
|
|
|
<SuccessCheckIcon variant="primary"></SuccessCheckIcon> Active
|
|
|
|
|
|
</div>
|
|
|
|
|
|
) : (
|
|
|
|
|
|
<div className="status--inactive">
|
|
|
|
|
|
<ErrorCircleIcon></ErrorCircleIcon> Inactive
|
|
|
|
|
|
</div>
|
|
|
|
|
|
)}
|
|
|
|
|
|
</Cell>,
|
|
|
|
|
|
]}></Row>
|
|
|
|
|
|
));
|
|
|
|
|
|
|
2024-11-01 15:07:30 +01:00
|
|
|
|
const actives = PeerUtils.countActives(sorted);
|
|
|
|
|
|
const degrees = PeerUtils.calculareDegrees(sorted);
|
|
|
|
|
|
const good = actives > 0;
|
2024-10-11 17:58:13 +02:00
|
|
|
|
|
2024-10-30 19:29:14 +01:00
|
|
|
|
const styles: CustomCSSProperties = {
|
2024-11-01 15:07:30 +01:00
|
|
|
|
"--codex-peers-degrees": degrees,
|
2024-10-30 19:29:14 +01:00
|
|
|
|
};
|
2024-10-11 17:58:13 +02:00
|
|
|
|
|
2024-10-30 19:29:14 +01:00
|
|
|
|
return (
|
|
|
|
|
|
<div className="peers">
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<div dangerouslySetInnerHTML={{ __html: svgMap }}></div>
|
2024-10-31 19:18:21 +01:00
|
|
|
|
<div>
|
|
|
|
|
|
<ul>
|
|
|
|
|
|
<li>Legend</li>
|
|
|
|
|
|
<li>1-3</li>
|
|
|
|
|
|
<li>3-5</li>
|
|
|
|
|
|
<li>5 +</li>
|
|
|
|
|
|
</ul>
|
|
|
|
|
|
<div className="connections">
|
|
|
|
|
|
<header>
|
|
|
|
|
|
<PeersIcon></PeersIcon>
|
|
|
|
|
|
<span>Connections</span>
|
|
|
|
|
|
</header>
|
|
|
|
|
|
<main style={styles}>
|
|
|
|
|
|
<div>
|
|
|
|
|
|
<div></div>
|
|
|
|
|
|
<span>{actives}</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</main>
|
|
|
|
|
|
<footer>
|
|
|
|
|
|
{good ? (
|
|
|
|
|
|
<>
|
|
|
|
|
|
<SuccessCheckIcon variant="primary"></SuccessCheckIcon>
|
|
|
|
|
|
<span>Peer connections in good standing. </span>
|
|
|
|
|
|
</>
|
|
|
|
|
|
) : (
|
|
|
|
|
|
<>
|
|
|
|
|
|
<ErrorCircleIcon />
|
|
|
|
|
|
<span>No peer connection active. </span>
|
|
|
|
|
|
</>
|
|
|
|
|
|
)}
|
|
|
|
|
|
</footer>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
2024-10-30 19:29:14 +01:00
|
|
|
|
</div>
|
|
|
|
|
|
|
2024-10-31 19:18:21 +01:00
|
|
|
|
<Table headers={headers} rows={rows} defaultSortIndex={2} />
|
2024-10-11 17:58:13 +02:00
|
|
|
|
</div>
|
|
|
|
|
|
);
|
|
|
|
|
|
};
|