file upload
This commit is contained in:
parent
1599371df5
commit
aaaebc20a5
5 changed files with 62 additions and 17 deletions
|
@ -1,6 +1,9 @@
|
|||
import { component$ } from "@builder.io/qwik";
|
||||
/* eslint-disable qwik/jsx-a */
|
||||
import { $, component$, useSignal } from "@builder.io/qwik";
|
||||
import { useNanostore$ } from "~/hooks/nanostores";
|
||||
import { isSettingsOpen } from "~/lib/stores";
|
||||
import { api } from "~/lib/api";
|
||||
import { isSettingsOpen, loadedFiles } from "~/lib/stores";
|
||||
import { StereoFile } from "~/lib/types";
|
||||
import { SolarLibraryLinear, SolarQuestionCircleLinear, SolarRoundedMagniferLinear, SolarSettingsLinear, SolarUploadMinimalisticLinear, StereoCircularProgress, StereoLogoLinear } from "../misc/Icons";
|
||||
|
||||
export default component$(() => {
|
||||
|
@ -8,6 +11,30 @@ export default component$(() => {
|
|||
const total = 15;
|
||||
|
||||
const settingsOpen = useNanostore$<boolean>(isSettingsOpen);
|
||||
const fileInputRef = useSignal<HTMLInputElement>();
|
||||
const files = useNanostore$<StereoFile[]>(loadedFiles, []);
|
||||
|
||||
const handleFileChange = $(async (event: Event) => {
|
||||
const input = event.target as HTMLInputElement;
|
||||
if (input.files && input.files.length > 0) {
|
||||
const fi: File[] = Array.from(input.files);
|
||||
|
||||
const metas: StereoFile[] = await Promise.all(
|
||||
fi.map(async (file) => {
|
||||
try {
|
||||
const id = (await (await api.upload(file)).json()).id;
|
||||
return await api.meta(id);
|
||||
} catch (error) {
|
||||
console.error("actionbar: file upload failed:", error);
|
||||
throw new Error("File upload failed");
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
input.value = "";
|
||||
files.value = [...files.value, ...metas];
|
||||
}
|
||||
});
|
||||
|
||||
return (
|
||||
<div class="absolute bottom-0 left-0 flex items-center justify-between py-7 px-16 w-full">
|
||||
|
@ -31,11 +58,25 @@ export default component$(() => {
|
|||
}} class="flex items-center justify-center px-6 py-4 gap-5 text-white text-3xl absolute left-1/2 transform -translate-x-1/2">
|
||||
<a onClick$={() => settingsOpen.value = true}><StereoLogoLinear /></a>
|
||||
<SolarLibraryLinear />
|
||||
<SolarUploadMinimalisticLinear />
|
||||
<a
|
||||
onClick$={(e) => {
|
||||
e.preventDefault();
|
||||
fileInputRef.value?.click();
|
||||
}}
|
||||
>
|
||||
<SolarUploadMinimalisticLinear />
|
||||
</a>
|
||||
<SolarRoundedMagniferLinear />
|
||||
<SolarSettingsLinear />
|
||||
</div>
|
||||
|
||||
<input
|
||||
ref={fileInputRef}
|
||||
type="file"
|
||||
class="hidden"
|
||||
onChange$={handleFileChange}
|
||||
/>
|
||||
|
||||
<div style={{
|
||||
borderRadius: "999px",
|
||||
border: "0.5px solid #FF264E",
|
||||
|
|
|
@ -14,8 +14,8 @@ function writeable<T>(store: Atom<T> | WritableAtom<T>): store is WritableAtom<T
|
|||
return typeof (store as WritableAtom<T>).set === 'function';
|
||||
}
|
||||
|
||||
export function useNanostoreQrl<T>(qrl: QRL<WritableAtom<T> | Atom<T>>): Signal<T> {
|
||||
const signal = useSignal<T | undefined>(undefined);
|
||||
export function useNanostoreQrl<T>(qrl: QRL<WritableAtom<T> | Atom<T>>, defaultValue?: T): Signal<T> {
|
||||
const signal = useSignal<T | undefined>(defaultValue);
|
||||
const storeSignal = useSignal<NoSerialize<WritableAtom<T> | Atom<T>> | undefined>(undefined);
|
||||
|
||||
useTask$(async ({ track }) => {
|
||||
|
|
|
@ -1,25 +1,26 @@
|
|||
import ky from 'ky';
|
||||
import { StereoFile } from './types';
|
||||
import ky from "ky";
|
||||
import { StereoFile, StereoUser } from "./types";
|
||||
|
||||
export const client = ky.create({
|
||||
prefixUrl: '/api',
|
||||
credentials: 'include'
|
||||
prefixUrl: "/api",
|
||||
credentials: "include"
|
||||
});
|
||||
|
||||
export const api = {
|
||||
file: async (uid: string) => await client.get<Blob>(uid),
|
||||
meta: async (uid: string) => await client.get<StereoFile>(uid + "/meta").json(),
|
||||
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));
|
||||
if (page !== undefined) searchParams.append("page", String(page));
|
||||
if (size !== undefined) searchParams.append("size", String(size));
|
||||
|
||||
return await client.get('list', { searchParams }).json<StereoFile[]>();
|
||||
return await client.get("list", { searchParams }).json<StereoFile[]>();
|
||||
},
|
||||
upload: async (file: File) => {
|
||||
const formData = new FormData();
|
||||
formData.append('file', file);
|
||||
return await client.post('upload', { body: formData });
|
||||
formData.append("file", file);
|
||||
return await client.post<{id: string, message: string}>("upload", { body: formData });
|
||||
},
|
||||
delete: async (uid: string) => await client.delete(uid).json(),
|
||||
me: async () => (await client.get('auth/me').json() as any).user,
|
||||
me: async () => (await client.get<any>("auth/me").json()).user as StereoUser,
|
||||
}
|
|
@ -11,3 +11,4 @@ export const userInfo = atom<StereoUser>({
|
|||
created_at: Date.now().toString(),
|
||||
});
|
||||
export const isSettingsOpen = atom<boolean>(false);
|
||||
export const loadedFiles = atom<StereoFile[]>([]);
|
|
@ -4,9 +4,11 @@ import { routeLoader$, type DocumentHead } from "@builder.io/qwik-city";
|
|||
import Actionbar from "~/components/dashboard/Actionbar";
|
||||
import Settings from "~/components/dashboard/Settings";
|
||||
import Titlebar from "~/components/dashboard/Titlebar";
|
||||
import { useNanostore$ } from "~/hooks/nanostores";
|
||||
// import Dropzone from "~/components/Dropzone";
|
||||
import { api } from "~/lib/api";
|
||||
import { debounce } from "~/lib/misc";
|
||||
import { loadedFiles } from "~/lib/stores";
|
||||
import { StereoFile } from "~/lib/types";
|
||||
|
||||
export const useAuthenticated = routeLoader$(({ cookie, redirect: r }) => {
|
||||
|
@ -111,7 +113,7 @@ const Files = component$(() => {
|
|||
const hasMore = useSignal(true);
|
||||
const sentinel = useSignal<HTMLDivElement>();
|
||||
|
||||
const files = useSignal<StereoFile[]>([]);
|
||||
const files = useNanostore$<StereoFile[]>(loadedFiles, []);
|
||||
const page = useSignal(1);
|
||||
|
||||
// TODO: make it load enough images to fill the viewport instead
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue