import { wallet } from '@/wallet'; import { StateController } from '@lit-app/state'; import type { NDKUser } from '@nostr-dev-kit/ndk'; import formatDateTime from '@utils/formatDateTime'; import { LitElement, css, html } from 'lit'; import { customElement, property, state } from 'lit/decorators.js'; import { classMap } from 'lit/directives/class-map.js'; import { ndk } from '@/ndk'; import '@components/MarkdownContent'; @customElement('arx-forum-post') export class ForumPost extends LitElement { @property({ type: String }) override id = ''; @property({ type: String }) topicId = ''; @property({ type: String }) npub = ''; @property({ type: Date }) date = new Date(); @property({ type: String }) content = ''; @property({ type: Boolean }) isHighlighted = false; @state() private zapAmountDialogOpen = false; @state() public authorProfile: NDKUser | undefined = undefined; override connectedCallback(): void { super.connectedCallback(); this.authorProfile = ndk.getUser({ pubkey: this.npub }); new StateController(this, wallet); wallet.loadWallet(); } static override styles = [ css` .post { display: flex; flex-direction: row; border-radius: var(--radius-box); background: var(--color-base-100); 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); margin-block-end: 1rem; overflow: hidden; transition: all 0.25s cubic-bezier(0.22, 1, 0.36, 1); isolation: isolate; border: var(--border) solid var(--color-base-300); will-change: transform; } .post:hover { transform: translateY(-2px); box-shadow: calc(var(--depth) * 3px) calc(var(--depth) * 3px) calc(var(--depth) * 6px) oklch(from var(--color-base-content) l c h / 0.15); } .post--highlighted { position: relative; border-inline-start: none; } .post--highlighted::before { content: ""; position: absolute; inset-inline-start: 0; top: 0; bottom: 0; width: 4px; background: var(--color-accent); border-radius: 4px 0 0 4px; } .post__sidebar { padding: clamp(1rem, 4vw, 1.5rem); border-right: var(--border) solid var(--color-base-300); flex: 0 0 auto; display: flex; flex-direction: column; align-items: center; background: var(--color-base-200); } .post__main { flex: 1; display: flex; flex-direction: column; min-width: 0; } .post__header { display: flex; align-items: center; padding: 1rem clamp(1rem, 4vw, 1.5rem); border-bottom: var(--border) solid var(--color-base-300); background: var(--color-base-200); } .post__time { display: flex; align-items: center; gap: 0.5rem; font-size: 0.875rem; color: var(--color-secondary); font-weight: 500; } .post__time iconify-icon { color: var(--color-secondary); opacity: 0.7; } .post__permalink { margin-inline-start: auto; color: var(--color-accent); border: none; background: transparent; cursor: pointer; border-radius: 50%; width: 2.25rem; height: 2.25rem; display: grid; place-items: center; transition: all 0.2s ease; } .post__permalink:hover { transform: scale(1.05); } .post__content { padding: clamp(1.25rem, 5vw, 1.75rem); line-height: 1.7; color: var(--color-base-content); overflow-wrap: break-word; flex: 1; font-size: clamp(0.95rem, 2vw, 1rem); letter-spacing: 0.01em; } .post__actions { display: flex; padding: 0.75rem clamp(0.75rem, 3vw, 1.25rem); border-top: var(--border) solid var(--color-base-300); gap: clamp(0.5rem, 2vw, 0.75rem); background: var(--color-base-200); } @media (max-width: 768px) { .post { flex-direction: column; margin-block-end: 1.5rem; } .post__sidebar { border-right: none; border-bottom: var(--border) solid var(--color-base-300); padding: 1rem; flex-direction: row; justify-content: flex-start; gap: 1rem; } } @media (max-width: 480px) { .post__actions { justify-content: space-between; padding: 0.5rem 0.75rem; } .post__content { padding: 1rem; } } `, ]; private _handleReply() { alert('Replying is not yet implemented'); this.dispatchEvent( new CustomEvent('reply', { detail: { postId: this.id, npub: this.npub }, bubbles: true, composed: true, }), ); } private _handleZap() { // setting to false and then to true forces the dialog to open, even when it wasn't closed correctly this.zapAmountDialogOpen = false; setTimeout(() => { this.zapAmountDialogOpen = true; }, 0); } private async _doZap(e: Event) { if (!(e instanceof CustomEvent)) return; e.preventDefault(); const zapAmount = Number.parseInt(e.detail.value); if (Number.isNaN(zapAmount) || zapAmount <= 10) { alert('Zap amount must be greater or equal to 10'); return; } await wallet.nutZap(zapAmount, this.authorProfile!, this.id); this.zapAmountDialogOpen = false; } private _handleDownzap() { alert('Downzapping is not yet implemented'); this.dispatchEvent( new CustomEvent('downzap', { detail: { postId: this.id, npub: this.npub }, bubbles: true, composed: true, }), ); } private _copyPermalink() { const permalink = `eve://phora/topics/${this.topicId}#post-${this.id}`; navigator.clipboard.writeText(permalink); } override render() { const permalink = `eve://phora/topics/${this.topicId}#post-${this.id}`; const postClasses = { post: true, 'post--highlighted': this.isHighlighted, }; return html` { this.zapAmountDialogOpen = false; }} showInput .open=${this.zapAmountDialogOpen} >
${formatDateTime(this.date)}
`; } }