fix: persist homeUserId for federated users to ensure consistent avatar colors

Store the original home snowflake ID (homeUserId) during federation replication
so that avatar gradient colors resolve identically across instances. Previously,
replicated users got new snowflake IDs on each instance, causing different
gradient colors. Now Avatar, UserProfilePopout, VoiceUser, StreamTile, and
VoiceChannel all resolve through homeUserId when available. Includes backfill
logic for existing federated users missing the field.
This commit is contained in:
Jannis Braun
2026-03-04 13:45:25 +01:00
parent 9432f81ba3
commit c7c3331dbf
15 changed files with 47 additions and 22 deletions
@@ -1,7 +1,5 @@
import React from 'react';
import { useVoiceStore } from '../../stores/voiceStore';
import { useAuthStore } from '../../stores/authStore';
import { isSelf } from '../../utils/identity';
const EMPTY_VOICE_USERS: string[] = [];
import { useServerStore } from '../../stores/serverStore';
@@ -20,8 +18,11 @@ export function VoiceChannel({ channelId, channelName, onClick }: VoiceChannelPr
const localIsDeafened = useVoiceStore((s) => s.isDeafened);
const localIsMuted = useVoiceStore((s) => s.isMuted);
const voiceUserStates = useVoiceStore((s) => s.voiceUserStates);
const authUser = useAuthStore((s) => s.user);
const currentUserId = authUser?.id;
const currentUserId = useVoiceStore((s) => {
// Derive from participants — avoids unnecessary authStore dependency
const local = s.participants.find(p => p.isLocal);
return local?.userId ?? null;
});
const members = useServerStore((s) => s.members);
const isActive = currentVoiceChannel === channelId;
@@ -75,7 +76,7 @@ export function VoiceChannel({ channelId, channelName, onClick }: VoiceChannelPr
name={displayName}
size={24}
status={status}
userId={(authUser && member?.user && isSelf(member.user, authUser)) ? authUser.id : userId}
userId={member?.user.homeUserId ?? userId}
/>
<span className="text-[13px] text-txt-secondary truncate flex-1 min-w-0">{displayName}</span>
{/* Status badges */}