88 lines
No EOL
3.4 KiB
TypeScript
88 lines
No EOL
3.4 KiB
TypeScript
import { $, component$, noSerialize, NoSerialize, useSignal, useVisibleTask$ } from "@builder.io/qwik";
|
|
import { useNanostore$ } from "~/hooks/nanostores";
|
|
import { api } from "~/lib/api";
|
|
import { areFilesLoaded, dashboardFiles } from "~/lib/stores";
|
|
import { StereoFile } from "~/lib/types";
|
|
import { SolarUploadLinear, SvgSpinnersBarsRotateFade } from "../misc/Icons";
|
|
import StereoLogo from "../misc/StereoLogo";
|
|
|
|
export default component$(() => {
|
|
const loaded = useNanostore$<boolean>(areFilesLoaded);
|
|
const files = useNanostore$<StereoFile[]>(dashboardFiles);
|
|
const fileInputRef = useSignal<HTMLInputElement>();
|
|
const uploadingFiles = useSignal<NoSerialize<File[]> | undefined>();
|
|
const now = useSignal(new Date());
|
|
|
|
useVisibleTask$(() => {
|
|
const interval = setInterval(() => {
|
|
now.value = new Date();
|
|
}, 500);
|
|
return () => clearInterval(interval);
|
|
});
|
|
|
|
const uploadFiles = $(async () => {
|
|
if (!uploadingFiles.value) {
|
|
console.error("No file(s) selected for upload.");
|
|
return;
|
|
}
|
|
|
|
try {
|
|
const ufiles = uploadingFiles.value as File[];
|
|
|
|
for (const file of ufiles) {
|
|
const name = file.name.replace(/[^a-zA-Z0-9_.-]/g, "_");
|
|
const f = new File([file], name, { type: file.type });
|
|
|
|
await api.upload(f);
|
|
}
|
|
|
|
files.value = await api.list();
|
|
} catch (error) {
|
|
console.error("Error uploading file:", error);
|
|
}
|
|
})
|
|
return (
|
|
<div class="z-[999999999] fixed bottom-4 left-1/2 transform -translate-x-1/2 bg-neutral-700/10 backdrop-blur-3xl lg:w-1/3 md:w-2/3 w-4/5 p-2 pr-4 rounded-lg flex items-center justify-between">
|
|
<input
|
|
type="file"
|
|
ref={fileInputRef}
|
|
style="display: none;"
|
|
onChange$={async (e: Event) => {
|
|
uploadingFiles.value = noSerialize(Object.values((e.target as HTMLInputElement).files || {}));
|
|
await uploadFiles();
|
|
}}
|
|
multiple
|
|
/>
|
|
|
|
<div class="flex items-center gap-2">
|
|
{/* TODO: replace this button with a modal with options like settings log out etc */}
|
|
<button
|
|
class="duration-100 hover:bg-white text-white hover:text-black p-2 rounded-lg"
|
|
onClick$={async () => {
|
|
loaded.value = false;
|
|
files.value = await api.list()
|
|
loaded.value = true;
|
|
}}
|
|
>
|
|
{
|
|
loaded.value ? (
|
|
<StereoLogo class="w-6 h-6" />
|
|
) : (
|
|
<SvgSpinnersBarsRotateFade class="w-6 h-6" />
|
|
)
|
|
}
|
|
</button>
|
|
|
|
<p class="text-white/25 font-light text-xl"> | </p>
|
|
|
|
<button
|
|
class="duration-100 hover:bg-white text-white hover:text-black p-2 rounded-lg"
|
|
onClick$={() => { fileInputRef.value?.click() }}
|
|
>
|
|
<SolarUploadLinear class="w-6 h-6" />
|
|
</button>
|
|
</div>
|
|
<p class="text-white font-medium">{now.value.toLocaleTimeString()}</p>
|
|
</div>
|
|
)
|
|
}) |