diff --git a/package-lock.json b/package-lock.json index 3789c69..23cea53 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@codex-storage/marketplace-ui-components", - "version": "0.0.11", + "version": "0.0.12", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@codex-storage/marketplace-ui-components", - "version": "0.0.11", + "version": "0.0.12", "license": "MIT", "dependencies": { "lucide-react": "^0.441.0" @@ -21,7 +21,6 @@ "@storybook/react": "^8.2.9", "@storybook/react-vite": "^8.2.9", "@storybook/test": "^8.2.9", - "@tanstack/react-query": "^5.51.24", "@typescript-eslint/eslint-plugin": "^8.6.0", "@typescript-eslint/parser": "^8.0.0", "@vitejs/plugin-react": "^4.3.1", @@ -42,7 +41,6 @@ }, "peerDependencies": { "@codex-storage/sdk-js": "0.0.6", - "@tanstack/react-query": "^5.51.24", "react": "^18.3.1", "react-dom": "^18.3.1" } @@ -2540,32 +2538,6 @@ "storybook": "^8.3.2" } }, - "node_modules/@tanstack/query-core": { - "version": "5.56.2", - "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.56.2.tgz", - "integrity": "sha512-gor0RI3/R5rVV3gXfddh1MM+hgl0Z4G7tj6Xxpq6p2I03NGPaJ8dITY9Gz05zYYb/EJq9vPas/T4wn9EaDPd4Q==", - "dev": true, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/tannerlinsley" - } - }, - "node_modules/@tanstack/react-query": { - "version": "5.56.2", - "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.56.2.tgz", - "integrity": "sha512-SR0GzHVo6yzhN72pnRhkEFRAHMsUo5ZPzAxfTMvUxFIDVS6W9LYUp6nXW3fcHVdg0ZJl8opSH85jqahvm6DSVg==", - "dev": true, - "dependencies": { - "@tanstack/query-core": "5.56.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/tannerlinsley" - }, - "peerDependencies": { - "react": "^18 || ^19" - } - }, "node_modules/@testing-library/dom": { "version": "10.4.0", "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.0.tgz", diff --git a/package.json b/package.json index 3d2ecec..a5b0e9a 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "type": "git", "url": "https://github.com/codex-storage/codex-marketplace-ui-components" }, - "version": "0.0.11", + "version": "0.0.12", "type": "module", "scripts": { "prepack": "npm run build", @@ -36,7 +36,6 @@ }, "peerDependencies": { "@codex-storage/sdk-js": "0.0.6", - "@tanstack/react-query": "^5.51.24", "react": "^18.3.1", "react-dom": "^18.3.1" }, @@ -50,7 +49,6 @@ "@storybook/react": "^8.2.9", "@storybook/react-vite": "^8.2.9", "@storybook/test": "^8.2.9", - "@tanstack/react-query": "^5.51.24", "@typescript-eslint/eslint-plugin": "^8.6.0", "@typescript-eslint/parser": "^8.0.0", "@vitejs/plugin-react": "^4.3.1", diff --git a/src/components/Upload/UploadFile.tsx b/src/components/Upload/UploadFile.tsx index d0418dd..2631a4e 100644 --- a/src/components/Upload/UploadFile.tsx +++ b/src/components/Upload/UploadFile.tsx @@ -12,7 +12,6 @@ import { Toast } from "../Toast/Toast"; import { UploadStatus } from "./types"; import { CircleCheck, TriangleAlert, CircleX, CircleStop } from "lucide-react"; import { Spinner } from "../Spinner/Spinner"; -import { useMutation, useQueryClient } from "@tanstack/react-query"; import { CodexData } from "@codex-storage/sdk-js"; import { WebFileIcon } from "../WebFileIcon/WebFileIcon"; import { ButtonIcon } from "../ButtonIcon/ButtonIcon"; @@ -55,6 +54,9 @@ type Action = | { type: "cancel"; } + | { + type: "delete"; + } | { type: "error"; error: string; @@ -81,20 +83,24 @@ function reducer(state: State, action: Action) { case "completed": { return { ...state, + + // Just to ensure the file upload is in done status, + // in case of the onprogress callback function was not called status: "done" as UploadStatus, + cid: action.cid, }; } case "cancel": { - if (state.status === "progress") { - return { - ...state, - status: "error" as UploadStatus, - error: "The upload has been cancelled.", - }; - } + return { + ...state, + status: "error" as UploadStatus, + error: "The upload has been cancelled.", + }; + } + case "delete": { return { progress: { loaded: 0, total: 0 }, cid: "", @@ -125,8 +131,7 @@ export function UploadFile({ // useWorker, }: UploadFileProps) { const abort = useRef<(() => void) | null>(null); - const queryClient = useQueryClient(); - const worker = useRef(null); + // const worker = useRef(null); const [toast, setToast] = useState({ time: 0, message: "" }); const [state, dispatch] = useReducer>(reducer, { progress: { loaded: 0, total: 0 }, @@ -135,47 +140,27 @@ export function UploadFile({ status: "progress" as UploadStatus, error: "", }); - const { mutateAsync } = useMutation({ - mutationKey: ["upload"], - mutationFn: (file: File) => { - const res = codexData.upload(file, onProgress); - abort.current = res.abort; + const upload = useCallback(async () => { + const { abort: a, result } = codexData.upload(file, onProgress); + + abort.current = a; + + const res = await result; + + if (res.error) { + dispatch({ type: "error", error: res.data.message }); + + return; + } + + dispatch({ type: "completed", cid: state.cid }); + + onSuccess?.(state.cid, file); + }, [state.cid, codexData, onSuccess, file]); - return res.result.then((safe) => - safe.error - ? Promise.reject(safe.data.message) - : Promise.resolve(safe.data) - ); - }, - onError: (error) => { - // worker.current?.terminate(); - dispatch({ type: "error", error: error.message }); - }, - onSuccess: (cid: string) => { - onInternalSuccess(cid); - }, - }); const init = useRef(false); - const onInternalSuccess = useCallback( - (cid: string) => { - worker.current?.terminate(); - - queryClient.invalidateQueries({ - queryKey: ["cids"], - }); - - if (onSuccess) { - onSuccess(cid, file); - dispatch({ type: "reset" }); - } else { - dispatch({ type: "completed", cid }); - } - }, - [onSuccess, dispatch, queryClient, file] - ); - const onProgress = (loaded: number, total: number) => { dispatch({ type: "progress", @@ -204,7 +189,7 @@ export function UploadFile({ reader.readAsDataURL(file); } - mutateAsync(file); + upload(); // if (useWorker) { // worker.current = new Worker(new URL("./worker", import.meta.url), { @@ -239,24 +224,28 @@ export function UploadFile({ // } else { // mutateAsync(file); // } - }, [file, mutateAsync, onInternalSuccess, codexData]); + }, [file, upload, codexData]); const onCancel = () => { - if (worker.current) { - worker.current.postMessage({ type: "abort" }); - } else { - abort.current?.(); - } + // if (worker.current) { + // worker.current.postMessage({ type: "abort" }); + // } else { + // abort.current?.(); + // } + abort.current?.(); - dispatch({ type: "cancel" }); + const type = state.status === "progress" ? "cancel" : "delete"; + dispatch({ type }); }; const onInternalClose = () => { - if (worker.current) { - worker.current.postMessage({ type: "abort" }); - } else { - abort.current?.(); - } + // if (worker.current) { + // worker.current.postMessage({ type: "abort" }); + // } else { + // abort.current?.(); + // } + + abort.current?.(); onClose(id); }; @@ -274,7 +263,7 @@ export function UploadFile({ const parts = file.name.split("."); const extension = parts.pop(); const filename = parts.join("."); - const { cid, error, preview, progress, status } = state; + const { cid, error = "", preview, progress, status } = state; const onAction = state.status === "progress" ? onCancel : onInternalClose; const percent = progress.total > 0 ? (progress.loaded / progress.total) * 100 : 0; @@ -350,7 +339,7 @@ export function UploadFile({ )} - {error && {error}} + {error ? error : <> } diff --git a/stories/Upload.stories.tsx b/stories/Upload.stories.tsx index c300698..343d317 100644 --- a/stories/Upload.stories.tsx +++ b/stories/Upload.stories.tsx @@ -1,9 +1,8 @@ import type { Meta } from "@storybook/react"; -import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { Upload } from "../src/components/Upload/Upload"; import { fn } from "@storybook/test"; import "./Upload.stories.css"; -import { CodexDataSdk, CodexDataSlowSdk } from "./sdk"; +import { CodexDataSdk, CodexDataSlowSdk, CodexDataErrorSdk } from "./sdk"; const meta = { title: "Advanced/Upload", @@ -25,8 +24,6 @@ const meta = { export default meta; -const queryClient = new QueryClient(); - type Props = { onClick?: () => void; @@ -40,11 +37,7 @@ type Props = { }; const Template = (p: Props) => { - return ( - - {} - - ); + return ; }; export const Multiple = Template.bind({}); @@ -52,9 +45,7 @@ export const Multiple = Template.bind({}); const SlowTemplate = (p: Props) => { return (
- - {} - +
); }; @@ -64,18 +55,33 @@ export const Slow = SlowTemplate.bind({}); const SingleTemplate = (p: Props) => { return (
- - { - - } - + { + + }
); }; export const Single = SingleTemplate.bind({}); + +const ErrorTemplate = (p: Props) => { + return ( +
+ { + + } +
+ ); +}; + +export const Error = ErrorTemplate.bind({}); diff --git a/stories/sdk.ts b/stories/sdk.ts index 27ad994..96c27a9 100644 --- a/stories/sdk.ts +++ b/stories/sdk.ts @@ -1,36 +1,34 @@ -import { CodexData, UploadResponse } from "@codex-storage/sdk-js"; +import { CodexData, CodexError, UploadResponse } from "@codex-storage/sdk-js"; class CodexDataMock extends CodexData { override upload( _: File, onProgress?: (loaded: number, total: number) => void - ): Promise { - return new Promise((resolve) => { - let timeout: number; + ): UploadResponse { + let timeout: number; - resolve({ - abort: () => { - window.clearInterval(timeout); - }, - result: new Promise((resolve) => { - let count = 0; - timeout = window.setInterval(() => { - count++; + return { + abort: () => { + window.clearInterval(timeout); + }, + result: new Promise((resolve) => { + let count = 0; + timeout = window.setInterval(() => { + count++; - onProgress?.(500 * count, 1500); + onProgress?.(500 * count, 1500); - if (count === 3) { - window.clearInterval(timeout); + if (count === 3) { + window.clearInterval(timeout); - resolve({ - error: false, - data: Date.now().toString(), - }); - } - }, 1500); - }), - }); - }); + resolve({ + error: false, + data: Date.now().toString(), + }); + } + }, 1500); + }), + }; } } @@ -40,34 +38,66 @@ class CodexDataSlowMock extends CodexData { override upload( _: File, onProgress?: (loaded: number, total: number) => void - ): Promise { - return new Promise((resolve) => { - let timeout: number; + ): UploadResponse { + let timeout: number; - resolve({ - abort: () => { - window.clearInterval(timeout); - }, - result: new Promise((resolve) => { - let count = 0; - timeout = window.setInterval(() => { - count++; + return { + abort: () => { + window.clearInterval(timeout); + }, + result: new Promise((resolve) => { + let count = 0; + timeout = window.setInterval(() => { + count++; - onProgress?.(500 * count, 1500); + onProgress?.(500 * count, 1500); - if (count === 3) { - window.clearInterval(timeout); + if (count === 3) { + window.clearInterval(timeout); - resolve({ - error: false, - data: Date.now().toString(), - }); - } - }, 1500); - }), - }); - }); + resolve({ + error: false, + data: Date.now().toString(), + }); + } + }, 1500); + }), + } } } export const CodexDataSlowSdk = new CodexDataSlowMock(""); + +class CodexDataErrorMock extends CodexData { + override upload( + _: File, + onProgress?: (loaded: number, total: number) => void + ): UploadResponse { + let timeout: number; + + return { + abort: () => { + window.clearInterval(timeout); + }, + result: new Promise((resolve) => { + let count = 0; + timeout = window.setInterval(() => { + count++; + + onProgress?.(500 * count, 1500); + + if (count === 3) { + window.clearInterval(timeout); + + resolve({ + error: true, + data: new CodexError("Some error here"), + }); + } + }, 1500); + }), + } + } +} + +export const CodexDataErrorSdk = new CodexDataErrorMock(""); \ No newline at end of file diff --git a/vite.config.ts b/vite.config.ts index a72445d..f38c32d 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -5,14 +5,13 @@ import react from "@vitejs/plugin-react"; import { libInjectCss } from "vite-plugin-lib-inject-css"; import { extname, relative } from "path"; import { fileURLToPath } from "node:url"; -import pkg from "glob"; -const { glob } = pkg; +import { globSync } from "glob"; // https://vitejs.dev/config/ export default defineConfig({ worker: { rollupOptions: { - external: ["@codex-storage/sdk-js", "@tanstack/react-query"], + external: ["@codex-storage/sdk-js"], output: { globals: { "@codex-storage/sdk-js": "codex-sdk-js", @@ -39,13 +38,11 @@ export default defineConfig({ "react", "react/jsx-runtime", "@codex-storage/sdk-js", - "@tanstack/react-query", ], input: Object.fromEntries( - glob - .sync("src/**/*.{ts,tsx}", { - ignore: ["src/**/*.d.ts"], - }) + globSync("src/**/*.{ts,tsx}", { + ignore: ["src/**/*.d.ts"], + }) .map((file) => [ // The name of the entry point // lib/nested/foo.ts becomes nested/foo