Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions src/js/ui/MessageContextMenuUI.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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');
Expand All @@ -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;
}
Expand Down
5 changes: 4 additions & 1 deletion src/js/ui/MessageRenderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
? '<span class="message-erasing inline-block animate-spin w-2.5 h-2.5 ml-1.5 align-middle border border-white/25 border-t-white/70 rounded-full" aria-label="Erasing from storage"></span>'
: '';
const moderationHtml = msg._hidden
? `<div class="message-moderation text-[11px] text-amber-400/80 mb-1">${msg._erased ? 'Hidden · erased from storage' : 'Hidden by moderation'}</div>`
? `<div class="message-moderation text-[11px] text-amber-400/80 mb-1">${msg._erased ? 'Hidden · erased from storage' : 'Hidden by moderation'}${erasingHtml}</div>`
: '';
const bubbleStyle = msg._hidden ? ' style="opacity:0.45"' : '';

Expand Down
19 changes: 19 additions & 0 deletions tests/unit/MessageRenderer.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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<span class="message-erasing');

const erased = messageRenderer.buildMessageHTML(
{ ...base, _erased: true }, false, '12:00', { html: '' }, 'Bob'
);
expect(erased).toContain('Hidden · erased from storage');
expect(erased).not.toContain('message-erasing');

const hidden = messageRenderer.buildMessageHTML(base, false, '12:00', { html: '' }, 'Bob');
expect(hidden).toContain('Hidden by moderation</div>');
expect(hidden).not.toContain('message-erasing');
});

it('says where an unsettled publish stands', () => {
const base = { id: 'msg-state', sender: '0xABC', text: 'Hello', type: 'text' };

Expand Down
Loading