feat: optimistic remove — agent vanishes instantly from sidebar

Mirror of the attach optimistic update. onMutate filters the member
out of the cache immediately, rolls back on error, invalidates on
settle to reconcile with server.
This commit is contained in:
Taylor Ho
2026-05-21 19:54:51 -07:00
parent af3028628c
commit c65fd6e2b0

View File

@@ -29,12 +29,14 @@ import type {
AddChannelMembersInput,
Channel,
ChannelDetail,
ChannelMember,
CreateChannelInput,
OpenDmInput,
SetChannelPurposeInput,
SetChannelTopicInput,
UpdateChannelInput,
} from "@/shared/api/types";
import { normalizePubkey } from "@/shared/lib/pubkey";
export const channelsQueryKey = ["channels"] as const;
const channelDetailQueryKey = (channelId: string) =>
@@ -410,6 +412,25 @@ export function useRemoveChannelMemberMutation(channelId: string | null) {
await removeChannelMemberWithManagedAgentCleanup(channelId, pubkey);
},
onMutate: async (pubkey) => {
if (!channelId) return;
const membersKey = channelMembersQueryKey(channelId);
await queryClient.cancelQueries({ queryKey: membersKey });
const previous =
queryClient.getQueryData<ChannelMember[]>(membersKey) ?? [];
queryClient.setQueryData<ChannelMember[]>(
membersKey,
previous.filter(
(m) => normalizePubkey(m.pubkey) !== normalizePubkey(pubkey),
),
);
return { previous, membersKey };
},
onError: (_err, _vars, context) => {
if (context?.membersKey) {
queryClient.setQueryData(context.membersKey, context.previous);
}
},
onSettled: async () => {
await Promise.all([
invalidateChannelState(queryClient, channelId),