Post source handling updates (#8472)

* Add debugs

* Key post-source using URI with handle

* Enhance

* EnHANCE

* ENHANCE

* ENHANCEEEECEE

* ᵉⁿʰᵃⁿᶜᵉ

* enhance
This commit is contained in:
Eric Bailey
2025-06-11 13:22:02 -05:00
committed by GitHub
parent 7341294df6
commit 143d5f3b81
8 changed files with 148 additions and 85 deletions
+78 -43
View File
@@ -1,62 +1,97 @@
import {createContext, useCallback, useContext, useRef, useState} from 'react'
import {type AppBskyFeedDefs} from '@atproto/api'
import {useEffect, useId, useState} from 'react'
import {type AppBskyFeedDefs, AtUri} from '@atproto/api'
import {type FeedDescriptor} from './queries/post-feed'
import {Logger} from '#/logger'
import {type FeedDescriptor} from '#/state/queries/post-feed'
/**
* For passing the source of the post (i.e. the original post, from the feed) to the threadview,
* without using query params. Deliberately unstable to avoid using query params, use for FeedFeedback
* and other ephemeral non-critical systems.
* Separate logger for better debugging
*/
const logger = Logger.create(Logger.Context.PostSource)
type Source = {
export type PostSource = {
post: AppBskyFeedDefs.FeedViewPost
feed?: FeedDescriptor
}
const SetUnstablePostSourceContext = createContext<
(key: string, source: Source) => void
>(() => {})
const ConsumeUnstablePostSourceContext = createContext<
(uri: string) => Source | undefined
>(() => undefined)
/**
* A cache of sources that will be consumed by the post thread view. This is
* cleaned up any time a source is consumed.
*/
const transientSources = new Map<string, PostSource>()
export function Provider({children}: {children: React.ReactNode}) {
const sourcesRef = useRef<Map<string, Source>>(new Map())
/**
* A cache of sources that have been consumed by the post thread view. This is
* not cleaned up, but because we use a new ID for each post thread view that
* consumes a source, this is never reused unless a user navigates back to a
* post thread view that has not been dropped from memory.
*/
const consumedSources = new Map<string, PostSource>()
const setUnstablePostSource = useCallback((key: string, source: Source) => {
sourcesRef.current.set(key, source)
}, [])
const consumeUnstablePostSource = useCallback((uri: string) => {
const source = sourcesRef.current.get(uri)
if (source) {
sourcesRef.current.delete(uri)
}
return source
}, [])
return (
<SetUnstablePostSourceContext.Provider value={setUnstablePostSource}>
<ConsumeUnstablePostSourceContext.Provider
value={consumeUnstablePostSource}>
{children}
</ConsumeUnstablePostSourceContext.Provider>
</SetUnstablePostSourceContext.Provider>
/**
* For stashing the feed that the user was browsing when they clicked on a post.
*
* Used for FeedFeedback and other ephemeral non-critical systems.
*/
export function setUnstablePostSource(key: string, source: PostSource) {
assertValid(
key,
`setUnstablePostSource key should be a URI containing a handle, received ${key} — use buildPostSourceKey`,
)
}
export function useSetUnstablePostSource() {
return useContext(SetUnstablePostSourceContext)
logger.debug('set', {key, source})
transientSources.set(key, source)
}
/**
* DANGER - This hook is unstable and should only be used for FeedFeedback
* and other ephemeral non-critical systems. Does not change when the URI changes.
* This hook is unstable and should only be used for FeedFeedback and other
* ephemeral non-critical systems. Views that use this hook will continue to
* return a reference to the same source until those views are dropped from
* memory.
*/
export function useUnstablePostSource(uri: string) {
const consume = useContext(ConsumeUnstablePostSourceContext)
export function useUnstablePostSource(key: string) {
const id = useId()
const [source] = useState(() => {
assertValid(
key,
`consumeUnstablePostSource key should be a URI containing a handle, received ${key} — use buildPostSourceKey`,
)
const source = consumedSources.get(id) || transientSources.get(key)
if (source) {
logger.debug('consume', {id, key, source})
transientSources.delete(key)
consumedSources.set(id, source)
}
return source
})
useEffect(() => {
return () => {
consumedSources.delete(id)
logger.debug('cleanup', {id})
}
}, [id])
const [source] = useState(() => consume(uri))
return source
}
/**
* Builds a post source key. This (atm) is a URI where the `host` is the post
* author's handle, not DID.
*/
export function buildPostSourceKey(key: string, handle: string) {
const urip = new AtUri(key)
urip.host = handle
return urip.toString()
}
/**
* Just a lil dev helper
*/
function assertValid(key: string, message: string) {
if (__DEV__) {
const urip = new AtUri(key)
if (urip.host.startsWith('did:')) {
throw new Error(message)
}
}
}