[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:
dan
2024-12-12 17:37:07 +00:00
committed by GitHub
parent 10e241e7eb
commit f8cdd6b9ae
15 changed files with 409 additions and 267 deletions
+14 -19
View File
@@ -9,13 +9,11 @@ import {msg} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {useInitialNumToRender} from '#/lib/hooks/useInitialNumToRender'
import {usePalette} from '#/lib/hooks/usePalette'
import {cleanError} from '#/lib/strings/errors'
import {s} from '#/lib/styles'
import {logger} from '#/logger'
import {useModerationOpts} from '#/state/preferences/moderation-opts'
import {useNotificationFeedQuery} from '#/state/queries/notifications/feed'
import {useUnreadNotificationsApi} from '#/state/queries/notifications/unread'
import {EmptyState} from '#/view/com/util/EmptyState'
import {ErrorMessage} from '#/view/com/util/error/ErrorMessage'
import {List, ListRef} from '#/view/com/util/List'
@@ -28,26 +26,26 @@ const LOAD_MORE_ERROR_ITEM = {_reactKey: '__load_more_error__'}
const LOADING_ITEM = {_reactKey: '__loading__'}
export function NotificationFeed({
filter,
enabled,
scrollElRef,
onPressTryAgain,
onScrolledDownChange,
ListHeaderComponent,
overridePriorityNotifications,
refreshNotifications,
}: {
filter: 'all' | 'mentions'
enabled: boolean
scrollElRef?: ListRef
onPressTryAgain?: () => void
onScrolledDownChange: (isScrolledDown: boolean) => void
ListHeaderComponent?: () => JSX.Element
overridePriorityNotifications?: boolean
refreshNotifications: () => Promise<void>
}) {
const initialNumToRender = useInitialNumToRender()
const [isPTRing, setIsPTRing] = React.useState(false)
const pal = usePalette('default')
const {_} = useLingui()
const moderationOpts = useModerationOpts()
const {checkUnread} = useUnreadNotificationsApi()
const {
data,
isFetching,
@@ -58,8 +56,8 @@ export function NotificationFeed({
isFetchingNextPage,
fetchNextPage,
} = useNotificationFeedQuery({
enabled: !!moderationOpts,
overridePriorityNotifications,
enabled: enabled && !!moderationOpts,
filter,
})
const isEmpty = !isFetching && !data?.pages[0]?.items.length
@@ -85,7 +83,7 @@ export function NotificationFeed({
const onRefresh = React.useCallback(async () => {
try {
setIsPTRing(true)
await checkUnread({invalidate: true})
await refreshNotifications()
} catch (err) {
logger.error('Failed to refresh notifications feed', {
message: err,
@@ -93,7 +91,7 @@ export function NotificationFeed({
} finally {
setIsPTRing(false)
}
}, [checkUnread, setIsPTRing])
}, [refreshNotifications, setIsPTRing])
const onEndReached = React.useCallback(async () => {
if (isFetching || !hasNextPage || isError) return
@@ -129,21 +127,18 @@ export function NotificationFeed({
/>
)
} else if (item === LOADING_ITEM) {
return (
<View style={[pal.border]}>
<NotificationFeedLoadingPlaceholder />
</View>
)
return <NotificationFeedLoadingPlaceholder />
}
return (
<NotificationFeedItem
highlightUnread={filter === 'all'}
item={item}
moderationOpts={moderationOpts!}
hideTopBorder={index === 0}
hideTopBorder={index === 0 && item.notification.isRead}
/>
)
},
[moderationOpts, _, onPressRetryLoadMore, pal.border],
[moderationOpts, _, onPressRetryLoadMore, filter],
)
const FeedFooter = React.useCallback(
@@ -79,10 +79,12 @@ interface Author {
let NotificationFeedItem = ({
item,
moderationOpts,
highlightUnread,
hideTopBorder,
}: {
item: FeedNotification
moderationOpts: ModerationOpts
highlightUnread: boolean
hideTopBorder?: boolean
}): React.ReactNode => {
const queryClient = useQueryClient()
@@ -151,6 +153,7 @@ let NotificationFeedItem = ({
if (!item.subject) {
return null
}
const isHighlighted = highlightUnread && !item.notification.isRead
return (
<Link
testID={`feedItem-by-${item.notification.author.handle}`}
@@ -160,12 +163,10 @@ let NotificationFeedItem = ({
<Post
post={item.subject}
style={
item.notification.isRead
? undefined
: {
backgroundColor: pal.colors.unreadNotifBg,
borderColor: pal.colors.unreadNotifBorder,
}
isHighlighted && {
backgroundColor: pal.colors.unreadNotifBg,
borderColor: pal.colors.unreadNotifBorder,
}
}
hideTopBorder={hideTopBorder}
/>
+8 -6
View File
@@ -136,12 +136,14 @@ export const Pager = forwardRef<PagerRef, React.PropsWithChildren<Props>>(
return (
<View testID={testID} style={[a.flex_1, native(a.overflow_hidden)]}>
{renderTabBar({
selectedPage,
onSelect: onTabBarSelect,
dragProgress,
dragState,
})}
<View style={a.z_10 /* Let tabbar bottom border cover the glimmer */}>
{renderTabBar({
selectedPage,
onSelect: onTabBarSelect,
dragProgress,
dragState,
})}
</View>
<GestureDetector gesture={nativeGesture}>
<AnimatedPagerView
ref={pagerView}