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