From 9d3f75b33c1122d12af75fcfc44f672f2a77d038 Mon Sep 17 00:00:00 2001 From: Jannis Braun <151788261+TheZwiss@users.noreply.github.com> Date: Sat, 25 Apr 2026 22:29:45 +0200 Subject: [PATCH] feat(web): WS handlers for friend_request_sent + friend_request_relay_failed Multi-tab sync: friend_request_sent appends the new outbound request to socialStore (deduped by id+origin), no toast. Async rollback: friend_request_relay_failed removes the row by id and surfaces a warning toast with the target handle and reason. Wires the client side of the rollback hook from T10. --- packages/web/src/hooks/useWebSocket.ts | 21 +++++++++++++++++++++ packages/web/src/stores/socialStore.ts | 12 ++++++++++++ 2 files changed, 33 insertions(+) diff --git a/packages/web/src/hooks/useWebSocket.ts b/packages/web/src/hooks/useWebSocket.ts index 3a5b537c..760f2279 100644 --- a/packages/web/src/hooks/useWebSocket.ts +++ b/packages/web/src/hooks/useWebSocket.ts @@ -862,6 +862,27 @@ function handleEvent(origin: string, event: ServerEvent): void { break; } + case 'friend_request_sent': { + // Multi-tab sync: another tab/device of the same user just created an outbound request. + if (!isHome && event.request.user) normalizeUserAssets(event.request.user, origin); + const { addOutboundRequest } = useSocialStore.getState(); + addOutboundRequest(event.request, origin); + break; + } + + case 'friend_request_relay_failed': { + // Async rollback notification: the federated friend_request_create was permanently rejected. + // Drop the optimistic row and surface a warning toast. + const { removeRequestById } = useSocialStore.getState(); + removeRequestById(event.requestId, origin); + const { addToast } = useUIStore.getState(); + addToast( + `Friend request to ${event.targetHandle} could not be delivered: ${event.message}`, + 'warning', + ); + break; + } + case 'friend_request_accepted': { if (!isHome) normalizeUserAssets(event.friend, origin); const { addFriendFromAccepted } = useSocialStore.getState(); diff --git a/packages/web/src/stores/socialStore.ts b/packages/web/src/stores/socialStore.ts index 22eda4f6..2553cd4e 100644 --- a/packages/web/src/stores/socialStore.ts +++ b/packages/web/src/stores/socialStore.ts @@ -57,6 +57,7 @@ interface SocialState { removeFriend: (id: string) => Promise; searchUsers: (query: string) => Promise; addIncomingRequest: (request: FriendRequest, origin: string) => void; + addOutboundRequest: (request: FriendRequest, origin: string) => void; addFriendFromAccepted: (friend: Friend, requestId: string, origin: string) => void; updateFriendPresence: (userId: string, status: string) => void; updateFriendProfile: (user: User) => void; @@ -343,6 +344,17 @@ export const useSocialStore = create((set, get) => ({ }); }, + // Called from WS handler for multi-tab sync when this user creates an outbound request + addOutboundRequest: (request: FriendRequest, origin: string) => { + set((state) => { + const canonicalId = request.user?.homeUserId ?? request.user?.id; + if (canonicalId && state.requests.some(r => (r.user?.homeUserId ?? r.user?.id) === canonicalId)) { + return state; + } + return { requests: [...state.requests, { ...request, _instanceOrigin: origin }] }; + }); + }, + // Called from WS handler when someone accepts your friend request addFriendFromAccepted: (friend: Friend, requestId: string, origin: string) => { set((state) => {