forked from
jollywhoppers.com/witchsky.app
Bluesky app fork with some witchin' additions 馃挮
1import {
2 type AppBskyActorDefs,
3 AppBskyEmbedRecord,
4 AppBskyEmbedRecordWithMedia,
5 type AppBskyFeedDefs,
6 AppBskyFeedPost,
7 type AtUri,
8} from '@atproto/api'
9import {
10 type InfiniteData,
11 type QueryClient,
12 type QueryKey,
13} from '@tanstack/react-query'
14
15import * as bsky from '#/types/bsky'
16
17export type StructuredQueryKey<T extends Record<string, unknown>> = readonly [
18 string,
19 T,
20 {
21 persistedVersion?: number
22 },
23]
24
25/**
26 * Helper method to ensure consistent query keys and key ordering
27 */
28export function createQueryKey<T extends Record<string, unknown>>(
29 /**
30 * The query key root. All queries must have a root.
31 */
32 root: string,
33 /**
34 * Any arguments the query depends on, and if changed, should result in the query being refetched.
35 */
36 args: T,
37 options: {
38 /**
39 * If provided, this indicates that the query is persisted and the version
40 * of the persisted query format.
41 *
42 * This is used to ensure that when we make breaking changes to the
43 * persisted query format, we can increment the version and avoid trying to
44 * read old persisted queries with the new format.
45 *
46 * If you're persisting your queries, you probably want to set `gcTime:
47 * GCTIME.INFINITY` for this query, otherwise it'll get busted immediately
48 * after being persisted.
49 */
50 persistedVersion?: number
51 } = {},
52): StructuredQueryKey<T> {
53 return [root, args, options] as const
54}
55
56export function isQueryPersisted(
57 queryKey: QueryKey,
58): queryKey is StructuredQueryKey<Record<string, unknown>> {
59 return (
60 Array.isArray(queryKey) &&
61 queryKey.length === 3 &&
62 typeof queryKey[0] === 'string' &&
63 typeof queryKey[1] === 'object' &&
64 queryKey[1] !== null &&
65 typeof queryKey[2] === 'object' &&
66 queryKey[2] !== null &&
67 'persistedVersion' in queryKey[2] &&
68 typeof queryKey[2].persistedVersion === 'number'
69 )
70}
71
72export async function truncateAndInvalidate<T = any>(
73 queryClient: QueryClient,
74 queryKey: QueryKey,
75) {
76 queryClient.setQueriesData<InfiniteData<T>>({queryKey}, data => {
77 if (data) {
78 return {
79 pageParams: data.pageParams.slice(0, 1),
80 pages: data.pages.slice(0, 1),
81 }
82 }
83 return data
84 })
85 return queryClient.invalidateQueries({queryKey})
86}
87
88// Given an AtUri, this function will check if the AtUri matches a
89// hit regardless of whether the AtUri uses a DID or handle as a host.
90//
91// AtUri should be the URI that is being searched for, while currentUri
92// is the URI that is being checked. currentAuthor is the author
93// of the currentUri that is being checked.
94export function didOrHandleUriMatches(
95 atUri: AtUri,
96 record: {uri: string; author: AppBskyActorDefs.ProfileViewBasic},
97) {
98 if (atUri.host.startsWith('did:')) {
99 return atUri.href === record.uri
100 }
101
102 return atUri.host === record.author.handle && record.uri.endsWith(atUri.rkey)
103}
104
105export function getEmbeddedPost(
106 v: unknown,
107): AppBskyEmbedRecord.ViewRecord | undefined {
108 if (
109 bsky.dangerousIsType<AppBskyEmbedRecord.View>(v, AppBskyEmbedRecord.isView)
110 ) {
111 if (
112 AppBskyEmbedRecord.isViewRecord(v.record) &&
113 AppBskyFeedPost.isRecord(v.record.value)
114 ) {
115 return v.record
116 }
117 }
118 if (
119 bsky.dangerousIsType<AppBskyEmbedRecordWithMedia.View>(
120 v,
121 AppBskyEmbedRecordWithMedia.isView,
122 )
123 ) {
124 if (
125 AppBskyEmbedRecord.isViewRecord(v.record.record) &&
126 AppBskyFeedPost.isRecord(v.record.record.value)
127 ) {
128 return v.record.record
129 }
130 }
131}
132
133export function embedViewRecordToPostView(
134 v: AppBskyEmbedRecord.ViewRecord,
135): AppBskyFeedDefs.PostView {
136 return {
137 uri: v.uri,
138 cid: v.cid,
139 author: v.author,
140 record: v.value,
141 indexedAt: v.indexedAt,
142 labels: v.labels,
143 embed: v.embeds?.[0],
144 likeCount: v.likeCount,
145 quoteCount: v.quoteCount,
146 replyCount: v.replyCount,
147 repostCount: v.repostCount,
148 }
149}