diff --git a/assets/controllers/comments_mercure_controller.js b/assets/controllers/comments_mercure_controller.js index 4584944..033f395 100644 --- a/assets/controllers/comments_mercure_controller.js +++ b/assets/controllers/comments_mercure_controller.js @@ -5,93 +5,32 @@ export default class extends Controller { static targets = ["list", "loading"] connect() { - this._debounceId = null; - this._liveRoot = this._findLiveRoot(); - this._liveReady = false; - this._queue = []; // buffer Mercure payloads until live connects - - // 1) Wait for Live to connect (or mark ready if already connected) - const live = this._getLiveController(); - if (live) { - this._liveReady = true; - } else if (this._liveRoot) { - this._onLiveConnect = () => { - this._liveReady = true; - this._flushQueue(); - }; - this._liveRoot.addEventListener('live:connect', this._onLiveConnect, { once: true }); - } - - // Optional: initial render to paint cached HTML - this._renderWhenReady(); + this._liveRoot = this.element.closest( + '[data-controller~="live"]' + ); - // 2) Subscribe to Mercure const hubUrl = window.MercureHubUrl || document.querySelector('meta[name="mercure-hub"]')?.content; if (!hubUrl) return; - const topic = `/comments/${this.coordinateValue}`; - const url = new URL(hubUrl); url.searchParams.append("topic", topic); + const url = new URL(hubUrl); + url.searchParams.append('topic', `/comments/${this.coordinateValue}`); - this.eventSource = new EventSource(url.toString()); - this.eventSource.onmessage = (event) => { - const data = JSON.parse(event.data); // { comments, profiles, ... } - const live = this._getLiveController(); - if (live) { - live.set('payload', JSON.stringify(data)); // <- updates the writable LiveProp - live.render(); // <- asks server to re-render - } - }; + this.es = new EventSource(url.toString()); + this.es.onmessage = (event) => this._pushToLive(event.data); } disconnect() { - if (this.eventSource) try { this.eventSource.close(); } catch {} - if (this._debounceId) clearTimeout(this._debounceId); - if (this._liveRoot && this._onLiveConnect) { - this._liveRoot.removeEventListener('live:connect', this._onLiveConnect); - } - } - - // ---- private ------------------------------------------------------------- - - _findLiveRoot() { - return this.element.closest( - '[data-controller~="live"],' + - '[data-controller~="symfony--ux-live-component--live"]' - ); - } - - _getLiveController() { - if (!this._liveRoot) return null; - return this.application.getControllerForElementAndIdentifier(this._liveRoot, 'live') - || this.application.getControllerForElementAndIdentifier(this._liveRoot, 'symfony--ux-live-component--live'); + if (this.es) try { this.es.close(); } catch {} } - _renderWhenReady() { - // If you also want an initial refresh from server/cache, you can do: - const tryRender = () => { - const live = this._getLiveController(); - if (!live || typeof live.render !== 'function') return setTimeout(tryRender, 50); - live.render(); - }; - tryRender(); - } - - _flushQueue() { - while (this._queue.length) { - const data = this._queue.shift(); - this._ingest(data); - } - } + _pushToLive(jsonString) { + if (!this._liveRoot) return; + // Find the hidden input bound to the LiveProp + const input = this._liveRoot.querySelector('input[type="hidden"][data-model="payloadJson"]'); + if (!input) return; - _ingest(payload) { - const live = this._getLiveController(); - if (!live || typeof live.action !== 'function') { - // if still not ready, re-buffer and retry soon - this._queue.unshift(payload); - return setTimeout(() => this._flushQueue(), 50); - } - // Call your LiveAction; it will mutate props and re-render server-side - // NOTE: payload must be a string; if you have an object, pass JSON.stringify(obj) - live.action('ingest', { payload }); + // Set value and dispatch an 'input' event so Live updates & re-renders + input.value = jsonString; + input.dispatchEvent(new Event('input', { bubbles: true })); } } diff --git a/src/Twig/Components/Organisms/Comments.php b/src/Twig/Components/Organisms/Comments.php index ce58c89..f08491f 100644 --- a/src/Twig/Components/Organisms/Comments.php +++ b/src/Twig/Components/Organisms/Comments.php @@ -20,7 +20,7 @@ final class Comments // Writable prop the browser can set #[LiveProp(writable: true)] - public string $payload; // { comments, profiles, ... } + public string $payloadJson ; // { comments, profiles, ... } // Live input #[LiveProp(writable: false)] @@ -60,21 +60,19 @@ final class Comments /** Expose a view model to the template; keeps all parsing server-side */ public function getPayload(): array { - if (!empty($this->payload)) { - $payload = json_decode($this->payload); - } else { - $payload = $this->redisCacheService->getCommentsPayload($this->current) ?? [ + $data = $this->payloadJson !== '' + ? (json_decode($this->payloadJson, true) ?: []) + : $this->redisCacheService->getCommentsPayload($this->current) ?? [ 'comments' => [], 'profiles' => [], 'zappers' => [], 'zapAmounts' => [], 'commentLinks' => [], ]; - } // If your handler doesn’t compute zaps/links yet, reuse your helpers here: - $this->list = $payload['comments']; - $this->authorsMetadata = $payload['profiles'] ?? []; + $this->list = $data['comments']; + $this->authorsMetadata = $data['profiles'] ?? []; $this->parseZaps(); // your existing method – fills $zapAmounts & $zappers $this->parseNostrLinks(); // your existing method – fills $commentLinks & $processedContent diff --git a/templates/components/Organisms/Comments.html.twig b/templates/components/Organisms/Comments.html.twig index 5b72ef2..d236661 100644 --- a/templates/components/Organisms/Comments.html.twig +++ b/templates/components/Organisms/Comments.html.twig @@ -2,6 +2,10 @@ {{ attributes }} data-comments-coordinate="{{ current }}"> + +