An ATproto social media client -- with an independent Appview.
1import {
2 type $Typed,
3 type AppBskyBookmarkGetBookmarks,
4 type AppBskyFeedDefs,
5} from '@atproto/api'
6import {
7 type InfiniteData,
8 type QueryClient,
9 type QueryKey,
10 useInfiniteQuery,
11} from '@tanstack/react-query'
12
13import {useAgent} from '#/state/session'
14
15export const bookmarksQueryKeyRoot = 'bookmarks'
16export const createBookmarksQueryKey = () => [bookmarksQueryKeyRoot]
17
18export function useBookmarksQuery() {
19 const agent = useAgent()
20
21 return useInfiniteQuery<
22 AppBskyBookmarkGetBookmarks.OutputSchema,
23 Error,
24 InfiniteData<AppBskyBookmarkGetBookmarks.OutputSchema>,
25 QueryKey,
26 string | undefined
27 >({
28 queryKey: createBookmarksQueryKey(),
29 async queryFn({pageParam}) {
30 const res = await agent.app.bsky.bookmark.getBookmarks({
31 cursor: pageParam,
32 })
33 return res.data
34 },
35 initialPageParam: undefined,
36 getNextPageParam: lastPage => lastPage.cursor,
37 })
38}
39
40export async function truncateAndInvalidate(qc: QueryClient) {
41 qc.setQueriesData<InfiniteData<AppBskyBookmarkGetBookmarks.OutputSchema>>(
42 {queryKey: [bookmarksQueryKeyRoot]},
43 data => {
44 if (data) {
45 return {
46 pageParams: data.pageParams.slice(0, 1),
47 pages: data.pages.slice(0, 1),
48 }
49 }
50 return data
51 },
52 )
53 return qc.invalidateQueries({queryKey: [bookmarksQueryKeyRoot]})
54}
55
56export async function optimisticallySaveBookmark(
57 qc: QueryClient,
58 post: AppBskyFeedDefs.PostView,
59) {
60 qc.setQueriesData<InfiniteData<AppBskyBookmarkGetBookmarks.OutputSchema>>(
61 {
62 queryKey: [bookmarksQueryKeyRoot],
63 },
64 data => {
65 if (!data) return data
66 return {
67 ...data,
68 pages: data.pages.map((page, index) => {
69 if (index === 0) {
70 post.$type = 'app.bsky.feed.defs#postView'
71 return {
72 ...page,
73 bookmarks: [
74 {
75 createdAt: new Date().toISOString(),
76 subject: {
77 uri: post.uri,
78 cid: post.cid,
79 },
80 item: post as $Typed<AppBskyFeedDefs.PostView>,
81 },
82 ...page.bookmarks,
83 ],
84 }
85 }
86 return page
87 }),
88 }
89 },
90 )
91}
92
93export async function optimisticallyDeleteBookmark(
94 qc: QueryClient,
95 {uri}: {uri: string},
96) {
97 qc.setQueriesData<InfiniteData<AppBskyBookmarkGetBookmarks.OutputSchema>>(
98 {
99 queryKey: [bookmarksQueryKeyRoot],
100 },
101 data => {
102 if (!data) return data
103 return {
104 ...data,
105 pages: data.pages.map(page => {
106 return {
107 ...page,
108 bookmarks: page.bookmarks.filter(b => b.subject.uri !== uri),
109 }
110 }),
111 }
112 },
113 )
114}