migrate the notification feed and unread checker to the appview client
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -33,7 +33,7 @@ import {
|
||||
|
||||
import {useModerationOpts} from '#/state/preferences/moderation-opts'
|
||||
import {STALE} from '#/state/queries'
|
||||
import {useAgent} from '#/state/session'
|
||||
import {useAppviewClient} from '#/state/session'
|
||||
import {useThreadgateHiddenReplyUris} from '#/state/threadgate-hidden-replies'
|
||||
import type * as bsky from '#/types/bsky'
|
||||
import {
|
||||
@@ -60,7 +60,7 @@ export function useNotificationFeedQuery(opts: {
|
||||
enabled?: boolean
|
||||
filter: 'all' | 'mentions'
|
||||
}) {
|
||||
const agent = useAgent()
|
||||
const client = useAppviewClient()
|
||||
const queryClient = useQueryClient()
|
||||
const moderationOpts = useModerationOpts()
|
||||
const unreads = useUnreadNotificationsApi()
|
||||
@@ -106,7 +106,7 @@ export function useNotificationFeedQuery(opts: {
|
||||
]
|
||||
}
|
||||
const {page: fetchedPage} = await fetchPage({
|
||||
agent,
|
||||
client,
|
||||
limit: PAGE_SIZE,
|
||||
cursor: pageParam,
|
||||
queryClient,
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
useState,
|
||||
} from 'react'
|
||||
import {AppState} from 'react-native'
|
||||
import {type ISODatetimeString} from '@atproto/syntax'
|
||||
import {useQueryClient} from '@tanstack/react-query'
|
||||
import {EventEmitter} from 'eventemitter3'
|
||||
|
||||
@@ -18,7 +19,8 @@ import BroadcastChannel from '#/lib/broadcast'
|
||||
import {resetBadgeCount} from '#/lib/notifications/notifications'
|
||||
import {useModerationOpts} from '#/state/preferences/moderation-opts'
|
||||
import {truncateAndInvalidate} from '#/state/queries/util'
|
||||
import {useAgent, useSession} from '#/state/session'
|
||||
import {useAppviewClient, useSession} from '#/state/session'
|
||||
import {app} from '#/lexicons'
|
||||
import {RQKEY as RQKEY_NOTIFS} from './feed'
|
||||
import {type CachedFeedPage, type FeedPage} from './types'
|
||||
import {fetchPage} from './util'
|
||||
@@ -52,7 +54,7 @@ apiContext.displayName = 'NotificationsUnreadApiContext'
|
||||
|
||||
export function Provider({children}: React.PropsWithChildren<{}>) {
|
||||
const {hasSession} = useSession()
|
||||
const agent = useAgent()
|
||||
const client = useAppviewClient()
|
||||
const queryClient = useQueryClient()
|
||||
const moderationOpts = useModerationOpts()
|
||||
|
||||
@@ -120,9 +122,10 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
|
||||
return {
|
||||
async markAllRead() {
|
||||
// update server
|
||||
await agent.updateSeenNotifications(
|
||||
cacheRef.current.syncedAt.toISOString(),
|
||||
)
|
||||
await client.call(app.bsky.notification.updateSeen, {
|
||||
// toISOString always emits the Z-suffixed form the format requires
|
||||
seenAt: cacheRef.current.syncedAt.toISOString() as ISODatetimeString,
|
||||
})
|
||||
|
||||
// update & broadcast
|
||||
setNumUnread('')
|
||||
@@ -135,7 +138,7 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
|
||||
isPoll,
|
||||
}: {invalidate?: boolean; isPoll?: boolean} = {}) {
|
||||
try {
|
||||
if (!agent.session) return
|
||||
if (!hasSession) return
|
||||
if (AppState.currentState !== 'active') {
|
||||
return
|
||||
}
|
||||
@@ -156,7 +159,7 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
|
||||
|
||||
// count
|
||||
const {page, indexedAt: lastIndexed} = await fetchPage({
|
||||
agent,
|
||||
client,
|
||||
cursor: undefined,
|
||||
limit: 40,
|
||||
queryClient,
|
||||
@@ -207,7 +210,7 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
|
||||
}
|
||||
},
|
||||
}
|
||||
}, [setNumUnread, queryClient, moderationOpts, agent])
|
||||
}, [setNumUnread, queryClient, moderationOpts, client, hasSession])
|
||||
checkUnreadRef.current = api.checkUnread
|
||||
|
||||
return (
|
||||
|
||||
@@ -6,15 +6,17 @@ import {
|
||||
type AppBskyGraphDefs,
|
||||
AppBskyGraphStarterpack,
|
||||
type AppBskyNotificationListNotifications,
|
||||
type AtpAgent,
|
||||
hasMutedWord,
|
||||
moderateNotification,
|
||||
type ModerationOpts,
|
||||
} from '@atproto/api'
|
||||
import {type Client} from '@atproto/lex'
|
||||
import {type AtUriString} from '@atproto/syntax'
|
||||
import {type QueryClient} from '@tanstack/react-query'
|
||||
import chunk from 'lodash.chunk'
|
||||
|
||||
import {labelIsHideableOffense} from '#/lib/moderation'
|
||||
import {app} from '#/lexicons'
|
||||
import * as bsky from '#/types/bsky'
|
||||
import {precacheProfile} from '../profile'
|
||||
import {
|
||||
@@ -38,7 +40,7 @@ const MS_2DAY = MS_1HR * 48
|
||||
// =
|
||||
|
||||
export async function fetchPage({
|
||||
agent,
|
||||
client,
|
||||
cursor,
|
||||
limit,
|
||||
queryClient,
|
||||
@@ -46,7 +48,7 @@ export async function fetchPage({
|
||||
fetchAdditionalData,
|
||||
reasons,
|
||||
}: {
|
||||
agent: AtpAgent
|
||||
client: Client
|
||||
cursor: string | undefined
|
||||
limit: number
|
||||
queryClient: QueryClient
|
||||
@@ -57,16 +59,16 @@ export async function fetchPage({
|
||||
page: FeedPage
|
||||
indexedAt: string | undefined
|
||||
}> {
|
||||
const res = await agent.listNotifications({
|
||||
const data = await client.call(app.bsky.notification.listNotifications, {
|
||||
limit,
|
||||
cursor,
|
||||
reasons,
|
||||
})
|
||||
|
||||
const indexedAt = res.data.notifications[0]?.indexedAt
|
||||
const indexedAt = data.notifications[0]?.indexedAt
|
||||
|
||||
// filter out notifs by mod rules
|
||||
const notifs = res.data.notifications.filter(
|
||||
const notifs = data.notifications.filter(
|
||||
notif => !shouldFilterNotif(notif, moderationOpts),
|
||||
)
|
||||
|
||||
@@ -76,7 +78,7 @@ export async function fetchPage({
|
||||
// we fetch subjects of notifications (usually posts) now instead of lazily
|
||||
// in the UI to avoid relayouts
|
||||
if (fetchAdditionalData) {
|
||||
const subjects = await fetchSubjects(agent, notifsGrouped)
|
||||
const subjects = await fetchSubjects(client, notifsGrouped)
|
||||
for (const notif of notifsGrouped) {
|
||||
if (notif.subjectUri) {
|
||||
if (
|
||||
@@ -96,17 +98,17 @@ export async function fetchPage({
|
||||
}
|
||||
}
|
||||
|
||||
let seenAt = res.data.seenAt ? new Date(res.data.seenAt) : new Date()
|
||||
let seenAt = data.seenAt ? new Date(data.seenAt) : new Date()
|
||||
if (Number.isNaN(seenAt.getTime())) {
|
||||
seenAt = new Date()
|
||||
}
|
||||
|
||||
return {
|
||||
page: {
|
||||
cursor: res.data.cursor,
|
||||
cursor: data.cursor,
|
||||
seenAt,
|
||||
items: notifsGrouped,
|
||||
priority: res.data.priority ?? false,
|
||||
priority: data.priority ?? false,
|
||||
},
|
||||
indexedAt,
|
||||
}
|
||||
@@ -207,7 +209,7 @@ export function groupNotifications(
|
||||
}
|
||||
|
||||
async function fetchSubjects(
|
||||
agent: AtpAgent,
|
||||
client: Client,
|
||||
groupedNotifs: FeedNotification[],
|
||||
): Promise<{
|
||||
posts: Map<string, AppBskyFeedDefs.PostView>
|
||||
@@ -224,18 +226,22 @@ async function fetchSubjects(
|
||||
packUris.add(notif.notification.reasonSubject)
|
||||
}
|
||||
}
|
||||
const postUriChunks = chunk(Array.from(postUris), 25)
|
||||
const packUriChunks = chunk(Array.from(packUris), 25)
|
||||
/*
|
||||
* Both uri sets are collected from notification fields the server already
|
||||
* validated as at-uris, so the branded cast reflects what the values are.
|
||||
*/
|
||||
const postUriChunks = chunk(Array.from(postUris) as AtUriString[], 25)
|
||||
const packUriChunks = chunk(Array.from(packUris) as AtUriString[], 25)
|
||||
const postsChunks = await Promise.all(
|
||||
postUriChunks.map(uris =>
|
||||
agent.app.bsky.feed.getPosts({uris}).then(res => res.data.posts),
|
||||
client.call(app.bsky.feed.getPosts, {uris}).then(data => data.posts),
|
||||
),
|
||||
)
|
||||
const packsChunks = await Promise.all(
|
||||
packUriChunks.map(uris =>
|
||||
agent.app.bsky.graph
|
||||
.getStarterPacks({uris})
|
||||
.then(res => res.data.starterPacks),
|
||||
client
|
||||
.call(app.bsky.graph.getStarterPacks, {uris})
|
||||
.then(data => data.starterPacks),
|
||||
),
|
||||
)
|
||||
const postsMap = new Map<string, AppBskyFeedDefs.PostView>()
|
||||
|
||||
Reference in New Issue
Block a user