Bluesky app fork with some witchin' additions 💫 witchsky.app
bluesky fork client
at main 103 lines 3.0 kB view raw
1import {useEffect, useId, useState} from 'react' 2import {type AppBskyFeedDefs, AtUri} from '@atproto/api' 3 4import {Logger} from '#/logger' 5import {type FeedSourceInfo} from '#/state/queries/feed' 6 7/** 8 * Separate logger for better debugging 9 */ 10const logger = Logger.create(Logger.Context.PostSource) 11 12export type PostSource = { 13 post: AppBskyFeedDefs.FeedViewPost 14 feedSourceInfo?: FeedSourceInfo 15} 16 17/** 18 * A cache of sources that will be consumed by the post thread view. This is 19 * cleaned up any time a source is consumed. 20 */ 21const transientSources = new Map<string, PostSource>() 22 23/** 24 * A cache of sources that have been consumed by the post thread view. This is 25 * not cleaned up, but because we use a new ID for each post thread view that 26 * consumes a source, this is never reused unless a user navigates back to a 27 * post thread view that has not been dropped from memory. 28 */ 29const consumedSources = new Map<string, PostSource>() 30 31/** 32 * For stashing the feed that the user was browsing when they clicked on a post. 33 * 34 * Used for FeedFeedback and other ephemeral non-critical systems. 35 */ 36export function setUnstablePostSource(key: string, source: PostSource) { 37 assertValidDevOnly( 38 key, 39 `setUnstablePostSource key should be a URI containing a handle, received ${key} — use buildPostSourceKey`, 40 ) 41 logger.debug('set', {key, source}) 42 transientSources.set(key, source) 43} 44 45/** 46 * This hook is unstable and should only be used for FeedFeedback and other 47 * ephemeral non-critical systems. Views that use this hook will continue to 48 * return a reference to the same source until those views are dropped from 49 * memory. 50 */ 51export function useUnstablePostSource(key: string) { 52 const id = useId() 53 const [source] = useState(() => { 54 assertValidDevOnly( 55 key, 56 `consumeUnstablePostSource key should be a URI containing a handle, received ${key} — be sure to use buildPostSourceKey when setting the source`, 57 true, 58 ) 59 const existingSource = consumedSources.get(id) || transientSources.get(key) 60 if (existingSource) { 61 logger.debug('consume', {id, key, source: existingSource}) 62 transientSources.delete(key) 63 consumedSources.set(id, existingSource) 64 } 65 return existingSource 66 }) 67 68 useEffect(() => { 69 return () => { 70 consumedSources.delete(id) 71 logger.debug('cleanup', {id}) 72 } 73 }, [id]) 74 75 return source 76} 77 78/** 79 * Builds a post source key. This (atm) is a URI where the `host` is the post 80 * author's handle, not DID. 81 */ 82export function buildPostSourceKey(key: string, handle: string) { 83 const urip = new AtUri(key) 84 // @ts-expect-error TODO new-sdk-migration 85 urip.host = handle 86 return urip.toString() 87} 88 89/** 90 * Just a lil dev helper 91 */ 92function assertValidDevOnly(key: string, message: string, beChill = false) { 93 if (__DEV__) { 94 const urip = new AtUri(key) 95 if (urip.host.startsWith('did:')) { 96 if (beChill) { 97 logger.warn(message) 98 } else { 99 throw new Error(message) 100 } 101 } 102 } 103}