0
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2025-04-08 02:52:39 -05:00

Wired 'Undo Repost' action with the backend API endpoint (#22124)

ref https://linear.app/ghost/issue/AP-699

- when the user clicks again on the Repost icon, we undo the Repost
action, by calling the corresponding API endpoint `POST
/actions/derepost/:id`
- UX/UI and copy are still WIP
- at this stage, we're still missing acceptance tests in the frontend
app. We will be starting acceptance testing in a separate commit
This commit is contained in:
Sag 2025-02-06 17:29:19 +07:00 committed by GitHub
parent c755df1e2f
commit 2bd95b3efc
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 65 additions and 3 deletions

View file

@ -224,6 +224,11 @@ export class ActivityPubAPI {
await this.fetchJSON(url, 'POST');
}
async derepost(id: string): Promise<void> {
const url = new URL(`.ghost/activitypub/actions/derepost/${encodeURIComponent(id)}`, this.apiUrl);
await this.fetchJSON(url, 'POST');
}
get activitiesApiUrl() {
return new URL(`.ghost/activitypub/activities/${this.handle}`, this.apiUrl);
}

View file

@ -1,7 +1,7 @@
import React, {useState} from 'react';
import {Button} from '@tryghost/admin-x-design-system';
import {ObjectProperties} from '@tryghost/admin-x-framework/api/activitypub';
import {useLikeMutationForUser, useRepostMutationForUser, useUnlikeMutationForUser} from '../../hooks/useActivityPubQueries';
import {useDerepostMutationForUser, useLikeMutationForUser, useRepostMutationForUser, useUnlikeMutationForUser} from '../../hooks/useActivityPubQueries';
interface FeedItemStatsProps {
object: ObjectProperties;
@ -27,6 +27,7 @@ const FeedItemStats: React.FC<FeedItemStatsProps> = ({
const likeMutation = useLikeMutationForUser('index');
const unlikeMutation = useUnlikeMutationForUser('index');
const repostMutation = useRepostMutationForUser('index');
const derepostMutation = useDerepostMutationForUser('index');
const handleLikeClick = async (e: React.MouseEvent<HTMLElement>) => {
e.stopPropagation();
@ -82,15 +83,18 @@ const FeedItemStats: React.FC<FeedItemStatsProps> = ({
id='repost'
label={new Intl.NumberFormat().format(repostCount)}
size='md'
title='Repost'
title={`${isReposted ? 'Undo repost' : 'Repost'}`}
unstyled={true}
onClick={(e?: React.MouseEvent<HTMLElement>) => {
e?.stopPropagation();
if (!isReposted) {
repostMutation.mutate(object.id);
setIsReposted(true);
} else {
derepostMutation.mutate(object.id);
}
setIsReposted(!isReposted);
}}
/>
</div>);

View file

@ -208,6 +208,59 @@ export function useRepostMutationForUser(handle: string) {
});
}
export function useDerepostMutationForUser(handle: string) {
const queryClient = useQueryClient();
return useMutation({
async mutationFn(id: string) {
const siteUrl = await getSiteUrl();
const api = createActivityPubAPI(handle, siteUrl);
return api.derepost(id);
},
onMutate: async (id) => {
const previousInbox = queryClient.getQueryData([`inbox:${handle}`]);
const previousReposted = queryClient.getQueryData([`reposted:${handle}`]);
if (previousInbox) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
queryClient.setQueryData([`inbox:${handle}`], (old?: any[]) => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
return old?.map((item: any) => {
if (item.object.id === id) {
return {
...item,
object: {
...item.object,
reposted: false
}
};
}
return item;
});
});
}
if (previousReposted) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
queryClient.setQueryData([`reposted:${handle}`], (old?: any[]) => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
return old?.filter((item: any) => item.object.id !== id);
});
}
// This sets the context for the onError handler
return {previousInbox, previousReposted};
},
onError: (_err, _id, context) => {
if (context?.previousInbox) {
queryClient.setQueryData([`inbox:${handle}`], context?.previousInbox);
}
if (context?.previousReposted) {
queryClient.setQueryData([`reposted:${handle}`], context?.previousReposted);
}
}
});
}
export function useUserDataForUser(handle: string) {
return useQuery({
queryKey: [`user:${handle}`],