fix: federation avatar colors, cross-instance DMs, and instance persistence

- Fix avatar gradient colors in 7 UI sites by using homeUserId fallback
- Merge remote DM channels in populateFromReady (no longer home-only)
- Remove isHome guards from 8 DM WS event handlers for cross-instance DMs
- Route DM typing events to correct instance origin
- Fix autoConnectAll deleting disconnected instances from localStorage
- Distinguish network errors from auth errors in instance reconnection
- Start WebSocket retry for network-unreachable instances (auto-recovery)
- Add reconnectInstance method and Reconnect button in UI
This commit is contained in:
Jannis Braun
2026-03-04 18:46:16 +01:00
parent 6e44a4ef2f
commit 33ae79bae9
11 changed files with 133 additions and 43 deletions
@@ -237,7 +237,7 @@ function FriendItem({ friend, onRemove, onDm }: { friend: TaggedFriend, onRemove
return (
<div className="flex items-center justify-between px-3 h-[62px] rounded-[8px] hover:bg-interactive-hover group transition-colors border-t border-interactive-muted mx-2">
<div className="flex items-center gap-3">
<Avatar src={friend.avatar} name={friend.displayName ?? friend.username} size={32} status={friend.status} userId={friend.id} />
<Avatar src={friend.avatar} name={friend.displayName ?? friend.username} size={32} status={friend.status} userId={friend.homeUserId ?? friend.id} />
<div className="flex flex-col leading-tight">
<div className="flex items-center gap-1.5">
<span className="text-txt-primary font-semibold text-[15px]">{friend.displayName ?? friend.username}</span>
@@ -289,7 +289,7 @@ function RequestItem({ request, type, onAccept, onDecline, onCancel }: {
return (
<div className="flex items-center justify-between px-3 py-2.5 rounded-lg hover:bg-interactive-hover group transition-colors border-t border-interactive-muted mx-2">
<div className="flex items-center gap-3">
<Avatar src={user.avatar} name={user.displayName ?? user.username} size={32} status={user.status as any} userId={user.id} />
<Avatar src={user.avatar} name={user.displayName ?? user.username} size={32} status={user.status as any} userId={user.homeUserId ?? user.id} />
<div className="flex flex-col">
<div className="flex items-center gap-1.5">
<span className="text-txt-primary font-bold text-sm">{user.displayName ?? user.username}</span>
@@ -71,7 +71,7 @@ export function MentionPopover({ query, selectedIndex, onSelect }: MentionPopove
name={displayName}
size={24}
status={member.user.status}
userId={member.user.id}
userId={member.user.homeUserId ?? member.user.id}
/>
<span
className="text-[14px] font-medium truncate"
@@ -59,7 +59,7 @@ export function MessageInput({ channelId, channelName }: MessageInputProps) {
if (typingTimeoutRef.current) return;
const isDm = isDmChannel(channelId);
if (isDm) {
wsSend({ type: 'dm_typing_start', dmChannelId: channelId });
wsSend({ type: 'dm_typing_start', dmChannelId: channelId }, getChannelOrigin(channelId));
} else {
wsSend({ type: 'typing_start', channelId }, getChannelOrigin(channelId));
}