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:
@@ -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));
|
||||
}
|
||||
|
||||
@@ -122,7 +122,7 @@ export function AddDmMemberModal() {
|
||||
disabled={isAdding}
|
||||
className="w-full flex items-center gap-3 px-3 py-2 rounded-[4px] hover:bg-interactive-hover transition-colors text-left disabled:opacity-50"
|
||||
>
|
||||
<Avatar src={user.avatar} name={user.displayName ?? user.username} size={36} status={user.status as any} userId={user.id} />
|
||||
<Avatar src={user.avatar} name={user.displayName ?? user.username} size={36} status={user.status as any} userId={user.homeUserId ?? user.id} />
|
||||
<div className="flex-1 min-w-0">
|
||||
<div className="text-[14px] font-medium text-txt-primary truncate">
|
||||
{user.displayName ?? user.username}
|
||||
|
||||
@@ -265,6 +265,7 @@ function AddInstanceFlow({ onDone }: { onDone: () => void }) {
|
||||
export function ConnectedInstances() {
|
||||
const instances = useInstanceStore((s) => s.instances);
|
||||
const removeInstance = useInstanceStore((s) => s.removeInstance);
|
||||
const reconnectInstance = useInstanceStore((s) => s.reconnectInstance);
|
||||
const [showAddForm, setShowAddForm] = useState(false);
|
||||
|
||||
return (
|
||||
@@ -312,13 +313,24 @@ export function ConnectedInstances() {
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<button
|
||||
onClick={() => removeInstance(inst.origin)}
|
||||
className="px-2 py-1 text-xs text-txt-danger hover:bg-accent-rose/10 rounded transition-colors shrink-0 ml-2"
|
||||
title="Disconnect"
|
||||
>
|
||||
Disconnect
|
||||
</button>
|
||||
<div className="flex items-center gap-1 shrink-0 ml-2">
|
||||
{(inst.status === 'disconnected' || inst.status === 'error') && (
|
||||
<button
|
||||
onClick={() => reconnectInstance(inst.origin)}
|
||||
className="px-2 py-1 text-xs text-accent-primary hover:bg-accent-primary/10 rounded transition-colors"
|
||||
title="Reconnect"
|
||||
>
|
||||
Reconnect
|
||||
</button>
|
||||
)}
|
||||
<button
|
||||
onClick={() => removeInstance(inst.origin)}
|
||||
className="px-2 py-1 text-xs text-txt-danger hover:bg-accent-rose/10 rounded transition-colors"
|
||||
title="Disconnect"
|
||||
>
|
||||
Disconnect
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
|
||||
|
||||
@@ -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"
|
||||
>
|
||||
<Avatar src={user.avatar} name={user.displayName ?? user.username} size={36} status={user.status as any} userId={user.id} />
|
||||
<Avatar src={user.avatar} name={user.displayName ?? user.username} size={36} status={user.status as any} userId={user.homeUserId ?? user.id} />
|
||||
<div className="flex-1 min-w-0">
|
||||
<div className="text-[14px] font-medium text-txt-primary truncate">
|
||||
{user.displayName ?? user.username}
|
||||
|
||||
@@ -414,7 +414,7 @@ function JoinRequestsSection({ serverId }: { serverId: string }) {
|
||||
src={user?.avatar}
|
||||
name={displayName}
|
||||
size={32}
|
||||
userId={user?.id}
|
||||
userId={user?.homeUserId ?? user?.id}
|
||||
/>
|
||||
<div className="flex-1 min-w-0">
|
||||
<div className="flex items-center gap-1.5">
|
||||
|
||||
@@ -65,7 +65,7 @@ export function UserSettingsModal() {
|
||||
name={user.displayName ?? user.username}
|
||||
size={64}
|
||||
status={user.status}
|
||||
userId={user.id}
|
||||
userId={user.homeUserId ?? user.id}
|
||||
/>
|
||||
<div>
|
||||
<div className="font-bold text-lg">{user.displayName ?? user.username}</div>
|
||||
|
||||
Reference in New Issue
Block a user