···5858import {Provider as SelectedFeedProvider} from '#/state/shell/selected-feed'
5959import {Provider as StarterPackProvider} from '#/state/shell/starter-pack'
6060import {Provider as HiddenRepliesProvider} from '#/state/threadgate-hidden-replies'
6161-import {Provider as UnstablePostSourceProvider} from '#/state/unstable-post-source'
6261import {TestCtrls} from '#/view/com/testing/TestCtrls'
6362import {Provider as VideoVolumeProvider} from '#/view/com/util/post-embeds/VideoVolumeContext'
6463import * as Toast from '#/view/com/util/Toast'
···151150 <MutedThreadsProvider>
152151 <ProgressGuideProvider>
153152 <ServiceAccountManager>
154154- <UnstablePostSourceProvider>
155155- <GestureHandlerRootView
156156- style={s.h100pct}>
157157- <IntentDialogProvider>
158158- <TestCtrls />
159159- <Shell />
160160- <NuxDialogs />
161161- </IntentDialogProvider>
162162- </GestureHandlerRootView>
163163- </UnstablePostSourceProvider>
153153+ <GestureHandlerRootView
154154+ style={s.h100pct}>
155155+ <IntentDialogProvider>
156156+ <TestCtrls />
157157+ <Shell />
158158+ <NuxDialogs />
159159+ </IntentDialogProvider>
160160+ </GestureHandlerRootView>
164161 </ServiceAccountManager>
165162 </ProgressGuideProvider>
166163 </MutedThreadsProvider>
+4-7
src/App.web.tsx
···4848import {Provider as SelectedFeedProvider} from '#/state/shell/selected-feed'
4949import {Provider as StarterPackProvider} from '#/state/shell/starter-pack'
5050import {Provider as HiddenRepliesProvider} from '#/state/threadgate-hidden-replies'
5151-import {Provider as UnstablePostSourceProvider} from '#/state/unstable-post-source'
5251import {Provider as ActiveVideoProvider} from '#/view/com/util/post-embeds/ActiveVideoWebContext'
5352import {Provider as VideoVolumeProvider} from '#/view/com/util/post-embeds/VideoVolumeContext'
5453import * as Toast from '#/view/com/util/Toast'
···132131 <SafeAreaProvider>
133132 <ProgressGuideProvider>
134133 <ServiceConfigProvider>
135135- <UnstablePostSourceProvider>
136136- <IntentDialogProvider>
137137- <Shell />
138138- <NuxDialogs />
139139- </IntentDialogProvider>
140140- </UnstablePostSourceProvider>
134134+ <IntentDialogProvider>
135135+ <Shell />
136136+ <NuxDialogs />
137137+ </IntentDialogProvider>
141138 </ServiceConfigProvider>
142139 </ProgressGuideProvider>
143140 </SafeAreaProvider>
+2
src/logger/types.ts
···1010 ConversationAgent = 'conversation-agent',
1111 DMsAgent = 'dms-agent',
1212 ReportDialog = 'report-dialog',
1313+ FeedFeedback = 'feed-feedback',
1414+ PostSource = 'post-source',
13151416 /**
1517 * METRIC IS FOR INTERNAL USE ONLY, don't create any other loggers using this
+7-1
src/state/feed-feedback.tsx
···12121313import {FEEDBACK_FEEDS, STAGING_FEEDS} from '#/lib/constants'
1414import {logEvent} from '#/lib/statsig/statsig'
1515-import {logger} from '#/logger'
1515+import {Logger} from '#/logger'
1616import {
1717 type FeedDescriptor,
1818 type FeedPostSliceItem,
1919} from '#/state/queries/post-feed'
2020import {getItemsForFeedback} from '#/view/com/posts/PostFeed'
2121import {useAgent} from './session'
2222+2323+const logger = Logger.create(Logger.Context.FeedFeedback)
22242325export type StateContext = {
2426 enabled: boolean
···8991 }
9092 sendOrAggregateInteractionsForStats(aggregatedStats.current, interactions)
9193 throttledFlushAggregatedStats()
9494+ logger.debug('flushed')
9295 }, [agent, throttledFlushAggregatedStats, feed])
93969497 const sendToFeed = useMemo(
···141144 if (!enabled) {
142145 return
143146 }
147147+ logger.debug('sendInteraction', {
148148+ ...interaction,
149149+ })
144150 if (!history.current.has(interaction)) {
145151 history.current.add(interaction)
146152 queue.current.add(toString(interaction))
+74-39
src/state/unstable-post-source.tsx
···11-import {createContext, useCallback, useContext, useRef, useState} from 'react'
22-import {type AppBskyFeedDefs} from '@atproto/api'
11+import {useEffect, useId, useState} from 'react'
22+import {type AppBskyFeedDefs, AtUri} from '@atproto/api'
3344-import {type FeedDescriptor} from './queries/post-feed'
44+import {Logger} from '#/logger'
55+import {type FeedDescriptor} from '#/state/queries/post-feed'
5667/**
77- * For passing the source of the post (i.e. the original post, from the feed) to the threadview,
88- * without using query params. Deliberately unstable to avoid using query params, use for FeedFeedback
99- * and other ephemeral non-critical systems.
88+ * Separate logger for better debugging
109 */
1010+const logger = Logger.create(Logger.Context.PostSource)
11111212-type Source = {
1212+export type PostSource = {
1313 post: AppBskyFeedDefs.FeedViewPost
1414 feed?: FeedDescriptor
1515}
16161717-const SetUnstablePostSourceContext = createContext<
1818- (key: string, source: Source) => void
1919->(() => {})
2020-const ConsumeUnstablePostSourceContext = createContext<
2121- (uri: string) => Source | undefined
2222->(() => undefined)
1717+/**
1818+ * A cache of sources that will be consumed by the post thread view. This is
1919+ * cleaned up any time a source is consumed.
2020+ */
2121+const transientSources = new Map<string, PostSource>()
23222424-export function Provider({children}: {children: React.ReactNode}) {
2525- const sourcesRef = useRef<Map<string, Source>>(new Map())
2323+/**
2424+ * A cache of sources that have been consumed by the post thread view. This is
2525+ * not cleaned up, but because we use a new ID for each post thread view that
2626+ * consumes a source, this is never reused unless a user navigates back to a
2727+ * post thread view that has not been dropped from memory.
2828+ */
2929+const consumedSources = new Map<string, PostSource>()
26302727- const setUnstablePostSource = useCallback((key: string, source: Source) => {
2828- sourcesRef.current.set(key, source)
2929- }, [])
3131+/**
3232+ * For stashing the feed that the user was browsing when they clicked on a post.
3333+ *
3434+ * Used for FeedFeedback and other ephemeral non-critical systems.
3535+ */
3636+export function setUnstablePostSource(key: string, source: PostSource) {
3737+ assertValid(
3838+ key,
3939+ `setUnstablePostSource key should be a URI containing a handle, received ${key} — use buildPostSourceKey`,
4040+ )
4141+ logger.debug('set', {key, source})
4242+ transientSources.set(key, source)
4343+}
30443131- const consumeUnstablePostSource = useCallback((uri: string) => {
3232- const source = sourcesRef.current.get(uri)
4545+/**
4646+ * This hook is unstable and should only be used for FeedFeedback and other
4747+ * ephemeral non-critical systems. Views that use this hook will continue to
4848+ * return a reference to the same source until those views are dropped from
4949+ * memory.
5050+ */
5151+export function useUnstablePostSource(key: string) {
5252+ const id = useId()
5353+ const [source] = useState(() => {
5454+ assertValid(
5555+ key,
5656+ `consumeUnstablePostSource key should be a URI containing a handle, received ${key} — use buildPostSourceKey`,
5757+ )
5858+ const source = consumedSources.get(id) || transientSources.get(key)
3359 if (source) {
3434- sourcesRef.current.delete(uri)
6060+ logger.debug('consume', {id, key, source})
6161+ transientSources.delete(key)
6262+ consumedSources.set(id, source)
3563 }
3664 return source
3737- }, [])
6565+ })
38663939- return (
4040- <SetUnstablePostSourceContext.Provider value={setUnstablePostSource}>
4141- <ConsumeUnstablePostSourceContext.Provider
4242- value={consumeUnstablePostSource}>
4343- {children}
4444- </ConsumeUnstablePostSourceContext.Provider>
4545- </SetUnstablePostSourceContext.Provider>
4646- )
4747-}
6767+ useEffect(() => {
6868+ return () => {
6969+ consumedSources.delete(id)
7070+ logger.debug('cleanup', {id})
7171+ }
7272+ }, [id])
48734949-export function useSetUnstablePostSource() {
5050- return useContext(SetUnstablePostSourceContext)
7474+ return source
5175}
52765377/**
5454- * DANGER - This hook is unstable and should only be used for FeedFeedback
5555- * and other ephemeral non-critical systems. Does not change when the URI changes.
7878+ * Builds a post source key. This (atm) is a URI where the `host` is the post
7979+ * author's handle, not DID.
5680 */
5757-export function useUnstablePostSource(uri: string) {
5858- const consume = useContext(ConsumeUnstablePostSourceContext)
8181+export function buildPostSourceKey(key: string, handle: string) {
8282+ const urip = new AtUri(key)
8383+ urip.host = handle
8484+ return urip.toString()
8585+}
59866060- const [source] = useState(() => consume(uri))
6161- return source
8787+/**
8888+ * Just a lil dev helper
8989+ */
9090+function assertValid(key: string, message: string) {
9191+ if (__DEV__) {
9292+ const urip = new AtUri(key)
9393+ if (urip.host.startsWith('did:')) {
9494+ throw new Error(message)
9595+ }
9696+ }
6297}