about summary refs log tree commit diff
path: root/src/state/unstable-post-source.tsx
blob: 17fe18840935647868f7e9fe92070477807e6a69 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
import {useEffect, useId, useState} from 'react'
import {type AppBskyFeedDefs, AtUri} from '@atproto/api'

import {Logger} from '#/logger'
import {type FeedSourceInfo} from '#/state/queries/feed'

/**
 * Separate logger for better debugging
 */
const logger = Logger.create(Logger.Context.PostSource)

export type PostSource = {
  post: AppBskyFeedDefs.FeedViewPost
  feedSourceInfo?: FeedSourceInfo
}

/**
 * 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>()

/**
 * 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>()

/**
 * 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) {
  assertValidDevOnly(
    key,
    `setUnstablePostSource key should be a URI containing a handle, received ${key} — use buildPostSourceKey`,
  )
  logger.debug('set', {key, source})
  transientSources.set(key, source)
}

/**
 * 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(() => {
    assertValidDevOnly(
      key,
      `consumeUnstablePostSource key should be a URI containing a handle, received ${key} — be sure to use buildPostSourceKey when setting the source`,
      true,
    )
    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])

  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 assertValidDevOnly(key: string, message: string, beChill = false) {
  if (__DEV__) {
    const urip = new AtUri(key)
    if (urip.host.startsWith('did:')) {
      if (beChill) {
        logger.warn(message)
      } else {
        throw new Error(message)
      }
    }
  }
}