From 06cd63a3abafb231dac8693953158c39db4001c5 Mon Sep 17 00:00:00 2001 From: Michael Barrett Date: Wed, 18 Dec 2024 19:05:31 +0000 Subject: [PATCH] Fixed intermittent error when posting a note in `admin-x-activitypub` (#21917) refs [AP-629](https://linear.app/ghost/issue/AP-629/notes-say-they-error-but-post-correctly) Fixed intermittent error when posting a note in `admin-x-activitypub` The error was intermittent due to it only occurring when a specific set of steps occurred, and the query cache being periodically cleared. The error itself was due to incorrectly expecting the `outbox:${handle}` query to be an array when it was in fact an object. This PR also resolves and issue where the reply count for new notes would display `NaN` (because the `replyCount` property was not present on newly created notes) --- apps/admin-x-activitypub/package.json | 2 +- .../src/api/activitypub.ts | 4 ++-- .../src/components/feed/ArticleModal.tsx | 4 ++-- .../src/components/modals/NewPostModal.tsx | 9 ++++++-- .../src/hooks/useActivityPubQueries.ts | 21 +++++++++++++++---- 5 files changed, 29 insertions(+), 11 deletions(-) diff --git a/apps/admin-x-activitypub/package.json b/apps/admin-x-activitypub/package.json index 5a4df2df64..e719c34dae 100644 --- a/apps/admin-x-activitypub/package.json +++ b/apps/admin-x-activitypub/package.json @@ -1,6 +1,6 @@ { "name": "@tryghost/admin-x-activitypub", - "version": "0.3.38", + "version": "0.3.39", "license": "MIT", "repository": { "type": "git", diff --git a/apps/admin-x-activitypub/src/api/activitypub.ts b/apps/admin-x-activitypub/src/api/activitypub.ts index 6b0a89078d..9fa797aee1 100644 --- a/apps/admin-x-activitypub/src/api/activitypub.ts +++ b/apps/admin-x-activitypub/src/api/activitypub.ts @@ -258,13 +258,13 @@ export class ActivityPubAPI { }; } - async reply(id: string, content: string) { + async reply(id: string, content: string): Promise { const url = new URL(`.ghost/activitypub/actions/reply/${encodeURIComponent(id)}`, this.apiUrl); const response = await this.fetchJSON(url, 'POST', {content}); return response; } - async note(content: string) { + async note(content: string): Promise { const url = new URL('.ghost/activitypub/actions/note', this.apiUrl); const response = await this.fetchJSON(url, 'POST', {content}); return response; diff --git a/apps/admin-x-activitypub/src/components/feed/ArticleModal.tsx b/apps/admin-x-activitypub/src/components/feed/ArticleModal.tsx index 988726a99a..5b3cd58153 100644 --- a/apps/admin-x-activitypub/src/components/feed/ArticleModal.tsx +++ b/apps/admin-x-activitypub/src/components/feed/ArticleModal.tsx @@ -356,13 +356,13 @@ const ArticleModal: React.FC = ({ updateActivity(activityId, { object: { ...object, - replyCount: object.replyCount + 1 + replyCount: (object.replyCount ?? 0) + 1 } } as Partial); // Update the replyCount on the current activity loaded in the modal // This is used for when we navigate via the history - object.replyCount = object.replyCount + 1; + object.replyCount = (object.replyCount ?? 0) + 1; } const replyBoxRef = useRef(null); diff --git a/apps/admin-x-activitypub/src/components/modals/NewPostModal.tsx b/apps/admin-x-activitypub/src/components/modals/NewPostModal.tsx index 5c1bf04f8a..12f600d1b0 100644 --- a/apps/admin-x-activitypub/src/components/modals/NewPostModal.tsx +++ b/apps/admin-x-activitypub/src/components/modals/NewPostModal.tsx @@ -17,23 +17,28 @@ const NewPostModal = NiceModal.create(() => { const handlePost = async () => { const trimmedContent = content.trim(); + if (!trimmedContent) { return; } - await noteMutation.mutate({content: trimmedContent}, { + noteMutation.mutate({content: trimmedContent}, { onSuccess() { showToast({ message: 'Note posted', type: 'success' }); + modal.remove(); }, - onError() { + onError(error) { showToast({ message: 'An error occurred while posting your note.', type: 'error' }); + + // eslint-disable-next-line no-console + console.error(error); } }); }; diff --git a/apps/admin-x-activitypub/src/hooks/useActivityPubQueries.ts b/apps/admin-x-activitypub/src/hooks/useActivityPubQueries.ts index 33aa23cab8..c9c9c0ee4e 100644 --- a/apps/admin-x-activitypub/src/hooks/useActivityPubQueries.ts +++ b/apps/admin-x-activitypub/src/hooks/useActivityPubQueries.ts @@ -488,7 +488,8 @@ export function useReplyMutationForUser(handle: string) { async mutationFn({id, content}: {id: string, content: string}) { const siteUrl = await getSiteUrl(); const api = createActivityPubAPI(handle, siteUrl); - return await api.reply(id, content) as Activity; + + return api.reply(id, content); } }); } @@ -500,15 +501,27 @@ export function useNoteMutationForUser(handle: string) { async mutationFn({content}: {content: string}) { const siteUrl = await getSiteUrl(); const api = createActivityPubAPI(handle, siteUrl); - return await api.note(content) as Activity; + + return api.note(content); }, onSuccess: (activity: Activity) => { - queryClient.setQueryData([`outbox:${handle}`], (current?: Activity[]) => { + queryClient.setQueryData([`outbox:${handle}`], (current?: {pages: {data: Activity[]}[]}) => { if (current === undefined) { return current; } - return [activity, ...current]; + return { + ...current, + pages: current.pages.map((page: {data: Activity[]}, index: number) => { + if (index === 0) { + return { + ...page, + data: [activity, ...page.data] + }; + } + return page; + }) + }; }); queryClient.setQueriesData([`activities:${handle}`, GET_ACTIVITIES_QUERY_KEY_FEED], (current?: {pages: {data: Activity[]}[]}) => {