[Notifications] Add a Mentions tab (#7044)
* Split out NotificationsTab * Remove unused route parameter * Refine the split between components * Hoist some logic out of NotificationFeed * Remove unused option * Add all|conversations to query, hardcode "all" * Add a Conversations tab * Rename to Mentions * Bump packages * Rename fields * Fix oopsie * Simplify header * Track active tab * Fix types * Separate logic for tabs * Better border for first unread * Highlight unread for all only * Fix spinner races * Fix fetchPage races * Fix bottom bar border being obscured by glimmer * Remember last tab within the session * One tab at a time * Fix TS * Handle all RQKEY usages * Nit
This commit is contained in:
@@ -52,25 +52,22 @@ const PAGE_SIZE = 30
|
||||
type RQPageParam = string | undefined
|
||||
|
||||
const RQKEY_ROOT = 'notification-feed'
|
||||
export function RQKEY(priority?: false) {
|
||||
return [RQKEY_ROOT, priority]
|
||||
export function RQKEY(filter: 'all' | 'mentions') {
|
||||
return [RQKEY_ROOT, filter]
|
||||
}
|
||||
|
||||
export function useNotificationFeedQuery(opts?: {
|
||||
export function useNotificationFeedQuery(opts: {
|
||||
enabled?: boolean
|
||||
overridePriorityNotifications?: boolean
|
||||
filter: 'all' | 'mentions'
|
||||
}) {
|
||||
const agent = useAgent()
|
||||
const queryClient = useQueryClient()
|
||||
const moderationOpts = useModerationOpts()
|
||||
const unreads = useUnreadNotificationsApi()
|
||||
const enabled = opts?.enabled !== false
|
||||
const enabled = opts.enabled !== false
|
||||
const filter = opts.filter
|
||||
const {uris: hiddenReplyUris} = useThreadgateHiddenReplyUris()
|
||||
|
||||
// false: force showing all notifications
|
||||
// undefined: let the server decide
|
||||
const priority = opts?.overridePriorityNotifications ? false : undefined
|
||||
|
||||
const selectArgs = useMemo(() => {
|
||||
return {
|
||||
moderationOpts,
|
||||
@@ -91,14 +88,23 @@ export function useNotificationFeedQuery(opts?: {
|
||||
RQPageParam
|
||||
>({
|
||||
staleTime: STALE.INFINITY,
|
||||
queryKey: RQKEY(priority),
|
||||
queryKey: RQKEY(filter),
|
||||
async queryFn({pageParam}: {pageParam: RQPageParam}) {
|
||||
let page
|
||||
if (!pageParam) {
|
||||
if (filter === 'all' && !pageParam) {
|
||||
// for the first page, we check the cached page held by the unread-checker first
|
||||
page = unreads.getCachedUnreadPage()
|
||||
}
|
||||
if (!page) {
|
||||
let reasons: string[] = []
|
||||
if (filter === 'mentions') {
|
||||
reasons = [
|
||||
// Anything that's a post
|
||||
'mention',
|
||||
'reply',
|
||||
'quote',
|
||||
]
|
||||
}
|
||||
const {page: fetchedPage} = await fetchPage({
|
||||
agent,
|
||||
limit: PAGE_SIZE,
|
||||
@@ -106,13 +112,13 @@ export function useNotificationFeedQuery(opts?: {
|
||||
queryClient,
|
||||
moderationOpts,
|
||||
fetchAdditionalData: true,
|
||||
priority,
|
||||
reasons,
|
||||
})
|
||||
page = fetchedPage
|
||||
}
|
||||
|
||||
// if the first page has an unread, mark all read
|
||||
if (!pageParam) {
|
||||
if (filter === 'all' && !pageParam) {
|
||||
// if the first page has an unread, mark all read
|
||||
unreads.markAllRead()
|
||||
}
|
||||
|
||||
|
||||
@@ -45,7 +45,8 @@ export function useNotificationSettingsMutation() {
|
||||
},
|
||||
onSettled: () => {
|
||||
invalidateCachedUnreadPage()
|
||||
queryClient.invalidateQueries({queryKey: RQKEY_NOTIFS()})
|
||||
queryClient.invalidateQueries({queryKey: RQKEY_NOTIFS('all')})
|
||||
queryClient.invalidateQueries({queryKey: RQKEY_NOTIFS('mentions')})
|
||||
},
|
||||
})
|
||||
}
|
||||
@@ -54,7 +55,7 @@ function eagerlySetCachedPriority(
|
||||
queryClient: ReturnType<typeof useQueryClient>,
|
||||
enabled: boolean,
|
||||
) {
|
||||
queryClient.setQueryData(RQKEY_NOTIFS(), (old: any) => {
|
||||
function updateData(old: any) {
|
||||
if (!old) return old
|
||||
return {
|
||||
...old,
|
||||
@@ -65,5 +66,7 @@ function eagerlySetCachedPriority(
|
||||
}
|
||||
}),
|
||||
}
|
||||
})
|
||||
}
|
||||
queryClient.setQueryData(RQKEY_NOTIFS('all'), updateData)
|
||||
queryClient.setQueryData(RQKEY_NOTIFS('mentions'), updateData)
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* A kind of companion API to ./feed.ts. See that file for more info.
|
||||
*/
|
||||
|
||||
import React from 'react'
|
||||
import React, {useRef} from 'react'
|
||||
import {AppState} from 'react-native'
|
||||
import {useQueryClient} from '@tanstack/react-query'
|
||||
import EventEmitter from 'eventemitter3'
|
||||
@@ -105,6 +105,8 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
|
||||
}
|
||||
}, [setNumUnread])
|
||||
|
||||
const isFetchingRef = useRef(false)
|
||||
|
||||
// create API
|
||||
const api = React.useMemo<ApiContext>(() => {
|
||||
return {
|
||||
@@ -138,6 +140,12 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
|
||||
}
|
||||
}
|
||||
|
||||
if (isFetchingRef.current) {
|
||||
return
|
||||
}
|
||||
// Do not move this without ensuring it gets a symmetrical reset in the finally block.
|
||||
isFetchingRef.current = true
|
||||
|
||||
// count
|
||||
const {page, indexedAt: lastIndexed} = await fetchPage({
|
||||
agent,
|
||||
@@ -145,6 +153,7 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
|
||||
limit: 40,
|
||||
queryClient,
|
||||
moderationOpts,
|
||||
reasons: [],
|
||||
|
||||
// only fetch subjects when the page is going to be used
|
||||
// in the notifications query, otherwise skip it
|
||||
@@ -174,11 +183,14 @@ export function Provider({children}: React.PropsWithChildren<{}>) {
|
||||
// update & broadcast
|
||||
setNumUnread(unreadCountStr)
|
||||
if (invalidate) {
|
||||
truncateAndInvalidate(queryClient, RQKEY_NOTIFS())
|
||||
truncateAndInvalidate(queryClient, RQKEY_NOTIFS('all'))
|
||||
truncateAndInvalidate(queryClient, RQKEY_NOTIFS('mentions'))
|
||||
}
|
||||
broadcast.postMessage({event: unreadCountStr})
|
||||
} catch (e) {
|
||||
logger.warn('Failed to check unread notifications', {error: e})
|
||||
} finally {
|
||||
isFetchingRef.current = false
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@ export async function fetchPage({
|
||||
queryClient,
|
||||
moderationOpts,
|
||||
fetchAdditionalData,
|
||||
reasons,
|
||||
}: {
|
||||
agent: BskyAgent
|
||||
cursor: string | undefined
|
||||
@@ -38,7 +39,7 @@ export async function fetchPage({
|
||||
queryClient: QueryClient
|
||||
moderationOpts: ModerationOpts | undefined
|
||||
fetchAdditionalData: boolean
|
||||
priority?: boolean
|
||||
reasons: string[]
|
||||
}): Promise<{
|
||||
page: FeedPage
|
||||
indexedAt: string | undefined
|
||||
@@ -46,7 +47,7 @@ export async function fetchPage({
|
||||
const res = await agent.listNotifications({
|
||||
limit,
|
||||
cursor,
|
||||
// priority,
|
||||
reasons,
|
||||
})
|
||||
|
||||
const indexedAt = res.data.notifications[0]?.indexedAt
|
||||
|
||||
Reference in New Issue
Block a user