DownloadButton.tsx
⎇
Raw
1import "./DownloadButton.css";
2import type { MediaFile, Directory, FileListing } from "music-server-shared/types";
3import {
4 deleteFile,
5 download,
6 type DownloadStatus,
7 getDownloadStatus,
8 getDownloadStatusWithChildren,
9 type StatusWithChildren,
10} from "../offline";
11import { createMemo, type Accessor, type ResourceActions, type Signal } from "solid-js";
12import type { AppOptions } from "../types";
13import { formatBytes, joinPath } from "../utils";
14import { DeleteSVG, RefreshSVG, SyncSVG } from "./SVG";
15import { LoadingSpinner } from "./LoadingScreen";
16import type { FlatFileListing } from "../App";
17
18interface DownloadButtonProps {
19 fileName: string;
20 item: MediaFile | Directory;
21 fetchFiles: ResourceActions<FlatFileListing>;
22 isOffline: Accessor<boolean>;
23 currentDir: Accessor<string>;
24 options: AppOptions;
25}
26
27export function DownloadButton(props: DownloadButtonProps) {
28 const fullPath = joinPath(props.currentDir(), props.fileName);
29
30 const containsProgressChildren = createMemo(() => {
31 const status = getDownloadStatusWithChildren(fullPath);
32 if (!status.children) return false;
33
34 function inProgress(signal: Signal<DownloadStatus>) {
35 const status = signal[0]();
36 return status === "UnspecificProgress" || (typeof status === "object" && "bytes" in status);
37 }
38
39 function recurse(statusWithChildren: StatusWithChildren) {
40 for (const [, child] of statusWithChildren.children || []) {
41 if (inProgress(child.status) || recurse(child)) return true;
42 }
43 return false;
44 }
45 return recurse(status);
46 });
47
48 return (
49 <div
50 class="download-item"
51 onClick={(event) => {
52 event.stopPropagation();
53 if (!props.isOffline())
54 download(props.fileName, props.item, props.currentDir(), props.currentDir, props.options, props.fetchFiles);
55 else deleteFile(props.fileName, props.item, props.currentDir(), props.fetchFiles);
56 }}
57 >
58 {(() => {
59 const [getStatus] = getDownloadStatus(fullPath);
60 const progress = getStatus();
61 if (progress === "UnspecificProgress" || containsProgressChildren()) return <LoadingSpinner center={true} />;
62 if (props.isOffline()) {
63 return (
64 <button title="Purge this file/these files from local sync storage">
65 <DeleteSVG />
66 </button>
67 );
68 }
69 if (progress === undefined) {
70 return (
71 <button title="Sync this file/directory to your browser for offline use">
72 <SyncSVG />
73 </button>
74 );
75 }
76 if (progress === "Synced") {
77 return (
78 <button title="Re-sync this file">
79 <RefreshSVG />
80 </button>
81 );
82 }
83 if ("bytes" in progress) return <span>{progress.bytes === 0 ? "Queued..." : formatBytes(progress.bytes)}</span>;
84 console.error("Unexpected", progress);
85 })()}
86 </div>
87 );
88}
89