Files
ncd-fe/packages/pl-fe/src/queries/accounts/use-relationship.ts
nicole mikołajczyk 72d86fd62d pl-fe: batch relationships requests
Signed-off-by: nicole mikołajczyk <git@mkljczk.pl>
2025-10-23 19:47:13 +02:00

270 lines
9.7 KiB
TypeScript

import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import { ACCOUNT_BLOCK_SUCCESS, ACCOUNT_MUTE_SUCCESS, type AccountsAction } from 'pl-fe/actions/accounts';
import { batcher } from 'pl-fe/api/batcher';
import { useAppDispatch } from 'pl-fe/hooks/use-app-dispatch';
import { useClient } from 'pl-fe/hooks/use-client';
import { useLoggedIn } from 'pl-fe/hooks/use-logged-in';
import type { MinifiedSuggestion } from '../trends/use-suggested-accounts';
import type { FollowAccountParams, MuteAccountParams, Relationship } from 'pl-api';
const updateRelationship = (accountId: string, changes: Partial<Relationship> | ((relationship: Relationship) => Relationship), queryClient: ReturnType<typeof useQueryClient>) => {
const previousRelationship = queryClient.getQueryData<Relationship>(['accountRelationships', accountId]);
if (!previousRelationship) return;
const newRelationship = typeof changes === 'function' ? changes(previousRelationship) : { ...previousRelationship, ...changes };
queryClient.setQueryData(['accountRelationships', accountId], newRelationship);
return { previousRelationship };
};
const restorePreviousRelationship = (
accountId: string,
context: { previousRelationship?: Relationship } | undefined,
queryClient: ReturnType<typeof useQueryClient>,
) => {
if (context?.previousRelationship) {
queryClient.setQueryData(['accountRelationships', accountId], context.previousRelationship);
}
};
const useRelationshipQuery = (accountId?: string) => {
const client = useClient();
const { isLoggedIn } = useLoggedIn();
return useQuery({
queryKey: ['accountRelationships', accountId],
queryFn: () => batcher.relationships(client).fetch(accountId!).then((data) => data || undefined),
enabled: isLoggedIn && !!accountId,
});
};
const useFollowAccountMutation = (accountId: string) => {
const client = useClient();
const queryClient = useQueryClient();
return useMutation({
mutationKey: ['accountRelationships', accountId],
mutationFn: (params?: FollowAccountParams) => client.accounts.followAccount(accountId, params),
onMutate: (params) => {
return updateRelationship(accountId, (relationship) => ({
...relationship,
requested: !relationship.following,
notifying: params?.notify ?? relationship.notifying,
showing_reblogs: params?.reblogs ?? relationship.showing_reblogs,
}), queryClient);
},
onError: (_err, _variables, context) => restorePreviousRelationship(accountId, context, queryClient),
onSuccess: (data) => {
queryClient.setQueryData(['accountRelationships', accountId], data);
},
});
};
const useUnfollowAccountMutation = (accountId: string) => {
const client = useClient();
const queryClient = useQueryClient();
return useMutation({
mutationKey: ['accountRelationships', accountId],
mutationFn: () => client.accounts.unfollowAccount(accountId),
onMutate: () => updateRelationship(accountId, {
following: false,
requested: false,
notifying: false,
showing_reblogs: false,
}, queryClient),
onError: (_err, _variables, context) => restorePreviousRelationship(accountId, context, queryClient),
onSuccess: (data) => {
queryClient.setQueryData(['accountRelationships', accountId], data);
},
});
};
const useBlockAccountMutation = (accountId: string) => {
const client = useClient();
const queryClient = useQueryClient();
const dispatch = useAppDispatch();
return useMutation({
mutationKey: ['accountRelationships', accountId],
mutationFn: () => client.filtering.blockAccount(accountId),
onMutate: () => updateRelationship(accountId, {
blocking: true,
followed_by: false,
following: false,
notifying: false,
requested: false,
}, queryClient),
onError: (_err, _variables, context) => restorePreviousRelationship(accountId, context, queryClient),
onSuccess: (data) => {
queryClient.setQueryData(['accountRelationships', accountId], data);
queryClient.setQueryData<Array<MinifiedSuggestion>>(['suggestions'], suggestions => suggestions
? suggestions.filter((suggestion) => suggestion.account_id !== accountId)
: undefined);
// Pass in entire statuses map so we can use it to filter stuff in different parts of the reducers
return dispatch<AccountsAction>((dispatch, getState) => dispatch({
type: ACCOUNT_BLOCK_SUCCESS,
relationship: data,
statuses: getState().statuses,
}));
},
});
};
const useUnblockAccountMutation = (accountId: string) => {
const client = useClient();
const queryClient = useQueryClient();
return useMutation({
mutationKey: ['accountRelationships', accountId],
mutationFn: () => client.filtering.unblockAccount(accountId),
onMutate: () => updateRelationship(accountId, {
blocking: true,
}, queryClient),
onError: (_err, _variables, context) => restorePreviousRelationship(accountId, context, queryClient),
onSuccess: (data) => {
queryClient.setQueryData(['accountRelationships', accountId], data);
},
});
};
const useMuteAccountMutation = (accountId: string) => {
const client = useClient();
const queryClient = useQueryClient();
const dispatch = useAppDispatch();
return useMutation({
mutationKey: ['accountRelationships', accountId],
mutationFn: (params?: MuteAccountParams) => client.filtering.muteAccount(accountId, params),
onMutate: () => updateRelationship(accountId, {
muting: true,
}, queryClient),
onError: (_err, _variables, context) => restorePreviousRelationship(accountId, context, queryClient),
onSuccess: (data) => {
queryClient.setQueryData(['accountRelationships', accountId], data);
queryClient.setQueryData<Array<MinifiedSuggestion>>(['suggestions'], suggestions => suggestions
? suggestions.filter((suggestion) => suggestion.account_id !== accountId)
: undefined);
// Pass in entire statuses map so we can use it to filter stuff in different parts of the reducers
return dispatch<AccountsAction>((dispatch, getState) => dispatch({
type: ACCOUNT_MUTE_SUCCESS,
relationship: data,
statuses: getState().statuses,
}));
},
});
};
const useUnmuteAccountMutation = (accountId: string) => {
const client = useClient();
const queryClient = useQueryClient();
return useMutation({
mutationKey: ['accountRelationships', accountId],
mutationFn: () => client.filtering.unmuteAccount(accountId),
onMutate: () => updateRelationship(accountId, {
muting: false,
}, queryClient),
onError: (_err, _variables, context) => restorePreviousRelationship(accountId, context, queryClient),
onSuccess: (data) => {
queryClient.setQueryData(['accountRelationships', accountId], data);
},
});
};
const usePinAccountMutation = (accountId: string) => {
const client = useClient();
const queryClient = useQueryClient();
const { me } = useLoggedIn();
return useMutation({
mutationKey: ['accountRelationships', accountId],
mutationFn: () => client.accounts.pinAccount(accountId),
onMutate: () => updateRelationship(accountId, {
endorsed: true,
}, queryClient),
onError: (_err, _variables, context) => restorePreviousRelationship(accountId, context, queryClient),
onSuccess: (data) => {
queryClient.setQueryData(['accountRelationships', accountId], data);
queryClient.invalidateQueries({
queryKey: ['accountsLists', 'endorsedAccounts', me],
});
},
});
};
const useUnpinAccountMutation = (accountId: string) => {
const client = useClient();
const queryClient = useQueryClient();
const { me } = useLoggedIn();
return useMutation({
mutationKey: ['accountRelationships', accountId],
mutationFn: () => client.accounts.unpinAccount(accountId),
onMutate: () => updateRelationship(accountId, {
endorsed: false,
}, queryClient),
onError: (_err, _variables, context) => restorePreviousRelationship(accountId, context, queryClient),
onSuccess: (data) => {
queryClient.setQueryData(['accountRelationships', accountId], data);
queryClient.invalidateQueries({
queryKey: ['accountsLists', 'endorsedAccounts', me],
});
},
});
};
const useRemoveAccountFromFollowersMutation = (accountId: string) => {
const client = useClient();
const queryClient = useQueryClient();
return useMutation({
mutationKey: ['accountRelationships', accountId],
mutationFn: () => client.accounts.removeAccountFromFollowers(accountId),
onMutate: () => updateRelationship(accountId, {
followed_by: false,
}, queryClient),
onError: (_err, _variables, context) => restorePreviousRelationship(accountId, context, queryClient),
onSuccess: (data) => {
queryClient.setQueryData(['accountRelationships', accountId], data);
},
});
};
const useUpdateAccountNoteMutation = (accountId: string) => {
const client = useClient();
const queryClient = useQueryClient();
return useMutation({
mutationKey: ['accountNote', accountId],
mutationFn: (note: string) => client.accounts.updateAccountNote(accountId, note),
onMutate: (note) => updateRelationship(accountId, {
note,
}, queryClient),
onError: (_err, _variables, context) => restorePreviousRelationship(accountId, context, queryClient),
onSuccess: (data) => {
queryClient.setQueryData(['accountRelationships', accountId], data);
},
});
};
export {
useRelationshipQuery,
useFollowAccountMutation,
useUnfollowAccountMutation,
useBlockAccountMutation,
useUnblockAccountMutation,
useMuteAccountMutation,
useUnmuteAccountMutation,
usePinAccountMutation,
useUnpinAccountMutation,
useRemoveAccountFromFollowersMutation,
useUpdateAccountNoteMutation,
};