import { getSigner, ndk } from '@/ndk'; import type { NDKKind, NDKSubscription } from '@nostr-dev-kit/ndk'; import { LitElement, css, html } from 'lit'; import { customElement, property, state } from 'lit/decorators.js'; import '@components/Arbor/ForumPost'; import '@components/Breadcrumbs'; import '@components/General/Button'; import { map } from 'lit/directives/map.js'; import { when } from 'lit/directives/when.js'; interface ForumPost { id: string; npub: string; date: Date; content: string; } @customElement('arx-arbor-thread-view') export class ArborThreadView extends LitElement { @property({ type: String }) threadId = ''; @state() override title = ''; @state() private posts: ForumPost[] = []; private subscription: NDKSubscription | undefined; static override styles = css` :host { display: block; margin: 0 auto; } .thread-container { background: var(--color-base-100); border-radius: var(--radius-box); border: var(--border) solid var(--color-base-300); overflow: hidden; margin-top: 1.5rem; box-shadow: calc(var(--depth) * 2px) calc(var(--depth) * 2px) calc(var(--depth) * 4px) oklch(from var(--color-base-content) l c h / 0.1), calc(var(--depth) * -1px) calc(var(--depth) * -1px) calc(var(--depth) * 3px) oklch(from var(--color-base-100) l c h / 0.4); } .header { background: var(--color-secondary); color: var(--color-secondary-content); padding: 1.5rem 2rem; font-weight: 600; font-size: 1.25rem; display: flex; justify-content: space-between; align-items: center; position: relative; text-shadow: 0 1px 2px oklch(from var(--color-base-content) l c h / 0.2); letter-spacing: 0.01em; border-bottom: var(--border) solid var(--color-base-300); } .posts-container { display: flex; flex-direction: column; gap: 0; background: var(--color-base-200); padding: 1.5rem; min-height: 300px; } .actions { background: var(--color-base-200); padding: 1.25rem 1.5rem; display: flex; justify-content: flex-end; border-top: var(--border) solid var(--color-base-300); } .empty-state { padding: 3rem; text-align: center; color: var(--color-secondary); font-style: italic; display: flex; flex-direction: column; align-items: center; justify-content: center; height: 200px; background: var(--color-base-100); border-radius: var(--radius-field); margin: 1rem 0; border: var(--border) dashed var(--color-base-300); } arx-breadcrumbs { margin-bottom: 1rem; } arx-forum-post { border-bottom: var(--border) solid var(--color-base-300); } arx-forum-post:last-child { border-bottom: none; } @media (max-width: 768px) { .header { padding: 1.25rem 1.5rem; font-size: 1.1rem; } .posts-container { padding: 1rem; } .actions { padding: 1rem; } } @media (max-width: 480px) { :host { padding: 0.5rem; } .header { padding: 1rem; flex-direction: column; align-items: flex-start; gap: 0.5rem; } .empty-state { padding: 2rem 1rem; } } `; override async connectedCallback() { super.connectedCallback(); await this.loadThread(); } override disconnectedCallback() { super.disconnectedCallback(); if (this.subscription) { this.subscription.stop(); } } private async loadThread() { try { await getSigner(); const event = await ndk.fetchEvent(this.threadId); if (!event) { throw new Error('Could not load thread'); } this.title = event.tags.find((tag) => tag[0] === 'name')?.[1] || ''; this.posts = [ { id: event.id, npub: event.pubkey, date: new Date((event.created_at || 0) * 1000), content: event.content, }, ]; // Subscribe to new posts this.subscription = ndk .subscribe({ kinds: [893 as NDKKind], '#E': [this.threadId], }) .on('event', (event) => { this.posts = [ ...this.posts, { id: event.id, npub: event.pubkey, date: new Date((event.created_at || 0) * 1000), content: event.content, }, ]; }); } catch (error) { console.error('Failed to load thread:', error); alert('Could not load thread'); } } override render() { const breadcrumbItems = [{ text: 'Home', href: '/' }, { text: 'Arbor', href: '/arbor' }, { text: this.title }]; return html`
${this.title || 'Loading thread...'}
${when( this.posts.length === 0, () => html`
No posts available
`, () => map( this.posts, (post) => html` `, ), )}
New Post
`; } }