spa(phase 4, tasks 23-25): tag-navigation
- loadPostsByTag(tagName): client-seitige Filterung der Post-Liste (case-insensitive). #t-Filter wird nicht von allen Relays zuverlässig unterstützt — wir laden alles und filtern lokal. - /tag/[name]/+page.ts+svelte: neue Tag-Route, Breadcrumb zurück zur Übersicht, #tagName als H1, dieselbe PostCard-Darstellung wie Home. - Tag-Chips in PostView sind bereits klickbar (aus Task 21). npm run check: 0 errors. Deploy live auf svelte.joerg-lohrer.de. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
feb336fc5b
commit
c089d9e429
|
|
@ -145,6 +145,19 @@ export async function loadReplies(
|
|||
return events.sort((a, b) => a.created_at - b.created_at);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filtert Post-Liste clientseitig nach Tag-Name.
|
||||
* (Relay-seitige #t-Filter werden nicht von allen Relays unterstützt — safer
|
||||
* ist es, die ganze Liste zu laden und lokal zu filtern.)
|
||||
*/
|
||||
export async function loadPostsByTag(tagName: string): Promise<NostrEvent[]> {
|
||||
const all = await loadPostList();
|
||||
const norm = tagName.toLowerCase();
|
||||
return all.filter((ev) =>
|
||||
ev.tags.some((t) => t[0] === 't' && t[1]?.toLowerCase() === norm)
|
||||
);
|
||||
}
|
||||
|
||||
export interface ReactionSummary {
|
||||
/** Emoji oder "+"/"-" */
|
||||
content: string;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,59 @@
|
|||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
import type { NostrEvent } from '$lib/nostr/loaders';
|
||||
import { loadPostsByTag } from '$lib/nostr/loaders';
|
||||
import PostCard from '$lib/components/PostCard.svelte';
|
||||
import LoadingOrError from '$lib/components/LoadingOrError.svelte';
|
||||
|
||||
let { data } = $props();
|
||||
const tagName = $derived(data.tagName);
|
||||
|
||||
let posts: NostrEvent[] = $state([]);
|
||||
let loading = $state(true);
|
||||
let error: string | null = $state(null);
|
||||
|
||||
onMount(async () => {
|
||||
try {
|
||||
posts = await loadPostsByTag(tagName);
|
||||
loading = false;
|
||||
if (posts.length === 0) {
|
||||
error = `Keine Posts mit Tag "${tagName}" gefunden.`;
|
||||
}
|
||||
} catch (e) {
|
||||
loading = false;
|
||||
error = e instanceof Error ? e.message : 'Unbekannter Fehler';
|
||||
}
|
||||
});
|
||||
|
||||
$effect(() => {
|
||||
document.title = `#${tagName} – Jörg Lohrer`;
|
||||
});
|
||||
</script>
|
||||
|
||||
<nav class="breadcrumb"><a href="/">← Zurück zur Übersicht</a></nav>
|
||||
|
||||
<h1 class="tag-title">#{tagName}</h1>
|
||||
|
||||
<LoadingOrError {loading} {error} />
|
||||
|
||||
{#each posts as post (post.id)}
|
||||
<PostCard event={post} />
|
||||
{/each}
|
||||
|
||||
<style>
|
||||
.breadcrumb {
|
||||
font-size: 0.9rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
.breadcrumb a {
|
||||
color: var(--accent);
|
||||
text-decoration: none;
|
||||
}
|
||||
.breadcrumb a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
.tag-title {
|
||||
margin: 0 0 1.5rem;
|
||||
font-size: 1.6rem;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
import type { PageLoad } from './$types';
|
||||
|
||||
export const load: PageLoad = async ({ params }) => {
|
||||
return { tagName: decodeURIComponent(params.name) };
|
||||
};
|
||||
Loading…
Reference in New Issue