···244244 notif.reason === 'follow' ||
245245 notif.reason === 'starterpack-joined' ||
246246 notif.reason === 'verified' ||
247247- notif.reason === 'unverified'
247247+ notif.reason === 'unverified' ||
248248+ notif.reason === 'like-via-repost' ||
249249+ notif.reason === 'repost-via-repost'
248250 ) {
249251 return notif.reason as NotificationType
250252 }
···257259): string | undefined {
258260 if (type === 'reply' || type === 'quote' || type === 'mention') {
259261 return notif.uri
260260- } else if (type === 'post-like' || type === 'repost') {
262262+ } else if (
263263+ type === 'post-like' ||
264264+ type === 'repost' ||
265265+ type === 'like-via-repost' ||
266266+ type === 'repost-via-repost'
267267+ ) {
261268 if (
262269 bsky.dangerousIsType<AppBskyFeedRepost.Record>(
263270 notif.record,
+13-9
src/state/queries/post.ts
···11import {useCallback} from 'react'
22-import {AppBskyActorDefs, AppBskyFeedDefs, AtUri} from '@atproto/api'
22+import {type AppBskyActorDefs, type AppBskyFeedDefs, AtUri} from '@atproto/api'
33import {useMutation, useQuery, useQueryClient} from '@tanstack/react-query'
4455import {useToggleMutationQueue} from '#/lib/hooks/useToggleMutationQueue'
66-import {logEvent, LogEvents, toClout} from '#/lib/statsig/statsig'
66+import {logEvent, type LogEvents, toClout} from '#/lib/statsig/statsig'
77import {updatePostShadow} from '#/state/cache/post-shadow'
88-import {Shadow} from '#/state/cache/types'
88+import {type Shadow} from '#/state/cache/types'
99import {useAgent, useSession} from '#/state/session'
1010import * as userActionHistory from '#/state/userActionHistory'
1111import {useIsThreadMuted, useSetThreadMute} from '../cache/thread-mutes'
···98989999export function usePostLikeMutationQueue(
100100 post: Shadow<AppBskyFeedDefs.PostView>,
101101+ viaRepost: {uri: string; cid: string} | undefined,
101102 logContext: LogEvents['post:like']['logContext'] &
102103 LogEvents['post:unlike']['logContext'],
103104) {
···115116 const {uri: likeUri} = await likeMutation.mutateAsync({
116117 uri: postUri,
117118 cid: postCid,
119119+ via: viaRepost,
118120 })
119121 userActionHistory.like([postUri])
120122 return likeUri
···167169 return useMutation<
168170 {uri: string}, // responds with the uri of the like
169171 Error,
170170- {uri: string; cid: string} // the post's uri and cid
172172+ {uri: string; cid: string; via?: {uri: string; cid: string}} // the post's uri and cid, and the repost uri/cid if present
171173 >({
172172- mutationFn: ({uri, cid}) => {
174174+ mutationFn: ({uri, cid, via}) => {
173175 let ownProfile: AppBskyActorDefs.ProfileViewDetailed | undefined
174176 if (currentAccount) {
175177 ownProfile = findProfileQueryData(queryClient, currentAccount.did)
···190192 ? toClout(post.likeCount + post.repostCount + post.replyCount)
191193 : undefined,
192194 })
193193- return agent.like(uri, cid)
195195+ return agent.like(uri, cid, via)
194196 },
195197 })
196198}
···209211210212export function usePostRepostMutationQueue(
211213 post: Shadow<AppBskyFeedDefs.PostView>,
214214+ viaRepost: {uri: string; cid: string} | undefined,
212215 logContext: LogEvents['post:repost']['logContext'] &
213216 LogEvents['post:unrepost']['logContext'],
214217) {
···226229 const {uri: repostUri} = await repostMutation.mutateAsync({
227230 uri: postUri,
228231 cid: postCid,
232232+ via: viaRepost,
229233 })
230234 return repostUri
231235 } else {
···272276 return useMutation<
273277 {uri: string}, // responds with the uri of the repost
274278 Error,
275275- {uri: string; cid: string} // the post's uri and cid
279279+ {uri: string; cid: string; via?: {uri: string; cid: string}} // the post's uri and cid, and the repost uri/cid if present
276280 >({
277277- mutationFn: post => {
281281+ mutationFn: ({uri, cid, via}) => {
278282 logEvent('post:repost', {logContext})
279279- return agent.repost(post.uri, post.cid)
283283+ return agent.repost(uri, cid, via)
280284 },
281285 })
282286}
+73
src/state/unstable-post-source.tsx
···11+import {createContext, useCallback, useContext, useState} from 'react'
22+import {type AppBskyFeedDefs} from '@atproto/api'
33+44+import {type FeedDescriptor} from './queries/post-feed'
55+66+/**
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.
1010+ */
1111+1212+type Source = {
1313+ post: AppBskyFeedDefs.FeedViewPost
1414+ feed?: FeedDescriptor
1515+}
1616+1717+const SetUnstablePostSourceContext = createContext<
1818+ (key: string, source: Source) => void
1919+>(() => {})
2020+const ConsumeUnstablePostSourceContext = createContext<
2121+ (uri: string) => Source | undefined
2222+>(() => undefined)
2323+2424+export function Provider({children}: {children: React.ReactNode}) {
2525+ const [sources, setSources] = useState<Map<string, Source>>(() => new Map())
2626+2727+ const setUnstablePostSource = useCallback((key: string, source: Source) => {
2828+ setSources(prev => {
2929+ const newMap = new Map(prev)
3030+ newMap.set(key, source)
3131+ return newMap
3232+ })
3333+ }, [])
3434+3535+ const consumeUnstablePostSource = useCallback(
3636+ (uri: string) => {
3737+ const source = sources.get(uri)
3838+ if (source) {
3939+ setSources(prev => {
4040+ const newMap = new Map(prev)
4141+ newMap.delete(uri)
4242+ return newMap
4343+ })
4444+ }
4545+ return source
4646+ },
4747+ [sources],
4848+ )
4949+5050+ return (
5151+ <SetUnstablePostSourceContext.Provider value={setUnstablePostSource}>
5252+ <ConsumeUnstablePostSourceContext.Provider
5353+ value={consumeUnstablePostSource}>
5454+ {children}
5555+ </ConsumeUnstablePostSourceContext.Provider>
5656+ </SetUnstablePostSourceContext.Provider>
5757+ )
5858+}
5959+6060+export function useSetUnstablePostSource() {
6161+ return useContext(SetUnstablePostSourceContext)
6262+}
6363+6464+/**
6565+ * DANGER - This hook is unstable and should only be used for FeedFeedback
6666+ * and other ephemeral non-critical systems. Does not change when the URI changes.
6767+ */
6868+export function useUnstablePostSource(uri: string) {
6969+ const consume = useContext(ConsumeUnstablePostSourceContext)
7070+7171+ const [source] = useState(() => consume(uri))
7272+ return source
7373+}
+2-2
src/view/com/notifications/NotificationFeed.tsx
···11import React from 'react'
22import {
33 ActivityIndicator,
44- ListRenderItemInfo,
44+ type ListRenderItemInfo,
55 StyleSheet,
66 View,
77} from 'react-native'
···1616import {useNotificationFeedQuery} from '#/state/queries/notifications/feed'
1717import {EmptyState} from '#/view/com/util/EmptyState'
1818import {ErrorMessage} from '#/view/com/util/error/ErrorMessage'
1919-import {List, ListRef} from '#/view/com/util/List'
1919+import {List, type ListRef} from '#/view/com/util/List'
2020import {NotificationFeedLoadingPlaceholder} from '#/view/com/util/LoadingPlaceholder'
2121import {LoadMoreRetryBtn} from '#/view/com/util/LoadMoreRetryBtn'
2222import {NotificationFeedItem} from './NotificationFeedItem'