fix: show inline reply form under webmention cards

Reply buttons on webmention interactions (Bluesky, Mastodon, IndieWeb)
now show an inline reply form directly under the card instead of
delegating to the hidden Comments section. The form posts via Micropub
with optional syndication targeting.

Confab-Link: http://localhost:8080/sessions/184584f4-67e1-485a-aba8-02ac34a600fe
This commit is contained in:
Ricardo
2026-03-14 22:49:15 +01:00
parent 02546950bf
commit 395750da9b

View File

@@ -617,6 +617,62 @@
// Wire reply buttons: unhide and attach click handlers for unwired buttons
// Called from owner:detected handler AND after dynamic replies are appended
// Close any open inline reply form
function closeActiveReplyForm() {
var existing = document.querySelector('.wm-inline-reply-form');
if (existing) existing.remove();
}
// Submit a Micropub reply
function submitMicropubReply(replyUrl, platform, syndicateTo, textarea, statusEl, submitBtn) {
var content = textarea.value.trim();
if (!content) return;
submitBtn.disabled = true;
submitBtn.textContent = 'Sending...';
statusEl.textContent = '';
var body = {
type: ['h-entry'],
properties: {
content: [content],
'in-reply-to': [replyUrl]
}
};
if (syndicateTo) {
body.properties['mp-syndicate-to'] = [syndicateTo];
} else {
body.properties['mp-syndicate-to'] = [];
}
fetch('/micropub', {
method: 'POST',
headers: { 'Content-Type': 'application/json', Accept: 'application/json' },
credentials: 'include',
body: JSON.stringify(body)
}).then(function(res) {
if (res.ok || res.status === 201 || res.status === 202) {
statusEl.className = 'text-xs text-green-600 dark:text-green-400 mt-1';
statusEl.textContent = 'Reply posted' + (syndicateTo ? ' and syndicated!' : '!');
textarea.value = '';
setTimeout(closeActiveReplyForm, 2000);
} else {
return res.json().catch(function() { return {}; }).then(function(data) {
statusEl.className = 'text-xs text-red-600 dark:text-red-400 mt-1';
statusEl.textContent = data.error_description || data.error || 'Failed to post reply';
submitBtn.disabled = false;
submitBtn.textContent = 'Send Reply';
});
}
}).catch(function(err) {
statusEl.className = 'text-xs text-red-600 dark:text-red-400 mt-1';
statusEl.textContent = 'Error: ' + err.message;
submitBtn.disabled = false;
submitBtn.textContent = 'Send Reply';
});
}
function wireReplyButtons() {
var ownerStore = Alpine.store && Alpine.store('owner');
if (!ownerStore || !ownerStore.isOwner) return;
@@ -632,15 +688,59 @@
if (platform === 'bluesky') syndicateTo = ownerStore.syndicationTargets.bluesky || null;
if (platform === 'mastodon') syndicateTo = ownerStore.syndicationTargets.mastodon || null;
// Find the commentsSection Alpine component and call startReply
var commentsEl = document.querySelector('[x-data*="commentsSection"]');
if (commentsEl && window.Alpine) {
var data = Alpine.$data(commentsEl);
if (data && data.startReply) {
data.startReply(replyUrl, platform, replyUrl, syndicateTo);
commentsEl.scrollIntoView({ behavior: 'smooth', block: 'start' });
}
// Close any existing reply form
closeActiveReplyForm();
// Find the owner-reply-slot next to this webmention card
var li = btn.closest('li') || btn.closest('.webmention-reply');
var slot = li ? li.querySelector('.wm-owner-reply-slot') : null;
if (!slot) {
// Fallback: insert after the button's parent
slot = document.createElement('div');
btn.parentElement.after(slot);
}
// Build inline reply form
var form = document.createElement('div');
form.className = 'wm-inline-reply-form mt-2 p-3 bg-surface-100 dark:bg-surface-900 rounded-lg border-l-2 border-primary-400';
var label = document.createElement('div');
label.className = 'text-xs text-surface-500 dark:text-surface-400 mb-1';
label.textContent = 'Replying via ' + platform + (syndicateTo ? ' (will syndicate)' : '');
var textarea = document.createElement('textarea');
textarea.rows = 3;
textarea.placeholder = 'Write your reply...';
textarea.className = 'w-full px-3 py-2 border rounded-lg text-sm dark:bg-surface-800 dark:border-surface-700 dark:text-surface-100';
var actions = document.createElement('div');
actions.className = 'flex items-center gap-2 mt-2';
var submitBtn = document.createElement('button');
submitBtn.className = 'button text-sm';
submitBtn.textContent = 'Send Reply';
var cancelBtn = document.createElement('button');
cancelBtn.className = 'text-xs text-surface-500 hover:underline';
cancelBtn.textContent = 'Cancel';
var statusEl = document.createElement('div');
statusEl.className = 'text-xs mt-1';
submitBtn.addEventListener('click', function() {
submitMicropubReply(replyUrl, platform, syndicateTo, textarea, statusEl, submitBtn);
});
cancelBtn.addEventListener('click', closeActiveReplyForm);
actions.appendChild(submitBtn);
actions.appendChild(cancelBtn);
form.appendChild(label);
form.appendChild(textarea);
form.appendChild(actions);
form.appendChild(statusEl);
slot.appendChild(form);
textarea.focus();
});
});
}