diff --git a/src/js/ui/MessageContextMenuUI.js b/src/js/ui/MessageContextMenuUI.js index dd61fb3..17f9e75 100644 --- a/src/js/ui/MessageContextMenuUI.js +++ b/src/js/ui/MessageContextMenuUI.js @@ -291,7 +291,7 @@ class MessageContextMenuUI { const { dmManager } = this.deps; const dmErase = currentChannel?.type === 'dm' && !isSelf && !!msgId && (dmManager?.inboxPurgeProviders?.length > 0); - const showErase = dmErase || (moderates && !!msgId && !message?._erased + const showErase = dmErase || (moderates && !!msgId && !message?._erased && !message?._erasing && (currentChannel?.purgeProviders?.length > 0)); // Cannot ban yourself or the channel admin. const showBan = moderates && !isSelf && !isCreator; @@ -502,6 +502,13 @@ class MessageContextMenuUI { message: `Remove this message from ${providers} storage provider${providers === 1 ? '' : 's'}. It stays hidden for everyone and cannot be recovered.`, confirmLabel: 'Erase' })) break; + const redraw = () => { + if (chatAreaUI?.updateMessage?.(msg) === false) { + chatAreaUI?.renderMessages?.(ch.messages, () => chatAreaUI._attachMessageListeners?.()); + } + }; + msg._erasing = true; + redraw(); try { const { authManager } = await import('../auth.js'); const { eraseMessage } = await import('../storagePurge.js'); @@ -517,10 +524,12 @@ class MessageContextMenuUI { const outcome = await eraseMessage(ch, msg, signer, channelManager.purgeOptions?.(ch)); if (outcome.erasedOn > 0) msg._erased = true; showNotification(purgeOutcomeText(outcome), outcome.erasedOn === outcome.providers ? 'success' : 'warning'); - chatAreaUI?.renderMessages?.(ch.messages, () => chatAreaUI._attachMessageListeners?.()); } catch (err) { console.warn('Erase from storage failed:', err?.message || err); showNotification(err?.message || 'Failed to erase message', 'error'); + } finally { + delete msg._erasing; + redraw(); } break; } diff --git a/src/js/ui/MessageRenderer.js b/src/js/ui/MessageRenderer.js index 2d2aa4d..7bd24e6 100644 --- a/src/js/ui/MessageRenderer.js +++ b/src/js/ui/MessageRenderer.js @@ -831,8 +831,11 @@ class MessageRenderer { // A moderator's view of a hidden message: dimmed, labelled, still // reachable by the context menu for Unhide and Erase. + const erasingHtml = msg._erasing && !msg._erased + ? '' + : ''; const moderationHtml = msg._hidden - ? `
` + ? `` : ''; const bubbleStyle = msg._hidden ? ' style="opacity:0.45"' : ''; diff --git a/tests/unit/MessageRenderer.test.js b/tests/unit/MessageRenderer.test.js index bb03dd8..1e6aa3d 100644 --- a/tests/unit/MessageRenderer.test.js +++ b/tests/unit/MessageRenderer.test.js @@ -490,6 +490,25 @@ describe('MessageRenderer', () => { expect(result).toContain('12:00'); }); + it('shows an erase from storage in progress on a hidden message until it answers', () => { + const base = { id: 'msg-hidden', sender: '0xDEF', text: 'spam', type: 'text', _hidden: true }; + + const erasing = messageRenderer.buildMessageHTML( + { ...base, _erasing: true }, false, '12:00', { html: '' }, 'Bob' + ); + expect(erasing).toContain('Hidden by moderation