From f79575ffa7e762f29c893957858f66b754704726 Mon Sep 17 00:00:00 2001 From: Jannis Braun <151788261+TheZwiss@users.noreply.github.com> Date: Mon, 2 Mar 2026 01:16:58 +0100 Subject: [PATCH] fix: consistent avatar gradient colors across all 17 call sites Avatar fallback gradients were hashed by display name alone when no user prop was passed, causing the same person to appear in different colors across messages, replies, member lists, voice panels, etc. Added userId prop to Avatar and wired it through all 17 call sites so the gradient always hashes by user ID. --- packages/web/src/components/chat/FriendsPage.tsx | 4 ++-- packages/web/src/components/chat/MentionPopover.tsx | 1 + packages/web/src/components/chat/Message.tsx | 2 +- packages/web/src/components/chat/MessageList.tsx | 2 +- packages/web/src/components/layout/ActivityPanel.tsx | 1 + packages/web/src/components/layout/ChannelSidebar.tsx | 4 ++-- packages/web/src/components/layout/MemberSidebar.tsx | 1 + packages/web/src/components/modals/AddDmMemberModal.tsx | 2 +- packages/web/src/components/modals/NewDmModal.tsx | 2 +- packages/web/src/components/modals/ServerSettings.tsx | 1 + packages/web/src/components/modals/UserSettings.tsx | 1 + packages/web/src/components/ui/Avatar.tsx | 5 +++-- packages/web/src/components/voice/PictureInPicture.tsx | 1 + packages/web/src/components/voice/StreamTile.tsx | 2 +- packages/web/src/components/voice/VoiceChannel.tsx | 1 + packages/web/src/components/voice/VoiceUser.tsx | 1 + 16 files changed, 20 insertions(+), 11 deletions(-) diff --git a/packages/web/src/components/chat/FriendsPage.tsx b/packages/web/src/components/chat/FriendsPage.tsx index 2b3cd0eb..8e5be27e 100644 --- a/packages/web/src/components/chat/FriendsPage.tsx +++ b/packages/web/src/components/chat/FriendsPage.tsx @@ -231,7 +231,7 @@ function FriendItem({ friend, onRemove, onDm }: { friend: Friend, onRemove: () = return (
- +
{friend.displayName ?? friend.username} @@ -277,7 +277,7 @@ function RequestItem({ request, type, onAccept, onDecline, onCancel }: { return (
- +
{user.displayName ?? user.username} diff --git a/packages/web/src/components/chat/MentionPopover.tsx b/packages/web/src/components/chat/MentionPopover.tsx index 1b745dc6..b2579ea1 100644 --- a/packages/web/src/components/chat/MentionPopover.tsx +++ b/packages/web/src/components/chat/MentionPopover.tsx @@ -71,6 +71,7 @@ export function MentionPopover({ query, selectedIndex, onSelect }: MentionPopove name={displayName} size={24} status={member.user.status} + userId={member.user.id} /> {message.replyTo && (
- +
- +

{displayName}

diff --git a/packages/web/src/components/layout/ActivityPanel.tsx b/packages/web/src/components/layout/ActivityPanel.tsx index c8a1a4e5..698217b3 100644 --- a/packages/web/src/components/layout/ActivityPanel.tsx +++ b/packages/web/src/components/layout/ActivityPanel.tsx @@ -54,6 +54,7 @@ export function ActivityPanel() { size={32} status={isOffline ? 'offline' : friend.status} className={isOffline ? 'opacity-60' : undefined} + userId={friend.id} />

diff --git a/packages/web/src/components/layout/ChannelSidebar.tsx b/packages/web/src/components/layout/ChannelSidebar.tsx index 47cb5827..029f75c7 100644 --- a/packages/web/src/components/layout/ChannelSidebar.tsx +++ b/packages/web/src/components/layout/ChannelSidebar.tsx @@ -215,12 +215,12 @@ export function ChannelSidebar() { zIndex: 2 - i, }} > - +
))}
) : ( - + )}
- +
{user.displayName ?? user.username} diff --git a/packages/web/src/components/modals/NewDmModal.tsx b/packages/web/src/components/modals/NewDmModal.tsx index f47ed057..36c04110 100644 --- a/packages/web/src/components/modals/NewDmModal.tsx +++ b/packages/web/src/components/modals/NewDmModal.tsx @@ -100,7 +100,7 @@ export function NewDmModal() { onClick={() => handleSelectUser(user)} className="w-full flex items-center gap-3 px-3 py-2 rounded-[4px] hover:bg-interactive-hover transition-colors text-left" > - +
{user.displayName ?? user.username} diff --git a/packages/web/src/components/modals/ServerSettings.tsx b/packages/web/src/components/modals/ServerSettings.tsx index 9b4f9323..b6ff1849 100644 --- a/packages/web/src/components/modals/ServerSettings.tsx +++ b/packages/web/src/components/modals/ServerSettings.tsx @@ -428,6 +428,7 @@ export function ServerSettingsModal() { name={displayName} size={32} status={member.user.status} + user={member.user} />
{displayName}
diff --git a/packages/web/src/components/modals/UserSettings.tsx b/packages/web/src/components/modals/UserSettings.tsx index 093f393f..baee273b 100644 --- a/packages/web/src/components/modals/UserSettings.tsx +++ b/packages/web/src/components/modals/UserSettings.tsx @@ -64,6 +64,7 @@ export function UserSettingsModal() { name={user.displayName ?? user.username} size={64} status={user.status} + userId={user.id} />
{user.displayName ?? user.username}
diff --git a/packages/web/src/components/ui/Avatar.tsx b/packages/web/src/components/ui/Avatar.tsx index 3c5105b0..f970419a 100644 --- a/packages/web/src/components/ui/Avatar.tsx +++ b/packages/web/src/components/ui/Avatar.tsx @@ -11,6 +11,7 @@ interface AvatarProps { className?: string; onClick?: (e: React.MouseEvent) => void; user?: User; + userId?: string; } const statusColors: Record = { @@ -20,11 +21,11 @@ const statusColors: Record = { offline: 'bg-status-offline', }; -export function Avatar({ src, name, size = 40, status, className = '', onClick, user }: AvatarProps) { +export function Avatar({ src, name, size = 40, status, className = '', onClick, user, userId }: AvatarProps) { const openUserProfile = useUIStore((s) => s.openUserProfile); const initials = name.charAt(0).toUpperCase(); const fontSize = size < 32 ? 'text-xs' : size < 48 ? 'text-sm' : 'text-lg'; - const gradient = getAvatarGradient(user?.id, name); + const gradient = getAvatarGradient(userId ?? user?.id, name); const handleClick = (e: React.MouseEvent) => { if (onClick) { diff --git a/packages/web/src/components/voice/PictureInPicture.tsx b/packages/web/src/components/voice/PictureInPicture.tsx index d5433597..fd7ff3c7 100644 --- a/packages/web/src/components/voice/PictureInPicture.tsx +++ b/packages/web/src/components/voice/PictureInPicture.tsx @@ -269,6 +269,7 @@ export function PictureInPicture() { {speakingParticipantIds.has(displayParticipant.identity) && (
diff --git a/packages/web/src/components/voice/StreamTile.tsx b/packages/web/src/components/voice/StreamTile.tsx index 2f2ba951..b9dc25a9 100644 --- a/packages/web/src/components/voice/StreamTile.tsx +++ b/packages/web/src/components/voice/StreamTile.tsx @@ -146,7 +146,7 @@ export function StreamTile({ tile, large }: StreamTileProps) { ) : (
- +

diff --git a/packages/web/src/components/voice/VoiceChannel.tsx b/packages/web/src/components/voice/VoiceChannel.tsx index e66c9ba2..11854d2b 100644 --- a/packages/web/src/components/voice/VoiceChannel.tsx +++ b/packages/web/src/components/voice/VoiceChannel.tsx @@ -67,6 +67,7 @@ export function VoiceChannel({ channelId, channelName, onClick }: VoiceChannelPr name={displayName} size={20} status={status} + userId={userId} /> {displayName} {/* Status badges */} diff --git a/packages/web/src/components/voice/VoiceUser.tsx b/packages/web/src/components/voice/VoiceUser.tsx index f32618f8..d538ec7c 100644 --- a/packages/web/src/components/voice/VoiceUser.tsx +++ b/packages/web/src/components/voice/VoiceUser.tsx @@ -95,6 +95,7 @@ export function VoiceUser({ tile, large }: VoiceUserProps) { src={null} name={participant.username} size={large ? 100 : 64} + userId={participant.userId} /> {isSpeaking && (