This commit is contained in:
grngxd 2025-07-31 10:25:40 +01:00
parent 9240263490
commit 5e5fc372cc
3 changed files with 94 additions and 73 deletions

View file

@ -8,7 +8,13 @@ export const client = ky.create({
export const api = {
file: async (uid: string) => await client.get<Blob>(uid),
list: async () => await client.get('list').json<StereoFile[]>(),
list: async (page?: number, size?: number) => {
const searchParams = new URLSearchParams();
if (page !== undefined) searchParams.append('page', String(page));
if (size !== undefined) searchParams.append('size', String(size));
return await client.get('list', { searchParams }).json<StereoFile[]>();
},
upload: async (file: File) => {
const formData = new FormData();
formData.append('file', file);

View file

@ -1,7 +1,7 @@
import type { RequestEvent, RequestHandler } from '@builder.io/qwik-city';
const proxy = async ({ send, url, pathname, request }: RequestEvent) => {
const targetUrl = new URL(`http://localhost:8081${pathname}`, url);
const targetUrl = new URL(`http://localhost:8081${pathname}${url.search}`, url);
const headers = new Headers(request.headers);
const fetchOptions: RequestInit = {

View file

@ -1,4 +1,4 @@
import { component$, Signal, useSignal, useTask$, useVisibleTask$ } from "@builder.io/qwik";
import { $, component$, Signal, useSignal, useTask$, useVisibleTask$ } from "@builder.io/qwik";
import { routeLoader$, type DocumentHead } from "@builder.io/qwik-city";
import Actionbar from "~/components/dashboard/Actionbar";
import Settings from "~/components/dashboard/Settings";
@ -35,7 +35,7 @@ export default component$(() => {
<Actionbar />
</div>
</>
);
);
});
const formatSize = (bytes: number) => {
@ -50,81 +50,96 @@ const Files = component$<{
files: Signal<StereoFile[]>;
loaded: Signal<boolean>;
}>(({ files }) => {
const File = component$(({ file }: { file: StereoFile }) => {
const Preview = component$(() => {
type FileType = "image" | "video" | "audio" | "other";
const fileType: Signal<FileType> = useSignal<FileType>("other");
const type = file.Mime.split("/")[1];
useTask$(() => {
if (["jpeg", "jpg", "png", "gif", "webp"].includes(type)) fileType.value = "image";
else if (["mp4", "webm", "ogg", "avi", "mov"].includes(type)) fileType.value = "video";
else if (["mp3", "wav", "flac", "aac"].includes(type)) fileType.value = "audio";
else fileType.value = "other";
});
return (
<div class="w-full h-max object-cover flex-grow relative overflow-clip">
{fileType.value === "image" && (
// eslint-disable-next-line qwik/jsx-img
<img
width={400}
src={`/api/${file.ID}`}
alt={file.Name}
class="w-full min-h-30 object-cover flex-grow hover:scale-[102.5%] transition-all duration-500"
/>
)}
{fileType.value === "video" && (
<video
width={400}
src={`/api/${file.ID}`}
controls
class="w-full min-h-30 object-cover flex-grow hover:scale-[102.5%] transition-all duration-500"
/>
)}
{fileType.value === "audio" && (
<audio
src={`/api/${file.ID}`}
controls
class="w-full min-h-30 object-cover flex-grow hover:scale-[102.5%] transition-all duration-500"
/>
)}
{fileType.value === "other" && (
<div class="w-full min-h-30 flex items-center justify-center bg-gray-200 text-gray-500">
<p>Unsupported file type</p>
const File = component$(({ file }: { file: StereoFile }) => {
const Preview = component$(() => {
type FileType = "image" | "video" | "audio" | "other";
const fileType: Signal<FileType> = useSignal<FileType>("other");
const type = file.Mime.split("/")[1];
useTask$(() => {
if (["jpeg", "jpg", "png", "gif", "webp"].includes(type)) fileType.value = "image";
else if (["mp4", "webm", "ogg", "avi", "mov"].includes(type)) fileType.value = "video";
else if (["mp3", "wav", "flac", "aac"].includes(type)) fileType.value = "audio";
else fileType.value = "other";
});
return (
<div class="w-full h-max object-cover flex-grow relative overflow-clip">
{fileType.value === "image" && (
// eslint-disable-next-line qwik/jsx-img
<img
width={400}
src={`/api/${file.ID}`}
alt={file.Name}
class="w-full min-h-30 object-cover flex-grow hover:scale-[102.5%] transition-all duration-500"
/>
)}
{fileType.value === "video" && (
<video
width={400}
src={`/api/${file.ID}`}
controls
class="w-full min-h-30 object-cover flex-grow hover:scale-[102.5%] transition-all duration-500"
/>
)}
{fileType.value === "audio" && (
<audio
src={`/api/${file.ID}`}
controls
class="w-full min-h-30 object-cover flex-grow hover:scale-[102.5%] transition-all duration-500"
/>
)}
{fileType.value === "other" && (
<div class="w-full min-h-30 flex items-center justify-center bg-gray-200 text-gray-500">
<p>Unsupported file type</p>
</div>
)}
</div>
);
});
return (
<div
style={{
background:
"linear-gradient(180deg, rgba(255, 38, 78, 0.00) 57.21%, rgba(255, 38, 78, 0.17) 100%); ",
boxShadow: "0px 4px 21.2px 2px rgba(255, 38, 78, 0.05)",
}}
class="transition-all rounded-3xl flex flex-col overflow-clip items-center justify-center mb-4 break-inside-avoid"
>
<Preview />
<div class="flex flex-col items-center justify-center text-center w-full h-full p-5">
<p class="text-xl truncate w-full">{file.Name}</p>
<p class="text-stereo/50 text-lg">
{formatSize(file.Size)}
<span class="text-stereo/40"> </span>
Uploaded on {new Date(file.CreatedAt).toLocaleDateString()}
</p>
</div>
)}
</div>
);
);
});
const loadingMore = useSignal(false);
const onScroll = $(async (e: Event) => {
const element = e.target as HTMLElement;
if (!element) return;
const isAtBottom = element.scrollHeight - element.scrollTop - element.clientHeight < threshold;
if (isAtBottom) {
console.log("Loading more files...");
}
});
return (
<div
style={{
background:
"linear-gradient(180deg, rgba(255, 38, 78, 0.00) 57.21%, rgba(255, 38, 78, 0.17) 100%); ",
boxShadow: "0px 4px 21.2px 2px rgba(255, 38, 78, 0.05)",
}}
class="transition-all rounded-3xl flex flex-col overflow-clip items-center justify-center mb-4 break-inside-avoid"
>
<Preview />
<div class="flex flex-col items-center justify-center text-center w-full h-full p-5">
<p class="text-xl truncate w-full">{file.Name}</p>
<p class="text-stereo/50 text-lg">
{formatSize(file.Size)}
<span class="text-stereo/40"> </span>
Uploaded on {new Date(file.CreatedAt).toLocaleDateString()}
</p>
<div class="px-2 mb-6 flex-grow overflow-y-auto mask-clip-content rounded-3xl" onScroll$={onScroll}>
<div class="w-full columns-1 md:columns-2 lg:columns-4 gap-2 space-y-2">
{files.value.map((file) => (
<File key={file.ID} file={file} />
))}
</div>
</div>
</div>
);
});
return (
<div class="px-2 mb-6 flex-grow overflow-y-auto mask-clip-content rounded-3xl">
<div class="w-full columns-1 md:columns-2 lg:columns-4 gap-2 space-y-2">
{files.value.map((file) => (
<File key={file.ID} file={file} />
))}
</div>
</div>
);
});
export const head: DocumentHead = {