mirror of https://git.lenooby09.tech/LeNooby09/social-app.git
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

at ruby-v 182 lines 6.3 kB view raw
1import React from 'react' 2import {View} from 'react-native' 3import {msg, Trans} from '@lingui/macro' 4import {useLingui} from '@lingui/react' 5import {useFocusEffect, useIsFocused} from '@react-navigation/native' 6import {useQueryClient} from '@tanstack/react-query' 7 8import {useNonReactiveCallback} from '#/lib/hooks/useNonReactiveCallback' 9import {ComposeIcon2} from '#/lib/icons' 10import { 11 NativeStackScreenProps, 12 NotificationsTabNavigatorParams, 13} from '#/lib/routes/types' 14import {s} from '#/lib/styles' 15import {logger} from '#/logger' 16import {isNative, isWeb} from '#/platform/detection' 17import {emitSoftReset, listenSoftReset} from '#/state/events' 18import {RQKEY as NOTIFS_RQKEY} from '#/state/queries/notifications/feed' 19import { 20 useUnreadNotifications, 21 useUnreadNotificationsApi, 22} from '#/state/queries/notifications/unread' 23import {truncateAndInvalidate} from '#/state/queries/util' 24import {useSetMinimalShellMode} from '#/state/shell' 25import {useComposerControls} from '#/state/shell/composer' 26import {Feed} from '#/view/com/notifications/Feed' 27import {FAB} from '#/view/com/util/fab/FAB' 28import {ListMethods} from '#/view/com/util/List' 29import {LoadLatestBtn} from '#/view/com/util/load-latest/LoadLatestBtn' 30import {MainScrollProvider} from '#/view/com/util/MainScrollProvider' 31import {atoms as a, useBreakpoints, useTheme} from '#/alf' 32import {Button, ButtonIcon} from '#/components/Button' 33import {SettingsGear2_Stroke2_Corner0_Rounded as SettingsIcon} from '#/components/icons/SettingsGear2' 34import * as Layout from '#/components/Layout' 35import {Link} from '#/components/Link' 36import {Loader} from '#/components/Loader' 37 38type Props = NativeStackScreenProps< 39 NotificationsTabNavigatorParams, 40 'Notifications' 41> 42export function NotificationsScreen({route: {params}}: Props) { 43 const t = useTheme() 44 const {gtTablet} = useBreakpoints() 45 const {_} = useLingui() 46 const setMinimalShellMode = useSetMinimalShellMode() 47 const [isScrolledDown, setIsScrolledDown] = React.useState(false) 48 const [isLoadingLatest, setIsLoadingLatest] = React.useState(false) 49 const scrollElRef = React.useRef<ListMethods>(null) 50 const queryClient = useQueryClient() 51 const unreadNotifs = useUnreadNotifications() 52 const unreadApi = useUnreadNotificationsApi() 53 const hasNew = !!unreadNotifs 54 const isScreenFocused = useIsFocused() 55 const {openComposer} = useComposerControls() 56 57 // event handlers 58 // = 59 const scrollToTop = React.useCallback(() => { 60 scrollElRef.current?.scrollToOffset({animated: isNative, offset: 0}) 61 setMinimalShellMode(false) 62 }, [scrollElRef, setMinimalShellMode]) 63 64 const onPressLoadLatest = React.useCallback(() => { 65 scrollToTop() 66 if (hasNew) { 67 // render what we have now 68 truncateAndInvalidate(queryClient, NOTIFS_RQKEY()) 69 } else { 70 // check with the server 71 setIsLoadingLatest(true) 72 unreadApi 73 .checkUnread({invalidate: true}) 74 .catch(() => undefined) 75 .then(() => setIsLoadingLatest(false)) 76 } 77 }, [scrollToTop, queryClient, unreadApi, hasNew, setIsLoadingLatest]) 78 79 const onFocusCheckLatest = useNonReactiveCallback(() => { 80 // on focus, check for latest, but only invalidate if the user 81 // isnt scrolled down to avoid moving content underneath them 82 let currentIsScrolledDown 83 if (isNative) { 84 currentIsScrolledDown = isScrolledDown 85 } else { 86 // On the web, this isn't always updated in time so 87 // we're just going to look it up synchronously. 88 currentIsScrolledDown = window.scrollY > 200 89 } 90 unreadApi.checkUnread({invalidate: !currentIsScrolledDown}) 91 }) 92 93 // on-visible setup 94 // = 95 useFocusEffect( 96 React.useCallback(() => { 97 setMinimalShellMode(false) 98 logger.debug('NotificationsScreen: Focus') 99 onFocusCheckLatest() 100 }, [setMinimalShellMode, onFocusCheckLatest]), 101 ) 102 React.useEffect(() => { 103 if (!isScreenFocused) { 104 return 105 } 106 return listenSoftReset(onPressLoadLatest) 107 }, [onPressLoadLatest, isScreenFocused]) 108 109 return ( 110 <Layout.Screen testID="notificationsScreen"> 111 <Layout.Header.Outer> 112 <Layout.Header.MenuButton /> 113 <Layout.Header.Content> 114 <Button 115 label={_(msg`Notifications`)} 116 accessibilityHint={_(msg`Refresh notifications`)} 117 onPress={emitSoftReset} 118 style={[a.justify_start]}> 119 {({hovered}) => ( 120 <Layout.Header.TitleText 121 style={[a.w_full, hovered && a.underline]}> 122 <Trans>Notifications</Trans> 123 {isWeb && gtTablet && hasNew && ( 124 <View 125 style={[ 126 a.rounded_full, 127 { 128 width: 8, 129 height: 8, 130 bottom: 3, 131 left: 6, 132 backgroundColor: t.palette.primary_500, 133 }, 134 ]} 135 /> 136 )} 137 </Layout.Header.TitleText> 138 )} 139 </Button> 140 </Layout.Header.Content> 141 <Layout.Header.Slot> 142 <Link 143 to="/notifications/settings" 144 label={_(msg`Notification settings`)} 145 size="small" 146 variant="ghost" 147 color="secondary" 148 shape="round" 149 style={[a.justify_center]}> 150 <ButtonIcon 151 icon={isLoadingLatest ? Loader : SettingsIcon} 152 size="lg" 153 /> 154 </Link> 155 </Layout.Header.Slot> 156 </Layout.Header.Outer> 157 158 <MainScrollProvider> 159 <Feed 160 onScrolledDownChange={setIsScrolledDown} 161 scrollElRef={scrollElRef} 162 overridePriorityNotifications={params?.show === 'all'} 163 /> 164 </MainScrollProvider> 165 {(isScrolledDown || hasNew) && ( 166 <LoadLatestBtn 167 onPress={onPressLoadLatest} 168 label={_(msg`Load new notifications`)} 169 showIndicator={hasNew} 170 /> 171 )} 172 <FAB 173 testID="composeFAB" 174 onPress={() => openComposer({})} 175 icon={<ComposeIcon2 strokeWidth={1.5} size={29} style={s.white} />} 176 accessibilityRole="button" 177 accessibilityLabel={_(msg`New post`)} 178 accessibilityHint="" 179 /> 180 </Layout.Screen> 181 ) 182}