diff --git a/src/api/hooks/index.ts b/src/api/hooks/index.ts index 990dda57b..001eb6322 100644 --- a/src/api/hooks/index.ts +++ b/src/api/hooks/index.ts @@ -45,6 +45,7 @@ export { useUpdateGroupTag } from './groups/useUpdateGroupTag.ts'; // Statuses export { useBookmarks } from './statuses/useBookmarks.ts'; +export { useBookmark } from './statuses/useBookmark.ts'; // Streaming export { useUserStream } from './streaming/useUserStream.ts'; diff --git a/src/api/hooks/statuses/useBookmark.ts b/src/api/hooks/statuses/useBookmark.ts new file mode 100644 index 000000000..f0263a905 --- /dev/null +++ b/src/api/hooks/statuses/useBookmark.ts @@ -0,0 +1,80 @@ +import { importEntities } from 'soapbox/entity-store/actions.ts'; +import { Entities } from 'soapbox/entity-store/entities.ts'; +import { useTransaction } from 'soapbox/entity-store/hooks/index.ts'; +import { useApi } from 'soapbox/hooks/useApi.ts'; +import { useAppDispatch } from 'soapbox/hooks/useAppDispatch.ts'; +import { useLoggedIn } from 'soapbox/hooks/useLoggedIn.ts'; +import { statusSchema } from 'soapbox/schemas/index.ts'; + +/** + * Bookmark and undo a bookmark, with optimistic update. + * + * https://docs.joinmastodon.org/methods/statuses/#bookmark + * POST /api/v1/statuses/:id/bookmark + * + * https://docs.joinmastodon.org/methods/statuses/#unbookmark + * POST /api/v1/statuses/:id/unbookmark + */ +function useBookmark() { + const api = useApi(); + const dispatch = useAppDispatch(); + const { isLoggedIn } = useLoggedIn(); + const { transaction } = useTransaction(); + + function bookmarkEffect(statusId: string) { + transaction({ + Statuses: { + [statusId]: (status) => ({ + ...status, + bookmarked: true, + }), + }, + }); + } + + function unbookmarkEffect(statusId: string) { + transaction({ + Statuses: { + [statusId]: (status) => ({ + ...status, + bookmarked: false, + }), + }, + }); + } + + async function bookmark(statusId: string) { + if (!isLoggedIn) return; + bookmarkEffect(statusId); + + try { + const response = await api.post(`/api/v1/statuses/${statusId}/bookmark`); + const result = statusSchema.safeParse(await response.json()); + if (result.success) { + dispatch(importEntities([result.data], Entities.STATUSES, 'bookmarks')); + } + } catch (e) { + unbookmarkEffect(statusId); + } + } + + async function unbookmark(statusId: string) { + if (!isLoggedIn) return; + unbookmarkEffect(statusId); + + try { + await api.post(`/api/v1/statuses/${statusId}/unbookmark`); + } catch (e) { + bookmarkEffect(statusId); + } + } + + return { + bookmark, + unbookmark, + bookmarkEffect, + unbookmarkEffect, + }; +} + +export { useBookmark }; \ No newline at end of file