fix: repair corrupted group DMs, prevent ownerId nulling, show empty groups

Three fixes for group DM data integrity and display:

1. processOwnershipTransferEvent: use resolveOrCreateReplicatedUser instead
   of resolveLocalUser to guarantee a valid ownerId. The previous ?? null
   fallback converted group DMs into 1-on-1s when resolution failed.

2. Self-healing migration: detect group DMs with UUID-format federated_id
   but NULL owner_id (corrupted by the old fallback) and restore owner from
   the first remaining member. Found and repaired 7 across both instances.

3. Sidebar: group DMs with 0 other members (last person standing) now show
   as "Empty Group" instead of being hidden. 1-on-1 DMs with 0 others are
   still correctly filtered out.
This commit is contained in:
Jannis Braun
2026-03-27 17:38:09 +01:00
parent 44b6317c16
commit 41658ec2ef
4 changed files with 51 additions and 10 deletions
@@ -489,14 +489,16 @@ export function ChannelSidebar() {
<div className="space-y-[2px]">
{dmChannels.map((dm) => {
const otherMembers = dm.members.filter(m => !isSelf(m, user));
if (otherMembers.length === 0) return null;
const isGroup = !!dm.ownerId;
if (otherMembers.length === 0 && !isGroup) return null;
const isDmUnread = unreadChannels.has(dm.id) && currentChannelId !== dm.id;
const firstOtherDm = isGroup ? null : otherMembers[0];
const { baseName: dmBaseName, domain: dmDomain } = parseFederatedUsername(firstOtherDm?.username ?? '');
const dmDisplayName = isGroup
? otherMembers.map(m => m.displayName ?? parseFederatedUsername(m.username).baseName).join(', ')
? (otherMembers.length > 0
? otherMembers.map(m => m.displayName ?? parseFederatedUsername(m.username).baseName).join(', ')
: 'Empty Group')
: firstOtherDm?.displayName ?? dmBaseName;
const dmItem = (