Browse Source

Refactor comments, yet again

imwald
Nuša Pukšič 3 months ago
parent
commit
0973eb7c7b
  1. 93
      assets/controllers/comments_mercure_controller.js
  2. 14
      src/Twig/Components/Organisms/Comments.php
  3. 4
      templates/components/Organisms/Comments.html.twig

93
assets/controllers/comments_mercure_controller.js

@ -5,93 +5,32 @@ export default class extends Controller {
static targets = ["list", "loading"] static targets = ["list", "loading"]
connect() { connect() {
this._debounceId = null; this._liveRoot = this.element.closest(
this._liveRoot = this._findLiveRoot(); '[data-controller~="live"]'
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();
// 2) Subscribe to Mercure
const hubUrl = window.MercureHubUrl || document.querySelector('meta[name="mercure-hub"]')?.content; const hubUrl = window.MercureHubUrl || document.querySelector('meta[name="mercure-hub"]')?.content;
if (!hubUrl) return; if (!hubUrl) return;
const topic = `/comments/${this.coordinateValue}`; const url = new URL(hubUrl);
const url = new URL(hubUrl); url.searchParams.append("topic", topic); url.searchParams.append('topic', `/comments/${this.coordinateValue}`);
this.eventSource = new EventSource(url.toString()); this.es = new EventSource(url.toString());
this.eventSource.onmessage = (event) => { this.es.onmessage = (event) => this._pushToLive(event.data);
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
}
};
} }
disconnect() { disconnect() {
if (this.eventSource) try { this.eventSource.close(); } catch {} if (this.es) try { this.es.close(); } catch {}
if (this._debounceId) clearTimeout(this._debounceId);
if (this._liveRoot && this._onLiveConnect) {
this._liveRoot.removeEventListener('live:connect', this._onLiveConnect);
} }
}
// ---- private -------------------------------------------------------------
_findLiveRoot() { _pushToLive(jsonString) {
return this.element.closest( if (!this._liveRoot) return;
'[data-controller~="live"],' + // Find the hidden input bound to the LiveProp
'[data-controller~="symfony--ux-live-component--live"]' const input = this._liveRoot.querySelector('input[type="hidden"][data-model="payloadJson"]');
); if (!input) return;
}
_getLiveController() { // Set value and dispatch an 'input' event so Live updates & re-renders
if (!this._liveRoot) return null; input.value = jsonString;
return this.application.getControllerForElementAndIdentifier(this._liveRoot, 'live') input.dispatchEvent(new Event('input', { bubbles: true }));
|| this.application.getControllerForElementAndIdentifier(this._liveRoot, 'symfony--ux-live-component--live');
}
_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);
}
}
_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 });
} }
} }

14
src/Twig/Components/Organisms/Comments.php

@ -20,7 +20,7 @@ final class Comments
// Writable prop the browser can set // Writable prop the browser can set
#[LiveProp(writable: true)] #[LiveProp(writable: true)]
public string $payload; // { comments, profiles, ... } public string $payloadJson ; // { comments, profiles, ... }
// Live input // Live input
#[LiveProp(writable: false)] #[LiveProp(writable: false)]
@ -60,21 +60,19 @@ final class Comments
/** Expose a view model to the template; keeps all parsing server-side */ /** Expose a view model to the template; keeps all parsing server-side */
public function getPayload(): array public function getPayload(): array
{ {
if (!empty($this->payload)) { $data = $this->payloadJson !== ''
$payload = json_decode($this->payload); ? (json_decode($this->payloadJson, true) ?: [])
} else { : $this->redisCacheService->getCommentsPayload($this->current) ?? [
$payload = $this->redisCacheService->getCommentsPayload($this->current) ?? [
'comments' => [], 'comments' => [],
'profiles' => [], 'profiles' => [],
'zappers' => [], 'zappers' => [],
'zapAmounts' => [], 'zapAmounts' => [],
'commentLinks' => [], 'commentLinks' => [],
]; ];
}
// If your handler doesn’t compute zaps/links yet, reuse your helpers here: // If your handler doesn’t compute zaps/links yet, reuse your helpers here:
$this->list = $payload['comments']; $this->list = $data['comments'];
$this->authorsMetadata = $payload['profiles'] ?? []; $this->authorsMetadata = $data['profiles'] ?? [];
$this->parseZaps(); // your existing method – fills $zapAmounts & $zappers $this->parseZaps(); // your existing method – fills $zapAmounts & $zappers
$this->parseNostrLinks(); // your existing method – fills $commentLinks & $processedContent $this->parseNostrLinks(); // your existing method – fills $commentLinks & $processedContent

4
templates/components/Organisms/Comments.html.twig

@ -2,6 +2,10 @@
{{ attributes }} {{ attributes }}
data-comments-coordinate="{{ current }}"> data-comments-coordinate="{{ current }}">
<input type="hidden"
data-model="payloadJson"
value="{{ payloadJson|default('') }}" />
<div <div
class="comments" class="comments"
data-controller="comments-mercure" data-controller="comments-mercure"

Loading…
Cancel
Save