svelte lol
This commit is contained in:
79
f0ck-svelte/src/routes/+page.svelte
Normal file
79
f0ck-svelte/src/routes/+page.svelte
Normal file
@@ -0,0 +1,79 @@
|
||||
<script lang="ts">
|
||||
import type { PageData } from './$types';
|
||||
import type { GridItem } from '$lib/types/item';
|
||||
import ItemCard from '$lib/components/item/ItemCard.svelte';
|
||||
import Pagination from '$lib/components/ui/Pagination.svelte';
|
||||
import { onSSE } from '$lib/stores/sse';
|
||||
import { pinEvent } from '$lib/stores/pin';
|
||||
import { onDestroy } from 'svelte';
|
||||
|
||||
let { data }: { data: PageData } = $props();
|
||||
|
||||
// Local mutable copy of the items list — SSE patches it live
|
||||
let items = $state<GridItem[]>(data.items);
|
||||
let total = $state(data.total);
|
||||
|
||||
// Re-sync when server data changes (e.g. navigation / invalidation)
|
||||
$effect(() => {
|
||||
items = data.items;
|
||||
total = data.total;
|
||||
});
|
||||
|
||||
// ── Live grid updates via SSE ─────────────────────────────────────────────
|
||||
const unsubSSE = onSSE((event) => {
|
||||
if (event.type === 'new_item') {
|
||||
const d = event.data as GridItem & { dest: string; username: string };
|
||||
if (!items.some(i => i.id === d.id)) {
|
||||
items = [d, ...items];
|
||||
total += 1;
|
||||
}
|
||||
} else if (event.type === 'delete_item') {
|
||||
const d = event.data as { id: number };
|
||||
const before = items.length;
|
||||
items = items.filter(i => i.id !== d.id);
|
||||
if (items.length < before) total = Math.max(0, total - 1);
|
||||
}
|
||||
});
|
||||
onDestroy(unsubSSE);
|
||||
|
||||
// ── Pin store: patch the grid card is_pinned live ─────────────────────────
|
||||
const unsubPin = pinEvent.subscribe((evt) => {
|
||||
if (!evt) return;
|
||||
items = items.map(i => i.id === evt.itemId ? { ...i, is_pinned: evt.pinned } : i);
|
||||
});
|
||||
onDestroy(unsubPin);
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
<title>f0ckm — {total} items</title>
|
||||
</svelte:head>
|
||||
|
||||
<div class="max-w-[1600px] mx-auto px-3 py-4">
|
||||
<!-- Grid header -->
|
||||
{#if total > 0}
|
||||
<p class="text-xs opacity-40 mb-3 font-mono">{total.toLocaleString()} items</p>
|
||||
{/if}
|
||||
|
||||
<!-- Thumbnail grid -->
|
||||
{#if items.length > 0}
|
||||
<div class="
|
||||
grid gap-1.5
|
||||
grid-cols-[repeat(auto-fill,minmax(140px,1fr))]
|
||||
sm:grid-cols-[repeat(auto-fill,minmax(180px,1fr))]
|
||||
md:grid-cols-[repeat(auto-fill,minmax(200px,1fr))]
|
||||
">
|
||||
{#each items as item (item.id)}
|
||||
<ItemCard {item} link={data.link} />
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
{#if data.pagination}
|
||||
<Pagination pagination={data.pagination} link={data.link} />
|
||||
{/if}
|
||||
{:else}
|
||||
<div class="flex flex-col items-center justify-center py-32 gap-4 opacity-40">
|
||||
<i class="fa-solid fa-box-open text-5xl"></i>
|
||||
<p class="text-sm">No items found.</p>
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
Reference in New Issue
Block a user