-
Review your request
+
Choose your criteria
+
+
+
+
+
+
+
-
-
-
-
-
-
+ data={data.availability.toString()}
+ comment={"Contract duration in " + data.availabilityUnit}
+ onChange={onAvailabilityChange}>
+ onChange={onRewardChange}>
+ onChange={onCollateralChange}>
-
-
- This request with CID {" "}
- {cid} will expire in
- {plurals("minute", price.expiration)}
- after the start.
-
-
-
+
}
+ title="Warning"
+ variant="warning"
+ className="storageRequestReview-alert">
+ This request with CID
+
{cid} will expire in
+
{plurals("minute", data.expiration)}
+ after the start.
+ If no suitable hosts are found matching your storage requirements, you
+ will incur a charge of X tokens.
+
-
Price comparaison with the market
-
@@ -142,7 +380,6 @@ export function StorageRequestReview({ onToggleNext }: Props) {
Excellent
-
diff --git a/src/components/StorageRequestSetup/StorageRequestSetup.css b/src/components/StorageRequestSetup/StorageRequestSetup.css
index b03bb1d..3a62156 100644
--- a/src/components/StorageRequestSetup/StorageRequestSetup.css
+++ b/src/components/StorageRequestSetup/StorageRequestSetup.css
@@ -1,21 +1,18 @@
.storageRequest {
background-color: var(--codex-background);
background-color: var(--codex-background);
- padding: 0.5rem 1rem;
border-radius: var(--codex-border-radius);
transition: transform 0.15s;
- position: fixed;
max-width: 800px;
- left: 50%;
- top: 50%;
- transform: translate(-50%, -50%) scale(0);
overflow-y: auto;
+ overflow-x: hidden;
opacity: 0;
+ z-index: -1;
}
.storageRequest-open {
- transform: translate(-50%, -50%) scale(1);
opacity: 1;
+ z-index: 10;
}
.storageRequest-title {
@@ -81,14 +78,26 @@
text-align: center;
}
-.storageRequest .inputGroup-input {
- width: 100%;
-}
-
@media (max-width: 800px) {
.storageRequest {
margin: auto;
width: 100%;
+ position: absolute;
+ top: 0;
+ left: 0;
+ min-height: 100%;
+ display: flex;
+ align-items: center;
+
+ .alert {
+ flex-direction: column;
+ align-items: flex-start;
+ }
+
+ .stepper-body,
+ .stepper {
+ width: calc(100% - 3rem);
+ }
}
}
@@ -97,4 +106,15 @@
margin: auto;
width: 85%;
}
+
+ .storageRequest {
+ left: 50%;
+ top: 50%;
+ transform: translate(-50%, -50%) scale(0);
+ position: fixed;
+ }
+
+ .storageRequest-open {
+ transform: translate(-50%, -50%) scale(1);
+ }
}
diff --git a/src/components/StorageRequestSetup/StorageRequestStepper.tsx b/src/components/StorageRequestSetup/StorageRequestStepper.tsx
index e2d6147..189439f 100644
--- a/src/components/StorageRequestSetup/StorageRequestStepper.tsx
+++ b/src/components/StorageRequestSetup/StorageRequestStepper.tsx
@@ -1,34 +1,29 @@
import { StorageRequestFileChooser } from "../../components/StorageRequestSetup/StorageRequestFileChooser";
-import { useEffect, useRef, useState } from "react";
-import { StorageRequestAvailability } from "../../components/StorageRequestSetup/StorageRequestAvailability";
-import { StorageRequestDurability } from "../../components/StorageRequestSetup/StorageRequestDurability";
-import { StorageRequestPrice } from "../../components/StorageRequestSetup/StorageRequestPrice";
+import { useCallback, useEffect, useRef, useState } from "react";
import { WebStorage } from "../../utils/web-storage";
import { STEPPER_DURATION } from "../../utils/constants";
import { StorageRequestReview } from "./StorageRequestReview";
import { CodexCreateStorageRequestInput } from "@codex/sdk-js";
import { useMutation, useQueryClient } from "@tanstack/react-query";
import { CodexSdk } from "../../sdk/codex";
-import {
- StorageAvailabilityValue,
- StorageDurabilityStepValue,
- StoragePriceStepValue,
-} from "./types";
+import { StorageAvailabilityUnit } from "./types";
import { Backdrop, Stepper } from "@codex/marketplace-ui-components";
import { classnames } from "../../utils/classnames";
+import { StorageRequestDone } from "./StorageRequestDone";
+import { PurchaseStorage } from "../../utils/purchases-storage";
-function calculateAvailability(value: StorageAvailabilityValue) {
- switch (value.unit) {
+function calculateAvailability(value: number, unit: StorageAvailabilityUnit) {
+ switch (unit) {
case "minutes":
- return 60 * value.value;
+ return 60 * value;
case "hours":
- return 60 * 60 * value.value;
+ return 60 * 60 * value;
case "days":
- return 24 * 60 * 60 * value.value;
+ return 24 * 60 * 60 * value;
case "months":
- return 30 * 24 * 60 * 60 * value.value;
+ return 30 * 24 * 60 * 60 * value;
case "years":
- return 365 * 30 * 60 * 60 * value.value;
+ return 365 * 30 * 60 * 60 * value;
}
}
@@ -41,37 +36,32 @@ type Props = {
export function StorageRequestStepper({ className, open, onClose }: Props) {
const [progress, setProgress] = useState(true);
const [step, setStep] = useState(0);
- const steps = useRef([
- "File",
- "Availability",
- "Durability",
- "Price",
- "Review",
- ]);
+ const steps = useRef(["File", "Criteria", "Success"]);
const [isNextDisable, setIsNextDisable] = useState(true);
const queryClient = useQueryClient();
+
const { mutateAsync, isPending, isError, error } = useMutation({
mutationKey: ["debug"],
mutationFn: (input: CodexCreateStorageRequestInput) =>
CodexSdk.marketplace().then((marketplace) =>
marketplace.createStorageRequest(input)
),
- onSuccess: async (data) => {
+ onSuccess: async (data, { cid }) => {
if (data.error) {
// TODO report error
console.error(data);
} else {
- await Promise.all([
- WebStorage.delete("storage-request-step"),
- WebStorage.delete("storage-request-step-1"),
- WebStorage.delete("storage-request-step-2"),
- WebStorage.delete("storage-request-step-3"),
- WebStorage.delete("storage-request-step-4"),
- ]);
-
- setStep(0);
+ // setStep((s) => (s = 1));
queryClient.invalidateQueries({ queryKey: ["purchases"] });
- onClose();
+
+ let requestId = data.data;
+
+ if (!requestId.startsWith("0x")) {
+ console.debug("No prefix detected");
+ requestId = "0x" + requestId;
+ }
+
+ PurchaseStorage.set(requestId, cid);
}
},
});
@@ -86,6 +76,11 @@ export function StorageRequestStepper({ className, open, onClose }: Props) {
});
}, []);
+ const onChangeNextState = useCallback(
+ (s: "enable" | "disable") => setIsNextDisable(s === "disable"),
+ []
+ );
+
if (isError) {
// TODO Report error
console.error(error);
@@ -94,53 +89,37 @@ export function StorageRequestStepper({ className, open, onClose }: Props) {
const components = [
StorageRequestFileChooser,
- StorageRequestAvailability,
- StorageRequestDurability,
- StorageRequestPrice,
+ // StorageRequestAvailability,
+ // StorageRequestDurability,
+ // StorageRequestPrice,
StorageRequestReview,
+ StorageRequestDone,
];
const onChangeStep = async (s: number, state: "before" | "end") => {
+ if (s === -1) {
+ setStep(0);
+ setIsNextDisable(true);
+ onClose();
+ return;
+ }
+
if (state === "before") {
setProgress(true);
return;
}
- if (s === -1) {
- setIsNextDisable(true);
- setProgress(false);
- onClose();
- return;
- }
-
- if (s === steps.current.length) {
+ if (s >= steps.current.length) {
setIsNextDisable(true);
setProgress(false);
- const [cid, availability, durability, price] = await Promise.all([
- WebStorage.get
("storage-request-step-1"),
- WebStorage.get("storage-request-step-2"),
- WebStorage.get("storage-request-step-3"),
- WebStorage.get("storage-request-step-4"),
- ]);
-
- if (!cid || !availability || !durability || !price) {
- return;
+ if (s >= steps.current.length) {
+ setStep(0);
+ WebStorage.delete("storage-request-step");
+ WebStorage.delete("storage-request-criteria");
}
- const { reward, collateral, expiration } = price;
- const { nodes, proofProbability, tolerance } = durability;
-
- mutateAsync({
- cid,
- collateral,
- duration: calculateAvailability(availability),
- expiry: expiration * 60,
- nodes,
- proofProbability,
- tolerance,
- reward,
- });
+ onClose();
return;
}
@@ -150,9 +129,49 @@ export function StorageRequestStepper({ className, open, onClose }: Props) {
setIsNextDisable(true);
setProgress(false);
setStep(s);
+
+ if (s == 2) {
+ setIsNextDisable(true);
+ setProgress(false);
+
+ const [cid, criteria] = await Promise.all([
+ WebStorage.get("storage-request-step-1"),
+ // TODO define criteria interface
+ // eslint-disable-next-line
+ WebStorage.get("storage-request-criteria"),
+ ]);
+
+ if (!cid || !criteria) {
+ return;
+ }
+
+ const {
+ availabilityUnit = "days",
+ availability,
+ reward,
+ collateral,
+ expiration,
+ nodes,
+ proofProbability,
+ tolerance,
+ } = criteria;
+
+ mutateAsync({
+ cid,
+ collateral,
+ duration: calculateAvailability(availability, availabilityUnit),
+ expiry: expiration * 60,
+ nodes,
+ proofProbability,
+ tolerance,
+ reward,
+ });
+ } else {
+ setIsNextDisable(false);
+ }
};
- const Body = components[step];
+ const Body = components[step] || components[0];
return (
<>
@@ -165,7 +184,7 @@ export function StorageRequestStepper({ className, open, onClose }: Props) {
)}>
setIsNextDisable(false)} />}
+ Body={ }
step={step}
onChangeStep={onChangeStep}
progress={progress || isPending}
diff --git a/src/components/TruncateCellRender/TruncateCellRender.tsx b/src/components/TruncateCellRender/TruncateCellRender.tsx
new file mode 100644
index 0000000..10d84a0
--- /dev/null
+++ b/src/components/TruncateCellRender/TruncateCellRender.tsx
@@ -0,0 +1,4 @@
+export function TruncateCellRender(cid: string) {
+ const truncated = cid.slice(0, 5) + ".".repeat(5) + cid.slice(-5);
+ return {truncated} ;
+}
diff --git a/src/components/Welcome/Welcome.css b/src/components/Welcome/Welcome.css
new file mode 100644
index 0000000..705a0e3
--- /dev/null
+++ b/src/components/Welcome/Welcome.css
@@ -0,0 +1,30 @@
+.welcome {
+ border-radius: var(--codex-border-radius);
+ border: 1px solid var(--codex-border-color);
+ background-color: var(--codex-background-secondary);
+ padding: 1rem 1.5rem;
+ display: flex;
+ align-items: flex-start;
+ flex-direction: column;
+}
+
+.welcome-title {
+ font-weight: bold;
+ font-size: 1.125rem;
+ line-height: 1.75rem;
+ margin-bottom: 0.75rem;
+}
+
+.welcome-body {
+ flex: 1;
+}
+
+.welcome-link {
+ display: flex;
+ align-items: center;
+ color: var(--codex-color-primary);
+}
+
+.welcome-link:hover {
+ text-decoration: underline;
+}
diff --git a/src/components/Welcome/Welcome.tsx b/src/components/Welcome/Welcome.tsx
new file mode 100644
index 0000000..e2a3956
--- /dev/null
+++ b/src/components/Welcome/Welcome.tsx
@@ -0,0 +1,21 @@
+import { SimpleText } from "@codex/marketplace-ui-components";
+import "./Welcome.css";
+import { Link } from "@tanstack/react-router";
+import { ChevronRight } from "lucide-react";
+
+export function Welcome() {
+ return (
+
+
Welcome to Codex Marketplace
+
+
+ You can start using Codex for testing purpose by uploading new files.
+
+
+
+
+ Explore more content
+
+
+ );
+}
diff --git a/src/hooks/useData.tsx b/src/hooks/useData.tsx
new file mode 100644
index 0000000..ed0b0e5
--- /dev/null
+++ b/src/hooks/useData.tsx
@@ -0,0 +1,49 @@
+import { useQuery } from "@tanstack/react-query";
+import { FilesStorage } from "../utils/file-storage";
+import { CodexSdk } from "../sdk/codex";
+
+export function useData() {
+ const { data = [] } = useQuery({
+ queryFn: () =>
+ CodexSdk.data().then(async (data) => {
+ const res = await data.cids();
+
+ if (res.error) {
+ // TODO error
+ return [];
+ }
+
+ const metadata = await FilesStorage.list();
+
+ return res.data.content.map((content, index) => {
+ const value = metadata.find(([cid]) => content.cid === cid);
+
+ if (!value) {
+ return {
+ ...content,
+ mimetype: "N/A",
+ uploadedAt: new Date(0, 0, 0, 0, 0, 0),
+ name: "N/A" + index,
+ };
+ }
+
+ const {
+ mimetype = "",
+ name = "",
+ uploadedAt = new Date(0, 0, 0, 0, 0, 0),
+ } = value[1];
+
+ return {
+ ...content,
+ mimetype,
+ name,
+ uploadedAt,
+ };
+ });
+ }),
+ queryKey: ["cids"],
+ refetchOnWindowFocus: false,
+ });
+
+ return data;
+}
diff --git a/src/index.css b/src/index.css
index 4323663..c15b467 100644
--- a/src/index.css
+++ b/src/index.css
@@ -6,7 +6,11 @@
--codex-background: rgb(23 23 23);
--codex-color: #e1e4d9;
--codex-color-contrast: #f8f8f8;
- --codex-color-error: #f85723;
+ --codex-color-error: 239, 68, 68;
+ --codex-color-warning: 234, 179, 8;
+ --codex-color-success: 20, 184, 166;
+ --codex-color-blue: 30, 64, 175;
+ --codex-color-grey: 170, 170, 170;
--codex-color-primary: #c1f0a4;
--codex-color-primary-variant: #c1f0a4cc;
--codex-color-on-primary: #333;
@@ -22,7 +26,6 @@
BlinkMacSystemFont, Segoe UI, Roboto, Helvetica Neue, Arial, Noto Sans,
sans-serif, Apple Color Emoji, Segoe UI Emoji, Segoe UI Symbol,
Noto Color Emoji;
- --codex-color-warning: rgb(234 179 8);
-webkit-tap-highlight-color: transparent;
-webkit-text-size-adjust: 100%;
@@ -31,7 +34,7 @@
font-feature-settings: normal;
font-variation-settings: normal;
tab-size: 4;
- font-size: 0.875rem;
+ font-size: 1.15rem;
font-size: var(--codex-font-size);
color-scheme: dark;
color: var(--codex-color);
@@ -62,12 +65,14 @@
html {
min-height: 100%;
display: flex;
+ max-width: 100%;
}
body {
margin: 0;
flex: 1;
display: flex;
+ max-width: 100%;
}
ul,
@@ -92,6 +97,7 @@ main {
flex: 1;
display: flex;
flex-direction: column;
+ max-width: 100%;
}
hr {
@@ -124,4 +130,9 @@ a {
.root {
display: flex;
flex: 1;
+ max-width: 100%;
+}
+
+.page {
+ max-width: 100%;
}
diff --git a/src/routes/dashboard.tsx b/src/routes/dashboard.tsx
index d48767d..a6f941c 100644
--- a/src/routes/dashboard.tsx
+++ b/src/routes/dashboard.tsx
@@ -3,17 +3,9 @@ import "./dashboard.css";
import {
MenuItem,
MenuItemComponentProps,
- NetworkIndicator,
Page,
} from "@codex/marketplace-ui-components";
-import {
- Home,
- Star,
- ShoppingBag,
- Server,
- Settings,
- HelpCircle,
-} from "lucide-react";
+import { Home, ShoppingBag, Server, Settings, HelpCircle } from "lucide-react";
import { ICON_SIZE } from "../utils/constants";
import { NodeIndicator } from "../components/NodeIndicator/NodeIndicator";
import { HttpNetworkIndicator } from "../components/HttpNetworkIndicator/HttpNetworkIndicator";
@@ -36,15 +28,6 @@ const Layout = () => {
),
},
- {
- type: "menu-item",
- Component: (p: MenuItemComponentProps) => (
-
-
- Favorites
-
- ),
- },
{
type: "separator",
},
@@ -77,6 +60,17 @@ const Layout = () => {
),
},
+ {
+ type: "separator",
+ },
+ {
+ type: "menu-item",
+ Component: (p: MenuItemComponentProps) => (
+
+ Help
+
+ ),
+ },
{
type: "menu-item",
Component: (p: MenuItemComponentProps) => (
@@ -86,17 +80,6 @@ const Layout = () => {
),
},
- {
- type: "separator",
- },
- {
- type: "menu-item",
- Component: (p: MenuItemComponentProps) => (
-
- Help
-
- ),
- },
] satisfies MenuItem[];
return } items={items} Right={Right} />;
diff --git a/src/routes/dashboard/about.tsx b/src/routes/dashboard/about.tsx
index 9cc58f8..525aa97 100644
--- a/src/routes/dashboard/about.tsx
+++ b/src/routes/dashboard/about.tsx
@@ -88,7 +88,7 @@ const About = () => {
+ href={"/api/codex/v1/data/" + c.cid}>
Download
diff --git a/src/routes/dashboard/favorites.tsx b/src/routes/dashboard/favorites.tsx
index 85acde2..72b0307 100644
--- a/src/routes/dashboard/favorites.tsx
+++ b/src/routes/dashboard/favorites.tsx
@@ -1,13 +1,13 @@
import { createFileRoute } from "@tanstack/react-router";
import { ErrorBoundary } from "../../components/ErrorBoundary/ErrorBoundary";
-import { Manifests } from "../../components/Manifests/Manitests";
+import { Files } from "../../components/Files/Files";
export const Route = createFileRoute("/dashboard/favorites")({
component: () => (
<>