Files
gugara/src/components/BlossomDrive.svelte
T
2026-05-25 14:23:03 +02:00

139 lines
3.6 KiB
Svelte

<script lang="ts">
import { onMount } from 'svelte';
import { get } from 'svelte/store';
import ndk from '$lib/stores/ndk';
import { parseDriveEvent, getBlobUrl, fileName, type BlossomFile } from '$lib/blossom';
import type { NDKFilter } from '@nostr-dev-kit/ndk';
import PdfCover from './PdfCover.svelte';
export let pubkey: string;
export let driveId: string;
let driveName = '';
let description = '';
let files: BlossomFile[] = [];
let servers: string[] = [];
let loading = true;
let error = '';
let activePdf: string | null = null;
onMount(() => {
const $ndk = get(ndk);
const filter: NDKFilter = {
kinds: [30563 as number],
authors: [pubkey],
'#d': [driveId]
};
$ndk
.fetchEvent(filter)
.then((event) => {
if (!event) {
error = 'Drive not found.';
return;
}
const drive = parseDriveEvent(event);
driveName = drive.name;
description = drive.description;
servers = drive.servers;
files = drive.files.filter(
(f) => f.mimeType === 'application/pdf' || f.path.endsWith('.pdf')
);
})
.catch((e: unknown) => {
error = e instanceof Error ? e.message : String(e);
})
.finally(() => {
loading = false;
});
});
function openPdf(file: BlossomFile) {
activePdf = getBlobUrl(file.sha256, servers, '.pdf');
}
function closePdf() {
activePdf = null;
}
</script>
{#if loading}
<p class="animate-pulse text-sm text-gray-500">Loading drive…</p>
{:else if error}
<p class="text-sm font-medium text-red-600">Error: {error}</p>
{:else}
<div class="grid grid-cols-2 gap-6 sm:grid-cols-3 md:grid-cols-4">
{#each files as file (file.sha256)}
{@const blobUrl = getBlobUrl(file.sha256, servers, '.pdf')}
{@const name = fileName(file.path)}
<div class="group flex flex-col gap-2">
<!-- Cover — clicking opens the viewer -->
<button
on:click={() => openPdf(file)}
class="block w-full overflow-hidden rounded-lg shadow-md ring-2 ring-transparent transition-all duration-200 group-hover:-translate-y-1 group-hover:shadow-xl group-hover:ring-indigo-400"
aria-label="Read {name}"
>
<PdfCover url={blobUrl} alt={name} />
</button>
<!-- Title -->
<p
class="truncate px-1 text-center text-xs leading-tight font-medium text-gray-700 transition-colors group-hover:text-indigo-600"
title={name}
>
{name.replace(/\.pdf$/i, '')}
</p>
<!-- Download link -->
<a
href={blobUrl}
download={name}
target="_blank"
rel="noopener noreferrer"
class="text-center text-xs text-gray-400 transition-colors hover:text-indigo-500"
>
⬇ Download
</a>
</div>
{/each}
</div>
{/if}
<!-- PDF Viewer Modal -->
{#if activePdf}
<!-- svelte-ignore a11y-click-events-have-key-events -->
<div
class="fixed inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-sm"
on:click={closePdf}
>
<!-- svelte-ignore a11y-click-events-have-key-events -->
<div
class="relative flex w-[90vw] max-w-4xl flex-col rounded-xl bg-white p-4 shadow-2xl"
on:click|stopPropagation
>
<!-- Modal header -->
<div class="mb-3 flex items-center justify-between">
<span class="text-sm font-medium text-gray-700">Fanzine Viewer</span>
<button
on:click={closePdf}
class="text-xl leading-none text-gray-400 transition-colors hover:text-gray-700"
aria-label="Close"
>
</button>
</div>
<!-- PDF iframe -->
<iframe
src={activePdf}
title="Fanzine PDF"
class="w-full rounded border border-gray-200"
style="height: 80vh;"
></iframe>
</div>
</div>
{/if}