mirror of
https://github.com/codex-storage/codex-marketplace-ui.git
synced 2025-02-23 21:28:26 +00:00
Improve design and merge folders
This commit is contained in:
parent
9ce4e55593
commit
635b115a4d
10
package-lock.json
generated
10
package-lock.json
generated
@ -9,7 +9,7 @@
|
||||
"version": "0.0.7",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@codex-storage/marketplace-ui-components": "^0.0.28",
|
||||
"@codex-storage/marketplace-ui-components": "^0.0.29",
|
||||
"@codex-storage/sdk-js": "^0.0.12",
|
||||
"@sentry/browser": "^8.32.0",
|
||||
"@sentry/react": "^8.31.0",
|
||||
@ -378,9 +378,9 @@
|
||||
"peer": true
|
||||
},
|
||||
"node_modules/@codex-storage/marketplace-ui-components": {
|
||||
"version": "0.0.28",
|
||||
"resolved": "https://registry.npmjs.org/@codex-storage/marketplace-ui-components/-/marketplace-ui-components-0.0.28.tgz",
|
||||
"integrity": "sha512-C2Wj6Rb4RzdkXRtXsj+dnzP5NwnJbmlHfQ1R2jxqgaJ4DXBDnAnEGiiljHzHE2oP0P4Exx7CQBjrP7vWChMpNg==",
|
||||
"version": "0.0.29",
|
||||
"resolved": "https://registry.npmjs.org/@codex-storage/marketplace-ui-components/-/marketplace-ui-components-0.0.29.tgz",
|
||||
"integrity": "sha512-ctbjE/m2bOu3SXvlHcB8hulZyzFFymizjKPonNjHHriyCwlGMgryLeKLty2XpzAWw/JR/WZPSZTwlWJ7qOy0hQ==",
|
||||
"dependencies": {
|
||||
"lucide-react": "^0.453.0"
|
||||
},
|
||||
@ -4841,4 +4841,4 @@
|
||||
"integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg=="
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -24,7 +24,7 @@
|
||||
"React"
|
||||
],
|
||||
"dependencies": {
|
||||
"@codex-storage/marketplace-ui-components": "^0.0.28",
|
||||
"@codex-storage/marketplace-ui-components": "^0.0.29",
|
||||
"@codex-storage/sdk-js": "^0.0.12",
|
||||
"@sentry/browser": "^8.32.0",
|
||||
"@sentry/react": "^8.31.0",
|
||||
|
@ -4,25 +4,37 @@ import { classnames } from "../../utils/classnames";
|
||||
import { useNetwork } from "../../network/useNetwork";
|
||||
import { NetworkFlashIcon } from "../NetworkFlashIcon/NetworkFlashIcon";
|
||||
import { useQueryClient } from "@tanstack/react-query";
|
||||
import { useEffect } from "react";
|
||||
import { ReactElement, useEffect } from "react";
|
||||
import { useCodexConnection } from "../../hooks/useCodexConnection";
|
||||
import { NodesIcon } from "../Menu/NodesIcon";
|
||||
import { usePersistence } from "../../hooks/usePersistence";
|
||||
import { useNavigate, useRouterState } from "@tanstack/react-router";
|
||||
import { PeersIcon } from "../Menu/PeersIcon";
|
||||
import { SettingsIcon } from "../Menu/SettingsIcon";
|
||||
|
||||
type Props = {
|
||||
/**
|
||||
* Event triggered when the menu is expanding, after a click on the
|
||||
* menu button.
|
||||
*/
|
||||
onExpand: () => void;
|
||||
onIconClick: () => void;
|
||||
};
|
||||
|
||||
export function AppBar(_: Props) {
|
||||
console.debug(_);
|
||||
const icons: Record<string, ReactElement> = {
|
||||
dashboard: <DashboardIcon />,
|
||||
peers: <PeersIcon />,
|
||||
settings: <SettingsIcon />,
|
||||
};
|
||||
|
||||
const descriptions: Record<string, string> = {
|
||||
dashboard: "Get Overview of your Codex Vault",
|
||||
peers: "Monitor your Codex peer connections",
|
||||
settings: "Manage your Codex Vault",
|
||||
};
|
||||
|
||||
export function AppBar({ onIconClick }: Props) {
|
||||
const online = useNetwork();
|
||||
const queryClient = useQueryClient();
|
||||
const codex = useCodexConnection();
|
||||
const persistence = usePersistence(codex.enabled);
|
||||
const router = useRouterState();
|
||||
const navigate = useNavigate({ from: router.location.pathname });
|
||||
|
||||
useEffect(() => {
|
||||
queryClient.invalidateQueries({
|
||||
@ -31,39 +43,45 @@ export function AppBar(_: Props) {
|
||||
});
|
||||
}, [queryClient, codex.enabled]);
|
||||
|
||||
const onNodeClick = () => navigate({ to: "/dashboard/settings" });
|
||||
|
||||
const offline = !online || !codex.enabled;
|
||||
|
||||
const title =
|
||||
router.location.pathname.split("/")[2] ||
|
||||
router.location.pathname.split("/")[1];
|
||||
|
||||
return (
|
||||
<div
|
||||
className={classnames(
|
||||
["app-bar"],
|
||||
["app-bar--offline", offline],
|
||||
["app-bar--no-persistence", !persistence.enabled]
|
||||
)}>
|
||||
<div className="row gap">
|
||||
{/* <a className="appBar-burger" onClick={onExpand}>
|
||||
<>
|
||||
<div
|
||||
className={classnames(
|
||||
["app-bar"],
|
||||
["app-bar--offline", offline],
|
||||
["app-bar--no-persistence", !persistence.enabled]
|
||||
)}>
|
||||
<div className="row gap">
|
||||
{/* <a className="appBar-burger" onClick={onExpand}>
|
||||
<Menu size={"1.25rem"} />
|
||||
</a> */}
|
||||
|
||||
<span>
|
||||
<DashboardIcon />
|
||||
</span>
|
||||
<span onClick={onIconClick}>{icons[title]}</span>
|
||||
|
||||
<div>
|
||||
<h1>Dashboard</h1>
|
||||
<h2>Get Overview of your Codex Vault</h2>
|
||||
<div>
|
||||
<h1>{title}</h1>
|
||||
<h2>{descriptions[title]}</h2>
|
||||
</div>
|
||||
</div>
|
||||
<aside className="row gap">
|
||||
<div className="row gap">
|
||||
<NetworkFlashIcon />
|
||||
<span>Network</span>
|
||||
</div>
|
||||
<div className="row gap" onClick={onNodeClick}>
|
||||
<NodesIcon variant={codex.enabled ? "success" : "failure"} />
|
||||
<span>Node</span>
|
||||
</div>
|
||||
</aside>
|
||||
</div>
|
||||
<aside className="row gap">
|
||||
<div className="row gap">
|
||||
<NetworkFlashIcon />
|
||||
<span>Network</span>
|
||||
</div>
|
||||
<div className="row gap">
|
||||
<NodesIcon variant={codex.enabled ? "success" : "failure"} />
|
||||
<span>Node</span>
|
||||
</div>
|
||||
</aside>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
@ -3,10 +3,7 @@
|
||||
justify-content: space-between;
|
||||
border-bottom: 1px solid var(--codex-border-color);
|
||||
display: flex;
|
||||
padding-right: 48px;
|
||||
padding-left: 48px;
|
||||
padding-top: 20px;
|
||||
padding-bottom: 20px;
|
||||
padding: 16px;
|
||||
border-bottom: 1px solid #96969633;
|
||||
box-sizing: border-box;
|
||||
background-color: #1c1c1c;
|
||||
@ -15,6 +12,12 @@
|
||||
top: 0;
|
||||
z-index: 1;
|
||||
|
||||
@media (min-width: 1000px) {
|
||||
& {
|
||||
padding: 20px 48px;
|
||||
}
|
||||
}
|
||||
|
||||
&:not(.app-bar--offline):not(.app-bar--no-persistence) {
|
||||
border-right-color: #6ccc93;
|
||||
}
|
||||
@ -33,6 +36,7 @@
|
||||
sans-serif;
|
||||
letter-spacing: -0.015em;
|
||||
color: white;
|
||||
text-transform: capitalize;
|
||||
}
|
||||
|
||||
h2 {
|
||||
@ -53,6 +57,13 @@
|
||||
justify-content: center;
|
||||
border: 1px solid #353639;
|
||||
border-radius: 50%;
|
||||
color: #969696;
|
||||
}
|
||||
|
||||
@media (max-width: 999px) {
|
||||
& {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -69,6 +80,16 @@
|
||||
sans-serif;
|
||||
letter-spacing: -0.006em;
|
||||
color: #8d8d8d;
|
||||
|
||||
@media (max-width: 999px) {
|
||||
& {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
div:last-child {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
.background-img {
|
||||
position: absolute;
|
||||
position: fixed;
|
||||
right: -40px;
|
||||
max-height: 90%;
|
||||
width: auto;
|
||||
@ -7,7 +7,7 @@
|
||||
transition: opacity 0.35s;
|
||||
opacity: 0.3;
|
||||
|
||||
@media (min-width: 1200px) {
|
||||
@media (min-width: 1580px) {
|
||||
& {
|
||||
opacity: 1;
|
||||
}
|
||||
|
@ -1,8 +1,4 @@
|
||||
import {
|
||||
Backdrop,
|
||||
ButtonIcon,
|
||||
SimpleText,
|
||||
} from "@codex-storage/marketplace-ui-components";
|
||||
import { Backdrop, ButtonIcon } from "@codex-storage/marketplace-ui-components";
|
||||
import { CheckCircle, Folder } from "lucide-react";
|
||||
import "./FolderButton.css";
|
||||
import { useState } from "react";
|
||||
@ -49,9 +45,9 @@ export function FolderButton({ folders, onFolderToggle }: Props) {
|
||||
<div>{folder}</div>
|
||||
<div>
|
||||
{isActive && (
|
||||
<SimpleText variant="primary">
|
||||
<span className="text--primary">
|
||||
<CheckCircle size={"1rem"}></CheckCircle>
|
||||
</SimpleText>
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
@ -1,85 +1,124 @@
|
||||
.address {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
gap: 16px;
|
||||
|
||||
> div {
|
||||
.health-checks {
|
||||
.address {
|
||||
display: flex;
|
||||
position: relative;
|
||||
}
|
||||
gap: 16px;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
|
||||
svg {
|
||||
position: absolute;
|
||||
top: 68px;
|
||||
bottom: 0;
|
||||
right: 18px;
|
||||
}
|
||||
@media (min-width: 1000px) {
|
||||
& {
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
.refresh {
|
||||
position: relative;
|
||||
top: 24px;
|
||||
cursor: pointer;
|
||||
> div {
|
||||
position: relative;
|
||||
|
||||
@media (max-width: 999px) {
|
||||
&:not(.refresh) {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
svg {
|
||||
position: initial;
|
||||
position: absolute;
|
||||
top: 68px;
|
||||
bottom: 0;
|
||||
right: 18px;
|
||||
}
|
||||
|
||||
&.address--fetching .refresh svg {
|
||||
animation: rotate 2s linear infinite;
|
||||
.refresh {
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
cursor: pointer;
|
||||
|
||||
@media (max-width: 999px) {
|
||||
& {
|
||||
top: 0px;
|
||||
left: 0;
|
||||
transform: scale(1.5);
|
||||
right: 0;
|
||||
margin: auto;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1000px) {
|
||||
& {
|
||||
top: 18px;
|
||||
}
|
||||
}
|
||||
|
||||
svg {
|
||||
position: initial;
|
||||
}
|
||||
|
||||
&.address--fetching .refresh svg {
|
||||
animation: rotate 2s linear infinite;
|
||||
}
|
||||
}
|
||||
|
||||
input[type="number"] {
|
||||
width: 150px;
|
||||
}
|
||||
|
||||
input[type="number"]::-webkit-outer-spin-button,
|
||||
input[type="number"]::-webkit-inner-spin-button {
|
||||
-webkit-appearance: none;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
input[type="number"] {
|
||||
-moz-appearance: textfield;
|
||||
}
|
||||
|
||||
@media (max-width: 999px) {
|
||||
input[type="number"],
|
||||
input {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
input[type="number"] {
|
||||
width: 150px;
|
||||
p {
|
||||
font-family: Azeret Mono;
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
line-height: 14px;
|
||||
color: #828282;
|
||||
padding-left: 1.25rem;
|
||||
margin-top: 1.75rem;
|
||||
margin-bottom: 3rem;
|
||||
}
|
||||
|
||||
input[type="number"]::-webkit-outer-spin-button,
|
||||
input[type="number"]::-webkit-inner-spin-button {
|
||||
-webkit-appearance: none;
|
||||
margin: 0;
|
||||
}
|
||||
ul {
|
||||
margin-bottom: 32px;
|
||||
|
||||
input[type="number"] {
|
||||
-moz-appearance: textfield;
|
||||
}
|
||||
}
|
||||
|
||||
.helper {
|
||||
font-family: Azeret Mono;
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
line-height: 14px;
|
||||
color: #828282;
|
||||
padding-left: 1.25rem;
|
||||
margin-top: 1.75rem;
|
||||
margin-bottom: 3rem;
|
||||
}
|
||||
|
||||
.health-checks {
|
||||
margin-bottom: 32px;
|
||||
|
||||
li {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 16px 0;
|
||||
gap: 16px;
|
||||
border-top: 1px solid #96969633;
|
||||
border-bottom: 1px solid #96969633;
|
||||
|
||||
&:first-child {
|
||||
font-family: Inter;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
line-height: 24px;
|
||||
letter-spacing: -0.011em;
|
||||
}
|
||||
|
||||
span {
|
||||
li {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
justify-content: center;
|
||||
padding: 16px 0;
|
||||
gap: 16px;
|
||||
border-top: 1px solid #96969633;
|
||||
border-bottom: 1px solid #96969633;
|
||||
|
||||
&:first-child {
|
||||
font-family: Inter;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
line-height: 24px;
|
||||
letter-spacing: -0.011em;
|
||||
}
|
||||
|
||||
span {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
import { useQueryClient } from "@tanstack/react-query";
|
||||
import { useEffect, useState, ClipboardEvent } from "react";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useDebug } from "../../hooks/useDebug";
|
||||
import { usePersistence } from "../../hooks/usePersistence";
|
||||
import { usePortForwarding } from "../../hooks/usePortForwarding";
|
||||
@ -11,7 +11,6 @@ import { HealthCheckIcon } from "./HealthCheckIcon";
|
||||
import { Input } from "@codex-storage/marketplace-ui-components";
|
||||
import { classnames } from "../../utils/classnames";
|
||||
import { DebugUtils } from "../../utils/debug";
|
||||
import { Strings } from "../../utils/strings";
|
||||
import { RefreshIcon } from "../RefreshIcon/RefreshIcon";
|
||||
import "./HealthChecks.css";
|
||||
|
||||
@ -27,8 +26,14 @@ export function HealthChecks({ online, onStepValid }: Props) {
|
||||
const codex = useDebug(throwOnError);
|
||||
const portForwarding = usePortForwarding(codex.data);
|
||||
const persistence = usePersistence(codex.isSuccess);
|
||||
const [isInvalid, setIsInvalid] = useState(false);
|
||||
const [url, setUrl] = useState(CodexSdk.url);
|
||||
const [isAddressInvalid, setIsAddressInvalid] = useState(false);
|
||||
const [isPortInvalid, setIsPortInvalid] = useState(false);
|
||||
const [address, setAddress] = useState(
|
||||
CodexSdk.url().split(":")[0] + ":" + CodexSdk.url().split(":")[1]
|
||||
);
|
||||
const [port, setPort] = useState(
|
||||
parseInt(CodexSdk.url().split(":")[2] || "80", 10)
|
||||
);
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
useEffect(() => {
|
||||
@ -46,48 +51,38 @@ export function HealthChecks({ online, onStepValid }: Props) {
|
||||
]);
|
||||
|
||||
const onAddressChange = (e: React.FormEvent<HTMLInputElement>) => {
|
||||
const [, port] = Strings.splitURLAndPort(url);
|
||||
const element = e.currentTarget;
|
||||
const value = e.currentTarget.value;
|
||||
const parts = e.currentTarget.value.split(":");
|
||||
|
||||
if (
|
||||
value.startsWith("http://") === false &&
|
||||
value.startsWith("https://") === false
|
||||
) {
|
||||
setIsInvalid(true);
|
||||
return;
|
||||
}
|
||||
setIsAddressInvalid(!element.checkValidity());
|
||||
|
||||
setIsInvalid(!element.checkValidity());
|
||||
setUrl(value + ":" + port);
|
||||
};
|
||||
if (parts.length > 2) {
|
||||
const [protocol, addr, port] = parts;
|
||||
setAddress(protocol + ":" + addr);
|
||||
|
||||
const onPaste = (e: ClipboardEvent) => {
|
||||
const text = e.clipboardData?.getData("text") || "";
|
||||
|
||||
try {
|
||||
new URL(text);
|
||||
setUrl(text);
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
} catch (_) {
|
||||
// Nothing to do here
|
||||
const p = parseInt(port, 10);
|
||||
if (!isNaN(p)) {
|
||||
setPort(p);
|
||||
}
|
||||
} else {
|
||||
setAddress(parts.join(":"));
|
||||
}
|
||||
};
|
||||
|
||||
const onPortChange = (e: React.FormEvent<HTMLInputElement>) => {
|
||||
const [address] = Strings.splitURLAndPort(url);
|
||||
const element = e.currentTarget;
|
||||
const value = element.value;
|
||||
|
||||
setUrl(address + ":" + value);
|
||||
setIsPortInvalid(!element.checkValidity());
|
||||
setPort(parseInt(value, 10));
|
||||
};
|
||||
|
||||
const onSave = () => {
|
||||
if (isInvalid === true) {
|
||||
if (isAddressInvalid || isPortInvalid) {
|
||||
return;
|
||||
}
|
||||
|
||||
CodexSdk.updateURL(url)
|
||||
CodexSdk.updateURL(address + ":" + port)
|
||||
.then(() => queryClient.invalidateQueries())
|
||||
.then(() => codex.refetch());
|
||||
};
|
||||
@ -100,10 +95,9 @@ export function HealthChecks({ online, onStepValid }: Props) {
|
||||
forwardingPortValue = port.data;
|
||||
}
|
||||
}
|
||||
const [address, port] = Strings.splitURLAndPort(url);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="health-checks">
|
||||
<div
|
||||
className={classnames(
|
||||
["address"],
|
||||
@ -111,15 +105,15 @@ export function HealthChecks({ online, onStepValid }: Props) {
|
||||
)}>
|
||||
<div>
|
||||
<Input
|
||||
onPaste={onPaste}
|
||||
id="url"
|
||||
type="url"
|
||||
label="Address"
|
||||
isInvalid={isInvalid}
|
||||
required
|
||||
isInvalid={isAddressInvalid}
|
||||
onChange={onAddressChange}
|
||||
value={address}
|
||||
placeholder="127.0.0.1"></Input>
|
||||
{isInvalid ? (
|
||||
{isAddressInvalid ? (
|
||||
<ErrorCircleIcon />
|
||||
) : (
|
||||
<SuccessCheckIcon variant="default" />
|
||||
@ -133,6 +127,7 @@ export function HealthChecks({ online, onStepValid }: Props) {
|
||||
type="number"
|
||||
onChange={onPortChange}
|
||||
value={port}
|
||||
isInvalid={isPortInvalid}
|
||||
placeholder="8080"></Input>
|
||||
<SuccessCheckIcon variant="default"></SuccessCheckIcon>
|
||||
</div>
|
||||
@ -142,11 +137,14 @@ export function HealthChecks({ online, onStepValid }: Props) {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ul className="helper">
|
||||
<li>Port forwarding should be default {forwardingPortValue}.</li>
|
||||
</ul>
|
||||
<p>
|
||||
<li>
|
||||
Port forwarding should be {forwardingPortValue} for TCP and 8090 by
|
||||
default for UDP.
|
||||
</li>
|
||||
</p>
|
||||
|
||||
<ul className="health-checks">
|
||||
<ul>
|
||||
<li>
|
||||
<span>
|
||||
<HealthCheckIcon />
|
||||
@ -194,6 +192,6 @@ export function HealthChecks({ online, onStepValid }: Props) {
|
||||
Marketplace
|
||||
</li>
|
||||
</ul>
|
||||
</>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
@ -44,7 +44,7 @@ export function Logotype({ height, width, className }: Props) {
|
||||
<path
|
||||
d="M63.4017 39.9316C63.4017 36.8129 65.93 34.2846 69.0487 34.2846H114.931C118.05 34.2846 120.578 36.8129 120.578 39.9316C120.578 43.0504 118.05 45.5787 114.931 45.5787H69.0487C65.93 45.5787 63.4017 43.0504 63.4017 39.9316Z"
|
||||
stroke="#7F948D"
|
||||
stroke-width="0.705882"
|
||||
strokeWidth="0.705882"
|
||||
/>
|
||||
<path
|
||||
d="M113.049 42.0462H109.012L108.46 43.1081H106.814L110.134 36.7551H111.863L115.284 43.1081H113.619L113.049 42.0462ZM112.378 40.7847L111.017 38.2798L109.674 40.7847H112.378Z"
|
||||
|
@ -1,7 +1,6 @@
|
||||
import { Backdrop } from "@codex-storage/marketplace-ui-components";
|
||||
import { attributes } from "../../utils/attributes";
|
||||
import "./menu.css";
|
||||
import { ComponentType, useEffect, useState } from "react";
|
||||
import { ComponentType, useState } from "react";
|
||||
import { Logo } from "../Logo/Logo";
|
||||
import { Logotype } from "../Logotype/Logotype";
|
||||
import { ExpandIcon } from "./ExpandIcon";
|
||||
@ -38,25 +37,12 @@ export type MenuItem =
|
||||
};
|
||||
|
||||
type Props = {
|
||||
/**
|
||||
* If true, the menu will be displayed
|
||||
*/
|
||||
expanded: boolean;
|
||||
|
||||
onClose: () => void;
|
||||
|
||||
onOpen?: () => void;
|
||||
isMobileMenuDisplayed: boolean;
|
||||
};
|
||||
|
||||
export function Menu({ expanded, onClose, onOpen }: Props) {
|
||||
export function Menu({ isMobileMenuDisplayed }: Props) {
|
||||
const [isExpanded, setIsExpanded] = useState<boolean | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (expanded && onOpen) {
|
||||
onOpen();
|
||||
}
|
||||
}, [expanded, onOpen]);
|
||||
|
||||
const onLogoClick = () => {
|
||||
if (isExpanded === false) {
|
||||
setIsExpanded(true);
|
||||
@ -67,15 +53,18 @@ export function Menu({ expanded, onClose, onOpen }: Props) {
|
||||
|
||||
return (
|
||||
<>
|
||||
<Backdrop onClose={onClose} open={expanded} />
|
||||
|
||||
<aside
|
||||
className={classnames(
|
||||
[`menu`],
|
||||
["menu--expanded", isExpanded === true],
|
||||
["menu--unexpanded", isExpanded === false]
|
||||
)}
|
||||
{...attributes({ "aria-expanded": expanded })}>
|
||||
className={classnames([`menu`])}
|
||||
{...attributes(
|
||||
isExpanded === null
|
||||
? {
|
||||
"aria-hidden": (!isMobileMenuDisplayed).toString(),
|
||||
}
|
||||
: {
|
||||
"aria-expanded": isExpanded.toString(),
|
||||
"aria-hidden": (!isMobileMenuDisplayed).toString(),
|
||||
}
|
||||
)}>
|
||||
<div>
|
||||
<header>
|
||||
<Logo onClick={onLogoClick} width={30} />
|
||||
|
@ -3,25 +3,34 @@
|
||||
flex-direction: column;
|
||||
background-color: #1c1c1c;
|
||||
border-radius: var(--codex-border-radius);
|
||||
transform: translatex(-500px);
|
||||
transition: transform 0.25s;
|
||||
transition: left 0.25s;
|
||||
position: sticky;
|
||||
z-index: 10;
|
||||
view-transition-name: main-menu;
|
||||
height: 100%;
|
||||
top: 0;
|
||||
width: 80px;
|
||||
transition: width 0.5s;
|
||||
transition:
|
||||
width 0.5s,
|
||||
font-size 0.5s,
|
||||
left 0.05s;
|
||||
min-width: 0;
|
||||
width: 272px;
|
||||
transform: translatex(0px);
|
||||
|
||||
&[aria-expanded] {
|
||||
transform: translatex(0);
|
||||
min-width: 200px;
|
||||
@media (max-width: 999px) {
|
||||
&,
|
||||
&[aria-hidden] {
|
||||
width: 272px;
|
||||
position: fixed;
|
||||
z-index: 12;
|
||||
left: -300px;
|
||||
}
|
||||
|
||||
&[aria-hidden="false"] {
|
||||
left: 0px;
|
||||
}
|
||||
}
|
||||
|
||||
&:not(.menu--unexpanded) a[data-title]:hover::after {
|
||||
&:not([aria-expanded="false"]) a[data-title]:hover::after {
|
||||
content: attr(data-title);
|
||||
background-color: #2f2f2f;
|
||||
color: #fff;
|
||||
@ -36,11 +45,11 @@
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
&.menu--expanded {
|
||||
&[aria-expanded="true"] {
|
||||
width: 272px;
|
||||
}
|
||||
|
||||
&.menu--unexpanded {
|
||||
&[aria-expanded="false"] {
|
||||
width: 80px;
|
||||
|
||||
.items {
|
||||
|
@ -1,16 +1,25 @@
|
||||
.onboarding {
|
||||
width: 100%;
|
||||
padding: 3rem 6rem;
|
||||
padding: 16px;
|
||||
display: flex;
|
||||
|
||||
@media (min-width: 1000px) {
|
||||
& {
|
||||
padding: 3rem 6rem;
|
||||
}
|
||||
}
|
||||
|
||||
> section {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
> section:first-child {
|
||||
max-width: 500px;
|
||||
@media (min-width: 1000px) {
|
||||
> section:first-child {
|
||||
max-width: 500px;
|
||||
}
|
||||
}
|
||||
|
||||
section {
|
||||
@ -177,12 +186,19 @@
|
||||
.navigation {
|
||||
cursor: pointer;
|
||||
position: absolute;
|
||||
right: 6rem;
|
||||
bottom: 3rem;
|
||||
right: 16px;
|
||||
bottom: 16px;
|
||||
border-bottom: none;
|
||||
text-decoration: none;
|
||||
z-index: 1;
|
||||
|
||||
@media (min-width: 1000px) {
|
||||
& {
|
||||
right: 6rem;
|
||||
bottom: 3rem;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
animation-name: example;
|
||||
animation-duration: 2.5s;
|
||||
|
@ -6,7 +6,7 @@ import { useEffect } from "react";
|
||||
|
||||
export type Props = {
|
||||
address: string;
|
||||
onPinAdd: (pin: PeerPin) => void;
|
||||
onPinAdd: (pin: PeerPin & { countryIso: string; ip: string }) => void;
|
||||
};
|
||||
|
||||
const getFlagEmoji = (countryCode: string) => {
|
||||
@ -50,6 +50,8 @@ export function PeerCountryCell({ address, onPinAdd }: Props) {
|
||||
onPinAdd({
|
||||
lat: data.latitude,
|
||||
lng: data.longitude,
|
||||
countryIso: data.country_iso,
|
||||
ip: data.ip,
|
||||
});
|
||||
}
|
||||
}, [data, onPinAdd]);
|
||||
|
@ -1,7 +1,16 @@
|
||||
.user-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 32px;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
gap: 16px;
|
||||
|
||||
@media (min-width: 1000px) {
|
||||
& {
|
||||
flex-direction: row;
|
||||
gap: 32px;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
|
||||
.emoji {
|
||||
position: relative;
|
||||
@ -9,8 +18,14 @@
|
||||
aside {
|
||||
position: absolute;
|
||||
top: -140px;
|
||||
left: 116px;
|
||||
left: 0px;
|
||||
z-index: 1;
|
||||
|
||||
@media (min-width: 1000px) {
|
||||
& {
|
||||
left: 116px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.input input {
|
||||
@ -19,4 +34,14 @@
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.input input {
|
||||
width: 100%;
|
||||
|
||||
@media (min-width: 1000px) {
|
||||
& {
|
||||
width: inherit;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -39,6 +39,7 @@ export function UserInfo({ onNameChange }: Props) {
|
||||
<div className="emoji">
|
||||
{areEmojiVisible && (
|
||||
<EmojiPicker
|
||||
width={"auto"}
|
||||
emojiStyle={EmojiStyle.NATIVE}
|
||||
theme={Theme.DARK}
|
||||
lazyLoadEmojis={true}
|
||||
|
@ -40,6 +40,7 @@ if (import.meta.env.PROD && !import.meta.env.CI) {
|
||||
// Create a new router instance
|
||||
const router = createRouter({
|
||||
routeTree,
|
||||
defaultPreload: "viewport",
|
||||
defaultNotFoundComponent: () => {
|
||||
return (
|
||||
<Failure
|
||||
|
@ -22,7 +22,6 @@ import { Route as DashboardWalletImport } from './routes/dashboard/wallet'
|
||||
import { Route as DashboardSettingsImport } from './routes/dashboard/settings'
|
||||
import { Route as DashboardRequestsImport } from './routes/dashboard/requests'
|
||||
import { Route as DashboardPurchasesImport } from './routes/dashboard/purchases'
|
||||
import { Route as DashboardPeersImport } from './routes/dashboard/peers'
|
||||
import { Route as DashboardNodesImport } from './routes/dashboard/nodes'
|
||||
import { Route as DashboardLogsImport } from './routes/dashboard/logs'
|
||||
import { Route as DashboardHelpImport } from './routes/dashboard/help'
|
||||
@ -30,7 +29,6 @@ import { Route as DashboardFilesImport } from './routes/dashboard/files'
|
||||
import { Route as DashboardFavoritesImport } from './routes/dashboard/favorites'
|
||||
import { Route as DashboardDisclaimerImport } from './routes/dashboard/disclaimer'
|
||||
import { Route as DashboardDeviceImport } from './routes/dashboard/device'
|
||||
import { Route as DashboardAvailabilitiesImport } from './routes/dashboard/availabilities'
|
||||
import { Route as DashboardAnalyticsImport } from './routes/dashboard/analytics'
|
||||
import { Route as DashboardAboutImport } from './routes/dashboard/about'
|
||||
|
||||
@ -73,11 +71,6 @@ const DashboardIndexRoute = DashboardIndexImport.update({
|
||||
getParentRoute: () => DashboardRoute,
|
||||
} as any)
|
||||
|
||||
const DashboardWalletRoute = DashboardWalletImport.update({
|
||||
id: '/wallet',
|
||||
path: '/wallet',
|
||||
getParentRoute: () => DashboardRoute,
|
||||
} as any)
|
||||
const DashboardPeersLazyRoute = DashboardPeersLazyImport.update({
|
||||
id: '/peers',
|
||||
path: '/peers',
|
||||
@ -95,6 +88,12 @@ const DashboardAvailabilitiesLazyRoute =
|
||||
import('./routes/dashboard/availabilities.lazy').then((d) => d.Route),
|
||||
)
|
||||
|
||||
const DashboardWalletRoute = DashboardWalletImport.update({
|
||||
id: '/wallet',
|
||||
path: '/wallet',
|
||||
getParentRoute: () => DashboardRoute,
|
||||
} as any)
|
||||
|
||||
const DashboardSettingsRoute = DashboardSettingsImport.update({
|
||||
id: '/settings',
|
||||
path: '/settings',
|
||||
@ -113,12 +112,6 @@ const DashboardPurchasesRoute = DashboardPurchasesImport.update({
|
||||
getParentRoute: () => DashboardRoute,
|
||||
} as any)
|
||||
|
||||
const DashboardPeersRoute = DashboardPeersImport.update({
|
||||
id: '/peers',
|
||||
path: '/peers',
|
||||
getParentRoute: () => DashboardRoute,
|
||||
} as any)
|
||||
|
||||
const DashboardNodesRoute = DashboardNodesImport.update({
|
||||
id: '/nodes',
|
||||
path: '/nodes',
|
||||
@ -161,12 +154,6 @@ const DashboardDeviceRoute = DashboardDeviceImport.update({
|
||||
getParentRoute: () => DashboardRoute,
|
||||
} as any)
|
||||
|
||||
const DashboardAvailabilitiesRoute = DashboardAvailabilitiesImport.update({
|
||||
id: '/availabilities',
|
||||
path: '/availabilities',
|
||||
getParentRoute: () => DashboardRoute,
|
||||
} as any)
|
||||
|
||||
const DashboardAnalyticsRoute = DashboardAnalyticsImport.update({
|
||||
id: '/analytics',
|
||||
path: '/analytics',
|
||||
@ -225,13 +212,6 @@ declare module '@tanstack/react-router' {
|
||||
preLoaderRoute: typeof DashboardAnalyticsImport
|
||||
parentRoute: typeof DashboardImport
|
||||
}
|
||||
'/dashboard/availabilities': {
|
||||
id: '/dashboard/availabilities'
|
||||
path: '/availabilities'
|
||||
fullPath: '/dashboard/availabilities'
|
||||
preLoaderRoute: typeof DashboardAvailabilitiesImport
|
||||
parentRoute: typeof DashboardImport
|
||||
}
|
||||
'/dashboard/device': {
|
||||
id: '/dashboard/device'
|
||||
path: '/device'
|
||||
@ -281,13 +261,6 @@ declare module '@tanstack/react-router' {
|
||||
preLoaderRoute: typeof DashboardNodesImport
|
||||
parentRoute: typeof DashboardImport
|
||||
}
|
||||
'/dashboard/peers': {
|
||||
id: '/dashboard/peers'
|
||||
path: '/peers'
|
||||
fullPath: '/dashboard/peers'
|
||||
preLoaderRoute: typeof DashboardPeersImport
|
||||
parentRoute: typeof DashboardImport
|
||||
}
|
||||
'/dashboard/purchases': {
|
||||
id: '/dashboard/purchases'
|
||||
path: '/purchases'
|
||||
@ -314,178 +287,154 @@ declare module '@tanstack/react-router' {
|
||||
path: '/wallet'
|
||||
fullPath: '/dashboard/wallet'
|
||||
preLoaderRoute: typeof DashboardWalletImport
|
||||
'/dashboard/availabilities': {
|
||||
id: '/dashboard/availabilities'
|
||||
path: '/availabilities'
|
||||
fullPath: '/dashboard/availabilities'
|
||||
preLoaderRoute: typeof DashboardAvailabilitiesLazyImport
|
||||
parentRoute: typeof DashboardImport
|
||||
}
|
||||
'/dashboard/peers': {
|
||||
id: '/dashboard/peers'
|
||||
path: '/peers'
|
||||
fullPath: '/dashboard/peers'
|
||||
preLoaderRoute: typeof DashboardPeersLazyImport
|
||||
parentRoute: typeof DashboardImport
|
||||
}
|
||||
'/dashboard/': {
|
||||
id: '/dashboard/'
|
||||
path: '/'
|
||||
fullPath: '/dashboard/'
|
||||
preLoaderRoute: typeof DashboardIndexImport
|
||||
parentRoute: typeof DashboardImport
|
||||
}
|
||||
parentRoute: typeof DashboardImport
|
||||
}
|
||||
'/dashboard/availabilities': {
|
||||
id: '/dashboard/availabilities'
|
||||
path: '/availabilities'
|
||||
fullPath: '/dashboard/availabilities'
|
||||
preLoaderRoute: typeof DashboardAvailabilitiesLazyImport
|
||||
parentRoute: typeof DashboardImport
|
||||
}
|
||||
'/dashboard/peers': {
|
||||
id: '/dashboard/peers'
|
||||
path: '/peers'
|
||||
fullPath: '/dashboard/peers'
|
||||
preLoaderRoute: typeof DashboardPeersLazyImport
|
||||
parentRoute: typeof DashboardImport
|
||||
}
|
||||
'/dashboard/': {
|
||||
id: '/dashboard/'
|
||||
path: '/'
|
||||
fullPath: '/dashboard/'
|
||||
preLoaderRoute: typeof DashboardIndexImport
|
||||
parentRoute: typeof DashboardImport
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create and export the route tree
|
||||
// Create and export the route tree
|
||||
|
||||
interface DashboardRouteChildren {
|
||||
DashboardAboutRoute: typeof DashboardAboutRoute
|
||||
DashboardAnalyticsRoute: typeof DashboardAnalyticsRoute
|
||||
DashboardAvailabilitiesRoute: typeof DashboardAvailabilitiesRoute
|
||||
DashboardDeviceRoute: typeof DashboardDeviceRoute
|
||||
DashboardDisclaimerRoute: typeof DashboardDisclaimerRoute
|
||||
DashboardFavoritesRoute: typeof DashboardFavoritesRoute
|
||||
DashboardFilesRoute: typeof DashboardFilesRoute
|
||||
DashboardHelpRoute: typeof DashboardHelpRoute
|
||||
DashboardLogsRoute: typeof DashboardLogsRoute
|
||||
DashboardNodesRoute: typeof DashboardNodesRoute
|
||||
DashboardPeersRoute: typeof DashboardPeersRoute
|
||||
DashboardPurchasesRoute: typeof DashboardPurchasesRoute
|
||||
DashboardRequestsRoute: typeof DashboardRequestsRoute
|
||||
DashboardSettingsRoute: typeof DashboardSettingsRoute
|
||||
DashboardWalletRoute: typeof DashboardWalletRoute
|
||||
DashboardPurchasesRoute: typeof DashboardPurchasesRoute
|
||||
DashboardRequestsRoute: typeof DashboardRequestsRoute
|
||||
DashboardSettingsRoute: typeof DashboardSettingsRoute
|
||||
DashboardAvailabilitiesLazyRoute: typeof DashboardAvailabilitiesLazyRoute
|
||||
DashboardPeersLazyRoute: typeof DashboardPeersLazyRoute
|
||||
DashboardIndexRoute: typeof DashboardIndexRoute
|
||||
}
|
||||
interface DashboardRouteChildren {
|
||||
DashboardAboutRoute: typeof DashboardAboutRoute
|
||||
DashboardAnalyticsRoute: typeof DashboardAnalyticsRoute
|
||||
DashboardDeviceRoute: typeof DashboardDeviceRoute
|
||||
DashboardDisclaimerRoute: typeof DashboardDisclaimerRoute
|
||||
DashboardFavoritesRoute: typeof DashboardFavoritesRoute
|
||||
DashboardFilesRoute: typeof DashboardFilesRoute
|
||||
DashboardHelpRoute: typeof DashboardHelpRoute
|
||||
DashboardLogsRoute: typeof DashboardLogsRoute
|
||||
DashboardNodesRoute: typeof DashboardNodesRoute
|
||||
DashboardPurchasesRoute: typeof DashboardPurchasesRoute
|
||||
DashboardRequestsRoute: typeof DashboardRequestsRoute
|
||||
DashboardSettingsRoute: typeof DashboardSettingsRoute
|
||||
DashboardWalletRoute: typeof DashboardWalletRoute
|
||||
DashboardAvailabilitiesLazyRoute: typeof DashboardAvailabilitiesLazyRoute
|
||||
DashboardPeersLazyRoute: typeof DashboardPeersLazyRoute
|
||||
DashboardIndexRoute: typeof DashboardIndexRoute
|
||||
}
|
||||
|
||||
const DashboardRouteChildren: DashboardRouteChildren = {
|
||||
DashboardAboutRoute: DashboardAboutRoute,
|
||||
DashboardAnalyticsRoute: DashboardAnalyticsRoute,
|
||||
DashboardAvailabilitiesRoute: DashboardAvailabilitiesRoute,
|
||||
DashboardDeviceRoute: DashboardDeviceRoute,
|
||||
DashboardDisclaimerRoute: DashboardDisclaimerRoute,
|
||||
DashboardFavoritesRoute: DashboardFavoritesRoute,
|
||||
DashboardFilesRoute: DashboardFilesRoute,
|
||||
DashboardHelpRoute: DashboardHelpRoute,
|
||||
DashboardLogsRoute: DashboardLogsRoute,
|
||||
DashboardNodesRoute: DashboardNodesRoute,
|
||||
DashboardPeersRoute: DashboardPeersRoute,
|
||||
DashboardPurchasesRoute: DashboardPurchasesRoute,
|
||||
DashboardRequestsRoute: DashboardRequestsRoute,
|
||||
DashboardSettingsRoute: DashboardSettingsRoute,
|
||||
DashboardWalletRoute: DashboardWalletRoute,
|
||||
DashboardPurchasesRoute: DashboardPurchasesRoute,
|
||||
DashboardRequestsRoute: DashboardRequestsRoute,
|
||||
DashboardSettingsRoute: DashboardSettingsRoute,
|
||||
DashboardAvailabilitiesLazyRoute: DashboardAvailabilitiesLazyRoute,
|
||||
DashboardPeersLazyRoute: DashboardPeersLazyRoute,
|
||||
DashboardIndexRoute: DashboardIndexRoute,
|
||||
}
|
||||
const DashboardRouteChildren: DashboardRouteChildren = {
|
||||
DashboardAboutRoute: DashboardAboutRoute,
|
||||
DashboardAnalyticsRoute: DashboardAnalyticsRoute,
|
||||
DashboardDeviceRoute: DashboardDeviceRoute,
|
||||
DashboardDisclaimerRoute: DashboardDisclaimerRoute,
|
||||
DashboardFavoritesRoute: DashboardFavoritesRoute,
|
||||
DashboardFilesRoute: DashboardFilesRoute,
|
||||
DashboardHelpRoute: DashboardHelpRoute,
|
||||
DashboardLogsRoute: DashboardLogsRoute,
|
||||
DashboardNodesRoute: DashboardNodesRoute,
|
||||
DashboardPurchasesRoute: DashboardPurchasesRoute,
|
||||
DashboardRequestsRoute: DashboardRequestsRoute,
|
||||
DashboardSettingsRoute: DashboardSettingsRoute,
|
||||
DashboardWalletRoute: DashboardWalletRoute,
|
||||
DashboardAvailabilitiesLazyRoute: DashboardAvailabilitiesLazyRoute,
|
||||
DashboardPeersLazyRoute: DashboardPeersLazyRoute,
|
||||
DashboardIndexRoute: DashboardIndexRoute,
|
||||
}
|
||||
|
||||
const DashboardRouteWithChildren = DashboardRoute._addFileChildren(
|
||||
DashboardRouteChildren,
|
||||
)
|
||||
const DashboardRouteWithChildren = DashboardRoute._addFileChildren(
|
||||
DashboardRouteChildren,
|
||||
)
|
||||
|
||||
export interface FileRoutesByFullPath {
|
||||
'/': typeof IndexRoute
|
||||
'/dashboard': typeof DashboardRouteWithChildren
|
||||
'/onboarding-checks': typeof OnboardingChecksRoute
|
||||
'/onboarding-name': typeof OnboardingNameRoute
|
||||
'/dashboard/about': typeof DashboardAboutRoute
|
||||
'/dashboard/analytics': typeof DashboardAnalyticsRoute
|
||||
'/dashboard/availabilities': typeof DashboardAvailabilitiesRoute
|
||||
'/dashboard/device': typeof DashboardDeviceRoute
|
||||
'/dashboard/disclaimer': typeof DashboardDisclaimerRoute
|
||||
'/dashboard/favorites': typeof DashboardFavoritesRoute
|
||||
'/dashboard/files': typeof DashboardFilesRoute
|
||||
'/dashboard/help': typeof DashboardHelpRoute
|
||||
'/dashboard/logs': typeof DashboardLogsRoute
|
||||
'/dashboard/nodes': typeof DashboardNodesRoute
|
||||
'/dashboard/peers': typeof DashboardPeersRoute
|
||||
'/dashboard/purchases': typeof DashboardPurchasesRoute
|
||||
'/dashboard/requests': typeof DashboardRequestsRoute
|
||||
'/dashboard/settings': typeof DashboardSettingsRoute
|
||||
'/dashboard/wallet': typeof DashboardWalletRoute
|
||||
'/dashboard/purchases': typeof DashboardPurchasesRoute
|
||||
'/dashboard/requests': typeof DashboardRequestsRoute
|
||||
'/dashboard/settings': typeof DashboardSettingsRoute
|
||||
'/dashboard/availabilities': typeof DashboardAvailabilitiesLazyRoute
|
||||
'/dashboard/peers': typeof DashboardPeersLazyRoute
|
||||
'/dashboard/': typeof DashboardIndexRoute
|
||||
}
|
||||
export interface FileRoutesByFullPath {
|
||||
'/': typeof IndexRoute
|
||||
'/dashboard': typeof DashboardRouteWithChildren
|
||||
'/onboarding-checks': typeof OnboardingChecksRoute
|
||||
'/onboarding-name': typeof OnboardingNameRoute
|
||||
'/dashboard/about': typeof DashboardAboutRoute
|
||||
'/dashboard/analytics': typeof DashboardAnalyticsRoute
|
||||
'/dashboard/device': typeof DashboardDeviceRoute
|
||||
'/dashboard/disclaimer': typeof DashboardDisclaimerRoute
|
||||
'/dashboard/favorites': typeof DashboardFavoritesRoute
|
||||
'/dashboard/files': typeof DashboardFilesRoute
|
||||
'/dashboard/help': typeof DashboardHelpRoute
|
||||
'/dashboard/logs': typeof DashboardLogsRoute
|
||||
'/dashboard/nodes': typeof DashboardNodesRoute
|
||||
'/dashboard/purchases': typeof DashboardPurchasesRoute
|
||||
'/dashboard/requests': typeof DashboardRequestsRoute
|
||||
'/dashboard/settings': typeof DashboardSettingsRoute
|
||||
'/dashboard/wallet': typeof DashboardWalletRoute
|
||||
'/dashboard/availabilities': typeof DashboardAvailabilitiesLazyRoute
|
||||
'/dashboard/peers': typeof DashboardPeersLazyRoute
|
||||
'/dashboard/': typeof DashboardIndexRoute
|
||||
}
|
||||
|
||||
export interface FileRoutesByTo {
|
||||
'/': typeof IndexRoute
|
||||
'/onboarding-checks': typeof OnboardingChecksRoute
|
||||
'/onboarding-name': typeof OnboardingNameRoute
|
||||
'/dashboard/about': typeof DashboardAboutRoute
|
||||
'/dashboard/analytics': typeof DashboardAnalyticsRoute
|
||||
'/dashboard/availabilities': typeof DashboardAvailabilitiesRoute
|
||||
'/dashboard/device': typeof DashboardDeviceRoute
|
||||
'/dashboard/disclaimer': typeof DashboardDisclaimerRoute
|
||||
'/dashboard/favorites': typeof DashboardFavoritesRoute
|
||||
'/dashboard/files': typeof DashboardFilesRoute
|
||||
'/dashboard/help': typeof DashboardHelpRoute
|
||||
'/dashboard/logs': typeof DashboardLogsRoute
|
||||
'/dashboard/nodes': typeof DashboardNodesRoute
|
||||
'/dashboard/peers': typeof DashboardPeersRoute
|
||||
'/dashboard/purchases': typeof DashboardPurchasesRoute
|
||||
'/dashboard/requests': typeof DashboardRequestsRoute
|
||||
'/dashboard/settings': typeof DashboardSettingsRoute
|
||||
'/dashboard/wallet': typeof DashboardWalletRoute
|
||||
'/dashboard/purchases': typeof DashboardPurchasesRoute
|
||||
'/dashboard/requests': typeof DashboardRequestsRoute
|
||||
'/dashboard/settings': typeof DashboardSettingsRoute
|
||||
'/dashboard/availabilities': typeof DashboardAvailabilitiesLazyRoute
|
||||
'/dashboard/peers': typeof DashboardPeersLazyRoute
|
||||
'/dashboard': typeof DashboardIndexRoute
|
||||
}
|
||||
export interface FileRoutesByTo {
|
||||
'/': typeof IndexRoute
|
||||
'/onboarding-checks': typeof OnboardingChecksRoute
|
||||
'/onboarding-name': typeof OnboardingNameRoute
|
||||
'/dashboard/about': typeof DashboardAboutRoute
|
||||
'/dashboard/analytics': typeof DashboardAnalyticsRoute
|
||||
'/dashboard/device': typeof DashboardDeviceRoute
|
||||
'/dashboard/disclaimer': typeof DashboardDisclaimerRoute
|
||||
'/dashboard/favorites': typeof DashboardFavoritesRoute
|
||||
'/dashboard/files': typeof DashboardFilesRoute
|
||||
'/dashboard/help': typeof DashboardHelpRoute
|
||||
'/dashboard/logs': typeof DashboardLogsRoute
|
||||
'/dashboard/nodes': typeof DashboardNodesRoute
|
||||
'/dashboard/purchases': typeof DashboardPurchasesRoute
|
||||
'/dashboard/requests': typeof DashboardRequestsRoute
|
||||
'/dashboard/settings': typeof DashboardSettingsRoute
|
||||
'/dashboard/wallet': typeof DashboardWalletRoute
|
||||
'/dashboard/availabilities': typeof DashboardAvailabilitiesLazyRoute
|
||||
'/dashboard/peers': typeof DashboardPeersLazyRoute
|
||||
'/dashboard': typeof DashboardIndexRoute
|
||||
}
|
||||
|
||||
export interface FileRoutesById {
|
||||
__root__: typeof rootRoute
|
||||
'/': typeof IndexRoute
|
||||
'/dashboard': typeof DashboardRouteWithChildren
|
||||
'/onboarding-checks': typeof OnboardingChecksRoute
|
||||
'/onboarding-name': typeof OnboardingNameRoute
|
||||
'/dashboard/about': typeof DashboardAboutRoute
|
||||
'/dashboard/analytics': typeof DashboardAnalyticsRoute
|
||||
'/dashboard/availabilities': typeof DashboardAvailabilitiesRoute
|
||||
'/dashboard/device': typeof DashboardDeviceRoute
|
||||
'/dashboard/disclaimer': typeof DashboardDisclaimerRoute
|
||||
'/dashboard/favorites': typeof DashboardFavoritesRoute
|
||||
'/dashboard/files': typeof DashboardFilesRoute
|
||||
'/dashboard/help': typeof DashboardHelpRoute
|
||||
'/dashboard/logs': typeof DashboardLogsRoute
|
||||
'/dashboard/nodes': typeof DashboardNodesRoute
|
||||
'/dashboard/peers': typeof DashboardPeersRoute
|
||||
'/dashboard/purchases': typeof DashboardPurchasesRoute
|
||||
'/dashboard/requests': typeof DashboardRequestsRoute
|
||||
'/dashboard/settings': typeof DashboardSettingsRoute
|
||||
'/dashboard/wallet': typeof DashboardWalletRoute
|
||||
'/dashboard/purchases': typeof DashboardPurchasesRoute
|
||||
'/dashboard/requests': typeof DashboardRequestsRoute
|
||||
'/dashboard/settings': typeof DashboardSettingsRoute
|
||||
'/dashboard/availabilities': typeof DashboardAvailabilitiesLazyRoute
|
||||
'/dashboard/peers': typeof DashboardPeersLazyRoute
|
||||
'/dashboard/': typeof DashboardIndexRoute
|
||||
}
|
||||
export interface FileRoutesById {
|
||||
__root__: typeof rootRoute
|
||||
'/': typeof IndexRoute
|
||||
'/dashboard': typeof DashboardRouteWithChildren
|
||||
'/onboarding-checks': typeof OnboardingChecksRoute
|
||||
'/onboarding-name': typeof OnboardingNameRoute
|
||||
'/dashboard/about': typeof DashboardAboutRoute
|
||||
'/dashboard/analytics': typeof DashboardAnalyticsRoute
|
||||
'/dashboard/device': typeof DashboardDeviceRoute
|
||||
'/dashboard/disclaimer': typeof DashboardDisclaimerRoute
|
||||
'/dashboard/favorites': typeof DashboardFavoritesRoute
|
||||
'/dashboard/files': typeof DashboardFilesRoute
|
||||
'/dashboard/help': typeof DashboardHelpRoute
|
||||
'/dashboard/logs': typeof DashboardLogsRoute
|
||||
'/dashboard/nodes': typeof DashboardNodesRoute
|
||||
'/dashboard/purchases': typeof DashboardPurchasesRoute
|
||||
'/dashboard/requests': typeof DashboardRequestsRoute
|
||||
'/dashboard/settings': typeof DashboardSettingsRoute
|
||||
'/dashboard/wallet': typeof DashboardWalletRoute
|
||||
'/dashboard/availabilities': typeof DashboardAvailabilitiesLazyRoute
|
||||
'/dashboard/peers': typeof DashboardPeersLazyRoute
|
||||
'/dashboard/': typeof DashboardIndexRoute
|
||||
}
|
||||
|
||||
export interface FileRouteTypes {
|
||||
fileRoutesByFullPath: FileRoutesByFullPath
|
||||
fullPaths:
|
||||
export interface FileRouteTypes {
|
||||
fileRoutesByFullPath: FileRoutesByFullPath
|
||||
fullPaths:
|
||||
| '/'
|
||||
| '/dashboard'
|
||||
| '/onboarding-checks'
|
||||
| '/onboarding-name'
|
||||
| '/dashboard/about'
|
||||
| '/dashboard/analytics'
|
||||
| '/dashboard/availabilities'
|
||||
| '/dashboard/device'
|
||||
| '/dashboard/disclaimer'
|
||||
| '/dashboard/favorites'
|
||||
@ -493,25 +442,20 @@ declare module '@tanstack/react-router' {
|
||||
| '/dashboard/help'
|
||||
| '/dashboard/logs'
|
||||
| '/dashboard/nodes'
|
||||
| '/dashboard/peers'
|
||||
| '/dashboard/purchases'
|
||||
| '/dashboard/requests'
|
||||
| '/dashboard/settings'
|
||||
| '/dashboard/wallet'
|
||||
| '/dashboard/purchases'
|
||||
| '/dashboard/requests'
|
||||
| '/dashboard/settings'
|
||||
| '/dashboard/availabilities'
|
||||
| '/dashboard/peers'
|
||||
| '/dashboard/'
|
||||
fileRoutesByTo: FileRoutesByTo
|
||||
to:
|
||||
fileRoutesByTo: FileRoutesByTo
|
||||
to:
|
||||
| '/'
|
||||
| '/onboarding-checks'
|
||||
| '/onboarding-name'
|
||||
| '/dashboard/about'
|
||||
| '/dashboard/analytics'
|
||||
| '/dashboard/availabilities'
|
||||
| '/dashboard/device'
|
||||
| '/dashboard/disclaimer'
|
||||
| '/dashboard/favorites'
|
||||
@ -519,18 +463,14 @@ declare module '@tanstack/react-router' {
|
||||
| '/dashboard/help'
|
||||
| '/dashboard/logs'
|
||||
| '/dashboard/nodes'
|
||||
| '/dashboard/peers'
|
||||
| '/dashboard/purchases'
|
||||
| '/dashboard/requests'
|
||||
| '/dashboard/settings'
|
||||
| '/dashboard/wallet'
|
||||
| '/dashboard/purchases'
|
||||
| '/dashboard/requests'
|
||||
| '/dashboard/settings'
|
||||
| '/dashboard/availabilities'
|
||||
| '/dashboard/peers'
|
||||
| '/dashboard'
|
||||
id:
|
||||
id:
|
||||
| '__root__'
|
||||
| '/'
|
||||
| '/dashboard'
|
||||
@ -538,7 +478,6 @@ declare module '@tanstack/react-router' {
|
||||
| '/onboarding-name'
|
||||
| '/dashboard/about'
|
||||
| '/dashboard/analytics'
|
||||
| '/dashboard/availabilities'
|
||||
| '/dashboard/device'
|
||||
| '/dashboard/disclaimer'
|
||||
| '/dashboard/favorites'
|
||||
@ -546,37 +485,33 @@ declare module '@tanstack/react-router' {
|
||||
| '/dashboard/help'
|
||||
| '/dashboard/logs'
|
||||
| '/dashboard/nodes'
|
||||
| '/dashboard/peers'
|
||||
| '/dashboard/purchases'
|
||||
| '/dashboard/requests'
|
||||
| '/dashboard/settings'
|
||||
| '/dashboard/wallet'
|
||||
| '/dashboard/purchases'
|
||||
| '/dashboard/requests'
|
||||
| '/dashboard/settings'
|
||||
| '/dashboard/availabilities'
|
||||
| '/dashboard/peers'
|
||||
| '/dashboard/'
|
||||
fileRoutesById: FileRoutesById
|
||||
}
|
||||
fileRoutesById: FileRoutesById
|
||||
}
|
||||
|
||||
export interface RootRouteChildren {
|
||||
IndexRoute: typeof IndexRoute
|
||||
DashboardRoute: typeof DashboardRouteWithChildren
|
||||
OnboardingChecksRoute: typeof OnboardingChecksRoute
|
||||
OnboardingNameRoute: typeof OnboardingNameRoute
|
||||
}
|
||||
export interface RootRouteChildren {
|
||||
IndexRoute: typeof IndexRoute
|
||||
DashboardRoute: typeof DashboardRouteWithChildren
|
||||
OnboardingChecksRoute: typeof OnboardingChecksRoute
|
||||
OnboardingNameRoute: typeof OnboardingNameRoute
|
||||
}
|
||||
|
||||
const rootRouteChildren: RootRouteChildren = {
|
||||
IndexRoute: IndexRoute,
|
||||
DashboardRoute: DashboardRouteWithChildren,
|
||||
OnboardingChecksRoute: OnboardingChecksRoute,
|
||||
OnboardingNameRoute: OnboardingNameRoute,
|
||||
}
|
||||
const rootRouteChildren: RootRouteChildren = {
|
||||
IndexRoute: IndexRoute,
|
||||
DashboardRoute: DashboardRouteWithChildren,
|
||||
OnboardingChecksRoute: OnboardingChecksRoute,
|
||||
OnboardingNameRoute: OnboardingNameRoute,
|
||||
}
|
||||
|
||||
export const routeTree = rootRoute
|
||||
._addFileChildren(rootRouteChildren)
|
||||
._addFileTypes<FileRouteTypes>()
|
||||
export const routeTree = rootRoute
|
||||
._addFileChildren(rootRouteChildren)
|
||||
._addFileTypes<FileRouteTypes>()
|
||||
|
||||
/* prettier-ignore-end */
|
||||
|
||||
@ -600,7 +535,6 @@ declare module '@tanstack/react-router' {
|
||||
"children": [
|
||||
"/dashboard/about",
|
||||
"/dashboard/analytics",
|
||||
"/dashboard/availabilities",
|
||||
"/dashboard/device",
|
||||
"/dashboard/disclaimer",
|
||||
"/dashboard/favorites",
|
||||
@ -608,14 +542,10 @@ declare module '@tanstack/react-router' {
|
||||
"/dashboard/help",
|
||||
"/dashboard/logs",
|
||||
"/dashboard/nodes",
|
||||
"/dashboard/peers",
|
||||
"/dashboard/purchases",
|
||||
"/dashboard/requests",
|
||||
"/dashboard/settings",
|
||||
"/dashboard/wallet",
|
||||
"/dashboard/purchases",
|
||||
"/dashboard/requests",
|
||||
"/dashboard/settings",
|
||||
"/dashboard/availabilities",
|
||||
"/dashboard/peers",
|
||||
"/dashboard/"
|
||||
@ -635,10 +565,6 @@ declare module '@tanstack/react-router' {
|
||||
"filePath": "dashboard/analytics.tsx",
|
||||
"parent": "/dashboard"
|
||||
},
|
||||
"/dashboard/availabilities": {
|
||||
"filePath": "dashboard/availabilities.tsx",
|
||||
"parent": "/dashboard"
|
||||
},
|
||||
"/dashboard/device": {
|
||||
"filePath": "dashboard/device.tsx",
|
||||
"parent": "/dashboard"
|
||||
@ -667,10 +593,6 @@ declare module '@tanstack/react-router' {
|
||||
"filePath": "dashboard/nodes.tsx",
|
||||
"parent": "/dashboard"
|
||||
},
|
||||
"/dashboard/peers": {
|
||||
"filePath": "dashboard/peers.tsx",
|
||||
"parent": "/dashboard"
|
||||
},
|
||||
"/dashboard/purchases": {
|
||||
"filePath": "dashboard/purchases.tsx",
|
||||
"parent": "/dashboard"
|
||||
@ -685,6 +607,8 @@ declare module '@tanstack/react-router' {
|
||||
},
|
||||
"/dashboard/wallet": {
|
||||
"filePath": "dashboard/wallet.tsx",
|
||||
"parent": "/dashboard"
|
||||
},
|
||||
"/dashboard/availabilities": {
|
||||
"filePath": "dashboard/availabilities.lazy.tsx",
|
||||
"parent": "/dashboard"
|
||||
|
@ -1,24 +1,45 @@
|
||||
import { createFileRoute, Outlet } from "@tanstack/react-router";
|
||||
import {
|
||||
createFileRoute,
|
||||
Outlet,
|
||||
useRouterState,
|
||||
} from "@tanstack/react-router";
|
||||
import "./layout.css";
|
||||
import { Menu } from "../components/Menu/Menu";
|
||||
import { useState } from "react";
|
||||
import { useEffect, useState } from "react";
|
||||
import { AppBar } from "../components/AppBar/AppBar";
|
||||
import { Backdrop } from "@codex-storage/marketplace-ui-components";
|
||||
|
||||
const Layout = () => {
|
||||
const [open, setOpen] = useState(false);
|
||||
const [hasMobileMenu, setHasMobileMenu] = useState(false);
|
||||
const router = useRouterState();
|
||||
|
||||
const onClose = () => setOpen(false);
|
||||
const onIconClick = () => {
|
||||
if (window.innerWidth <= 999) {
|
||||
setHasMobileMenu(true);
|
||||
}
|
||||
};
|
||||
|
||||
const onExpand = () => setOpen(true);
|
||||
useEffect(() => {
|
||||
setHasMobileMenu(false);
|
||||
}, [router.location.pathname]);
|
||||
|
||||
const onClose = () => setHasMobileMenu(false);
|
||||
|
||||
const isMobileMenuDisplayed =
|
||||
hasMobileMenu === true && window.innerWidth <= 999;
|
||||
|
||||
return (
|
||||
<div className="layout">
|
||||
<Menu expanded={open} onClose={onClose}></Menu>
|
||||
<Menu isMobileMenuDisplayed={isMobileMenuDisplayed}></Menu>
|
||||
|
||||
<main>
|
||||
<AppBar onExpand={onExpand} />
|
||||
<Outlet />
|
||||
<AppBar onIconClick={onIconClick} />
|
||||
<div>
|
||||
<Outlet />
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<Backdrop onClose={onClose} open={hasMobileMenu}></Backdrop>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
@ -1,5 +1,4 @@
|
||||
.dashboard {
|
||||
padding: 24px 48px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
|
||||
|
@ -2,34 +2,46 @@
|
||||
max-width: 1320px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
width: calc(100% - 2 * 24px);
|
||||
padding: 24px;
|
||||
|
||||
> div {
|
||||
max-width: 1320px;
|
||||
}
|
||||
|
||||
> div:first-child {
|
||||
width: calc(100% - 128px - 16px);
|
||||
width: calc(100% - 16px);
|
||||
border: 1px solid #96969633;
|
||||
padding: 16px 16px 16px 128px;
|
||||
padding: 16px;
|
||||
border-radius: 16px;
|
||||
position: relative;
|
||||
height: 600px;
|
||||
|
||||
@media (min-width: 1000px) {
|
||||
& {
|
||||
width: calc(100% - 128px - 16px);
|
||||
padding: 16px 16px 16px 128px;
|
||||
height: 600px;
|
||||
}
|
||||
}
|
||||
|
||||
circle[fill="#d6ff79"] {
|
||||
stroke: var(--codex-color-primary);
|
||||
stroke-width: 0.6px;
|
||||
fill: #141414;
|
||||
animation: circle-pulse 1.5s infinite;
|
||||
animation: circle-pulse 3s infinite;
|
||||
}
|
||||
|
||||
ul {
|
||||
list-style-type: none;
|
||||
width: 71px;
|
||||
position: absolute;
|
||||
left: 16px;
|
||||
top: 16px;
|
||||
display: none;
|
||||
|
||||
@media (min-width: 1000px) {
|
||||
& {
|
||||
list-style-type: none;
|
||||
width: 71px;
|
||||
position: absolute;
|
||||
left: 16px;
|
||||
top: 16px;
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
|
||||
li {
|
||||
border-bottom: 1px solid #969696cc;
|
||||
@ -86,11 +98,33 @@
|
||||
background-color: #232323;
|
||||
border: 1px solid #96969633;
|
||||
border-radius: 16px;
|
||||
position: absolute;
|
||||
bottom: 16px;
|
||||
left: 16px;
|
||||
width: 280px;
|
||||
max-width: 280px;
|
||||
padding: 16px;
|
||||
transform: scale(0.7);
|
||||
width: 280px;
|
||||
|
||||
@media (max-width: 999px) {
|
||||
& {
|
||||
position: relative;
|
||||
bottom: -32px;
|
||||
left: -32px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1000px) {
|
||||
& {
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1000px) {
|
||||
& {
|
||||
position: absolute;
|
||||
bottom: 16px;
|
||||
left: 16px;
|
||||
width: 280px;
|
||||
}
|
||||
}
|
||||
|
||||
header {
|
||||
display: flex;
|
||||
@ -116,10 +150,16 @@
|
||||
width: 350px;
|
||||
height: 175px;
|
||||
overflow: hidden;
|
||||
transform: scale(0.73);
|
||||
transform: scale(0.5);
|
||||
margin: auto;
|
||||
left: -32px;
|
||||
|
||||
@media (min-width: 1000px) {
|
||||
& {
|
||||
transform: scale(0.73);
|
||||
}
|
||||
}
|
||||
|
||||
*,
|
||||
&::before {
|
||||
box-sizing: border-box;
|
||||
@ -188,7 +228,8 @@
|
||||
}
|
||||
|
||||
> div:nth-child(2) {
|
||||
width: calc(100% - 64px);
|
||||
margin-top: 32px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
table {
|
||||
|
@ -1,10 +1,15 @@
|
||||
import { Cell, Row, Table } from "@codex-storage/marketplace-ui-components";
|
||||
import {
|
||||
Cell,
|
||||
Row,
|
||||
Table,
|
||||
TabSortState,
|
||||
} from "@codex-storage/marketplace-ui-components";
|
||||
import { getMapJSON } from "dotted-map";
|
||||
import DottedMap from "dotted-map/without-countries";
|
||||
import { Promises } from "../../utils/promises";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { PeerCountryCell } from "../../components/Peers/PeerCountryCell";
|
||||
import { useCallback, useState } from "react";
|
||||
import { useCallback, useRef, useState } from "react";
|
||||
import { PeerPin } from "../../components/Peers/types";
|
||||
import "./peers.css";
|
||||
import { CodexSdk } from "../../sdk/codex";
|
||||
@ -14,6 +19,7 @@ import { PeersIcon } from "../../components/Menu/PeersIcon";
|
||||
import { SuccessCheckIcon } from "../../components/SuccessCheckIcon/SuccessCheckIcon";
|
||||
import { ErrorCircleIcon } from "../../components/ErrorCircleIcon/ErrorCircleIcon";
|
||||
import { createLazyFileRoute } from "@tanstack/react-router";
|
||||
import { Network } from "../../utils/network";
|
||||
|
||||
// This function accepts the same arguments as DottedMap in the example above.
|
||||
const mapJsonString = getMapJSON({ height: 60, grid: "diagonal" });
|
||||
@ -22,8 +28,29 @@ type CustomCSSProperties = React.CSSProperties & {
|
||||
"--codex-peers-percent": number;
|
||||
};
|
||||
|
||||
type Node = {
|
||||
nodeId: string;
|
||||
peerId: string;
|
||||
record: string;
|
||||
address: string;
|
||||
seen: boolean;
|
||||
};
|
||||
|
||||
type SortFn = (a: Node, b: Node) => number;
|
||||
|
||||
const sortByBooleanValue = (state: TabSortState) => {
|
||||
return (a: Node, b: Node) => {
|
||||
const order = state === "desc" ? 1 : -1;
|
||||
return a?.seen === b?.seen ? 0 : b?.seen ? order : -order;
|
||||
};
|
||||
};
|
||||
|
||||
const Peers = () => {
|
||||
const ips = useRef<Record<string, string>>({});
|
||||
const [pins, setPins] = useState<[PeerPin, number][]>([]);
|
||||
const [sortFn, setSortFn] = useState<SortFn | null>(() =>
|
||||
sortByBooleanValue("desc")
|
||||
);
|
||||
const { data } = useQuery({
|
||||
queryFn: () =>
|
||||
CodexSdk.debug()
|
||||
@ -47,13 +74,21 @@ const Peers = () => {
|
||||
throwOnError: true,
|
||||
});
|
||||
|
||||
const onPinAdd = useCallback((pin: PeerPin) => {
|
||||
setPins((val) => {
|
||||
const [, quantity = 0] =
|
||||
val.find(([p]) => p.lat === pin.lat && p.lng == pin.lng) || [];
|
||||
return [...val, [pin, quantity + 1]];
|
||||
});
|
||||
}, []);
|
||||
const onPinAdd = useCallback(
|
||||
({
|
||||
countryIso,
|
||||
ip,
|
||||
...pin
|
||||
}: PeerPin & { countryIso: string; ip: string }) => {
|
||||
setPins((val) => {
|
||||
const [, quantity = 0] =
|
||||
val.find(([p]) => p.lat === pin.lat && p.lng == pin.lng) || [];
|
||||
return [...val, [pin, quantity + 1]];
|
||||
});
|
||||
ips.current[ip] = countryIso;
|
||||
},
|
||||
[]
|
||||
);
|
||||
|
||||
// It’s safe to re-create the map at each render, because of the
|
||||
// pre-computation it’s super fast ⚡️
|
||||
@ -74,67 +109,111 @@ const Peers = () => {
|
||||
backgroundColor: "#141414",
|
||||
});
|
||||
|
||||
const headers = ["Country", "PeerId", "Active"];
|
||||
const onSortByCountry = (state: TabSortState) => {
|
||||
if (!state) {
|
||||
setSortFn(null);
|
||||
return;
|
||||
}
|
||||
|
||||
const rows =
|
||||
(data?.table?.nodes || []).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>
|
||||
)) || [];
|
||||
setSortFn(() => (a: Node, b: Node) => {
|
||||
const countryA = ips.current[Network.getIp(a.address)] || "";
|
||||
const countryB = ips.current[Network.getIp(b.address)] || "";
|
||||
|
||||
const actives =
|
||||
data?.table.nodes.reduce((acc, cur) => acc + (cur.seen ? 1 : 0), 0) || 0;
|
||||
return state === "desc"
|
||||
? countryA.localeCompare(countryB)
|
||||
: countryB.localeCompare(countryA);
|
||||
});
|
||||
};
|
||||
|
||||
const onSortActive = (state: TabSortState) => {
|
||||
console.info("fdf");
|
||||
if (!state) {
|
||||
setSortFn(null);
|
||||
return;
|
||||
}
|
||||
|
||||
setSortFn(() => sortByBooleanValue(state));
|
||||
};
|
||||
|
||||
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>
|
||||
));
|
||||
|
||||
const actives = sorted.reduce((acc, cur) => acc + (cur.seen ? 1 : 0), 0) || 0;
|
||||
const total = data?.table.nodes.length || 1;
|
||||
|
||||
const styles: CustomCSSProperties = {
|
||||
"--codex-peers-percent": (actives / total) * 180,
|
||||
};
|
||||
|
||||
const good = actives > 0;
|
||||
|
||||
return (
|
||||
<div className="peers">
|
||||
<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>
|
||||
<SuccessCheckIcon variant="primary"></SuccessCheckIcon>{" "}
|
||||
<span>Peer connections in Good standing. </span>
|
||||
</footer>
|
||||
</div>
|
||||
<div dangerouslySetInnerHTML={{ __html: svgMap }}></div>
|
||||
<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>
|
||||
</div>
|
||||
|
||||
<Table headers={headers} rows={rows} />
|
||||
<Table headers={headers} rows={rows} defaultSortIndex={2} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
@ -1,6 +1,4 @@
|
||||
.settings {
|
||||
padding: 24px 48px;
|
||||
|
||||
header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
@ -6,6 +6,14 @@
|
||||
> main {
|
||||
flex: 1;
|
||||
background-color: #141414;
|
||||
|
||||
> div {
|
||||
padding: 16px;
|
||||
|
||||
@media (min-width: 1000px) {
|
||||
padding: 24px 48px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -13,3 +13,5 @@ export const EXPLORER_URL = "https://explorer.testnet.codex.storage/tx";
|
||||
export const GB = 1_073_741_824;
|
||||
|
||||
export const TB = 1_099_511_627_776;
|
||||
|
||||
export const MOBILE_MAX_WIDTH = 999
|
6
src/utils/network.ts
Normal file
6
src/utils/network.ts
Normal file
@ -0,0 +1,6 @@
|
||||
export const Network = {
|
||||
getIp(address: string) {
|
||||
const [ip] = address.split(":")
|
||||
return ip
|
||||
}
|
||||
}
|
@ -15,6 +15,7 @@ export default defineConfig({
|
||||
output: {
|
||||
manualChunks: {
|
||||
"@sentry/react": ["@sentry/react"],
|
||||
"emoji-picker-react": ["emoji-picker-react"]
|
||||
}
|
||||
},
|
||||
onwarn(warning, defaultHandler) {
|
||||
@ -24,11 +25,6 @@ export default defineConfig({
|
||||
|
||||
defaultHandler(warning);
|
||||
},
|
||||
output: {
|
||||
manualChunks: {
|
||||
"emoji-picker-react": ["emoji-picker-react"]
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
},
|
||||
|
Loading…
x
Reference in New Issue
Block a user