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`