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 { @@ -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 }));
}
}

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

@ -20,7 +20,7 @@ final class Comments @@ -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 @@ -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

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

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

Loading…
Cancel
Save