- disconnectInstance: save cached tokens BEFORE filtering the instance out, so
reconnectInstance can restore disconnected entries from localStorage
- reconnectInstance: if instance is not in active instances array, attempt to
restore it from cached localStorage token before proceeding with reconnect
- reconnectInstance: update registry to unreachable/auth_expired on failure
- forceRemoveEntry: now tears down WS, removes from instances array, purges
token from localStorage, and cleans up space store (was registry-only before)
removeChannelStates cleared messages and readStates but not hasMore.
When a closed DM was reopened with the same channel ID, loadMessages
saw the stale hasMore entry and skipped loading — showing an empty
chat. Now clears hasMore so messages reload on reopen.
Broadcasts dm_typing_stop to DM members before dm_message_created,
so the typing indicator clears immediately when a message arrives
instead of lingering for up to 3 seconds after delivery.
DMs now come exclusively from the home instance's ready payload.
Remote origins' DM channels are ignored. The deduplication logic
that preferred home copies over remote copies is no longer needed.
Removed dead loadDmChannels() (zero call sites).
All DM creation and add-member call sites now use the home api client
and pass homeUserId/homeInstance instead of routing to the remote instance.
Also updates addMember in the API client to accept AddDmMemberRequest.
- Resolve homeUserId from DB in sendFederatedCallStart/End (not raw userId)
- Clear existing timeout in createFederatedCall before overwriting
- Clear federatedCallToken/Url in leaveVoice and handleForceDisconnect
- Remove unnecessary `as any` cast in relay processor
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.
- Remove redundant `leaveGroup` API method from client.ts (duplicated `leave`); update MessageList.tsx WelcomeHeader to call `api.dm.leave` directly
- Add optional `type` field to shared `Message` interface so `MessageWithUser` carries it; remove `(msg as any).type` casts in `isSameGroup` and the render branch in MessageList.tsx
- Fix `processOwnershipTransferEvent` in federation.ts: replace `channel.ownerId` fallbacks (pre-update, old owner) with `event.ownership.newOwner.homeUserId` in the db update, dm_owner_updated broadcast, and both system message content payloads
- Render system messages (member_added, member_removed, owner_changed) inline
in MessageList with icon + human-readable text; system messages never group
with adjacent user messages
- Rewrite WelcomeHeader to branch on ownerId: group DMs show overlapping avatars,
group name, creator attribution, federated privacy note, and a Leave Group button
- Add dm_owner_updated ServerEvent; broadcast from dm.ts leave handler and
federation processOwnershipTransferEvent so all clients update ownerId in real-time
- Add updateDmOwner action to spaceStore and handle dm_owner_updated in useWebSocket
- Add leaveGroup alias to API client dm namespace
- Use inline badges for file/audio attachments (next to file size)
instead of absolute positioning which overflowed the container
- Keep overlay badges for images/video (overflow-hidden containers)
- Match tooltip text to toast notification text for consistency
- Fix critical: outbox worker now copies file_rejected payload fields
(attachmentId, sourceFilename, rejectionReason, rejectionLimit,
affectedUserIds) so the reverse relay actually delivers them
- Fix: add sourceFilename to file_rejected event for reliable
multi-attachment matching on the sender side
- Fix: change text-accent-warning to text-accent-amber (valid class)
- Add Array.isArray guard on federationMeta parse
The leave-group-DM ConfirmDialog in the space/channel sidebar return
path was unreachable — group DMs only render in the DM view branch,
so setLeaveGroupDmId is never called from the space view.
The X button on group DMs now opens a ConfirmDialog instead of
soft-closing. The right-click 'Leave Group' context menu also
routes through the same confirmation. Prevents dead group DM data
from accumulating when users soft-close instead of leaving.
members.length > 2 caused group DMs to lose their identity when a
member left and only 2 remained. ownerId is the canonical marker set
at group creation and persists through the group's lifecycle.
Federated 1-on-1 DMs showed the raw snowflake ID as the display name
and no avatar when the remote user had no pre-existing local record.
processCreateEvent used resolveLocalUser (find-only) instead of
resolveOrCreateReplicatedUser, and relay events carried no profile data
for participants.
- Add profile snapshot (displayName, avatar, avatarColor) to
FederationRelayParticipant and populate it in getDmParticipants
- Change processCreateEvent to auto-create replicated user stubs and
hydrate them with profile data from the relay event
- Fix hydrateReplicatedUserProfile URL resolution for homeInstance
values without protocol prefix
- Fix WelcomeHeader: return null while DM data is loading (eliminates
"unknown" flash on reload), use displayName for @mention text
Federated relay messages can have local snowflake IDs that don't match
chronological (createdAt) order — a message sent earlier on a remote
instance can arrive later and get a higher local ID. This caused a
permanent mismatch between the ready event's lastMessage (MAX id) and
the acked message (last in createdAt display order), making federated
DM channels appear unread after every server restart.
- Server: change ready event DM lastMessage query from MAX(id) to
ORDER BY created_at DESC (matching the DM REST API)
- Frontend: change ackChannel to ack MAX(id) among loaded messages
instead of last in display order (consistent with server comparison)
- Fix federated username display fallback in UserDiscoverCard
When converting a 1-on-1 DM to a group, the existing DM partner was
incorrectly required to be your friend. DMs don't require friendship,
so this check was over-strict. Added fromDmChannelId parameter to
createGroup — existing members of the source DM are exempt from the
friendship validation.
Replace global user search with socialStore friends list, fixing
federation ID mismatch that caused false 'not your friend' errors.
Add multi-select with chips, immediate friend list display, and
client-side search filtering.