Use react-query and zod for announcements
Signed-off-by: marcin mikołajczak <git@mkljczk.pl>
This commit is contained in:
89
src/api/hooks/admin/useAnnouncements.ts
Normal file
89
src/api/hooks/admin/useAnnouncements.ts
Normal file
@ -0,0 +1,89 @@
|
||||
import { useMutation, useQuery } from '@tanstack/react-query';
|
||||
|
||||
import { useApi } from 'soapbox/hooks';
|
||||
import { queryClient } from 'soapbox/queries/client';
|
||||
import { adminAnnouncementSchema, type AdminAnnouncement } from 'soapbox/schemas';
|
||||
|
||||
import { useAnnouncements as useUserAnnouncements } from '../announcements';
|
||||
|
||||
import type { AxiosResponse } from 'axios';
|
||||
|
||||
interface CreateAnnouncementParams {
|
||||
content: string;
|
||||
starts_at?: string | null;
|
||||
ends_at?: string | null;
|
||||
all_day?: boolean;
|
||||
}
|
||||
|
||||
interface UpdateAnnouncementParams extends CreateAnnouncementParams {
|
||||
id: string;
|
||||
}
|
||||
|
||||
const useAnnouncements = () => {
|
||||
const api = useApi();
|
||||
const userAnnouncements = useUserAnnouncements();
|
||||
|
||||
const getAnnouncements = async () => {
|
||||
const { data } = await api.get<AdminAnnouncement[]>('/api/v1/pleroma/admin/announcements');
|
||||
|
||||
const normalizedData = data.map((announcement) => adminAnnouncementSchema.parse(announcement));
|
||||
return normalizedData;
|
||||
};
|
||||
|
||||
const result = useQuery<ReadonlyArray<AdminAnnouncement>>({
|
||||
queryKey: ['admin', 'announcements'],
|
||||
queryFn: getAnnouncements,
|
||||
placeholderData: [],
|
||||
});
|
||||
|
||||
const {
|
||||
mutate: createAnnouncement,
|
||||
isPending: isCreating,
|
||||
} = useMutation({
|
||||
mutationFn: (params: CreateAnnouncementParams) => api.post('/api/v1/pleroma/admin/announcements', params),
|
||||
retry: false,
|
||||
onSuccess: ({ data }: AxiosResponse) =>
|
||||
queryClient.setQueryData(['admin', 'announcements'], (prevResult: ReadonlyArray<AdminAnnouncement>) =>
|
||||
[...prevResult, adminAnnouncementSchema.parse(data)],
|
||||
),
|
||||
onSettled: () => userAnnouncements.refetch(),
|
||||
});
|
||||
|
||||
const {
|
||||
mutate: updateAnnouncement,
|
||||
isPending: isUpdating,
|
||||
} = useMutation({
|
||||
mutationFn: ({ id, ...params }: UpdateAnnouncementParams) => api.patch(`/api/v1/pleroma/admin/announcements/${id}`, params),
|
||||
retry: false,
|
||||
onSuccess: ({ data }: AxiosResponse) =>
|
||||
queryClient.setQueryData(['admin', 'announcements'], (prevResult: ReadonlyArray<AdminAnnouncement>) =>
|
||||
prevResult.map((announcement) => announcement.id === data.id ? adminAnnouncementSchema.parse(data) : announcement),
|
||||
),
|
||||
onSettled: () => userAnnouncements.refetch(),
|
||||
});
|
||||
|
||||
const {
|
||||
mutate: deleteAnnouncement,
|
||||
isPending: isDeleting,
|
||||
} = useMutation({
|
||||
mutationFn: (id: string) => api.delete(`/api/v1/pleroma/admin/announcements/${id}`),
|
||||
retry: false,
|
||||
onSuccess: (_, id) =>
|
||||
queryClient.setQueryData(['admin', 'announcements'], (prevResult: ReadonlyArray<AdminAnnouncement>) =>
|
||||
prevResult.filter(({ id: announcementId }) => announcementId !== id),
|
||||
),
|
||||
onSettled: () => userAnnouncements.refetch(),
|
||||
});
|
||||
|
||||
return {
|
||||
...result,
|
||||
createAnnouncement,
|
||||
isCreating,
|
||||
updateAnnouncement,
|
||||
isUpdating,
|
||||
deleteAnnouncement,
|
||||
isDeleting,
|
||||
};
|
||||
};
|
||||
|
||||
export { useAnnouncements };
|
||||
1
src/api/hooks/announcements/index.ts
Normal file
1
src/api/hooks/announcements/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export { useAnnouncements } from './useAnnouncements';
|
||||
95
src/api/hooks/announcements/useAnnouncements.ts
Normal file
95
src/api/hooks/announcements/useAnnouncements.ts
Normal file
@ -0,0 +1,95 @@
|
||||
import { useMutation, useQuery } from '@tanstack/react-query';
|
||||
|
||||
import { useApi } from 'soapbox/hooks';
|
||||
import { queryClient } from 'soapbox/queries/client';
|
||||
import { announcementReactionSchema, announcementSchema, type Announcement, type AnnouncementReaction } from 'soapbox/schemas';
|
||||
|
||||
const updateReaction = (reaction: AnnouncementReaction, count: number, me?: boolean, overwrite?: boolean) => announcementReactionSchema.parse({
|
||||
...reaction,
|
||||
me: typeof me === 'boolean' ? me : reaction.me,
|
||||
count: overwrite ? count : (reaction.count + count),
|
||||
});
|
||||
|
||||
export const updateReactions = (reactions: AnnouncementReaction[], name: string, count: number, me?: boolean, overwrite?: boolean) => {
|
||||
const idx = reactions.findIndex(reaction => reaction.name === name);
|
||||
|
||||
if (idx > -1) {
|
||||
reactions = reactions.map(reaction => reaction.name === name ? updateReaction(reaction, count, me, overwrite) : reaction);
|
||||
}
|
||||
|
||||
return [...reactions, updateReaction(announcementReactionSchema.parse({ name }), count, me, overwrite)];
|
||||
};
|
||||
|
||||
const useAnnouncements = () => {
|
||||
const api = useApi();
|
||||
|
||||
const getAnnouncements = async () => {
|
||||
const { data } = await api.get<Announcement[]>('/api/v1/announcements');
|
||||
|
||||
const normalizedData = data?.map((announcement) => announcementSchema.parse(announcement));
|
||||
return normalizedData;
|
||||
};
|
||||
|
||||
const { data, ...result } = useQuery<ReadonlyArray<Announcement>>({
|
||||
queryKey: ['announcements'],
|
||||
queryFn: getAnnouncements,
|
||||
placeholderData: [],
|
||||
});
|
||||
|
||||
const {
|
||||
mutate: addReaction,
|
||||
} = useMutation({
|
||||
mutationFn: ({ announcementId, name }: { announcementId: string; name: string }) =>
|
||||
api.put<Announcement>(`/api/v1/announcements/${announcementId}/reactions/${name}`),
|
||||
retry: false,
|
||||
onMutate: ({ announcementId: id, name }) => {
|
||||
queryClient.setQueryData(['announcements'], (prevResult: Announcement[]) =>
|
||||
prevResult.map(value => value.id !== id ? value : announcementSchema.parse({
|
||||
...value,
|
||||
reactions: updateReactions(value.reactions, name, 1, true),
|
||||
})),
|
||||
);
|
||||
},
|
||||
onError: (_, { announcementId: id, name }) => {
|
||||
queryClient.setQueryData(['announcements'], (prevResult: Announcement[]) =>
|
||||
prevResult.map(value => value.id !== id ? value : announcementSchema.parse({
|
||||
...value,
|
||||
reactions: updateReactions(value.reactions, name, -1, false),
|
||||
})),
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
const {
|
||||
mutate: removeReaction,
|
||||
} = useMutation({
|
||||
mutationFn: ({ announcementId, name }: { announcementId: string; name: string }) =>
|
||||
api.delete<Announcement>(`/api/v1/announcements/${announcementId}/reactions/${name}`),
|
||||
retry: false,
|
||||
onMutate: ({ announcementId: id, name }) => {
|
||||
queryClient.setQueryData(['announcements'], (prevResult: Announcement[]) =>
|
||||
prevResult.map(value => value.id !== id ? value : announcementSchema.parse({
|
||||
...value,
|
||||
reactions: updateReactions(value.reactions, name, -1, false),
|
||||
})),
|
||||
);
|
||||
},
|
||||
onError: (_, { announcementId: id, name }) => {
|
||||
queryClient.setQueryData(['announcements'], (prevResult: Announcement[]) =>
|
||||
prevResult.map(value => value.id !== id ? value : announcementSchema.parse({
|
||||
...value,
|
||||
reactions: updateReactions(value.reactions, name, 1, true),
|
||||
})),
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
data: data?.toSorted((a, b) => new Date(a.starts_at || a.published_at).getDate() - new Date(b.starts_at || b.published_at).getDate()),
|
||||
...result,
|
||||
addReaction,
|
||||
removeReaction,
|
||||
};
|
||||
};
|
||||
|
||||
export { useAnnouncements };
|
||||
@ -1,4 +1,3 @@
|
||||
import { fetchAnnouncements } from 'soapbox/actions/announcements';
|
||||
import { expandNotifications } from 'soapbox/actions/notifications';
|
||||
import { expandHomeTimeline } from 'soapbox/actions/timelines';
|
||||
import { useStatContext } from 'soapbox/contexts/stat-context';
|
||||
@ -24,8 +23,7 @@ function useUserStream() {
|
||||
/** Refresh home timeline and notifications. */
|
||||
function refresh(dispatch: AppDispatch, done?: () => void) {
|
||||
return dispatch(expandHomeTimeline({}, () =>
|
||||
dispatch(expandNotifications({}, () =>
|
||||
dispatch(fetchAnnouncements(done))))));
|
||||
dispatch(expandNotifications({}, done))));
|
||||
}
|
||||
|
||||
export { useUserStream };
|
||||
Reference in New Issue
Block a user