fix(channels): backfill raw-resend buttons when displayMessages races loadStatus

loadStatus and loadMessages fire in parallel at page init. Whichever lost
the race left own-message bubbles without the raw-resend button — and once
the row was rendered, neither displayMessages (no re-render on switch
without going back through createMessageElement, which is gated on
window.deviceCaps at the time of call) nor refreshMessagesMeta (skips rows
that already have route info) would patch it back in. So the button only
ever appeared for messages sent in the current session.

Walk visible own bubbles in two places — at the end of loadStatus and at
the end of displayMessages — and inject the button on any row missing it.
Idempotent (skips bubbles that already have .btn-raw-resend), cheap (no
network), and covers both the page-reload race and the channel-switch /
archive-view paths.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
MarekWo
2026-06-09 19:01:07 +02:00
parent e3211dd536
commit 2f8f765af5
+37
View File
@@ -1046,6 +1046,13 @@ function displayMessages(messages) {
markChannelAsRead(currentChannelIdx, latestTimestamp);
}
// Backfill raw-resend buttons on own messages — handles the case where
// displayMessages ran before window.deviceCaps was populated (initial
// page load race), and the channel-switch case where createMessageElement
// does render the button but we want belt-and-suspenders for any path
// (e.g. archive view) that might bypass it.
injectRawResendButtonsForVisibleMessages();
// Re-apply filter if active
clearFilterState();
}
@@ -1760,6 +1767,11 @@ async function loadStatus() {
supports_raw_resend: !!data.supports_raw_resend,
fw_ver_code: data.fw_ver_code ?? null,
};
// loadStatus and loadMessages run in parallel at page init, so
// any messages rendered before this point missed the deviceCaps
// check and have no raw-resend button. Walk visible own bubbles
// and inject the button where it's missing.
injectRawResendButtonsForVisibleMessages();
}
} catch (error) {
console.error('Error loading status:', error);
@@ -1767,6 +1779,31 @@ async function loadStatus() {
}
}
/**
* Walk the messagesList and inject the raw-resend button on any own message
* bubble that's missing it (e.g. rendered before window.deviceCaps was set,
* or re-rendered by displayMessages on channel switch). Safe to call any
* time — does nothing when the device doesn't support raw resend.
*/
function injectRawResendButtonsForVisibleMessages() {
if (!window.deviceCaps?.supports_raw_resend) return;
const container = document.getElementById('messagesList');
if (!container) return;
const wrappers = container.querySelectorAll('.message-wrapper.own[data-msg-id]');
for (const wrapper of wrappers) {
const msgId = wrapper.dataset.msgId;
if (!msgId || msgId.startsWith('_pending_')) continue;
const actionsEl = wrapper.querySelector('.message-actions');
if (!actionsEl || actionsEl.querySelector('.btn-raw-resend')) continue;
const rawBtn = document.createElement('button');
rawBtn.className = 'btn btn-outline-secondary btn-msg-action btn-raw-resend';
rawBtn.setAttribute('onclick', `resendChannelMessageRaw(${msgId}, this)`);
rawBtn.title = 'Resend (rebroadcast same packet so unreached repeaters can pick it up)';
rawBtn.innerHTML = '<i class="bi bi-arrow-repeat"></i>';
actionsEl.appendChild(rawBtn);
}
}
/**
* Copy text to clipboard with visual feedback
*/