diff --git a/assets/controllers/nostr_comment_controller.js b/assets/controllers/nostr_comment_controller.js new file mode 100644 index 0000000..6b3d7b9 --- /dev/null +++ b/assets/controllers/nostr_comment_controller.js @@ -0,0 +1,170 @@ +import { Controller } from '@hotwired/stimulus'; + +// NIP-22 Comment Publishing Controller +// Usage: Attach to a form with data attributes for root/parent context +export default class extends Controller { + static targets = ['publishButton', 'status']; + static values = { + publishUrl: String, + csrfToken: String + }; + + connect() { + console.log('Nostr comment controller connected'); + try { + console.debug('[nostr-comment] publishUrl:', this.publishUrlValue || '(none)'); + console.debug('[nostr-comment] has csrfToken:', Boolean(this.csrfTokenValue)); + } catch (_) {} + } + + async publish(event) { + event.preventDefault(); + + if (!this.publishUrlValue) { + this.showError('Publish URL is not configured'); + return; + } + if (!this.csrfTokenValue) { + this.showError('Missing CSRF token'); + return; + } + if (!window.nostr) { + this.showError('Nostr extension not found'); + return; + } + + this.publishButtonTarget.disabled = true; + this.showStatus('Preparing comment for signing...'); + + try { + // Collect form data and context + const formData = this.collectFormData(); + + // Validate required fields + if (!formData.content) { + throw new Error('Comment content is required'); + } + if (!formData.root || !formData.parent) { + throw new Error('Missing root or parent context'); + } + if (!this.isPlaintext(formData.content)) { + throw new Error('Comment must be plaintext (no formatting)'); + } + + // Create NIP-22 event + const nostrEvent = await this.createNip22Event(formData); + + this.showStatus('Requesting signature from Nostr extension...'); + const signedEvent = await window.nostr.signEvent(nostrEvent); + + this.showStatus('Publishing comment...'); + await this.sendToBackend(signedEvent, formData); + + this.showSuccess('Comment published successfully!'); + // Optionally reload or clear form + setTimeout(() => { + window.location.reload(); + }, 1500); + } catch (error) { + console.error('Publishing error:', error); + this.showError(`Publishing failed: ${error.message}`); + } finally { + this.publishButtonTarget.disabled = false; + } + } + + collectFormData() { + // Use the form element directly (this.element is the
diff --git a/templates/pages/article.html.twig b/templates/pages/article.html.twig index c2e7f3d..b031f2b 100644 --- a/templates/pages/article.html.twig +++ b/templates/pages/article.html.twig @@ -82,6 +82,24 @@ +