From 2fe7e242601c2a2a1441ffb05b134078e67abbe9 Mon Sep 17 00:00:00 2001 From: Ryan Skinner Date: Thu, 18 Apr 2024 15:14:18 -0400 Subject: [PATCH 1/8] adding notification filters --- src/view/com/notifications/Feed.tsx | 83 +++++++++++++++++++---------- 1 file changed, 55 insertions(+), 28 deletions(-) diff --git a/src/view/com/notifications/Feed.tsx b/src/view/com/notifications/Feed.tsx index dd439d4755..f32cf5d35a 100644 --- a/src/view/com/notifications/Feed.tsx +++ b/src/view/com/notifications/Feed.tsx @@ -1,26 +1,37 @@ import React from 'react' -import {CenteredView} from '../util/Views' import {ActivityIndicator, StyleSheet, View} from 'react-native' -import {FeedItem} from './FeedItem' -import {NotificationFeedLoadingPlaceholder} from '../util/LoadingPlaceholder' -import {ErrorMessage} from '../util/error/ErrorMessage' -import {LoadMoreRetryBtn} from '../util/LoadMoreRetryBtn' -import {EmptyState} from '../util/EmptyState' -import {s} from 'lib/styles' +import {msg} from '@lingui/macro' +import {useLingui} from '@lingui/react' + +import {usePalette} from '#/lib/hooks/usePalette' +import {cleanError} from '#/lib/strings/errors' +import {logger} from '#/logger' import {useNotificationFeedQuery} from '#/state/queries/notifications/feed' import {useUnreadNotificationsApi} from '#/state/queries/notifications/unread' -import {logger} from '#/logger' -import {cleanError} from '#/lib/strings/errors' import {useModerationOpts} from '#/state/queries/preferences' +import {s} from 'lib/styles' +import {TabBar} from '../pager/TabBar' +import {EmptyState} from '../util/EmptyState' +import {ErrorMessage} from '../util/error/ErrorMessage' import {List, ListRef} from '../util/List' -import {useLingui} from '@lingui/react' -import {msg} from '@lingui/macro' -import {usePalette} from '#/lib/hooks/usePalette' +import {NotificationFeedLoadingPlaceholder} from '../util/LoadingPlaceholder' +import {LoadMoreRetryBtn} from '../util/LoadMoreRetryBtn' +import {CenteredView} from '../util/Views' +import {FeedItem} from './FeedItem' const EMPTY_FEED_ITEM = {_reactKey: '__empty__'} const LOAD_MORE_ERROR_ITEM = {_reactKey: '__load_more_error__'} const LOADING_ITEM = {_reactKey: '__loading__'} +const notificationTypes = [ + {type: 'All', label: 'All'}, + {type: 'post-like', label: 'Likes'}, + {type: 'follow', label: 'Follows'}, + {type: 'reply', label: 'Replies'}, + {type: 'quote', label: 'Quotes'}, + {type: 'repost', label: 'Reposts'}, +] + export function Feed({ scrollElRef, onPressTryAgain, @@ -33,6 +44,7 @@ export function Feed({ ListHeaderComponent?: () => JSX.Element }) { const [isPTRing, setIsPTRing] = React.useState(false) + const [filterType, setFilterType] = React.useState('All') const pal = usePalette('default') const {_} = useLingui() @@ -48,26 +60,27 @@ export function Feed({ isFetchingNextPage, fetchNextPage, } = useNotificationFeedQuery({enabled: !!moderationOpts}) - const isEmpty = !isFetching && !data?.pages[0]?.items.length + + const handleTabSelect = (index: number) => { + setFilterType(notificationTypes[index].type) + } const items = React.useMemo(() => { - let arr: any[] = [] - if (isFetched) { - if (isEmpty) { - arr = arr.concat([EMPTY_FEED_ITEM]) - } else if (data) { - for (const page of data?.pages) { - arr = arr.concat(page.items) - } - } - if (isError && !isEmpty) { - arr = arr.concat([LOAD_MORE_ERROR_ITEM]) - } - } else { - arr.push(LOADING_ITEM) + let arr = [] + if (isFetched && data) { + data.pages.forEach(page => { + page.items.forEach(item => { + if (filterType === 'All' || item.type === filterType) { + arr.push(item) + } + }) + }) } + if (isError && arr.length === 0) arr.push(LOAD_MORE_ERROR_ITEM) + if (!isFetched && arr.length === 0) arr.push(LOADING_ITEM) + if (isFetched && arr.length === 0) arr.push(EMPTY_FEED_ITEM) return arr - }, [isFetched, isError, isEmpty, data]) + }, [isFetched, isError, data, filterType]) const onRefresh = React.useCallback(async () => { try { @@ -145,6 +158,15 @@ export function Feed({ return ( + + nt.type === filterType, + )} + items={notificationTypes.map(nt => nt.label)} + onSelect={handleTabSelect} + /> + {error && ( Date: Fri, 19 Apr 2024 09:43:20 -0400 Subject: [PATCH 2/8] simplifying filter options --- src/view/com/notifications/Feed.tsx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/view/com/notifications/Feed.tsx b/src/view/com/notifications/Feed.tsx index f32cf5d35a..d02735e3a8 100644 --- a/src/view/com/notifications/Feed.tsx +++ b/src/view/com/notifications/Feed.tsx @@ -25,11 +25,7 @@ const LOADING_ITEM = {_reactKey: '__loading__'} const notificationTypes = [ {type: 'All', label: 'All'}, - {type: 'post-like', label: 'Likes'}, - {type: 'follow', label: 'Follows'}, - {type: 'reply', label: 'Replies'}, - {type: 'quote', label: 'Quotes'}, - {type: 'repost', label: 'Reposts'}, + {type: 'Mentions', label: 'Mentions'}, ] export function Feed({ @@ -70,7 +66,11 @@ export function Feed({ if (isFetched && data) { data.pages.forEach(page => { page.items.forEach(item => { - if (filterType === 'All' || item.type === filterType) { + if ( + filterType === 'All' || + (filterType === 'Mentions' && + (item.type === 'reply' || item.type === 'quote')) + ) { arr.push(item) } }) From be31cfcc94d854218749b581d73bf40b122aa1f7 Mon Sep 17 00:00:00 2001 From: Ryan Skinner Date: Fri, 19 Apr 2024 16:26:14 -0400 Subject: [PATCH 3/8] update to use Pager --- src/view/com/notifications/Feed.tsx | 63 +++++++------------ src/view/screens/Notifications.tsx | 97 ++++++++++++++++++++--------- 2 files changed, 90 insertions(+), 70 deletions(-) diff --git a/src/view/com/notifications/Feed.tsx b/src/view/com/notifications/Feed.tsx index d02735e3a8..221653161b 100644 --- a/src/view/com/notifications/Feed.tsx +++ b/src/view/com/notifications/Feed.tsx @@ -10,7 +10,6 @@ import {useNotificationFeedQuery} from '#/state/queries/notifications/feed' import {useUnreadNotificationsApi} from '#/state/queries/notifications/unread' import {useModerationOpts} from '#/state/queries/preferences' import {s} from 'lib/styles' -import {TabBar} from '../pager/TabBar' import {EmptyState} from '../util/EmptyState' import {ErrorMessage} from '../util/error/ErrorMessage' import {List, ListRef} from '../util/List' @@ -23,24 +22,20 @@ const EMPTY_FEED_ITEM = {_reactKey: '__empty__'} const LOAD_MORE_ERROR_ITEM = {_reactKey: '__load_more_error__'} const LOADING_ITEM = {_reactKey: '__loading__'} -const notificationTypes = [ - {type: 'All', label: 'All'}, - {type: 'Mentions', label: 'Mentions'}, -] - export function Feed({ scrollElRef, onPressTryAgain, onScrolledDownChange, ListHeaderComponent, + filterType, }: { scrollElRef?: ListRef onPressTryAgain?: () => void onScrolledDownChange: (isScrolledDown: boolean) => void ListHeaderComponent?: () => JSX.Element + filterType: string }) { const [isPTRing, setIsPTRing] = React.useState(false) - const [filterType, setFilterType] = React.useState('All') const pal = usePalette('default') const {_} = useLingui() @@ -56,31 +51,31 @@ export function Feed({ isFetchingNextPage, fetchNextPage, } = useNotificationFeedQuery({enabled: !!moderationOpts}) - - const handleTabSelect = (index: number) => { - setFilterType(notificationTypes[index].type) - } + const isEmpty = !isFetching && !data?.pages[0]?.items.length const items = React.useMemo(() => { - let arr = [] - if (isFetched && data) { - data.pages.forEach(page => { - page.items.forEach(item => { - if ( - filterType === 'All' || - (filterType === 'Mentions' && - (item.type === 'reply' || item.type === 'quote')) - ) { - arr.push(item) - } - }) + let arr: any[] = [] + if (isFetched && !isEmpty) { + data?.pages.forEach(page => { + let filteredItems = page.items + if (filterType === 'Mentions') { + let mentions = page.items.filter( + item => item.type === 'reply' || item.type === 'quote', + ) + filteredItems = mentions + } + + arr = arr.concat(filteredItems) }) + } else if (isEmpty) { + arr.push(EMPTY_FEED_ITEM) + } else if (isError) { + arr.push(LOAD_MORE_ERROR_ITEM) + } else { + arr.push(LOADING_ITEM) } - if (isError && arr.length === 0) arr.push(LOAD_MORE_ERROR_ITEM) - if (!isFetched && arr.length === 0) arr.push(LOADING_ITEM) - if (isFetched && arr.length === 0) arr.push(EMPTY_FEED_ITEM) return arr - }, [isFetched, isError, data, filterType]) + }, [isFetched, isError, isEmpty, data, filterType]) const onRefresh = React.useCallback(async () => { try { @@ -158,15 +153,6 @@ export function Feed({ return ( - - nt.type === filterType, - )} - items={notificationTypes.map(nt => nt.label)} - onSelect={handleTabSelect} - /> - {error && ( (null) const {screen} = useAnalytics() const pal = usePalette('default') @@ -51,6 +56,17 @@ export function NotificationsScreen({}: Props) { const hasNew = !!unreadNotifs const isScreenFocused = useIsFocused() const {openComposer} = useComposerControls() + const pagerRef = React.useRef(null) + + const handleTabChange = React.useCallback( + (index: number) => { + const types = ['All', 'Mentions'] + if (filterType !== types[index]) { + setFilterType(types[index]) + } + }, + [filterType], + ) // event handlers // = @@ -146,13 +162,36 @@ export function NotificationsScreen({}: Props) { return ( - - - + ( + + + + )}> + + + + + + + {(isScrolledDown || hasNew) && ( Date: Fri, 19 Apr 2024 17:07:20 -0400 Subject: [PATCH 4/8] remove the Notifications header text --- src/view/screens/Notifications.tsx | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/view/screens/Notifications.tsx b/src/view/screens/Notifications.tsx index 85a5a48aa0..30fc8e5474 100644 --- a/src/view/screens/Notifications.tsx +++ b/src/view/screens/Notifications.tsx @@ -34,7 +34,6 @@ import {Pager, PagerRef} from '../com/pager/Pager' import {TabBar} from '../com/pager/TabBar' import {FAB} from '../com/util/fab/FAB' import {MainScrollProvider} from '../com/util/MainScrollProvider' -import {ViewHeader} from '../com/util/ViewHeader' import {CenteredView} from '../com/util/Views' type Props = NativeStackScreenProps< @@ -161,7 +160,6 @@ export function NotificationsScreen({}: Props) { return ( - Date: Fri, 19 Apr 2024 17:13:27 -0400 Subject: [PATCH 5/8] was missing a type --- src/view/com/notifications/Feed.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/view/com/notifications/Feed.tsx b/src/view/com/notifications/Feed.tsx index 221653161b..e2e1e0cb8b 100644 --- a/src/view/com/notifications/Feed.tsx +++ b/src/view/com/notifications/Feed.tsx @@ -60,7 +60,10 @@ export function Feed({ let filteredItems = page.items if (filterType === 'Mentions') { let mentions = page.items.filter( - item => item.type === 'reply' || item.type === 'quote', + item => + item.type === 'reply' || + item.type === 'quote' || + item.type === 'mention', ) filteredItems = mentions } From bf0b8e368054630ac57b49ef4426593e3b1eac88 Mon Sep 17 00:00:00 2001 From: Ryan Skinner Date: Fri, 19 Apr 2024 17:21:05 -0400 Subject: [PATCH 6/8] updating header --- src/view/screens/Notifications.tsx | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/src/view/screens/Notifications.tsx b/src/view/screens/Notifications.tsx index 30fc8e5474..40c820fed9 100644 --- a/src/view/screens/Notifications.tsx +++ b/src/view/screens/Notifications.tsx @@ -29,11 +29,13 @@ import {colors, s} from 'lib/styles' import {TextLink} from 'view/com/util/Link' import {ListMethods} from 'view/com/util/List' import {LoadLatestBtn} from 'view/com/util/load-latest/LoadLatestBtn' +import {Text} from 'view/com/util/text/Text' import {Feed} from '../com/notifications/Feed' import {Pager, PagerRef} from '../com/pager/Pager' import {TabBar} from '../com/pager/TabBar' import {FAB} from '../com/util/fab/FAB' import {MainScrollProvider} from '../com/util/MainScrollProvider' +import {SimpleViewHeader} from '../com/util/SimpleViewHeader' import {CenteredView} from '../com/util/Views' type Props = NativeStackScreenProps< @@ -56,6 +58,7 @@ export function NotificationsScreen({}: Props) { const isScreenFocused = useIsFocused() const {openComposer} = useComposerControls() const pagerRef = React.useRef(null) + const {isMobile} = useWebMediaQueries() const handleTabChange = React.useCallback( (index: number) => { @@ -160,6 +163,19 @@ export function NotificationsScreen({}: Props) { return ( + + + + Notifications + + + Date: Fri, 19 Apr 2024 17:25:54 -0400 Subject: [PATCH 7/8] fixing header --- src/view/screens/Notifications.tsx | 50 ++---------------------------- 1 file changed, 2 insertions(+), 48 deletions(-) diff --git a/src/view/screens/Notifications.tsx b/src/view/screens/Notifications.tsx index 40c820fed9..eb6fe2dc22 100644 --- a/src/view/screens/Notifications.tsx +++ b/src/view/screens/Notifications.tsx @@ -8,7 +8,7 @@ import {useQueryClient} from '@tanstack/react-query' import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback' import {logger} from '#/logger' import {isNative} from '#/platform/detection' -import {emitSoftReset, listenSoftReset} from '#/state/events' +import {listenSoftReset} from '#/state/events' import {RQKEY as NOTIFS_RQKEY} from '#/state/queries/notifications/feed' import { useUnreadNotifications, @@ -25,8 +25,7 @@ import { NativeStackScreenProps, NotificationsTabNavigatorParams, } from 'lib/routes/types' -import {colors, s} from 'lib/styles' -import {TextLink} from 'view/com/util/Link' +import {s} from 'lib/styles' import {ListMethods} from 'view/com/util/List' import {LoadLatestBtn} from 'view/com/util/load-latest/LoadLatestBtn' import {Text} from 'view/com/util/text/Text' @@ -50,7 +49,6 @@ export function NotificationsScreen({}: Props) { const scrollElRef = React.useRef(null) const {screen} = useAnalytics() const pal = usePalette('default') - const {isDesktop} = useWebMediaQueries() const queryClient = useQueryClient() const unreadNotifs = useUnreadNotifications() const unreadApi = useUnreadNotificationsApi() @@ -119,48 +117,6 @@ export function NotificationsScreen({}: Props) { return listenSoftReset(onPressLoadLatest) }, [onPressLoadLatest, isScreenFocused]) - const ListHeaderComponent = React.useCallback(() => { - if (isDesktop) { - return ( - - - Notifications{' '} - {hasNew && ( - - )} - - } - onPress={emitSoftReset} - /> - - ) - } - return <> - }, [isDesktop, pal, hasNew]) - return ( @@ -202,7 +157,6 @@ export function NotificationsScreen({}: Props) { filterType="Mentions" onScrolledDownChange={setIsScrolledDown} scrollElRef={scrollElRef} - ListHeaderComponent={ListHeaderComponent} /> From 9e81040758e285120b8ad67ce1103bfa3e4e2894 Mon Sep 17 00:00:00 2001 From: Ryan Skinner Date: Sun, 21 Apr 2024 10:31:45 -0400 Subject: [PATCH 8/8] using PagerWithHeader w sticky tabs --- src/view/com/notifications/Feed.tsx | 14 +++-- src/view/screens/Notifications.tsx | 80 ++++++++++++++--------------- 2 files changed, 49 insertions(+), 45 deletions(-) diff --git a/src/view/com/notifications/Feed.tsx b/src/view/com/notifications/Feed.tsx index e2e1e0cb8b..14ba234418 100644 --- a/src/view/com/notifications/Feed.tsx +++ b/src/view/com/notifications/Feed.tsx @@ -1,5 +1,11 @@ import React from 'react' -import {ActivityIndicator, StyleSheet, View} from 'react-native' +import { + ActivityIndicator, + FlatList, + ScrollView, + StyleSheet, + View, +} from 'react-native' import {msg} from '@lingui/macro' import {useLingui} from '@lingui/react' @@ -12,7 +18,7 @@ import {useModerationOpts} from '#/state/queries/preferences' import {s} from 'lib/styles' import {EmptyState} from '../util/EmptyState' import {ErrorMessage} from '../util/error/ErrorMessage' -import {List, ListRef} from '../util/List' +import {List} from '../util/List' import {NotificationFeedLoadingPlaceholder} from '../util/LoadingPlaceholder' import {LoadMoreRetryBtn} from '../util/LoadMoreRetryBtn' import {CenteredView} from '../util/Views' @@ -23,13 +29,12 @@ const LOAD_MORE_ERROR_ITEM = {_reactKey: '__load_more_error__'} const LOADING_ITEM = {_reactKey: '__loading__'} export function Feed({ - scrollElRef, onPressTryAgain, onScrolledDownChange, ListHeaderComponent, filterType, }: { - scrollElRef?: ListRef + scrollElRef?: React.RefObject | ScrollView | null> onPressTryAgain?: () => void onScrolledDownChange: (isScrolledDown: boolean) => void ListHeaderComponent?: () => JSX.Element @@ -166,7 +171,6 @@ export function Feed({ )} item._reactKey} renderItem={renderItem} diff --git a/src/view/screens/Notifications.tsx b/src/view/screens/Notifications.tsx index eb6fe2dc22..59e879ebb3 100644 --- a/src/view/screens/Notifications.tsx +++ b/src/view/screens/Notifications.tsx @@ -30,12 +30,11 @@ import {ListMethods} from 'view/com/util/List' import {LoadLatestBtn} from 'view/com/util/load-latest/LoadLatestBtn' import {Text} from 'view/com/util/text/Text' import {Feed} from '../com/notifications/Feed' -import {Pager, PagerRef} from '../com/pager/Pager' -import {TabBar} from '../com/pager/TabBar' +import {PagerRef} from '../com/pager/Pager' +import {PagerWithHeader} from '../com/pager/PagerWithHeader' import {FAB} from '../com/util/fab/FAB' import {MainScrollProvider} from '../com/util/MainScrollProvider' import {SimpleViewHeader} from '../com/util/SimpleViewHeader' -import {CenteredView} from '../com/util/Views' type Props = NativeStackScreenProps< NotificationsTabNavigatorParams, @@ -68,6 +67,22 @@ export function NotificationsScreen({}: Props) { [filterType], ) + const renderHeader = () => { + return ( + + + + Notifications + + + + ) + } + + const sectionTitles = [_('All'), _('Mentions')] + // event handlers // = const scrollToTop = React.useCallback(() => { @@ -119,47 +134,32 @@ export function NotificationsScreen({}: Props) { return ( - - - - Notifications - - - - ( - - + {({scrollElRef}) => ( + + + + )} + {({scrollElRef}) => ( + + - - )}> - - - - - - - + + )} + {(isScrolledDown || hasNew) && (