spa(phase 3, tasks 15-22): routing, komponenten, home, postview
Phase 3 komplett:
- Task 15: LoadingOrError-Komponente (loading/error-states, Habla-Fallback)
- Task 16: app.html mit CSS-Variablen (light/dark), Base-Typography
- Task 17: +layout.svelte mit Container + bootstrapReadRelays onMount
- Task 18: ProfileCard-Komponente (Avatar, Name, About, NIP-05, Website)
- Task 19: PostCard-Komponente (Thumbnail + Titel/Summary/Datum), responsive
- Task 20: +page.svelte als Home (Profil + Liste, Promise.all für beides)
- Task 21: PostView-Komponente (Titel, Meta, Cover, Summary, Markdown-Body)
- Task 22: [...slug]/+page.ts+svelte — Catch-all-Route mit Legacy-301-Redirect
Alle $props()-abhängigen Werte via $derived() (Svelte-5-Runes-Konformität).
npm run check: 0 errors, 0 warnings, 592 files. npm run build grün.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 17:39:24 +02:00
|
|
|
|
<script lang="ts">
|
|
|
|
|
|
import { onMount } from 'svelte';
|
|
|
|
|
|
import type { NostrEvent, Profile } from '$lib/nostr/loaders';
|
|
|
|
|
|
import { loadPostList, loadProfile } from '$lib/nostr/loaders';
|
|
|
|
|
|
import ProfileCard from '$lib/components/ProfileCard.svelte';
|
|
|
|
|
|
import PostCard from '$lib/components/PostCard.svelte';
|
|
|
|
|
|
import LoadingOrError from '$lib/components/LoadingOrError.svelte';
|
|
|
|
|
|
|
|
|
|
|
|
let profile: Profile | null = $state(null);
|
|
|
|
|
|
let posts: NostrEvent[] = $state([]);
|
|
|
|
|
|
let loading = $state(true);
|
|
|
|
|
|
let error: string | null = $state(null);
|
|
|
|
|
|
|
|
|
|
|
|
onMount(async () => {
|
|
|
|
|
|
try {
|
|
|
|
|
|
const [p, list] = await Promise.all([loadProfile(), loadPostList()]);
|
|
|
|
|
|
profile = p;
|
|
|
|
|
|
posts = list;
|
|
|
|
|
|
loading = false;
|
|
|
|
|
|
if (list.length === 0) {
|
|
|
|
|
|
error = 'Keine Posts gefunden auf den abgefragten Relays.';
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
loading = false;
|
|
|
|
|
|
error = e instanceof Error ? e.message : 'Unbekannter Fehler';
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
$effect(() => {
|
|
|
|
|
|
const name = profile?.display_name ?? profile?.name ?? 'Jörg Lohrer';
|
|
|
|
|
|
document.title = `${name} – Blog`;
|
|
|
|
|
|
});
|
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
<ProfileCard {profile} />
|
|
|
|
|
|
|
|
|
|
|
|
<h1 class="list-title">Beiträge</h1>
|
|
|
|
|
|
|
|
|
|
|
|
<LoadingOrError {loading} {error} />
|
|
|
|
|
|
|
|
|
|
|
|
{#each posts as post (post.id)}
|
|
|
|
|
|
<PostCard event={post} />
|
|
|
|
|
|
{/each}
|
|
|
|
|
|
|
|
|
|
|
|
<style>
|
|
|
|
|
|
.list-title {
|
|
|
|
|
|
margin: 0 0 1rem;
|
|
|
|
|
|
font-size: 1.4rem;
|
|
|
|
|
|
}
|
|
|
|
|
|
</style>
|