mirror of
https://github.com/logos-storage/logos-storage-marketplace-ui.git
synced 2026-01-04 06:23:08 +00:00
Complete onboarding screens
This commit is contained in:
commit
7f60e54a63
16
package-lock.json
generated
16
package-lock.json
generated
@ -9,8 +9,8 @@
|
||||
"version": "0.0.7",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@codex-storage/marketplace-ui-components": "^0.0.25",
|
||||
"@codex-storage/sdk-js": "^0.0.10",
|
||||
"@codex-storage/marketplace-ui-components": "^0.0.26",
|
||||
"@codex-storage/sdk-js": "^0.0.12",
|
||||
"@sentry/browser": "^8.32.0",
|
||||
"@sentry/react": "^8.31.0",
|
||||
"@tanstack/react-query": "^5.51.15",
|
||||
@ -374,9 +374,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@codex-storage/marketplace-ui-components": {
|
||||
"version": "0.0.25",
|
||||
"resolved": "https://registry.npmjs.org/@codex-storage/marketplace-ui-components/-/marketplace-ui-components-0.0.25.tgz",
|
||||
"integrity": "sha512-oWR/E0yNWK/Lj2LI3lAqKDzuMw6sTUyYfkhZBbV4yK/4VnGtIIoW0OU+jggishJYAwh3y4s71OfMjWaGGafWlQ==",
|
||||
"version": "0.0.26",
|
||||
"resolved": "https://registry.npmjs.org/@codex-storage/marketplace-ui-components/-/marketplace-ui-components-0.0.26.tgz",
|
||||
"integrity": "sha512-x8niBv1HRJU6VnKJ5+tv3+yYMxOj0tnMdNaPGzHVXMTHpj5WeHRcqUR1UhxxxnIlnsOjhmjX2tVUYPxpAz+OEw==",
|
||||
"dependencies": {
|
||||
"lucide-react": "^0.453.0"
|
||||
},
|
||||
@ -398,9 +398,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@codex-storage/sdk-js": {
|
||||
"version": "0.0.10",
|
||||
"resolved": "https://registry.npmjs.org/@codex-storage/sdk-js/-/sdk-js-0.0.10.tgz",
|
||||
"integrity": "sha512-DUq4YJF9r+45CaUf+ry0I6cawsfQA2RWbHmxASh9UuUu8L2xNlAbk8Uc2zmMr2HiixGDwO6b+BCtwZkDLBhTPQ==",
|
||||
"version": "0.0.12",
|
||||
"resolved": "https://registry.npmjs.org/@codex-storage/sdk-js/-/sdk-js-0.0.12.tgz",
|
||||
"integrity": "sha512-WjMZ9fkeVFBdDlFViWRDIirk2s4nX0U5r4WixAeieAkDBlRq5nqWcm9rqz5/8u8EQudgk/y3p0vPZc8cpyK4bA==",
|
||||
"dependencies": {
|
||||
"valibot": "^0.32.0"
|
||||
},
|
||||
|
||||
@ -24,8 +24,8 @@
|
||||
"React"
|
||||
],
|
||||
"dependencies": {
|
||||
"@codex-storage/marketplace-ui-components": "^0.0.25",
|
||||
"@codex-storage/sdk-js": "^0.0.11",
|
||||
"@codex-storage/marketplace-ui-components": "^0.0.26",
|
||||
"@codex-storage/sdk-js": "^0.0.12",
|
||||
"@sentry/browser": "^8.32.0",
|
||||
"@sentry/react": "^8.31.0",
|
||||
"@tanstack/react-query": "^5.51.15",
|
||||
|
||||
21
src/components/ErrorCircleIcon/ErrorCircleIcon.tsx
Normal file
21
src/components/ErrorCircleIcon/ErrorCircleIcon.tsx
Normal file
@ -0,0 +1,21 @@
|
||||
type Props = {
|
||||
className?: string;
|
||||
};
|
||||
|
||||
export function ErrorCircleIcon({ className = "" }: Props) {
|
||||
return (
|
||||
<span className={"onboarding-healthCheck-icon " + className}>
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M8 15.5C3.85775 15.5 0.5 12.1423 0.5 8C0.5 3.85775 3.85775 0.5 8 0.5C12.1423 0.5 15.5 3.85775 15.5 8C15.5 12.1423 12.1423 15.5 8 15.5ZM8 6.9395L5.879 4.81775L4.81775 5.879L6.9395 8L4.81775 10.121L5.879 11.1823L8 9.0605L10.121 11.1823L11.1823 10.121L9.0605 8L11.1823 5.879L10.121 4.81775L8 6.9395Z"
|
||||
fill="#FB3748"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
@ -2,23 +2,26 @@ import { Button, Input } from "@codex-storage/marketplace-ui-components";
|
||||
import "./ManifestFetch.css";
|
||||
import { ChangeEvent, useState } from "react";
|
||||
import { CodexSdk } from "../../sdk/codex";
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import { useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
import { Promises } from "../../utils/promises";
|
||||
|
||||
export function ManifestFetch() {
|
||||
const [cid, setCid] = useState("");
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
const { refetch } = useQuery({
|
||||
queryFn: () =>
|
||||
CodexSdk.data()
|
||||
queryFn: () => {
|
||||
return CodexSdk.data()
|
||||
.fetchManifest(cid)
|
||||
.then((s) => {
|
||||
if (s.error === false) {
|
||||
setCid("");
|
||||
queryClient.invalidateQueries({ queryKey: ["cids"] });
|
||||
}
|
||||
return Promises.rejectOnError(s);
|
||||
}),
|
||||
queryKey: ["cids"],
|
||||
});
|
||||
},
|
||||
queryKey: ["manifest"],
|
||||
|
||||
// Disable the fetch to make it available on refetch only
|
||||
enabled: false,
|
||||
@ -44,6 +47,7 @@ export function ManifestFetch() {
|
||||
<div className="fetch-inputContainer">
|
||||
<Input
|
||||
id="cid"
|
||||
value={cid}
|
||||
placeholder="CID"
|
||||
inputClassName="fetch-input"
|
||||
onChange={onCidChange}></Input>
|
||||
|
||||
@ -1,4 +1,12 @@
|
||||
export function AlphaIcon() {
|
||||
type Props = {
|
||||
variant: "primary" | "error";
|
||||
};
|
||||
|
||||
export function AlphaIcon({ variant }: Props) {
|
||||
const color =
|
||||
variant === "primary"
|
||||
? "var(--codex-color-primary)"
|
||||
: "var(--codex-color-error-hexa)";
|
||||
return (
|
||||
<svg
|
||||
width="27"
|
||||
@ -8,15 +16,10 @@ export function AlphaIcon() {
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M12.957 1.38171C13.0441 1.33141 13.1515 1.33141 13.2386 1.38171L23.1736 7.11769C23.2607 7.16799 23.3144 7.26095 23.3144 7.36156V18.8335C23.3144 18.9341 23.2607 19.0271 23.1736 19.0774L13.2386 24.8134C13.1515 24.8637 13.0441 24.8637 12.957 24.8134L3.02198 19.0774L2.47465 20.0254L3.02198 19.0774C2.93485 19.0271 2.88118 18.9341 2.88118 18.8335V7.36156C2.88118 7.26095 2.93485 7.16799 3.02198 7.11769L2.45878 6.14219L3.02198 7.11769L12.957 1.38171Z"
|
||||
stroke="var(--codex-color-error-hexa)"
|
||||
stroke={color}
|
||||
strokeWidth="2.25282"
|
||||
/>
|
||||
<circle
|
||||
cx="13.0978"
|
||||
cy="13.0975"
|
||||
r="3.92933"
|
||||
fill="var(--codex-color-error-hexa)"
|
||||
/>
|
||||
<circle cx="13.0978" cy="13.0975" r="3.92933" fill={color} />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
15
src/components/OnBoarding/HealthCheckIcon.tsx
Normal file
15
src/components/OnBoarding/HealthCheckIcon.tsx
Normal file
@ -0,0 +1,15 @@
|
||||
export function HealthCheckIcon() {
|
||||
return (
|
||||
<svg
|
||||
width="18"
|
||||
height="18"
|
||||
viewBox="0 0 18 18"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M16.1999 0C16.6967 0 17.0999 0.4032 17.0999 0.9V17.1C17.0999 17.5968 16.6967 18 16.1999 18H3.5999C3.1031 18 2.6999 17.5968 2.6999 17.1V15.3H0.899902V13.5H2.6999V11.7H0.899902V9.9H2.6999V8.1H0.899902V6.3H2.6999V4.5H0.899902V2.7H2.6999V0.9C2.6999 0.4032 3.1031 0 3.5999 0H16.1999ZM15.2999 1.8H4.4999V16.2H15.2999V1.8ZM10.7999 5.4V8.1H13.4999V9.9H10.799L10.7999 12.6H8.9999L8.999 9.9H6.2999V8.1H8.9999V5.4H10.7999Z"
|
||||
fill="#6FCB94"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
5
src/components/OnBoarding/HealthCheckItem.css
Normal file
5
src/components/OnBoarding/HealthCheckItem.css
Normal file
@ -0,0 +1,5 @@
|
||||
.onboarding-healthCheckItem {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
}
|
||||
21
src/components/OnBoarding/HealthCheckItem.tsx
Normal file
21
src/components/OnBoarding/HealthCheckItem.tsx
Normal file
@ -0,0 +1,21 @@
|
||||
import { classnames } from "../../utils/classnames";
|
||||
import { OnBoardingStatusIcon } from "./OnBoardingStatusIcon";
|
||||
import "./HealthCheckItem.css";
|
||||
|
||||
type Props = {
|
||||
value: "success" | "failure" | "warning";
|
||||
text: string;
|
||||
};
|
||||
|
||||
export function HealthCheckItem({ value, text }: Props) {
|
||||
return (
|
||||
<div
|
||||
data-testid="network"
|
||||
className={classnames(["onboarding-healthCheckItem"])}>
|
||||
<OnBoardingStatusIcon value={value} />
|
||||
<div>
|
||||
<p>{text}</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
import "./OmBoardingImage.css";
|
||||
import "./OnBoardingImage.css";
|
||||
|
||||
export function OnBoardingImage() {
|
||||
return (
|
||||
|
||||
33
src/components/OnBoarding/OnBoardingStatusIcon.tsx
Normal file
33
src/components/OnBoarding/OnBoardingStatusIcon.tsx
Normal file
@ -0,0 +1,33 @@
|
||||
import { ErrorCircleIcon } from "../ErrorCircleIcon/ErrorCircleIcon";
|
||||
import { SuccessCheckIcon } from "../SuccessCheckIcon/SuccessCheckIcon";
|
||||
|
||||
type Props = {
|
||||
value: "success" | "failure" | "warning";
|
||||
};
|
||||
|
||||
export function OnBoardingStatusIcon({ value }: Props) {
|
||||
switch (value) {
|
||||
case "success":
|
||||
return <SuccessCheckIcon variant="primary"></SuccessCheckIcon>;
|
||||
|
||||
case "failure":
|
||||
return <ErrorCircleIcon />;
|
||||
|
||||
case "warning":
|
||||
return (
|
||||
<span className="onboarding-healthCheck-icon">
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M8 15.5C3.85775 15.5 0.5 12.1423 0.5 8C0.5 3.85775 3.85775 0.5 8 0.5C12.1423 0.5 15.5 3.85775 15.5 8C15.5 12.1423 12.1423 15.5 8 15.5ZM4.25 7.25V8.75H11.75V7.25H4.25Z"
|
||||
fill="#FF8447"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -19,7 +19,7 @@ export function OnBoardingStepOne({ onNextStep }: Props) {
|
||||
<>
|
||||
<div className="index-column-section">
|
||||
<div>
|
||||
<AlphaIcon />
|
||||
<AlphaIcon variant="primary" />
|
||||
</div>
|
||||
<div className="index-alphaText">
|
||||
<p>
|
||||
|
||||
@ -27,10 +27,6 @@
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.onboarding-check-refresh--fetching {
|
||||
animation: rotateAnimation 2s linear infinite;
|
||||
}
|
||||
|
||||
.onboarding-group {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@ -43,6 +39,75 @@
|
||||
padding-left: 1rem;
|
||||
}
|
||||
|
||||
.onboarding-deviceCheck {
|
||||
margin-top: 1rem;
|
||||
margin-bottom: 1rem;
|
||||
font-family: Azeret Mono;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
line-height: 16.34px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.onboarding--deviceCheck-block {
|
||||
flex: 0.3;
|
||||
}
|
||||
|
||||
.onboarding-displayName {
|
||||
margin-bottom: 1.75rem;
|
||||
}
|
||||
|
||||
.onboarding-healthCheck-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 16px 0;
|
||||
gap: 16px;
|
||||
border-top: 1px solid #3c3d3e;
|
||||
border-bottom: 1px solid #3c3d3e;
|
||||
}
|
||||
|
||||
.onboarding-healthCheck-itemText {
|
||||
font-family: Inter;
|
||||
font-size: 16px;
|
||||
font-weight: 500;
|
||||
line-height: 24px;
|
||||
letter-spacing: -0.011em;
|
||||
}
|
||||
|
||||
.onboarding-healthChecks {
|
||||
margin-bottom: 32px;
|
||||
}
|
||||
|
||||
.onboarding-healthCheck-icon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.onboarding-refresh {
|
||||
position: relative;
|
||||
top: 14px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.onboarding-refresh--fetching {
|
||||
animation: rotate 2s linear infinite;
|
||||
}
|
||||
|
||||
.onboarding-portContainer,
|
||||
.onboarding-addressContainer {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.onboarding--addressSuccessIcon {
|
||||
position: absolute;
|
||||
top: 53px;
|
||||
bottom: 0;
|
||||
right: 18px;
|
||||
}
|
||||
|
||||
@keyframes rotate {
|
||||
from {
|
||||
transform: rotate(0deg); /* Start at 0 degrees */
|
||||
|
||||
@ -1,20 +1,21 @@
|
||||
import { CheckIcon, RefreshCcw, Save, ShieldAlert, X } from "lucide-react";
|
||||
import { classnames } from "../../utils/classnames";
|
||||
import "./OnBoardingStepThree.css";
|
||||
import { usePortForwarding } from "../../hooks/usePortForwarding";
|
||||
import { useCodexConnection } from "../../hooks/useCodexConnection";
|
||||
import {
|
||||
Alert,
|
||||
ButtonIcon,
|
||||
Input,
|
||||
SimpleText,
|
||||
} from "@codex-storage/marketplace-ui-components";
|
||||
import { useEffect, useState } from "react";
|
||||
import { Input, SimpleText } from "@codex-storage/marketplace-ui-components";
|
||||
import { ClipboardEvent, useEffect, useState } from "react";
|
||||
import { CodexSdk } from "../../sdk/codex";
|
||||
import { useQueryClient } from "@tanstack/react-query";
|
||||
import { usePersistence } from "../../hooks/usePersistence";
|
||||
import { useDebug } from "../../hooks/useDebug";
|
||||
import { DebugUtils } from "../../utils/debug";
|
||||
import { AlphaIcon } from "./AlphaIcon";
|
||||
import { OnBoardingUtils } from "../../utils/onboarding";
|
||||
import { RefreshIcon } from "../RefreshIcon/RefreshIcon";
|
||||
import { HealthCheckIcon } from "./HealthCheckIcon";
|
||||
import { HealthCheckItem } from "./HealthCheckItem";
|
||||
import { Strings } from "../../utils/strings";
|
||||
import { SuccessCheckIcon } from "../SuccessCheckIcon/SuccessCheckIcon";
|
||||
import { ErrorCircleIcon } from "../ErrorCircleIcon/ErrorCircleIcon";
|
||||
|
||||
type Props = {
|
||||
online: boolean;
|
||||
@ -30,6 +31,7 @@ export function OnBoardingStepThree({ online, onStepValid }: Props) {
|
||||
const persistence = usePersistence(codex.isSuccess);
|
||||
const [url, setUrl] = useState(CodexSdk.url);
|
||||
const queryClient = useQueryClient();
|
||||
const [isInvalid, setIsInvalid] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
onStepValid(online && portForwarding.enabled && codex.isSuccess);
|
||||
@ -38,179 +40,180 @@ export function OnBoardingStepThree({ online, onStepValid }: Props) {
|
||||
useEffect(() => {
|
||||
if (codex.isSuccess) {
|
||||
persistence.refetch();
|
||||
portForwarding.refetch();
|
||||
}
|
||||
}, [codex.isSuccess]);
|
||||
}, [persistence, portForwarding, codex.isSuccess]);
|
||||
|
||||
const onChange = (e: React.FormEvent<HTMLInputElement>) => {
|
||||
const onAddressChange = (e: React.FormEvent<HTMLInputElement>) => {
|
||||
const [, port] = Strings.splitURLAndPort(url);
|
||||
const element = e.currentTarget;
|
||||
const value = e.currentTarget.value;
|
||||
if (value) {
|
||||
setUrl(value);
|
||||
|
||||
if (
|
||||
value.startsWith("http://") === false &&
|
||||
value.startsWith("https://") === false
|
||||
) {
|
||||
setIsInvalid(true);
|
||||
return;
|
||||
}
|
||||
|
||||
console.info("isInvalid", isInvalid);
|
||||
|
||||
setIsInvalid(!element.checkValidity());
|
||||
setUrl(value + ":" + port);
|
||||
};
|
||||
|
||||
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 onSave = () =>
|
||||
const onPortChange = (e: React.FormEvent<HTMLInputElement>) => {
|
||||
const [address] = Strings.splitURLAndPort(url);
|
||||
const element = e.currentTarget;
|
||||
const value = element.value;
|
||||
|
||||
setUrl(address + ":" + value);
|
||||
};
|
||||
|
||||
const onSave = () => {
|
||||
if (isInvalid === true) {
|
||||
return;
|
||||
}
|
||||
|
||||
CodexSdk.updateURL(url)
|
||||
.then(() => queryClient.invalidateQueries())
|
||||
.then(() => codex.refetch());
|
||||
};
|
||||
|
||||
const InternetIcon = online ? CheckIcon : X;
|
||||
const PortForWarningIcon = portForwarding.enabled ? CheckIcon : X;
|
||||
const CodexIcon = codex.isSuccess ? CheckIcon : X;
|
||||
const PersistenceIcon = persistence.enabled ? CheckIcon : ShieldAlert;
|
||||
|
||||
let hasPortForwarningWarning = false;
|
||||
let portValue = 0;
|
||||
let forwardingPortValue = defaultPort;
|
||||
|
||||
if (codex.isSuccess && codex.data) {
|
||||
const port = DebugUtils.getTcpPort(codex.data);
|
||||
if (port.error === false && port.data !== defaultPort) {
|
||||
hasPortForwarningWarning = true;
|
||||
}
|
||||
if (!port.error) {
|
||||
portValue = port.data;
|
||||
forwardingPortValue = port.data;
|
||||
}
|
||||
}
|
||||
|
||||
const displayName = OnBoardingUtils.getDisplayName();
|
||||
const [address, port] = Strings.splitURLAndPort(url);
|
||||
|
||||
return (
|
||||
<div className="index-column-section">
|
||||
<div className="onboarding-group">
|
||||
<>
|
||||
<div className="index-column-section onboarding--deviceCheck-block">
|
||||
<div>
|
||||
<Input
|
||||
id="url"
|
||||
label="Codex client node URL"
|
||||
onChange={onChange}
|
||||
value={url}
|
||||
inputClassName="settings-input"></Input>
|
||||
<AlphaIcon variant="primary" />
|
||||
</div>
|
||||
|
||||
<ButtonIcon Icon={Save} onClick={onSave}></ButtonIcon>
|
||||
</div>
|
||||
<div
|
||||
data-testid="network"
|
||||
className={classnames(
|
||||
["onboarding-check"],
|
||||
["onboarding-check--valid", online]
|
||||
)}>
|
||||
<InternetIcon
|
||||
className={classnames(
|
||||
["onboarding-check-icon--valid", online],
|
||||
["onboarding-check-icon--invalid", !online]
|
||||
)}
|
||||
/>
|
||||
<div>
|
||||
<p>Internet connection</p>
|
||||
<SimpleText variant="light">
|
||||
Status indicator for the Internet.
|
||||
</SimpleText>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className={classnames(
|
||||
["onboarding-check"],
|
||||
["onboarding-check--valid", portForwarding.enabled]
|
||||
)}>
|
||||
<PortForWarningIcon
|
||||
className={classnames(
|
||||
["onboarding-check-icon--valid", portForwarding.enabled],
|
||||
["onboarding-check-icon--invalid", !portForwarding.enabled]
|
||||
)}
|
||||
/>
|
||||
<div className="onboarding-check-line">
|
||||
<div>
|
||||
<p>Port forwarding</p>
|
||||
<SimpleText variant="light">
|
||||
Status indicator for port forwarding activation.
|
||||
</SimpleText>
|
||||
{portValue && (
|
||||
<>
|
||||
<br />
|
||||
<SimpleText variant="light">
|
||||
TCP Port detected: {portValue}.
|
||||
</SimpleText>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
{!portForwarding.enabled && (
|
||||
<a
|
||||
className="onboarding-check-refresh"
|
||||
onClick={() => portForwarding.refetch()}>
|
||||
<RefreshCcw
|
||||
size={"1.25rem"}
|
||||
className={classnames([
|
||||
"onboarding-check-refresh--fetching",
|
||||
portForwarding.isFetching,
|
||||
])}
|
||||
/>
|
||||
</a>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<p>Codex</p>
|
||||
<div className="onboarding-codex">
|
||||
<div
|
||||
className={classnames(
|
||||
["onboarding-check"],
|
||||
["onboarding-check--valid", codex.isSuccess]
|
||||
)}>
|
||||
<CodexIcon
|
||||
className={classnames(
|
||||
["onboarding-check-icon--valid", codex.isSuccess],
|
||||
["onboarding-check-icon--invalid", !codex.isSuccess]
|
||||
)}
|
||||
/>
|
||||
<div className="onboarding-check-line">
|
||||
<div>
|
||||
<p>Codex connection</p>
|
||||
<SimpleText variant="light">
|
||||
Status indicator for the Codex network.
|
||||
</SimpleText>
|
||||
</div>
|
||||
{!persistence.enabled && (
|
||||
<a
|
||||
className="onboarding-check-refresh"
|
||||
onClick={() => persistence.refetch()}>
|
||||
<RefreshCcw
|
||||
size={"1.25rem"}
|
||||
className={classnames([
|
||||
"onboarding-check-refresh--fetching",
|
||||
persistence.isFetching,
|
||||
])}
|
||||
/>
|
||||
</a>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
className={classnames(
|
||||
["onboarding-check"],
|
||||
["onboarding-check--valid", persistence.enabled]
|
||||
)}>
|
||||
<PersistenceIcon
|
||||
className={classnames(
|
||||
["onboarding-check-icon--valid", persistence.enabled],
|
||||
["onboarding-check-icon--warning", !persistence.enabled]
|
||||
)}
|
||||
/>
|
||||
<div className="onboarding-check-line">
|
||||
<div>
|
||||
<p>Marketplace</p>
|
||||
<SimpleText variant="light">
|
||||
Status indicator for the marketplace on the Codex node.
|
||||
</SimpleText>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{hasPortForwarningWarning && (
|
||||
<Alert variant="warning" title="Warning">
|
||||
<SimpleText variant="primary" className="onboarding-deviceCheck">
|
||||
<span>
|
||||
It seems like you are using a different port than the default one (
|
||||
{defaultPort}). Be sure the port forwarning is enabled for the port
|
||||
you are running.
|
||||
Connection /<br />
|
||||
Device Health Check
|
||||
</span>
|
||||
</Alert>
|
||||
)}
|
||||
</div>
|
||||
</SimpleText>
|
||||
</div>
|
||||
<div className="index-column-section">
|
||||
<h3 className="index-mainTitle onboarding-displayName">
|
||||
Nice to meet {displayName},<br />
|
||||
Let’s establish our connection.
|
||||
</h3>
|
||||
<div className="onboarding-group">
|
||||
<div className="onboarding-addressAndPort">
|
||||
<div className="onboarding-addressContainer">
|
||||
<Input
|
||||
onPaste={onPaste}
|
||||
id="url"
|
||||
type="url"
|
||||
label="Address"
|
||||
isInvalid={isInvalid}
|
||||
onChange={onAddressChange}
|
||||
value={address}
|
||||
placeholder="127.0.0.1"></Input>
|
||||
{isInvalid ? (
|
||||
<ErrorCircleIcon className="onboarding--addressSuccessIcon" />
|
||||
) : (
|
||||
<SuccessCheckIcon
|
||||
className="onboarding--addressSuccessIcon"
|
||||
variant="default"
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
<div className="onboarding-portContainer">
|
||||
<Input
|
||||
inputClassName="onboarding-port"
|
||||
id="port"
|
||||
label="Port"
|
||||
type="number"
|
||||
onChange={onPortChange}
|
||||
value={port}
|
||||
placeholder="8080"></Input>
|
||||
<SuccessCheckIcon
|
||||
className="onboarding--addressSuccessIcon"
|
||||
variant="default"></SuccessCheckIcon>
|
||||
</div>
|
||||
|
||||
<RefreshIcon
|
||||
onClick={onSave}
|
||||
className={classnames(
|
||||
["onboarding-refresh"],
|
||||
[
|
||||
"onboarding-refresh--fetching",
|
||||
portForwarding.isFetching || codex.isPending,
|
||||
]
|
||||
)}></RefreshIcon>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ul className="onboarding-portForwardingHelp">
|
||||
<li>Port forwarding should be default {forwardingPortValue}.</li>
|
||||
</ul>
|
||||
|
||||
<div className="onboarding-healthChecks">
|
||||
<div className="onboarding-healthCheck-item">
|
||||
<span className="onboarding-healthCheck-icon">
|
||||
<HealthCheckIcon />
|
||||
</span>
|
||||
|
||||
<span className="onboarding-healthCheck-itemText">
|
||||
Health Check
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div className="onboarding-healthCheck-item">
|
||||
<HealthCheckItem
|
||||
value={online ? "success" : "failure"}
|
||||
text="Internet connection"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="onboarding-healthCheck-item">
|
||||
<HealthCheckItem
|
||||
value={portForwarding.enabled ? "success" : "failure"}
|
||||
text="Port forwarding"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="onboarding-healthCheck-item">
|
||||
<HealthCheckItem
|
||||
value={codex.isSuccess ? "success" : "failure"}
|
||||
text="Codex connection"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="onboarding-healthCheck-item">
|
||||
<HealthCheckItem
|
||||
value={codex.isSuccess ? "success" : "warning"}
|
||||
text="Marketplace"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
44
src/components/OnBoarding/OnBoardingStepTwo.css
Normal file
44
src/components/OnBoarding/OnBoardingStepTwo.css
Normal file
@ -0,0 +1,44 @@
|
||||
.onboarding-personalization {
|
||||
margin-top: 1rem;
|
||||
font-family: Azeret Mono;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
line-height: 16.34px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.onboarding--personalization-block {
|
||||
flex: 0.3;
|
||||
}
|
||||
|
||||
.onboarding-addressAndPort {
|
||||
display: flex;
|
||||
gap: 1.5rem;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.onboarding-port {
|
||||
width: 150px;
|
||||
}
|
||||
|
||||
.onboarding-port::-webkit-outer-spin-button,
|
||||
.onboarding-port::-webkit-inner-spin-button {
|
||||
-webkit-appearance: none;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* Firefox */
|
||||
.onboarding-port[type="number"] {
|
||||
-moz-appearance: textfield;
|
||||
}
|
||||
|
||||
.onboarding-portForwardingHelp {
|
||||
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;
|
||||
}
|
||||
@ -1,5 +1,8 @@
|
||||
import { Input } from "@codex-storage/marketplace-ui-components";
|
||||
import { Input, SimpleText } from "@codex-storage/marketplace-ui-components";
|
||||
import { ChangeEvent, useState } from "react";
|
||||
import { AlphaIcon } from "./AlphaIcon";
|
||||
import "./OnBoardingStepTwo.css";
|
||||
import { OnBoardingUtils } from "../../utils/onboarding";
|
||||
|
||||
type Props = {
|
||||
onStepValid: (valid: boolean) => void;
|
||||
@ -9,18 +12,34 @@ export function OnBoardingStepTwo({ onStepValid }: Props) {
|
||||
const [displayName, setDisplayName] = useState("");
|
||||
|
||||
const onDisplayNameChange = (e: ChangeEvent<HTMLInputElement>) => {
|
||||
setDisplayName(e.currentTarget.value);
|
||||
onStepValid(!!e.currentTarget.value);
|
||||
const value = e.currentTarget.value;
|
||||
OnBoardingUtils.setDisplayName(value);
|
||||
setDisplayName(value);
|
||||
onStepValid(!!value);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="index-column-section onboarding--personalization-block">
|
||||
<div>
|
||||
<AlphaIcon variant="primary" />
|
||||
</div>
|
||||
<SimpleText variant="primary" className="onboarding-personalization">
|
||||
<span>Personalization</span>
|
||||
</SimpleText>
|
||||
</div>
|
||||
<div className="index-column-section">
|
||||
<Input
|
||||
onChange={onDisplayNameChange}
|
||||
label="Display name"
|
||||
id="displayName"
|
||||
value={displayName}></Input>
|
||||
<h3 className="index-mainTitle">
|
||||
Let’s get you setup. <br />
|
||||
What do you want to be called?
|
||||
</h3>
|
||||
<div className="index-displayName">
|
||||
<Input
|
||||
onChange={onDisplayNameChange}
|
||||
label="Display name"
|
||||
id="displayName"
|
||||
value={displayName}></Input>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
||||
@ -52,7 +52,7 @@ export function PeerCountryCell({ address, onPinAdd }: Props) {
|
||||
lng: data.longitude,
|
||||
});
|
||||
}
|
||||
}, [data]);
|
||||
}, [data, onPinAdd]);
|
||||
|
||||
return (
|
||||
<Cell>
|
||||
|
||||
22
src/components/RefreshIcon/RefreshIcon.tsx
Normal file
22
src/components/RefreshIcon/RefreshIcon.tsx
Normal file
@ -0,0 +1,22 @@
|
||||
type Props = {
|
||||
onClick?: () => void;
|
||||
className?: string;
|
||||
};
|
||||
|
||||
export function RefreshIcon({ className, onClick }: Props) {
|
||||
return (
|
||||
<svg
|
||||
className={className}
|
||||
onClick={onClick}
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M12 24C5.3724 24 0 18.6276 0 12C0 5.3724 5.3724 0 12 0C18.6276 0 24 5.3724 24 12C24 18.6276 18.6276 24 12 24ZM17.784 18.0912C19.2325 16.7182 20.1449 14.8744 20.3576 12.8899C20.5703 10.9055 20.0695 8.91012 18.9449 7.26133C17.8203 5.61253 16.1454 4.41803 14.2202 3.89182C12.295 3.36561 10.2453 3.54208 8.4384 4.3896L9.6084 6.4956C10.5215 6.09874 11.519 5.93535 12.511 6.02015C13.503 6.10495 14.4584 6.43528 15.2909 6.98135C16.1234 7.52743 16.8069 8.27209 17.2798 9.14821C17.7528 10.0243 18.0003 11.0044 18 12H14.4L17.784 18.0912ZM15.5616 19.6104L14.3916 17.5044C13.4785 17.9013 12.481 18.0646 11.489 17.9798C10.497 17.8951 9.54165 17.5647 8.70914 17.0186C7.87664 16.4726 7.1931 15.7279 6.72016 14.8518C6.24722 13.9757 5.99973 12.9956 6 12H9.6L6.216 5.9088C4.76747 7.28176 3.85511 9.12563 3.64239 11.1101C3.42966 13.0945 3.93047 15.0899 5.05508 16.7387C6.17969 18.3875 7.85462 19.582 9.77982 20.1082C11.705 20.6344 13.7547 20.4579 15.5616 19.6104V19.6104Z"
|
||||
fill="var(--codex-color-primary)"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
24
src/components/SuccessCheckIcon/SuccessCheckIcon.tsx
Normal file
24
src/components/SuccessCheckIcon/SuccessCheckIcon.tsx
Normal file
@ -0,0 +1,24 @@
|
||||
type Props = {
|
||||
variant: "primary" | "default";
|
||||
className?: string;
|
||||
};
|
||||
|
||||
export function SuccessCheckIcon({ variant, className = "" }: Props) {
|
||||
const color = variant === "primary" ? "#1FC16B" : "#444444";
|
||||
|
||||
return (
|
||||
<span className={"onboarding-healthCheck-icon " + className}>
|
||||
<svg
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg">
|
||||
<path
|
||||
d="M8 15.5C3.85775 15.5 0.5 12.1423 0.5 8C0.5 3.85775 3.85775 0.5 8 0.5C12.1423 0.5 15.5 3.85775 15.5 8C15.5 12.1423 12.1423 15.5 8 15.5ZM7.25225 11L12.5548 5.69675L11.4943 4.63625L7.25225 8.879L5.1305 6.75725L4.07 7.81775L7.25225 11Z"
|
||||
fill={color}
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
@ -31,6 +31,7 @@
|
||||
--codex-color-disabled: #717171;
|
||||
--codex-color-light: rgb(150 150 150);
|
||||
--codex-border-color: #2b303b;
|
||||
--codex-input-border-color: #494949;
|
||||
--codex-background-secondary: rgb(38 38 38);
|
||||
--codex-highlight-color: #2f2f2f;
|
||||
--codex-background-light: rgb(64 64 64);
|
||||
@ -41,6 +42,10 @@
|
||||
BlinkMacSystemFont, Segoe UI, Roboto, Helvetica Neue, Arial, Noto Sans,
|
||||
sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol,
|
||||
Noto Color Emoji;
|
||||
--codex-input-label-color: #7b7b7b;
|
||||
--codex-input-border-color: #494949;
|
||||
--codex-input-background: #232323;
|
||||
--codex-input-color-error: #fb3748;
|
||||
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
@ -173,3 +178,11 @@ a[aria-disabled] {
|
||||
.page {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
input:-webkit-autofill,
|
||||
input:-webkit-autofill:hover,
|
||||
input:-webkit-autofill:focus,
|
||||
input:-webkit-autofill:active {
|
||||
-webkit-transition-delay: 9999s;
|
||||
transition-delay: 9999s;
|
||||
}
|
||||
|
||||
@ -139,15 +139,15 @@
|
||||
}
|
||||
|
||||
.index-column-section {
|
||||
max-width: 450px;
|
||||
max-width: 500px;
|
||||
}
|
||||
|
||||
.index-column-section:not(:first-child) {
|
||||
.index-column-section {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.index-column-section:first-child {
|
||||
flex: 0.7;
|
||||
flex: 0.5;
|
||||
}
|
||||
|
||||
.index-column-section:last-child {
|
||||
@ -185,6 +185,10 @@
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.index-displayName {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
|
||||
@keyframes pulse {
|
||||
0% {
|
||||
opacity: 0.4;
|
||||
|
||||
@ -2,7 +2,6 @@ import { createFileRoute, useNavigate } from "@tanstack/react-router";
|
||||
import "./index.css";
|
||||
import { ArrowRightCircle } from "../components/ArrowRightCircle/ArrowRightCircle";
|
||||
import { useNetwork } from "../network/useNetwork";
|
||||
import { NetworkIcon } from "../components/NetworkIcon/NetworkIcon";
|
||||
import { Logotype } from "../components/Logotype/Logotype";
|
||||
import { useState } from "react";
|
||||
import { OnBoardingStepOne } from "../components/OnBoarding/OnBoardingStepOne";
|
||||
@ -10,7 +9,6 @@ import { OnBoardingStepTwo } from "../components/OnBoarding/OnBoardingStepTwo";
|
||||
import { classnames } from "../utils/classnames";
|
||||
import { OnBoardingStepThree } from "../components/OnBoarding/OnBoardingStepThree";
|
||||
import { attributes } from "../utils/attributes";
|
||||
import { CodexLogo } from "../components/CodexLogo/CodexLogo";
|
||||
import { OnBoardingImage } from "../components/OnBoarding/OnBoardingImage";
|
||||
import { OnBoardingUtils } from "../utils/onboarding";
|
||||
|
||||
@ -52,7 +50,7 @@ function Index() {
|
||||
<OnBoardingStepThree online={online} onStepValid={onStepValid} />,
|
||||
];
|
||||
|
||||
const text = online ? "Network connected" : "Network disconnected";
|
||||
// const text = online ? "Network connected" : "Network disconnected";
|
||||
|
||||
return (
|
||||
<div className="index">
|
||||
@ -88,13 +86,14 @@ function Index() {
|
||||
<OnBoardingImage />
|
||||
</div>
|
||||
<div className="index-columnRight">
|
||||
<div className="index-logo">
|
||||
<div></div>
|
||||
{/* <div className="index-logo">
|
||||
<div className="index-network">
|
||||
<p className="index-network-text">{text}</p>
|
||||
<NetworkIcon active={online}></NetworkIcon>
|
||||
</div>
|
||||
<CodexLogo></CodexLogo>
|
||||
</div>
|
||||
</div> */}
|
||||
<a
|
||||
className="index-link2"
|
||||
{...attributes({ "aria-disabled": !isStepValid })}
|
||||
|
||||
@ -5,5 +5,13 @@ export const OnBoardingUtils = {
|
||||
|
||||
setStep(step: number) {
|
||||
localStorage.setItem("onboarding-step", step.toString())
|
||||
},
|
||||
|
||||
setDisplayName(displayName: string) {
|
||||
localStorage.setItem("display-name", displayName)
|
||||
},
|
||||
|
||||
getDisplayName() {
|
||||
return localStorage.getItem("display-name") || ""
|
||||
}
|
||||
}
|
||||
@ -1,3 +1,11 @@
|
||||
export const Strings = {
|
||||
shortId: (id: string) => id.slice(0, 5) + "..." + id.slice(-5),
|
||||
|
||||
splitURLAndPort(url: string) {
|
||||
const [protocol, hostname = "", port = ""] = url.split(":")
|
||||
|
||||
return [protocol + ":" + hostname, port]
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user