DownloadButton.tsx
⎇
Raw
1import "./DownloadButton.css";
2import type { Directory, MediaFile } from "music-server-shared/types";
3import { type Accessor, createMemo, type ResourceActions } from "solid-js";
4import type { FlatFileListing } from "../App";
5import { deleteFile, download, getDownloadStatus } from "../offline";
6import type { AppOptions } from "../types";
7import { formatBytes } from "../utils";
8import { LoadingSpinner } from "./LoadingScreen";
9import { DeleteSVG, RefreshSVG, SyncSVG } from "./SVG";
10
11interface DownloadButtonProps {
12 fullPath: string;
13 item: MediaFile | Directory;
14 fetchFiles: ResourceActions<FlatFileListing>;
15 isOffline: Accessor<boolean>;
16 options: AppOptions;
17}
18
19export function DownloadButton(props: DownloadButtonProps) {
20 return (
21 <div
22 class="download-item"
23 onClick={(event) => {
24 event.stopPropagation();
25 //ignore clicks while this item is already downloading or a directory walk is still running:
26 //the button shows a byte counter/spinner then, and a second click would start a duplicate download
27 const progress = getDownloadStatus(props.fullPath);
28 if (typeof progress === "object" && ("amount" in progress || typeof progress.status === "number")) return;
29 if (!props.isOffline())
30 download(props.fullPath, props.item, props.options, props.fetchFiles, "metadata" in props.item);
31 else deleteFile(props.fullPath, props.item, props.fetchFiles);
32 }}
33 >
34 {(() => {
35 //memo needed so progress amount doesn't cause rerenders
36 type ButtonType = "Spinner" | "Delete" | "Sync" | "Refresh" | { status: number };
37 const progressMemo = createMemo<ButtonType>(() => {
38 const progress = getDownloadStatus(props.fullPath);
39 if (typeof progress === "object" && "amount" in progress) return "Spinner";
40 if (props.isOffline()) return "Delete";
41 if (progress === "Synced") return "Refresh";
42 if (progress === undefined || ("status" in progress && typeof progress.status !== "number")) return "Sync";
43 if (typeof progress.status === "number") return { status: progress.status };
44 throw new Error(`Unexpected: ${progress}`);
45 });
46 const progress = progressMemo();
47 if (progress === "Spinner") return <LoadingSpinner center={true} />;
48 if (progress === "Delete") {
49 return (
50 <button title="Purge this file/these files from local sync storage">
51 <DeleteSVG />
52 </button>
53 );
54 }
55 if (progress === "Sync") {
56 return (
57 <button title="Sync missing files this file/directory to your browser for offline use">
58 <SyncSVG />
59 </button>
60 );
61 }
62 if (progress === "Refresh") {
63 return (
64 <button title="Re-sync this file">
65 <RefreshSVG />
66 </button>
67 );
68 }
69 if ("status" in progress) return <span>{formatBytes(progress.status)}</span>;
70 console.error("Unexpected", progress);
71 })()}
72 </div>
73 );
74}
75