forked from
jollywhoppers.com/witchsky.app
Bluesky app fork with some witchin' additions 馃挮
1import {
2 type $Typed,
3 type AppBskyBookmarkGetBookmarks,
4 AppBskyFeedDefs,
5 AtUri,
6} from '@atproto/api'
7import {
8 type InfiniteData,
9 type QueryClient,
10 type QueryKey,
11 useInfiniteQuery,
12} from '@tanstack/react-query'
13
14import {
15 didOrHandleUriMatches,
16 embedViewRecordToPostView,
17 getEmbeddedPost,
18} from '#/state/queries/util'
19import {useAgent} from '#/state/session'
20import * as bsky from '#/types/bsky'
21
22export const bookmarksQueryKeyRoot = 'bookmarks'
23export const createBookmarksQueryKey = () => [bookmarksQueryKeyRoot]
24
25export function useBookmarksQuery() {
26 const agent = useAgent()
27
28 return useInfiniteQuery<
29 AppBskyBookmarkGetBookmarks.OutputSchema,
30 Error,
31 InfiniteData<AppBskyBookmarkGetBookmarks.OutputSchema>,
32 QueryKey,
33 string | undefined
34 >({
35 queryKey: createBookmarksQueryKey(),
36 async queryFn({pageParam}) {
37 const res = await agent.app.bsky.bookmark.getBookmarks({
38 cursor: pageParam,
39 })
40 return res.data
41 },
42 initialPageParam: undefined,
43 getNextPageParam: lastPage => lastPage.cursor,
44 })
45}
46
47export async function truncateAndInvalidate(qc: QueryClient) {
48 qc.setQueriesData<InfiniteData<AppBskyBookmarkGetBookmarks.OutputSchema>>(
49 {queryKey: [bookmarksQueryKeyRoot]},
50 data => {
51 if (data) {
52 return {
53 pageParams: data.pageParams.slice(0, 1),
54 pages: data.pages.slice(0, 1),
55 }
56 }
57 return data
58 },
59 )
60 return qc.invalidateQueries({queryKey: [bookmarksQueryKeyRoot]})
61}
62
63export async function optimisticallySaveBookmark(
64 qc: QueryClient,
65 post: AppBskyFeedDefs.PostView,
66) {
67 qc.setQueriesData<InfiniteData<AppBskyBookmarkGetBookmarks.OutputSchema>>(
68 {
69 queryKey: [bookmarksQueryKeyRoot],
70 },
71 data => {
72 if (!data) return data
73 return {
74 ...data,
75 pages: data.pages.map((page, index) => {
76 if (index === 0) {
77 post.$type = 'app.bsky.feed.defs#postView'
78 return {
79 ...page,
80 bookmarks: [
81 {
82 createdAt: new Date().toISOString(),
83 subject: {
84 uri: post.uri,
85 cid: post.cid,
86 },
87 item: post as $Typed<AppBskyFeedDefs.PostView>,
88 },
89 ...page.bookmarks,
90 ],
91 }
92 }
93 return page
94 }),
95 }
96 },
97 )
98}
99
100export async function optimisticallyDeleteBookmark(
101 qc: QueryClient,
102 {uri}: {uri: string},
103) {
104 qc.setQueriesData<InfiniteData<AppBskyBookmarkGetBookmarks.OutputSchema>>(
105 {
106 queryKey: [bookmarksQueryKeyRoot],
107 },
108 data => {
109 if (!data) return data
110 return {
111 ...data,
112 pages: data.pages.map(page => {
113 return {
114 ...page,
115 bookmarks: page.bookmarks.filter(b => b.subject.uri !== uri),
116 }
117 }),
118 }
119 },
120 )
121}
122
123export function* findAllPostsInQueryData(
124 queryClient: QueryClient,
125 uri: string,
126): Generator<AppBskyFeedDefs.PostView, undefined> {
127 const queryDatas = queryClient.getQueriesData<
128 InfiniteData<AppBskyBookmarkGetBookmarks.OutputSchema>
129 >({
130 queryKey: [bookmarksQueryKeyRoot],
131 })
132 const atUri = new AtUri(uri)
133
134 for (const [_queryKey, queryData] of queryDatas) {
135 if (!queryData?.pages) {
136 continue
137 }
138 for (const page of queryData?.pages) {
139 for (const bookmark of page.bookmarks) {
140 if (
141 !bsky.dangerousIsType<AppBskyFeedDefs.PostView>(
142 bookmark.item,
143 AppBskyFeedDefs.isPostView,
144 )
145 )
146 continue
147
148 if (didOrHandleUriMatches(atUri, bookmark.item)) {
149 yield bookmark.item
150 }
151
152 const quotedPost = getEmbeddedPost(bookmark.item.embed)
153 if (quotedPost && didOrHandleUriMatches(atUri, quotedPost)) {
154 yield embedViewRecordToPostView(quotedPost)
155 }
156 }
157 }
158 }
159}