diff --git a/src/components/Files/FileActions.tsx b/src/components/Files/FileActions.tsx new file mode 100644 index 0000000..c520797 --- /dev/null +++ b/src/components/Files/FileActions.tsx @@ -0,0 +1,49 @@ +import { ButtonIcon, Cell } from "@codex-storage/marketplace-ui-components"; +import { Download, ReceiptText } from "lucide-react"; +import { ICON_SIZE } from "../../utils/constants"; +import { FolderButton } from "./FolderButton"; +import { CodexDataContent } from "@codex-storage/sdk-js"; +import { CodexSdk } from "../../sdk/codex"; + +type Props = { + content: CodexDataContent; + folders: [string, string[]][]; + onFolderToggle: (cid: string, folder: string) => void; + onDetails: (cid: string) => void; +}; + +export function FileActions({ + content, + folders, + onFolderToggle, + onDetails, +}: Props) { + const url = CodexSdk.url() + "/api/codex/v1/data/"; + + return ( + +
+ window.open(url + content.cid, "_blank")} + Icon={(props) => ( + + )}> + + [ + folder, + files.includes(content.cid), + ])} + onFolderToggle={(folder) => onFolderToggle(content.cid, folder)} + /> + + onDetails(content.cid)} + Icon={() => }> +
+
+ ); +} diff --git a/src/components/Files/FileCell.tsx b/src/components/Files/FileCell.tsx new file mode 100644 index 0000000..de06a5f --- /dev/null +++ b/src/components/Files/FileCell.tsx @@ -0,0 +1,35 @@ +import { + ButtonIcon, + Cell, + WebFileIcon, +} from "@codex-storage/marketplace-ui-components"; +import { CodexDataContent } from "@codex-storage/sdk-js"; +import { Copy } from "lucide-react"; + +type Props = { + content: CodexDataContent; +}; + +export function FileCell({ content }: Props) { + const onCopy = (cid: string) => navigator.clipboard.writeText(cid); + + return ( + +
+ + +
+ {content.manifest.filename} +
+ {content.cid} + onCopy(content.cid)} + animation="buzz" + Icon={(props) => }> +
+
+
+
+ ); +} diff --git a/src/components/Files/FileFilters.tsx b/src/components/Files/FileFilters.tsx new file mode 100644 index 0000000..28e30e3 --- /dev/null +++ b/src/components/Files/FileFilters.tsx @@ -0,0 +1,44 @@ +import { CodexDataContent } from "@codex-storage/sdk-js"; +import { Files } from "../../utils/files"; +import { classnames } from "../../utils/classnames"; +import { Check } from "lucide-react"; + +type Props = { + files: CodexDataContent[]; + onFilterToggle: (filter: string) => void; +}; + +const archiveMimetypes = [ + "application/zip", + "application/x-rar-compressed", + "application/x-tar", + "application/gzip", + "application/x-7z-compressed", + "application/gzip", // for .tar.gz + "application/x-bzip2", + "application/x-xz", +]; + +export function FilterFilters({ files, onFilterToggle }: Props) { + const filters = Array.from( + new Set( + files.map((file) => + archiveMimetypes.includes(file.manifest.mimetype) + ? "archive" + : Files.type(file.manifest.mimetype) + ) + ) + ); + + return filters.map((type) => ( + onFilterToggle(type)}> + {type} + + )); +} diff --git a/src/components/Files/Files.tsx b/src/components/Files/Files.tsx index 706a832..104cd98 100644 --- a/src/components/Files/Files.tsx +++ b/src/components/Files/Files.tsx @@ -1,21 +1,9 @@ -import { - Check, - Copy, - Download, - FilesIcon, - Folder, - Plus, - ReceiptText, - X, -} from "lucide-react"; +import { FilesIcon, Folder, Plus, X } from "lucide-react"; import { ChangeEvent, useEffect, useState } from "react"; import { PrettyBytes } from "../../utils/bytes"; import { Dates } from "../../utils/dates"; import "./Files.css"; -import { ICON_SIZE } from "../../utils/constants"; import { - ButtonIcon, - WebFileIcon, Tabs, Input, Button, @@ -23,15 +11,17 @@ import { Table, Row, Cell, + TabSortState, } from "@codex-storage/marketplace-ui-components"; import { FileDetails } from "./FileDetails.tsx"; import { useData } from "../../hooks/useData.tsx"; import { WebStorage } from "../../utils/web-storage.ts"; -import { CodexSdk } from "../../sdk/codex.ts"; -import { FolderButton } from "./FolderButton.tsx"; import { classnames } from "../../utils/classnames.ts"; import { CodexDataContent } from "@codex-storage/sdk-js"; import { Files as F } from "../../utils/files.ts"; +import { FilterFilters } from "./FileFilters.tsx"; +import { FileCell } from "./FileCell.tsx"; +import { FileActions } from "./FileActions.tsx"; type SortFn = (a: CodexDataContent, b: CodexDataContent) => number; @@ -49,9 +39,7 @@ export function Files() { WebStorage.folders.list().then((items) => setFolders(items)); }, []); - const onClose = () => { - setDetails(null); - }; + const onClose = () => setDetails(null); const onTabChange = async (i: number) => setIndex(i); @@ -154,7 +142,25 @@ export function Files() { ), })); - const onSortBySize = (state: "" | "asc" | "desc") => { + const onSortByFilename = (state: TabSortState) => { + if (!state) { + setSortFn(null); + return; + } + + setSortFn( + () => (a: CodexDataContent, b: CodexDataContent) => + state === "desc" + ? b.manifest.filename + .toLocaleLowerCase() + .localeCompare(a.manifest.filename.toLocaleLowerCase()) + : a.manifest.filename + .toLocaleLowerCase() + .localeCompare(b.manifest.filename.toLocaleLowerCase()) + ); + }; + + const onSortBySize = (state: TabSortState) => { if (!state) { setSortFn(null); return; @@ -168,7 +174,7 @@ export function Files() { ); }; - const onSortByDate = (state: "" | "asc" | "desc") => { + const onSortByDate = (state: TabSortState) => { if (!state) { setSortFn(null); return; @@ -189,8 +195,6 @@ export function Files() { ? setFilters(filters.filter((f) => f !== filter)) : setFilters([...filters, filter]); - const onCopy = (cid: string) => navigator.clipboard.writeText(cid); - tabs.unshift({ label: "All files", Icon: () => , @@ -201,18 +205,13 @@ export function Files() { ? files : files.filter((file) => folders[index - 1][1].includes(file.cid)); - const url = CodexSdk.url() + "/api/codex/v1/data/"; - const headers = [ - ["file"], + ["file", onSortByFilename], ["size", onSortBySize], ["date", onSortByDate], ["actions"], - ]; + ] satisfies [string, ((state: TabSortState) => void)?][]; - const types = Array.from( - new Set(files.map((file) => F.type(file.manifest.mimetype))) - ); const filtered = items.filter( (item) => filters.length === 0 || filters.includes(F.type(item.manifest.mimetype)) @@ -223,52 +222,14 @@ export function Files() { sorted.map((c) => ( -
- - -
- {c.manifest.filename} -
- {c.cid} -
-
-
- , + , {PrettyBytes(c.manifest.datasetSize)}, {Dates.format(c.manifest.uploadedAt).toString()}, - -
- window.open(url + c.cid, "_blank")} - Icon={(props) => ( - - )}> - - [ - folder, - files.includes(c.cid), - ])} - onFolderToggle={(folder) => onFolderToggle(c.cid, folder)} - /> - - onCopy(c.cid)} - animation="buzz" - Icon={(props) => ( - - )}> - - onDetails(c.cid)} - Icon={() => }> -
-
, + , ]}>
)) || []; @@ -303,21 +264,11 @@ export function Files() {
- {types.map((type) => ( - onToggleFilter(type)}> - {type} - - ))} +
- +