fix: real-time friend request cancel/decline via WebSocket

Cancelled requests now disappear from receiver's UI instantly, and
declined requests revert the sender's discover card from "Request
Pending" to "Send Friend Request" — no page refresh needed.

Also includes the discover endpoint and sendFriendRequest return type
changes from the prior session.
This commit is contained in:
Jannis Braun
2026-03-13 22:42:25 +01:00
parent d3101c4ba4
commit 9382477e33
4 changed files with 203 additions and 6 deletions
+14 -4
View File
@@ -26,7 +26,7 @@ interface SocialState {
error: string | null;
loadFriends: () => Promise<void>;
loadRequests: () => Promise<void>;
sendFriendRequest: (username: string) => Promise<void>;
sendFriendRequest: (username: string) => Promise<string | undefined>;
updateFriendRequest: (id: string, status: 'accepted' | 'declined') => Promise<void>;
cancelFriendRequest: (id: string) => Promise<void>;
removeFriend: (id: string) => Promise<void>;
@@ -36,6 +36,7 @@ interface SocialState {
updateFriendPresence: (userId: string, status: string) => void;
updateFriendProfile: (user: User) => void;
removeFriendLocally: (userId: string, origin: string) => void;
removeRequestById: (requestId: string, origin: string) => void;
reset: () => void;
}
@@ -117,10 +118,11 @@ export const useSocialStore = create<SocialState>((set, get) => ({
set({ isLoading: true, error: null });
try {
const atIndex = username.lastIndexOf('@');
let res: { success: boolean; requestId?: string };
if (atIndex === -1) {
// No @ → local user on home instance
await api.social.sendRequest(username);
res = await api.social.sendRequest(username);
} else {
const baseName = username.slice(0, atIndex);
const domain = username.slice(atIndex + 1);
@@ -128,7 +130,7 @@ export const useSocialStore = create<SocialState>((set, get) => ({
// Check if domain matches home instance
if (domain === window.location.host) {
// Strip domain, send to home API
await api.social.sendRequest(baseName);
res = await api.social.sendRequest(baseName);
} else {
// Find a connected instance matching this domain
const instances = useInstanceStore.getState().instances;
@@ -149,11 +151,12 @@ export const useSocialStore = create<SocialState>((set, get) => ({
}
// On the remote instance, the user is just "alice", not "alice@orbit"
await match.api.social.sendRequest(baseName);
res = await match.api.social.sendRequest(baseName);
}
}
await get().loadRequests();
return res.requestId;
} catch (err) {
set({ error: (err as Error).message, isLoading: false });
throw err;
@@ -281,6 +284,13 @@ export const useSocialStore = create<SocialState>((set, get) => ({
}));
},
// Called from WS handler when a friend request is cancelled or declined
removeRequestById: (requestId: string, origin: string) => {
set((state) => ({
requests: state.requests.filter(r => !(r.id === requestId && r._instanceOrigin === origin)),
}));
},
// Called from WS handler on presence_update to keep friend status live
updateFriendPresence: (userId: string, status: string) => {
set((state) => ({