Refactored like and unlike API requests

We don't want our components littered with fetch calls, as it makes it
difficult to test. Instead we move our http api code into the ActivityPubAPI,
giving us a central place for adding authentication and tests in the future.

We also make sure that the components use a react query wrapped call - so that
we can take advantage of the query invalidation.
This commit is contained in:
Fabien O'Carroll 2024-09-06 11:42:14 +07:00 committed by Fabien 'egg' O'Carroll
parent ee514a397c
commit c0386b6c69
3 changed files with 38 additions and 11 deletions

View File

@ -117,4 +117,14 @@ export class ActivityPubAPI {
const json = await this.fetchJSON(new URL(url));
return json as Actor;
}
async like(id: string): Promise<void> {
const url = new URL(`.ghost/activitypub/actions/like/${encodeURIComponent(id)}`, this.apiUrl);
await this.fetchJSON(url, 'POST');
}
async unlike(id: string): Promise<void> {
const url = new URL(`.ghost/activitypub/actions/unlike/${encodeURIComponent(id)}`, this.apiUrl);
await this.fetchJSON(url, 'POST');
}
}

View File

@ -4,6 +4,7 @@ import getRelativeTimestamp from '../../utils/get-relative-timestamp';
import getUsername from '../../utils/get-username';
import {ActorProperties, ObjectProperties} from '@tryghost/admin-x-framework/api/activitypub';
import {Button, Heading, Icon} from '@tryghost/admin-x-design-system';
import {useLikeMutationForUser, useUnlikeMutationForUser} from '../../hooks/useActivityPubQueries';
export function renderFeedAttachment(object: ObjectProperties, layout: string) {
let attachment;
@ -135,20 +136,16 @@ const FeedItemStats: React.FC<{
}> = ({object, likeCount, commentCount, onLikeClick, onCommentClick}) => {
const [isClicked, setIsClicked] = useState(false);
const [isLiked, setIsLiked] = useState(object.liked);
const likeMutation = useLikeMutationForUser('index');
const unlikeMutation = useUnlikeMutationForUser('index');
const handleLikeClick = async () => {
setIsClicked(true);
let req;
if (!isLiked) {
req = fetch(`/.ghost/activitypub/actions/like/${encodeURIComponent(object.id)}`, {
method: 'POST'
});
likeMutation.mutate(object.id);
} else {
req = fetch(`/.ghost/activitypub/actions/unlike/${encodeURIComponent(object.id)}`, {
method: 'POST'
});
unlikeMutation.mutate(object.id);
}
await req;
setIsLiked(!isLiked);
setIsClicked(false); // Reset the animation class after request completed
@ -389,4 +386,4 @@ const FeedItem: React.FC<FeedItemProps> = ({actor, object, layout, type, last})
return (<></>);
};
export default FeedItem;
export default FeedItem;

View File

@ -1,6 +1,6 @@
import {ActivityPubAPI} from '../api/activitypub';
import {useBrowseSite} from '@tryghost/admin-x-framework/api/site';
import {useQuery} from '@tanstack/react-query';
import {useMutation, useQuery} from '@tanstack/react-query';
const useSiteUrl = () => {
const site = useBrowseSite();
@ -15,6 +15,26 @@ function createActivityPubAPI(handle: string, siteUrl: string) {
);
}
export function useLikeMutationForUser(handle: string) {
const siteUrl = useSiteUrl();
const api = createActivityPubAPI(handle, siteUrl);
return useMutation({
mutationFn(id: string) {
return api.like(id);
}
});
}
export function useUnlikeMutationForUser(handle: string) {
const siteUrl = useSiteUrl();
const api = createActivityPubAPI(handle, siteUrl);
return useMutation({
mutationFn(id: string) {
return api.unlike(id);
}
});
}
export function useFollowersCountForUser(handle: string) {
const siteUrl = useSiteUrl();
const api = createActivityPubAPI(handle, siteUrl);
@ -58,4 +78,4 @@ export function useFollowersForUser(handle: string) {
return api.getFollowers();
}
});
}
}