Bluesky app fork with some witchin' additions 馃挮
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

at theme-changes 149 lines 3.9 kB view raw
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}