about summary refs log tree commit diff
path: root/src/state
diff options
context:
space:
mode:
authorEric Bailey <git@esb.lol>2025-06-11 13:22:02 -0500
committerGitHub <noreply@github.com>2025-06-11 13:22:02 -0500
commit143d5f3b814f1ce707fdfc87dabff7af5349bd06 (patch)
tree86cd639d45da0c994f8bf1d9160b1a8497f53e0c /src/state
parent7341294df6156afdf24ab43e1e27ebba94f265ad (diff)
downloadvoidsky-143d5f3b814f1ce707fdfc87dabff7af5349bd06.tar.zst
Post source handling updates (#8472)
* Add debugs

* Key post-source using URI with handle

* Enhance

* EnHANCE

* ENHANCE

* ENHANCEEEECEE

* ᵉⁿʰᵃⁿᶜᵉ

* enhance
Diffstat (limited to 'src/state')
-rw-r--r--src/state/feed-feedback.tsx8
-rw-r--r--src/state/unstable-post-source.tsx115
2 files changed, 82 insertions, 41 deletions
diff --git a/src/state/feed-feedback.tsx b/src/state/feed-feedback.tsx
index 225b495d3..a718a761d 100644
--- a/src/state/feed-feedback.tsx
+++ b/src/state/feed-feedback.tsx
@@ -12,7 +12,7 @@ import throttle from 'lodash.throttle'
 
 import {FEEDBACK_FEEDS, STAGING_FEEDS} from '#/lib/constants'
 import {logEvent} from '#/lib/statsig/statsig'
-import {logger} from '#/logger'
+import {Logger} from '#/logger'
 import {
   type FeedDescriptor,
   type FeedPostSliceItem,
@@ -20,6 +20,8 @@ import {
 import {getItemsForFeedback} from '#/view/com/posts/PostFeed'
 import {useAgent} from './session'
 
+const logger = Logger.create(Logger.Context.FeedFeedback)
+
 export type StateContext = {
   enabled: boolean
   onItemSeen: (item: any) => void
@@ -89,6 +91,7 @@ export function useFeedFeedback(
     }
     sendOrAggregateInteractionsForStats(aggregatedStats.current, interactions)
     throttledFlushAggregatedStats()
+    logger.debug('flushed')
   }, [agent, throttledFlushAggregatedStats, feed])
 
   const sendToFeed = useMemo(
@@ -141,6 +144,9 @@ export function useFeedFeedback(
       if (!enabled) {
         return
       }
+      logger.debug('sendInteraction', {
+        ...interaction,
+      })
       if (!history.current.has(interaction)) {
         history.current.add(interaction)
         queue.current.add(toString(interaction))
diff --git a/src/state/unstable-post-source.tsx b/src/state/unstable-post-source.tsx
index 43aac6f4d..ac126d79c 100644
--- a/src/state/unstable-post-source.tsx
+++ b/src/state/unstable-post-source.tsx
@@ -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)
-  }, [])
+/**
+ * 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`,
+  )
+  logger.debug('set', {key, source})
+  transientSources.set(key, source)
+}
 
-  const consumeUnstablePostSource = useCallback((uri: string) => {
-    const source = sourcesRef.current.get(uri)
+/**
+ * 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(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) {
-      sourcesRef.current.delete(uri)
+      logger.debug('consume', {id, key, source})
+      transientSources.delete(key)
+      consumedSources.set(id, source)
     }
     return source
-  }, [])
-
-  return (
-    <SetUnstablePostSourceContext.Provider value={setUnstablePostSource}>
-      <ConsumeUnstablePostSourceContext.Provider
-        value={consumeUnstablePostSource}>
-        {children}
-      </ConsumeUnstablePostSourceContext.Provider>
-    </SetUnstablePostSourceContext.Provider>
-  )
-}
+  })
 
-export function useSetUnstablePostSource() {
-  return useContext(SetUnstablePostSourceContext)
+  useEffect(() => {
+    return () => {
+      consumedSources.delete(id)
+      logger.debug('cleanup', {id})
+    }
+  }, [id])
+
+  return 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.
+ * Builds a post source key. This (atm) is a URI where the `host` is the post
+ * author's handle, not DID.
  */
-export function useUnstablePostSource(uri: string) {
-  const consume = useContext(ConsumeUnstablePostSourceContext)
+export function buildPostSourceKey(key: string, handle: string) {
+  const urip = new AtUri(key)
+  urip.host = handle
+  return urip.toString()
+}
 
-  const [source] = useState(() => consume(uri))
-  return source
+/**
+ * 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)
+    }
+  }
 }