mock dashboard + 2 way sync in nanostores (i hate ctx)
This commit is contained in:
parent
d792cec873
commit
31594891e5
5 changed files with 150 additions and 70 deletions
|
@ -1,35 +1,57 @@
|
|||
import { component$, useSignal } from "@builder.io/qwik";
|
||||
import { atom } from "nanostores";
|
||||
import { $, component$, noSerialize, NoSerialize, useSignal, useVisibleTask$ } from "@builder.io/qwik";
|
||||
import { useNanostore$ } from "~/hooks/nanostores";
|
||||
import { api } from "~/lib/api";
|
||||
import { DashboardFiles } from "~/lib/stores";
|
||||
import { StereoFile } from "~/lib/types";
|
||||
import { SolarUploadLinear } from "./Icons";
|
||||
|
||||
const a = atom(0);
|
||||
|
||||
export default component$(() => {
|
||||
const files = useNanostore$<StereoFile[]>(DashboardFiles);
|
||||
const fileInputRef = useSignal<HTMLInputElement>();
|
||||
|
||||
const uploadingFile = useSignal<NoSerialize<File> | undefined>();
|
||||
const now = useSignal(new Date());
|
||||
|
||||
useVisibleTask$(() => {
|
||||
const interval = setInterval(() => {
|
||||
now.value = new Date();
|
||||
}, 500);
|
||||
return () => clearInterval(interval);
|
||||
});
|
||||
|
||||
const uploadFile = $(async () => {
|
||||
if (!uploadingFile.value) {
|
||||
console.error("No file selected for upload.");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await api.upload(uploadingFile.value as File)
|
||||
files.value = await api.list();
|
||||
} catch (error) {
|
||||
console.error("Error uploading file:", error);
|
||||
}
|
||||
})
|
||||
return (
|
||||
<div class="fixed bottom-4 left-1/2 transform -translate-x-1/2 bg-red-400 w-1/3 p-2 rounded-lg flex items-center justify-start">
|
||||
{/* Hidden file input */}
|
||||
<div class="fixed bottom-4 left-1/2 transform -translate-x-1/2 bg-gray-950/50 backdrop-blur-3xl w-1/3 p-2 rounded-lg flex items-center justify-between">
|
||||
<input
|
||||
type="file"
|
||||
ref={fileInputRef}
|
||||
style="display: none;"
|
||||
onChange$={(e) => {
|
||||
// You can handle the selected file here or emit an event
|
||||
const files = (e.target as HTMLInputElement).files;
|
||||
if (files && files.length > 0) {
|
||||
console.log("File selected:", files[0]);
|
||||
}
|
||||
onChange$={async (e: Event) => {
|
||||
uploadingFile.value = noSerialize((e.target as HTMLInputElement).files![0]);
|
||||
await uploadFile();
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* Button that triggers the file dialog */}
|
||||
<button
|
||||
class="duration-100 hover:bg-white p-2 rounded-lg"
|
||||
onClick$={() => { fileInputRef.value?.click() }}
|
||||
>
|
||||
<SolarUploadLinear class="w-6 h-6 text-black" />
|
||||
</button>
|
||||
<div class="flex items-center gap-1">
|
||||
<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>
|
||||
)
|
||||
})
|
|
@ -1,15 +1,44 @@
|
|||
import { component$ } from "@builder.io/qwik";
|
||||
import { StereoFile } from "~/lib/types";
|
||||
|
||||
export default component$(({ file }: { file: StereoFile }) => {
|
||||
type FileProps = {
|
||||
file: StereoFile;
|
||||
}
|
||||
|
||||
const getBase64Size = (b: string) => {
|
||||
if (!b) return 0;
|
||||
const padding = (b.match(/=+$/) || [""])[0].length;
|
||||
return Math.floor((b.length * 3) / 4) - padding;
|
||||
};
|
||||
|
||||
const formatSize = (bytes: number) => {
|
||||
if (bytes < 1024) return `${bytes} B`;
|
||||
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
||||
return `${(bytes / (1024 * 1024)).toFixed(2)} MB`;
|
||||
}
|
||||
|
||||
export default component$(({ file }: FileProps) => {
|
||||
return (
|
||||
<div key={file.ID}>
|
||||
<h2>Owner: {file.Owner}</h2>
|
||||
<p>File ID: {file.ID}</p>
|
||||
<p>Created: {new Date(file.CreatedAt).toLocaleString()}</p>
|
||||
<div class="rounded-xl bg-slate-900 flex flex-col overflow-hidden">
|
||||
{ file.Base64 && (file.ID.endsWith(".png") || file.ID.endsWith(".jpg") || file.ID.endsWith(".jpeg")) && (
|
||||
<img src={`data:image/png;base64,${file.Base64}`} alt="Stereo File" class="w-full h-auto" />
|
||||
<img
|
||||
width={400}
|
||||
height={300}
|
||||
src={`data:image/png;base64,${file.Base64}`}
|
||||
alt={file.ID}
|
||||
class="w-full h-80 object-cover bg-gray-800 flex-grow"
|
||||
/>
|
||||
)}
|
||||
<div class="p-4 flex-grow-0 text-center">
|
||||
<p class="text-lg font-semibold text-white w-full truncate">
|
||||
{file.ID.split("_").slice(1).join("_") || "Untitled"}
|
||||
</p>
|
||||
<div class="flex gap-1 text-sm text-gray-400 items-center justify-center">
|
||||
<span>{formatSize(getBase64Size(file.Base64))}</span>
|
||||
<span class="text-gray-600">•</span>
|
||||
<p>Uploaded on {new Date(file.CreatedAt).toLocaleDateString()}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})
|
Loading…
Add table
Add a link
Reference in a new issue