feat: stateless federated identity resolver + federation UX improvements

Add identity.ts with isSelf() and resolveDisplayIdentity() — pure
stateless functions that detect replicated-self using the immutable
(username, homeInstance) composite key. No store lookups, no data
mutation. Fixes wrong avatar gradient and missing edit/delete on
own messages in remote channels.

Also includes: optimistic message dedup fix for cross-instance
messages (content-only matching), federation toast notifications,
Username component with @domain display, invite parser, and
deploy script simplification.
This commit is contained in:
Jannis Braun
2026-03-04 03:37:49 +01:00
parent 64fd8edfc9
commit dca9c4dc83
19 changed files with 713 additions and 110 deletions
+8 -4
View File
@@ -312,9 +312,12 @@ export const useChatStore = create<ChatState>((set, get) => ({
const current = newMessages.get(channelId) ?? [];
// Avoid duplicates
if (current.find(m => m.id === message.id)) return state;
// Remove any optimistic temp message from same user with same content
// Remove any optimistic temp message with same content.
// Don't require userId match — for federated messages the home user ID
// differs from the replicated user ID, but content match is sufficient
// since temp messages are unique within the short optimistic window.
const filtered = current.filter(m => {
if (!m.id.startsWith('temp_') || m.userId !== message.userId) return true;
if (!m.id.startsWith('temp_')) return true;
return m.content !== message.content;
});
let updated = [...filtered, message];
@@ -333,9 +336,10 @@ export const useChatStore = create<ChatState>((set, get) => ({
const current = newMessages.get(channelId) ?? [];
// Avoid duplicates
if (current.find(m => m.id === message.id)) return state;
// Remove any optimistic temp message from same user with same content
// Remove any optimistic temp message with same content (no userId check —
// federated messages arrive with a different replicated user ID)
const filtered = current.filter(m => {
if (!m.id.startsWith('temp_') || m.userId !== message.userId) return true;
if (!m.id.startsWith('temp_')) return true;
return m.content !== message.content;
});
let updated = [...filtered, message];