diff options
Diffstat (limited to 'src/state/queries/post.ts')
-rw-r--r-- | src/state/queries/post.ts | 178 |
1 files changed, 178 insertions, 0 deletions
diff --git a/src/state/queries/post.ts b/src/state/queries/post.ts new file mode 100644 index 000000000..b31696446 --- /dev/null +++ b/src/state/queries/post.ts @@ -0,0 +1,178 @@ +import React from 'react' +import {AppBskyFeedDefs, AtUri} from '@atproto/api' +import {useQuery, useMutation, useQueryClient} from '@tanstack/react-query' + +import {getAgent} from '#/state/session' +import {updatePostShadow} from '#/state/cache/post-shadow' + +export const RQKEY = (postUri: string) => ['post', postUri] + +export function usePostQuery(uri: string | undefined) { + return useQuery<AppBskyFeedDefs.PostView>({ + queryKey: RQKEY(uri || ''), + async queryFn() { + const res = await getAgent().getPosts({uris: [uri!]}) + if (res.success && res.data.posts[0]) { + return res.data.posts[0] + } + + throw new Error('No data') + }, + enabled: !!uri, + }) +} + +export function useGetPost() { + const queryClient = useQueryClient() + return React.useCallback( + async ({uri}: {uri: string}) => { + return queryClient.fetchQuery({ + queryKey: RQKEY(uri || ''), + async queryFn() { + const urip = new AtUri(uri) + + if (!urip.host.startsWith('did:')) { + const res = await getAgent().resolveHandle({ + handle: urip.host, + }) + urip.host = res.data.did + } + + const res = await getAgent().getPosts({ + uris: [urip.toString()!], + }) + + if (res.success && res.data.posts[0]) { + return res.data.posts[0] + } + + throw new Error('useGetPost: post not found') + }, + }) + }, + [queryClient], + ) +} + +export function usePostLikeMutation() { + return useMutation< + {uri: string}, // responds with the uri of the like + Error, + {uri: string; cid: string; likeCount: number} // the post's uri, cid, and likes + >({ + mutationFn: post => getAgent().like(post.uri, post.cid), + onMutate(variables) { + // optimistically update the post-shadow + updatePostShadow(variables.uri, { + likeCount: variables.likeCount + 1, + likeUri: 'pending', + }) + }, + onSuccess(data, variables) { + // finalize the post-shadow with the like URI + updatePostShadow(variables.uri, { + likeUri: data.uri, + }) + }, + onError(error, variables) { + // revert the optimistic update + updatePostShadow(variables.uri, { + likeCount: variables.likeCount, + likeUri: undefined, + }) + }, + }) +} + +export function usePostUnlikeMutation() { + return useMutation< + void, + Error, + {postUri: string; likeUri: string; likeCount: number} + >({ + mutationFn: async ({likeUri}) => { + await getAgent().deleteLike(likeUri) + }, + onMutate(variables) { + // optimistically update the post-shadow + updatePostShadow(variables.postUri, { + likeCount: variables.likeCount - 1, + likeUri: undefined, + }) + }, + onError(error, variables) { + // revert the optimistic update + updatePostShadow(variables.postUri, { + likeCount: variables.likeCount, + likeUri: variables.likeUri, + }) + }, + }) +} + +export function usePostRepostMutation() { + return useMutation< + {uri: string}, // responds with the uri of the repost + Error, + {uri: string; cid: string; repostCount: number} // the post's uri, cid, and reposts + >({ + mutationFn: post => getAgent().repost(post.uri, post.cid), + onMutate(variables) { + // optimistically update the post-shadow + updatePostShadow(variables.uri, { + repostCount: variables.repostCount + 1, + repostUri: 'pending', + }) + }, + onSuccess(data, variables) { + // finalize the post-shadow with the repost URI + updatePostShadow(variables.uri, { + repostUri: data.uri, + }) + }, + onError(error, variables) { + // revert the optimistic update + updatePostShadow(variables.uri, { + repostCount: variables.repostCount, + repostUri: undefined, + }) + }, + }) +} + +export function usePostUnrepostMutation() { + return useMutation< + void, + Error, + {postUri: string; repostUri: string; repostCount: number} + >({ + mutationFn: async ({repostUri}) => { + await getAgent().deleteRepost(repostUri) + }, + onMutate(variables) { + // optimistically update the post-shadow + updatePostShadow(variables.postUri, { + repostCount: variables.repostCount - 1, + repostUri: undefined, + }) + }, + onError(error, variables) { + // revert the optimistic update + updatePostShadow(variables.postUri, { + repostCount: variables.repostCount, + repostUri: variables.repostUri, + }) + }, + }) +} + +export function usePostDeleteMutation() { + return useMutation<void, Error, {uri: string}>({ + mutationFn: async ({uri}) => { + await getAgent().deletePost(uri) + }, + onSuccess(data, variables) { + updatePostShadow(variables.uri, {isDeleted: true}) + }, + }) +} |