diff --git a/src/components/Availability/AvailabilitySpaceAllocation.tsx b/src/components/Availability/AvailabilitySpaceAllocation.tsx
index 86cf24e..79bbe7c 100644
--- a/src/components/Availability/AvailabilitySpaceAllocation.tsx
+++ b/src/components/Availability/AvailabilitySpaceAllocation.tsx
@@ -1,6 +1,5 @@
import { CodexNodeSpace } from "@codex-storage/sdk-js";
import { AvailabilityState } from "./types";
-import { GB, TB } from "../../utils/constants";
import { SpaceAllocation } from "@codex-storage/marketplace-ui-components";
import "./AvailabilitySpaceAllocation.css";
import { availabilityUnit } from "./availability.domain";
@@ -24,15 +23,15 @@ export function AvailabilitySpaceAllocation({ availability, space }: Props) {
const spaceData = [
{
title: "Space allocated",
- size: allocated,
+ size: Math.trunc(allocated),
},
{
title: "New space allocation",
- size: size,
+ size: Math.trunc(size),
},
{
title: "Remaining space",
- size: remaining,
+ size: Math.trunc(remaining),
},
];
diff --git a/src/components/Availability/availability.domain.ts b/src/components/Availability/availability.domain.ts
index 3175fa5..909f326 100644
--- a/src/components/Availability/availability.domain.ts
+++ b/src/components/Availability/availability.domain.ts
@@ -5,16 +5,15 @@ import { AvailabilityState } from "./types";
export const availabilityUnit = (unit: "gb" | "tb") =>
unit === "gb" ? GB : TB;
-export const availabilityMax = (space: CodexNodeSpace, unit: "gb" | "tb") => {
- const bytes = availabilityUnit(unit);
- return space.quotaMaxBytes / bytes - space.quotaReservedBytes / bytes;
-};
+export const availabilityMax = (space: CodexNodeSpace) =>
+ space.quotaMaxBytes - space.quotaReservedBytes - space.quotaUsedBytes;
export const isAvailabilityValid = (
- totalSize: string | number,
+ availability: AvailabilityState,
max: number
) => {
- const size = parseFloat(totalSize.toString());
+ const unit = availabilityUnit(availability.totalSizeUnit);
+ const size = parseFloat(availability.totalSize.toString()) * unit;
return size > 0 && size <= max;
};
diff --git a/src/components/Availailibities/Availabilities.tsx b/src/components/Availailibities/Availabilities.tsx
deleted file mode 100644
index 257d7de..0000000
--- a/src/components/Availailibities/Availabilities.tsx
+++ /dev/null
@@ -1,100 +0,0 @@
-import {
- SpaceAllocation,
- Spinner,
-} from "@codex-storage/marketplace-ui-components";
-import { useQuery } from "@tanstack/react-query";
-import { Promises } from "../../utils/promises";
-import { AvailabilityCreate } from "../Availability/AvailabilityCreate";
-import { CodexSdk } from "../../sdk/codex";
-import { Strings } from "../../utils/strings";
-import { AvailabilitiesTable } from "../Availability/AvailabilitiesTable";
-import "./Availabilities.css";
-
-const defaultSpace = {
- quotaMaxBytes: 0,
- quotaReservedBytes: 0,
- quotaUsedBytes: 0,
- totalBlocks: 0,
-};
-
-export function Availabilities() {
- {
- // Error will be catched in ErrorBounday
- const { data: availabilities = [], isPending } = useQuery({
- queryFn: () =>
- CodexSdk.marketplace
- .availabilities()
- .then((s) => Promises.rejectOnError(s))
- .then((res) => res.sort((a, b) => b.totalSize - a.totalSize)),
- queryKey: ["availabilities"],
- initialData: [],
-
- // No need to retry because if the connection to the node
- // is back again, all the queries will be invalidated.
- retry: false,
-
- // The client node should be local, so display the cache value while
- // making a background request looks good.
- staleTime: 0,
-
- // Refreshing when focus returns can be useful if a user comes back
- // to the UI after performing an operation in the terminal.
- refetchOnWindowFocus: true,
- });
-
- // Error will be catched in ErrorBounday
- const { data: space = defaultSpace } = useQuery({
- queryFn: () =>
- CodexSdk.data.space().then((s) => Promises.rejectOnError(s)),
- queryKey: ["space"],
- initialData: defaultSpace,
-
- // No need to retry because if the connection to the node
- // is back again, all the queries will be invalidated.
- retry: false,
-
- // The client node should be local, so display the cache value while
- // making a background request looks good.
- staleTime: 0,
-
- // Refreshing when focus returns can be useful if a user comes back
- // to the UI after performing an operation in the terminal.
- refetchOnWindowFocus: true,
- });
-
- const allocation = availabilities
- .map((a) => ({
- title: Strings.shortId(a.id),
- size: a.totalSize,
- }))
- .slice(0, 6);
-
- return (
-
-
- {isPending ? (
-
-
-
- ) : (
-
- )}
-
-
-
-
- );
- }
-}
diff --git a/src/components/StorageRequestSetup/StorageRequestStepper.css b/src/components/StorageRequestSetup/StorageRequestCreate.css
similarity index 66%
rename from src/components/StorageRequestSetup/StorageRequestStepper.css
rename to src/components/StorageRequestSetup/StorageRequestCreate.css
index 64ac1c3..09e57a1 100644
--- a/src/components/StorageRequestSetup/StorageRequestStepper.css
+++ b/src/components/StorageRequestSetup/StorageRequestCreate.css
@@ -1,5 +1,5 @@
@media (min-width: 801px) {
- .storageRequest-stepper {
+ .storageRequestCreate {
min-width: 700px;
}
}
diff --git a/src/components/StorageRequestSetup/StorageRequestStepper.tsx b/src/components/StorageRequestSetup/StorageRequestCreate.tsx
similarity index 90%
rename from src/components/StorageRequestSetup/StorageRequestStepper.tsx
rename to src/components/StorageRequestSetup/StorageRequestCreate.tsx
index 7401520..a352b32 100644
--- a/src/components/StorageRequestSetup/StorageRequestStepper.tsx
+++ b/src/components/StorageRequestSetup/StorageRequestCreate.tsx
@@ -1,4 +1,4 @@
-import { StorageRequestFileChooser } from "../../components/StorageRequestSetup/StorageRequestFileChooser";
+import { StorageRequestFileChooser } from "./StorageRequestFileChooser";
import { useEffect, useRef, useState } from "react";
import { WebStorage } from "../../utils/web-storage";
import { STEPPER_DURATION } from "../../utils/constants";
@@ -10,11 +10,11 @@ import {
Stepper,
useStepperReducer,
} from "@codex-storage/marketplace-ui-components";
-import { StorageRequestDone } from "./StorageRequestDone";
+import { StorageRequestSuccess } from "./StorageRequestSuccess";
import { Times } from "../../utils/times";
import { useStorageRequestMutation } from "./useStorageRequestMutation";
import { Plus } from "lucide-react";
-import "./StorageRequestStepper.css";
+import "./StorageRequestCreate.css";
import { StorageRequestError } from "./StorageRequestError";
const CONFIRM_STATE = 2;
@@ -31,7 +31,7 @@ const defaultStorageRequest: StorageRequest = {
expiration: 300,
};
-export function StorageRequestStepper() {
+export function StorageRequestCreate() {
const [storageRequest, setStorageRequest] = useState(
defaultStorageRequest
);
@@ -60,7 +60,7 @@ export function StorageRequestStepper() {
const components = [
StorageRequestFileChooser,
StorageRequestReview,
- error ? StorageRequestError : StorageRequestDone,
+ error ? StorageRequestError : StorageRequestSuccess,
];
const onNextStep = async (step: number) => {
@@ -133,7 +133,7 @@ export function StorageRequestStepper() {
duration={STEPPER_DURATION}
onNextStep={onNextStep}
backLabel={backLabel}
- className="storageRequest-stepper"
+ className="storageRequestCreate"
nextLabel={nextLabel}>
{
- // TODO Move this to proxy object
- WebStorage.set(data, {
- type: file.type,
- name: file.name,
- });
-
+ const onSuccess = (data: string) => {
onStorageRequestChange({ cid: data });
};
diff --git a/src/components/StorageRequestSetup/StorageRequestDone.css b/src/components/StorageRequestSetup/StorageRequestSuccess.css
similarity index 100%
rename from src/components/StorageRequestSetup/StorageRequestDone.css
rename to src/components/StorageRequestSetup/StorageRequestSuccess.css
diff --git a/src/components/StorageRequestSetup/StorageRequestDone.tsx b/src/components/StorageRequestSetup/StorageRequestSuccess.tsx
similarity index 84%
rename from src/components/StorageRequestSetup/StorageRequestDone.tsx
rename to src/components/StorageRequestSetup/StorageRequestSuccess.tsx
index e0d2ea6..685c39c 100644
--- a/src/components/StorageRequestSetup/StorageRequestDone.tsx
+++ b/src/components/StorageRequestSetup/StorageRequestSuccess.tsx
@@ -1,11 +1,12 @@
import { Placeholder } from "@codex-storage/marketplace-ui-components";
import { CircleCheck } from "lucide-react";
-import "./StorageRequestDone.css";
+import "./StorageRequestSuccess.css";
import { StorageRequestComponentProps } from "./types";
import { useEffect } from "react";
-// TODO rename
-export function StorageRequestDone({ dispatch }: StorageRequestComponentProps) {
+export function StorageRequestSuccess({
+ dispatch,
+}: StorageRequestComponentProps) {
useEffect(() => {
dispatch({
type: "toggle-buttons",
diff --git a/src/hooks/useData.tsx b/src/hooks/useData.tsx
index 835dc4d..bf4a815 100644
--- a/src/hooks/useData.tsx
+++ b/src/hooks/useData.tsx
@@ -1,76 +1,28 @@
import { useQuery } from "@tanstack/react-query";
-import { FilesStorage } from "../utils/file-storage";
import { CodexSdk } from "../sdk/codex";
-import * as Sentry from "@sentry/browser";
-import { CodexDataContent } from "@codex-storage/sdk-js";
+import { CodexDataResponse } from "@codex-storage/sdk-js";
+import { Promises } from "../utils/promises";
export function useData() {
- const { data = [] } = useQuery({
- queryFn: (): Promise => {
- // TODO refactor
- return Promise.resolve().then(async () => {
- const res = await CodexSdk.data.cids();
+ const { data = { content: [] } satisfies CodexDataResponse } =
+ useQuery({
+ queryFn: (_) =>
+ CodexSdk.data.cids().then((res) => Promises.rejectOnError(res)),
+ queryKey: ["cids"],
- if (res.error) {
- if (import.meta.env.PROD) {
- Sentry.captureException(res.data);
- }
- return [];
- }
+ initialData: { content: [] } satisfies CodexDataResponse,
- const metadata = await FilesStorage.list();
+ // No need to retry because if the connection to the node
+ // is back again, all the queries will be invalidated.
+ retry: false,
- return res.data.content.map((content, index) => {
- if (content.manifest.filename) {
- return content;
- }
+ // The client node should be local, so display the cache value while
+ // making a background request looks good.
+ staleTime: 0,
- const value = metadata.find(([cid]) => content.cid === cid);
+ // Don't expect something new when coming back to the UI
+ refetchOnWindowFocus: false,
+ });
- if (!value) {
- return {
- cid: content.cid,
- manifest: {
- ...content.manifest,
- mimetype: "N/A",
- uploadedAt: new Date(0, 0, 0, 0, 0, 0).toJSON(),
- filename: "N/A" + index,
- },
- };
- }
-
- const {
- mimetype = "",
- name = "",
- uploadedAt = new Date(0, 0, 0, 0, 0, 0).toJSON(),
- } = value[1];
-
- return {
- cid: content.cid,
- manifest: {
- ...content.manifest,
- mimetype,
- filename: name,
- uploadedAt: uploadedAt,
- },
- };
- });
- });
- },
- queryKey: ["cids"],
- initialData: [],
-
- // No need to retry because if the connection to the node
- // is back again, all the queries will be invalidated.
- retry: false,
-
- // The client node should be local, so display the cache value while
- // making a background request looks good.
- staleTime: 0,
-
- // Don't expect something new when coming back to the UI
- refetchOnWindowFocus: false,
- });
-
- return data;
+ return data.content;
}
diff --git a/src/proxy.ts b/src/proxy.ts
new file mode 100644
index 0000000..46c5e2f
--- /dev/null
+++ b/src/proxy.ts
@@ -0,0 +1,147 @@
+import {
+ CodexData,
+ CodexDataResponse,
+ CodexMarketplace,
+ SafeValue,
+ UploadResponse,
+} from "@codex-storage/sdk-js";
+import { CodexSdk as Sdk } from "./sdk/codex";
+import { Promises } from "./utils/promises";
+import { WebStorage } from "./utils/web-storage";
+import * as Sentry from "@sentry/browser";
+import { FilesStorage } from "./utils/file-storage";
+
+class CodexDataMock extends CodexData {
+ override upload(
+ file: File,
+ onProgress?: (loaded: number, total: number) => void
+ ): Promise {
+ const res = super.upload(file, onProgress);
+
+ return res.then(({ result, abort }) => {
+ return {
+ abort,
+ result: result.then((safe) => {
+ if (!safe.error) {
+ return WebStorage.set(safe.data, {
+ type: file.type,
+ name: file.name,
+ }).then(() => safe);
+ }
+
+ return safe;
+ }),
+ };
+ });
+ }
+
+ override async cids(): Promise> {
+ const res = await super.cids();
+
+ if (res.error) {
+ return res;
+ }
+
+ const metadata = await FilesStorage.list();
+
+ const content = res.data.content.map((content, index) => {
+ if (content.manifest.filename) {
+ return content;
+ }
+
+ const value = metadata.find(([cid]) => content.cid === cid);
+
+ if (!value) {
+ return {
+ cid: content.cid,
+ manifest: {
+ ...content.manifest,
+ mimetype: "N/A",
+ uploadedAt: new Date(0, 0, 0, 0, 0, 0).toJSON(),
+ filename: "N/A" + index,
+ },
+ };
+ }
+
+ const {
+ mimetype = "",
+ name = "",
+ uploadedAt = new Date(0, 0, 0, 0, 0, 0).toJSON(),
+ } = value[1];
+
+ return {
+ cid: content.cid,
+ manifest: {
+ ...content.manifest,
+ mimetype,
+ filename: name,
+ uploadedAt: uploadedAt,
+ },
+ };
+ });
+
+ return { error: false, data: { content } };
+ }
+}
+
+class CodexMarketplaceMock extends CodexMarketplace {
+ // override createStorageRequest(
+ // input: CodexCreateStorageRequestInput
+ // ): Promise> {
+ // return Promise.resolve({
+ // error: true,
+ // data: {
+ // message: "C'est balo",
+ // },
+ // });
+ // }
+ // override createAvailability(): Promise<
+ // SafeValue
+ // > {
+ // return Promise.resolve({
+ // error: true,
+ // data: {
+ // message: "C'est balo",
+ // },
+ // });
+ // }
+ // override reservations(): Promise> {
+ // return Promise.resolve({
+ // error: false,
+ // data: [
+ // {
+ // id: "0x123456789",
+ // availabilityId: "0x12345678910",
+ // requestId: "0x1234567891011",
+ // /**
+ // * Size in bytes
+ // */
+ // size: 500_000_000 + "",
+ // /**
+ // * Slot Index as hexadecimal string
+ // */
+ // slotIndex: "2",
+ // },
+ // {
+ // id: "0x987654321",
+ // availabilityId: "0x9876543210",
+ // requestId: "0x98765432100",
+ // /**
+ // * Size in bytes
+ // */
+ // size: 500_000_000 + "",
+ // /**
+ // * Slot Index as hexadecimal string
+ // */
+ // slotIndex: "1",
+ // },
+ // ],
+ // });
+ // }
+}
+
+export const CodexSdk = {
+ ...Sdk,
+ marketplace: new CodexMarketplaceMock(import.meta.env.VITE_CODEX_API_URL),
+ data: new CodexDataMock(import.meta.env.VITE_CODEX_API_URL),
+};
diff --git a/src/components/Availailibities/Availabilities.css b/src/routes/dashboard/availabilities.css
similarity index 100%
rename from src/components/Availailibities/Availabilities.css
rename to src/routes/dashboard/availabilities.css
diff --git a/src/routes/dashboard/availabilities.tsx b/src/routes/dashboard/availabilities.tsx
index 368b6d8..b4850f4 100644
--- a/src/routes/dashboard/availabilities.tsx
+++ b/src/routes/dashboard/availabilities.tsx
@@ -1,7 +1,106 @@
import { createFileRoute } from "@tanstack/react-router";
-import { Availabilities } from "../../components/Availailibities/Availabilities";
import { ErrorBoundary } from "@sentry/react";
import { ErrorPlaceholder } from "../../components/ErrorPlaceholder/ErrorPlaceholder";
+import {
+ SpaceAllocation,
+ Spinner,
+} from "@codex-storage/marketplace-ui-components";
+import { useQuery } from "@tanstack/react-query";
+import { Promises } from "../../utils/promises";
+import { CodexSdk } from "../../sdk/codex";
+import { Strings } from "../../utils/strings";
+import "./availabilities.css";
+import { AvailabilitiesTable } from "../../components/Availability/AvailabilitiesTable";
+import { AvailabilityCreate } from "../../components/Availability/AvailabilityCreate";
+
+const defaultSpace = {
+ quotaMaxBytes: 0,
+ quotaReservedBytes: 0,
+ quotaUsedBytes: 0,
+ totalBlocks: 0,
+};
+
+export function Availabilities() {
+ {
+ // Error will be catched in ErrorBounday
+ const { data: availabilities = [], isPending } = useQuery({
+ queryFn: () =>
+ CodexSdk.marketplace
+ .availabilities()
+ .then((s) => Promises.rejectOnError(s))
+ .then((res) => res.sort((a, b) => b.totalSize - a.totalSize)),
+ queryKey: ["availabilities"],
+ initialData: [],
+
+ // No need to retry because if the connection to the node
+ // is back again, all the queries will be invalidated.
+ retry: false,
+
+ // The client node should be local, so display the cache value while
+ // making a background request looks good.
+ staleTime: 0,
+
+ // Refreshing when focus returns can be useful if a user comes back
+ // to the UI after performing an operation in the terminal.
+ refetchOnWindowFocus: true,
+ });
+
+ // Error will be catched in ErrorBounday
+ const { data: space = defaultSpace } = useQuery({
+ queryFn: () =>
+ CodexSdk.data.space().then((s) => Promises.rejectOnError(s)),
+ queryKey: ["space"],
+ initialData: defaultSpace,
+
+ // No need to retry because if the connection to the node
+ // is back again, all the queries will be invalidated.
+ retry: false,
+
+ // The client node should be local, so display the cache value while
+ // making a background request looks good.
+ staleTime: 0,
+
+ // Refreshing when focus returns can be useful if a user comes back
+ // to the UI after performing an operation in the terminal.
+ refetchOnWindowFocus: true,
+ });
+
+ const allocation = availabilities
+ .map((a) => ({
+ title: Strings.shortId(a.id),
+ size: a.totalSize,
+ }))
+ .slice(0, 6);
+
+ return (
+
+
+ {isPending ? (
+
+
+
+ ) : (
+
+ )}
+
+
+
+
+ );
+ }
+}
export const Route = createFileRoute("/dashboard/availabilities")({
component: () => (
diff --git a/src/routes/dashboard/purchases.tsx b/src/routes/dashboard/purchases.tsx
index 54c6173..371afb3 100644
--- a/src/routes/dashboard/purchases.tsx
+++ b/src/routes/dashboard/purchases.tsx
@@ -2,7 +2,7 @@ import { useQuery } from "@tanstack/react-query";
import { createFileRoute } from "@tanstack/react-router";
import { CodexSdk } from "../../sdk/codex";
import { Cell, Spinner, Table } from "@codex-storage/marketplace-ui-components";
-import { StorageRequestStepper } from "../../components/StorageRequestSetup/StorageRequestStepper";
+import { StorageRequestCreate } from "../../components/StorageRequestSetup/StorageRequestCreate";
import "./purchases.css";
import { FileCell } from "../../components/FileCellRender/FileCell";
import { CustomStateCellRender } from "../../components/CustomStateCellRender/CustomStateCellRender";
@@ -66,12 +66,10 @@ const Purchases = () => {
];
}) || [];
- // TODO make name uniforms
-
return (
-
+
@@ -79,8 +77,6 @@ const Purchases = () => {
);
};
-// TODO make uniforms for availabilities
-
export const Route = createFileRoute("/dashboard/purchases")({
component: () => (
diff --git a/src/sdk/codex.ts b/src/sdk/codex.ts
index e403635..491afeb 100644
--- a/src/sdk/codex.ts
+++ b/src/sdk/codex.ts
@@ -23,20 +23,11 @@ export const CodexSdk = {
return WebStorage.set("codex-node-url", url);
},
- // TODO Change this
- get debug() {
- return client.debug;
- },
+ debug: client.debug,
- get data() {
- return client.data;
- },
+ data: client.data,
- get node() {
- return client.node;
- },
+ node: client.node,
- get marketplace() {
- return client.marketplace;
- },
+ marketplace: client.marketplace,
};
diff --git a/vite.config.ts b/vite.config.ts
index 2db29db..395f15e 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -17,4 +17,10 @@ export default defineConfig({
},
},
},
+ resolve: {
+ alias: {
+ "../sdk/codex": "../proxy",
+ "../../sdk/codex": "../../proxy",
+ },
+ },
});