feat(dm): read-only composer notice for Deleted-User 1-on-1 threads (C2)

This commit is contained in:
Jannis Braun
2026-07-02 16:20:38 +02:00
parent 2a0574ee6c
commit ae07ae66e4
5 changed files with 56 additions and 5 deletions
+17 -1
View File
@@ -1,5 +1,5 @@
import { describe, it, expect, vi, afterEach } from 'vitest';
import { formatDmTimestamp, formatDmPreview, formatDmSidebarPreview, formatDmHeaderName, formatDmInputLabel } from './dmFormatters';
import { formatDmTimestamp, formatDmPreview, formatDmSidebarPreview, formatDmHeaderName, formatDmInputLabel, isDeletedPartnerDm } from './dmFormatters';
import type { DmChannel, DmLastMessagePreview, User } from '@backspace/shared';
/** Build a local-time Date: new Date(year, month-1, day, hour, minute) as a timestamp. */
@@ -417,3 +417,19 @@ describe('formatDmSidebarPreview — icon_changed system message', () => {
expect(formatDmSidebarPreview(dm, { id: 'OTHER', username: 'other' })).toBe('Unknown updated the group icon');
});
});
describe('isDeletedPartnerDm', () => {
const me = { id: 'me', username: 'me' } as User;
it('true for a 1-on-1 whose only other member is deleted', () => {
const dm = { ownerId: null, members: [me, { id: 'x', username: 'Deleted User', isDeleted: true } as User] };
expect(isDeletedPartnerDm(dm as any, me)).toBe(true);
});
it('false for a live partner', () => {
const dm = { ownerId: null, members: [me, { id: 'x', username: 'p', isDeleted: false } as User] };
expect(isDeletedPartnerDm(dm as any, me)).toBe(false);
});
it('false for a group even with a deleted member', () => {
const dm = { ownerId: 'me', members: [me, { id: 'x', isDeleted: true } as User] };
expect(isDeletedPartnerDm(dm as any, me)).toBe(false);
});
});
+11
View File
@@ -314,3 +314,14 @@ export function formatDmInputLabel(dm: DmChannel, currentUser: AuthLike): string
if (!partner) return '@unknown';
return `@${memberDisplayName(partner) || 'unknown'}`;
}
/**
* True when `dm` is a 1-on-1 whose only other participant(s) are tombstoned.
* Drives the read-only composer — you cannot message a deleted user.
*/
export function isDeletedPartnerDm(dm: Pick<DmChannel, 'ownerId' | 'members'>, currentUser: AuthLike): boolean {
if (dm.ownerId) return false; // group
const others = dm.members.filter(m => !isSelf(m, currentUser));
if (others.length === 0) return false;
return others.every(m => m.isDeleted === true);
}