fix: reply buttons on dynamic webmentions + owner comment form
- Add .wm-reply-btn button and .wm-owner-reply-slot to dynamically created reply elements (parity with build-time Nunjucks template) - Extract wireReplyButtons() so buttons are wired both on owner:detected and after dynamic replies are appended (fixes timing gap) - Use data-wired attribute to prevent double-wiring - Show comment form for site owner (isOwner) not just IndieAuth users - Fix "Signed in as" display to use ownerProfile when user is null Confab-Link: http://localhost:8080/sessions/184584f4-67e1-485a-aba8-02ac34a600fe
This commit is contained in:
@@ -46,12 +46,13 @@
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{# Comment form (shown when authenticated) #}
|
{# Comment form (shown when authenticated via IndieAuth OR as site owner) #}
|
||||||
<div x-show="user" x-cloak>
|
<div x-show="user || isOwner" x-cloak>
|
||||||
<div class="flex items-center gap-2 mb-3 text-sm text-surface-600 dark:text-surface-400">
|
<div class="flex items-center gap-2 mb-3 text-sm text-surface-600 dark:text-surface-400">
|
||||||
<span>Signed in as</span>
|
<span>Signed in as</span>
|
||||||
<a x-bind:href="user?.url" class="font-medium hover:underline" x-text="user?.name || user?.url" target="_blank" rel="noopener"></a>
|
<a x-bind:href="user?.url || ownerProfile?.url" class="font-medium hover:underline"
|
||||||
<button x-on:click="signOut()" class="text-xs underline hover:no-underline">Sign out</button>
|
x-text="user?.name || ownerProfile?.name || user?.url || 'Owner'" target="_blank" rel="noopener"></a>
|
||||||
|
<button x-show="user" x-on:click="signOut()" class="text-xs underline hover:no-underline">Sign out</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<form x-on:submit.prevent="submitComment()">
|
<form x-on:submit.prevent="submitComment()">
|
||||||
|
|||||||
+34
-5
@@ -337,16 +337,36 @@
|
|||||||
replyDiv.className = 'text-surface-700 dark:text-surface-300 prose dark:prose-invert prose-sm max-w-none';
|
replyDiv.className = 'text-surface-700 dark:text-surface-300 prose dark:prose-invert prose-sm max-w-none';
|
||||||
replyDiv.textContent = content.text || '';
|
replyDiv.textContent = content.text || '';
|
||||||
|
|
||||||
|
// Reply button (hidden by default, shown for owner)
|
||||||
|
const replyBtn = document.createElement('button');
|
||||||
|
replyBtn.className = 'wm-reply-btn hidden text-xs text-primary-600 dark:text-primary-400 hover:underline mt-2';
|
||||||
|
replyBtn.dataset.replyUrl = item.url || '';
|
||||||
|
replyBtn.dataset.platform = platform;
|
||||||
|
replyBtn.textContent = 'Reply';
|
||||||
|
|
||||||
contentDiv.appendChild(headerDiv);
|
contentDiv.appendChild(headerDiv);
|
||||||
contentDiv.appendChild(replyDiv);
|
contentDiv.appendChild(replyDiv);
|
||||||
|
contentDiv.appendChild(replyBtn);
|
||||||
|
|
||||||
wrapper.appendChild(avatarLink);
|
wrapper.appendChild(avatarLink);
|
||||||
wrapper.appendChild(contentDiv);
|
wrapper.appendChild(contentDiv);
|
||||||
li.appendChild(wrapper);
|
li.appendChild(wrapper);
|
||||||
|
|
||||||
|
// Owner reply slot for threaded replies
|
||||||
|
const ownerReplySlot = document.createElement('div');
|
||||||
|
ownerReplySlot.className = 'wm-owner-reply-slot ml-13 mt-2';
|
||||||
|
li.appendChild(ownerReplySlot);
|
||||||
|
|
||||||
|
// Also set data attributes for build-time parity
|
||||||
|
li.dataset.wmSource = item['wm-source'] || '';
|
||||||
|
li.dataset.authorUrl = author.url || '';
|
||||||
|
|
||||||
// Prepend to show newest first
|
// Prepend to show newest first
|
||||||
list.insertBefore(li, list.firstChild);
|
list.insertBefore(li, list.firstChild);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Wire up new reply buttons if owner is already detected
|
||||||
|
wireReplyButtons();
|
||||||
}
|
}
|
||||||
|
|
||||||
function appendMentions(selector, items) {
|
function appendMentions(selector, items) {
|
||||||
@@ -595,13 +615,15 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Show reply buttons and wire click handlers if owner is detected
|
// Wire reply buttons: unhide and attach click handlers for unwired buttons
|
||||||
// Listen for custom event dispatched by comments.js after async owner check
|
// Called from owner:detected handler AND after dynamic replies are appended
|
||||||
document.addEventListener('owner:detected', function() {
|
function wireReplyButtons() {
|
||||||
var ownerStore = Alpine.store && Alpine.store('owner');
|
var ownerStore = Alpine.store && Alpine.store('owner');
|
||||||
if (!ownerStore || !ownerStore.isOwner) return;
|
if (!ownerStore || !ownerStore.isOwner) return;
|
||||||
|
|
||||||
document.querySelectorAll('.wm-reply-btn').forEach(function(btn) {
|
document.querySelectorAll('.wm-reply-btn').forEach(function(btn) {
|
||||||
|
if (btn.dataset.wired) return; // already wired
|
||||||
|
btn.dataset.wired = 'true';
|
||||||
btn.classList.remove('hidden');
|
btn.classList.remove('hidden');
|
||||||
btn.addEventListener('click', function() {
|
btn.addEventListener('click', function() {
|
||||||
var replyUrl = btn.dataset.replyUrl;
|
var replyUrl = btn.dataset.replyUrl;
|
||||||
@@ -614,10 +636,8 @@
|
|||||||
var commentsEl = document.querySelector('[x-data*="commentsSection"]');
|
var commentsEl = document.querySelector('[x-data*="commentsSection"]');
|
||||||
if (commentsEl && commentsEl.__x) {
|
if (commentsEl && commentsEl.__x) {
|
||||||
commentsEl.__x.$data.startReply(replyUrl, platform, replyUrl, syndicateTo);
|
commentsEl.__x.$data.startReply(replyUrl, platform, replyUrl, syndicateTo);
|
||||||
// Scroll to the comments section where the form will appear
|
|
||||||
commentsEl.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
commentsEl.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
||||||
} else if (window.Alpine) {
|
} else if (window.Alpine) {
|
||||||
// Alternative: dispatch event for Alpine component to handle
|
|
||||||
var evt = new CustomEvent('owner-reply', {
|
var evt = new CustomEvent('owner-reply', {
|
||||||
detail: { replyUrl: replyUrl, platform: platform, syndicateTo: syndicateTo },
|
detail: { replyUrl: replyUrl, platform: platform, syndicateTo: syndicateTo },
|
||||||
bubbles: true
|
bubbles: true
|
||||||
@@ -626,6 +646,15 @@
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show reply buttons and wire click handlers if owner is detected
|
||||||
|
// Listen for custom event dispatched by comments.js after async owner check
|
||||||
|
document.addEventListener('owner:detected', function() {
|
||||||
|
var ownerStore = Alpine.store && Alpine.store('owner');
|
||||||
|
if (!ownerStore || !ownerStore.isOwner) return;
|
||||||
|
|
||||||
|
wireReplyButtons();
|
||||||
|
|
||||||
// Render threaded owner replies under matching webmention cards
|
// Render threaded owner replies under matching webmention cards
|
||||||
var ownerReplies = ownerStore.replies || [];
|
var ownerReplies = ownerStore.replies || [];
|
||||||
|
|||||||
Reference in New Issue
Block a user