diff --git a/src/App.native.tsx b/src/App.native.tsx
index 80e7d57f32..25d186dcfb 100644
--- a/src/App.native.tsx
+++ b/src/App.native.tsx
@@ -58,7 +58,6 @@ import {Provider as ProgressGuideProvider} from '#/state/shell/progress-guide'
import {Provider as SelectedFeedProvider} from '#/state/shell/selected-feed'
import {Provider as StarterPackProvider} from '#/state/shell/starter-pack'
import {Provider as HiddenRepliesProvider} from '#/state/threadgate-hidden-replies'
-import {Provider as UnstablePostSourceProvider} from '#/state/unstable-post-source'
import {TestCtrls} from '#/view/com/testing/TestCtrls'
import {Provider as VideoVolumeProvider} from '#/view/com/util/post-embeds/VideoVolumeContext'
import * as Toast from '#/view/com/util/Toast'
@@ -152,18 +151,16 @@ function InnerApp() {
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
diff --git a/src/App.web.tsx b/src/App.web.tsx
index ca23d0c668..fa8e24e53d 100644
--- a/src/App.web.tsx
+++ b/src/App.web.tsx
@@ -48,7 +48,6 @@ import {Provider as ProgressGuideProvider} from '#/state/shell/progress-guide'
import {Provider as SelectedFeedProvider} from '#/state/shell/selected-feed'
import {Provider as StarterPackProvider} from '#/state/shell/starter-pack'
import {Provider as HiddenRepliesProvider} from '#/state/threadgate-hidden-replies'
-import {Provider as UnstablePostSourceProvider} from '#/state/unstable-post-source'
import {Provider as ActiveVideoProvider} from '#/view/com/util/post-embeds/ActiveVideoWebContext'
import {Provider as VideoVolumeProvider} from '#/view/com/util/post-embeds/VideoVolumeContext'
import * as Toast from '#/view/com/util/Toast'
@@ -133,14 +132,12 @@ function InnerApp() {
-
-
-
-
-
-
-
-
+
+
+
+
+
+
diff --git a/src/components/Link.tsx b/src/components/Link.tsx
index d73a3db4ae..49c9c52358 100644
--- a/src/components/Link.tsx
+++ b/src/components/Link.tsx
@@ -4,7 +4,6 @@ import {sanitizeUrl} from '@braintree/sanitize-url'
import {
type LinkProps as RNLinkProps,
StackActions,
- useLinkBuilder,
} from '@react-navigation/native'
import {BSKY_DOWNLOAD_URL} from '#/lib/constants'
@@ -95,20 +94,19 @@ export function useLink({
shouldProxy?: boolean
}) {
const navigation = useNavigationDeduped()
- const {buildHref} = useLinkBuilder()
const href = useMemo(() => {
return typeof to === 'string'
? convertBskyAppUrlIfNeeded(sanitizeUrl(to))
: to.screen
- ? buildHref(to.screen, to.params)
+ ? router.matchName(to.screen)?.build(to.params)
: to.href
? convertBskyAppUrlIfNeeded(sanitizeUrl(to.href))
: undefined
- }, [to, buildHref])
+ }, [to])
if (!href) {
throw new Error(
- 'Link `to` prop must be a string or an object with `screen` and `params` properties',
+ 'Could not resolve screen. Link `to` prop must be a string or an object with `screen` and `params` properties',
)
}
diff --git a/src/lib/routes/router.ts b/src/lib/routes/router.ts
index 45f9c85fdb..ba76b1bdac 100644
--- a/src/lib/routes/router.ts
+++ b/src/lib/routes/router.ts
@@ -1,4 +1,4 @@
-import {Route, RouteParams} from './types'
+import {type Route, type RouteParams} from './types'
export class Router {
routes: [string, Route][] = []
@@ -45,7 +45,7 @@ function createRoute(pattern: string): Route {
})
const matcherRe = new RegExp(`^${matcherReInternal}([?]|$)`, 'i')
return {
- match(path: string) {
+ match(path) {
const {pathname, searchParams} = new URL(path, 'http://throwaway.com')
const addedParams = Object.fromEntries(searchParams.entries())
@@ -55,10 +55,10 @@ function createRoute(pattern: string): Route {
}
return undefined
},
- build(params: Record) {
+ build(params = {}) {
const str = pattern.replace(
/:([\w]+)/g,
- (_m, name) => params[name] || 'undefined',
+ (_m, name) => params[encodeURIComponent(name)] || 'undefined',
)
let hasQp = false
diff --git a/src/lib/routes/types.ts b/src/lib/routes/types.ts
index 6f102d438a..f587423900 100644
--- a/src/lib/routes/types.ts
+++ b/src/lib/routes/types.ts
@@ -143,5 +143,5 @@ export type RouteParams = Record
export type MatchResult = {params: RouteParams}
export type Route = {
match: (path: string) => MatchResult | undefined
- build: (params: RouteParams) => string
+ build: (params?: Record) => string
}
diff --git a/src/logger/types.ts b/src/logger/types.ts
index d14e21a9d4..88d8d9d93d 100644
--- a/src/logger/types.ts
+++ b/src/logger/types.ts
@@ -10,6 +10,8 @@ export enum LogContext {
ConversationAgent = 'conversation-agent',
DMsAgent = 'dms-agent',
ReportDialog = 'report-dialog',
+ FeedFeedback = 'feed-feedback',
+ PostSource = 'post-source',
/**
* METRIC IS FOR INTERNAL USE ONLY, don't create any other loggers using this
diff --git a/src/state/feed-feedback.tsx b/src/state/feed-feedback.tsx
index 225b495d3f..a718a761d5 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 43aac6f4d0..ac126d79c6 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()
-export function Provider({children}: {children: React.ReactNode}) {
- const sourcesRef = useRef