Complete onboarding screens

This commit is contained in:
Arnaud 2024-10-25 18:27:17 +02:00
commit 7f60e54a63
No known key found for this signature in database
GPG Key ID: 69D6CE281FCAE663
24 changed files with 518 additions and 207 deletions

16
package-lock.json generated
View File

@ -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"
},

View File

@ -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",

View 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>
);
}

View File

@ -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>

View File

@ -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>
);
}

View 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>
);
}

View File

@ -0,0 +1,5 @@
.onboarding-healthCheckItem {
display: flex;
align-items: center;
gap: 16px;
}

View 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>
);
}

View File

@ -1,4 +1,4 @@
import "./OmBoardingImage.css";
import "./OnBoardingImage.css";
export function OnBoardingImage() {
return (

View 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>
);
}
}

View File

@ -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>

View File

@ -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 */

View File

@ -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 />
Lets 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>
</>
);
}

View 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;
}

View File

@ -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">
Lets 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>
</>
);

View File

@ -52,7 +52,7 @@ export function PeerCountryCell({ address, onPinAdd }: Props) {
lng: data.longitude,
});
}
}, [data]);
}, [data, onPinAdd]);
return (
<Cell>

View 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>
);
}

View 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>
);
}

View File

@ -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;
}

View File

@ -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;

View File

@ -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 })}

View File

@ -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") || ""
}
}

View File

@ -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]
}
};