Shell behaviors update (react-query refactor) (#1915)

* Move tick-every-minute into a hook/context

* Move soft-reset event out of the shell model

* Update soft-reset listener on new search page

* Implement session-loaded and session-dropped events

* Update analytics and push-notifications to use new session system
This commit is contained in:
Paul Frazee 2023-11-15 17:17:50 -08:00 committed by GitHub
parent f23e9978d8
commit 6616b2bff0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 186 additions and 136 deletions

View file

@ -14,7 +14,6 @@ import {ComposeIcon2} from 'lib/icons'
import {colors, s} from 'lib/styles'
import React from 'react'
import {FlatList, View, useWindowDimensions} from 'react-native'
import {useStores} from 'state/index'
import {Feed} from '../posts/Feed'
import {TextLink} from '../util/Link'
import {FAB} from '../util/fab/FAB'
@ -23,6 +22,7 @@ import {msg} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {useSession} from '#/state/session'
import {useComposerControls} from '#/state/shell/composer'
import {listenSoftReset, emitSoftReset} from '#/state/events'
const POLL_FREQ = 30e3 // 30sec
@ -41,7 +41,6 @@ export function FeedPage({
renderEmptyState: () => JSX.Element
renderEndOfFeed?: () => JSX.Element
}) {
const store = useStores()
const {isSandbox} = useSession()
const pal = usePalette('default')
const {_} = useLingui()
@ -73,12 +72,9 @@ export function FeedPage({
if (!isPageFocused || !isScreenFocused) {
return
}
const softResetSub = store.onScreenSoftReset(onSoftReset)
screen('Feed')
return () => {
softResetSub.remove()
}
}, [store, onSoftReset, screen, feed, isPageFocused, isScreenFocused])
return listenSoftReset(onSoftReset)
}, [onSoftReset, screen, isPageFocused, isScreenFocused])
const onPressCompose = React.useCallback(() => {
track('HomeScreen:PressCompose')
@ -125,7 +121,7 @@ export function FeedPage({
)}
</>
}
onPress={() => store.emitScreenSoftReset()}
onPress={emitSoftReset}
/>
<TextLink
type="title-lg"
@ -144,16 +140,7 @@ export function FeedPage({
)
}
return <></>
}, [
isDesktop,
pal.view,
pal.text,
pal.textLight,
store,
hasNew,
_,
isSandbox,
])
}, [isDesktop, pal.view, pal.text, pal.textLight, hasNew, _, isSandbox])
return (
<View testID={testID} style={s.h100pct}>

View file

@ -20,6 +20,7 @@ import {ImagesLightbox} from 'state/models/ui/shell'
import {useLingui} from '@lingui/react'
import {msg} from '@lingui/macro'
import {useSetDrawerOpen} from '#/state/shell'
import {emitSoftReset} from '#/state/events'
export const ProfileSubpageHeader = observer(function HeaderImpl({
isLoading,
@ -145,7 +146,7 @@ export const ProfileSubpageHeader = observer(function HeaderImpl({
href={href}
style={[pal.text, {fontWeight: 'bold'}]}
text={title || ''}
onPress={() => store.emitScreenSoftReset()}
onPress={emitSoftReset}
numberOfLines={4}
/>
)}

View file

@ -1,24 +1,23 @@
import React from 'react'
import {observer} from 'mobx-react-lite'
import {ago} from 'lib/strings/time'
import {useStores} from 'state/index'
import {useTickEveryMinute} from '#/state/shell'
// FIXME(dan): Figure out why the false positives
/* eslint-disable react/prop-types */
export const TimeElapsed = observer(function TimeElapsed({
export function TimeElapsed({
timestamp,
children,
}: {
timestamp: string
children: ({timeElapsed}: {timeElapsed: string}) => JSX.Element
}) {
const stores = useStores()
const tick = useTickEveryMinute()
const [timeElapsed, setTimeAgo] = React.useState(ago(timestamp))
React.useEffect(() => {
setTimeAgo(ago(timestamp))
}, [timestamp, setTimeAgo, stores.shell.tickEveryMinute])
}, [timestamp, setTimeAgo, tick])
return children({timeElapsed})
})
}

View file

@ -9,15 +9,14 @@ import {FollowingEndOfFeed} from 'view/com/posts/FollowingEndOfFeed'
import {CustomFeedEmptyState} from 'view/com/posts/CustomFeedEmptyState'
import {FeedsTabBar} from '../com/pager/FeedsTabBar'
import {Pager, PagerRef, RenderTabBarFnProps} from 'view/com/pager/Pager'
import {useStores} from 'state/index'
import {FeedPage} from 'view/com/feeds/FeedPage'
import {useSetMinimalShellMode, useSetDrawerSwipeDisabled} from '#/state/shell'
import {usePreferencesQuery} from '#/state/queries/preferences'
import {emitSoftReset} from '#/state/events'
type Props = NativeStackScreenProps<HomeTabNavigatorParams, 'Home'>
export const HomeScreen = withAuthRequired(
observer(function HomeScreenImpl({}: Props) {
const store = useStores()
const setMinimalShellMode = useSetMinimalShellMode()
const setDrawerSwipeDisabled = useSetDrawerSwipeDisabled()
const pagerRef = React.useRef<PagerRef>(null)
@ -74,8 +73,8 @@ export const HomeScreen = withAuthRequired(
)
const onPressSelected = React.useCallback(() => {
store.emitScreenSoftReset()
}, [store])
emitSoftReset()
}, [])
const onPageScrollStateChanged = React.useCallback(
(state: 'idle' | 'dragging' | 'settling') => {

View file

@ -11,7 +11,6 @@ import {ViewHeader} from '../com/util/ViewHeader'
import {Feed} from '../com/notifications/Feed'
import {TextLink} from 'view/com/util/Link'
import {LoadLatestBtn} from 'view/com/util/load-latest/LoadLatestBtn'
import {useStores} from 'state/index'
import {useOnMainScroll} from 'lib/hooks/useOnMainScroll'
import {usePalette} from 'lib/hooks/usePalette'
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
@ -21,6 +20,7 @@ import {logger} from '#/logger'
import {useSetMinimalShellMode} from '#/state/shell'
import {useUnreadNotifications} from '#/state/queries/notifications/unread'
import {RQKEY as NOTIFS_RQKEY} from '#/state/queries/notifications/feed'
import {listenSoftReset, emitSoftReset} from '#/state/events'
type Props = NativeStackScreenProps<
NotificationsTabNavigatorParams,
@ -28,7 +28,6 @@ type Props = NativeStackScreenProps<
>
export const NotificationsScreen = withAuthRequired(
function NotificationsScreenImpl({}: Props) {
const store = useStores()
const setMinimalShellMode = useSetMinimalShellMode()
const [onMainScroll, isScrolledDown, resetMainScroll] = useOnMainScroll()
const scrollElRef = React.useRef<FlatList>(null)
@ -57,13 +56,9 @@ export const NotificationsScreen = withAuthRequired(
React.useCallback(() => {
setMinimalShellMode(false)
logger.debug('NotificationsScreen: Updating feed')
const softResetSub = store.onScreenSoftReset(onPressLoadLatest)
screen('Notifications')
return () => {
softResetSub.remove()
}
}, [store, screen, onPressLoadLatest, setMinimalShellMode]),
return listenSoftReset(onPressLoadLatest)
}, [screen, onPressLoadLatest, setMinimalShellMode]),
)
const ListHeaderComponent = React.useCallback(() => {
@ -100,13 +95,13 @@ export const NotificationsScreen = withAuthRequired(
)}
</>
}
onPress={() => store.emitScreenSoftReset()}
onPress={emitSoftReset}
/>
</View>
)
}
return <></>
}, [isDesktop, pal, store, hasNew])
}, [isDesktop, pal, hasNew])
return (
<View testID="notificationsScreen" style={s.hContentRegion}>

View file

@ -12,7 +12,6 @@ import {ScreenHider} from 'view/com/util/moderation/ScreenHider'
import {Feed} from 'view/com/posts/Feed'
import {ProfileLists} from '../com/lists/ProfileLists'
import {ProfileFeedgens} from '../com/feeds/ProfileFeedgens'
import {useStores} from 'state/index'
import {ProfileHeader} from '../com/profile/ProfileHeader'
import {PagerWithHeader} from 'view/com/pager/PagerWithHeader'
import {ErrorScreen} from '../com/util/error/ErrorScreen'
@ -37,6 +36,7 @@ import {cleanError} from '#/lib/strings/errors'
import {LoadLatestBtn} from '../com/util/load-latest/LoadLatestBtn'
import {useQueryClient} from '@tanstack/react-query'
import {useComposerControls} from '#/state/shell/composer'
import {listenSoftReset} from '#/state/events'
type Props = NativeStackScreenProps<CommonNavigatorParams, 'Profile'>
export const ProfileScreen = withAuthRequired(function ProfileScreenImpl({
@ -126,7 +126,6 @@ function ProfileScreenLoaded({
hideBackButton: boolean
}) {
const profile = useProfileShadow(profileUnshadowed, dataUpdatedAt)
const store = useStores()
const {currentAccount} = useSession()
const setMinimalShellMode = useSetMinimalShellMode()
const {openComposer} = useComposerControls()
@ -169,11 +168,10 @@ function ProfileScreenLoaded({
React.useCallback(() => {
setMinimalShellMode(false)
screen('Profile')
const softResetSub = store.onScreenSoftReset(() => {
return listenSoftReset(() => {
viewSelectorRef.current?.scrollToTop()
})
return () => softResetSub.remove()
}, [store, viewSelectorRef, setMinimalShellMode, screen]),
}, [viewSelectorRef, setMinimalShellMode, screen]),
)
useFocusEffect(

View file

@ -42,8 +42,8 @@ import {MagnifyingGlassIcon} from '#/lib/icons'
import {useModerationOpts} from '#/state/queries/preferences'
import {SearchResultCard} from '#/view/shell/desktop/Search'
import {useSetMinimalShellMode, useSetDrawerSwipeDisabled} from '#/state/shell'
import {useStores} from '#/state'
import {isWeb} from '#/platform/detection'
import {listenSoftReset} from '#/state/events'
function Loader() {
const pal = usePalette('default')
@ -421,7 +421,6 @@ export function SearchScreenMobile(
const moderationOpts = useModerationOpts()
const search = useActorAutocompleteFn()
const setMinimalShellMode = useSetMinimalShellMode()
const store = useStores()
const {isTablet} = useWebMediaQueries()
const searchDebounceTimeout = React.useRef<NodeJS.Timeout | undefined>(
@ -490,14 +489,9 @@ export function SearchScreenMobile(
useFocusEffect(
React.useCallback(() => {
const softResetSub = store.onScreenSoftReset(onSoftReset)
setMinimalShellMode(false)
return () => {
softResetSub.remove()
}
}, [store, onSoftReset, setMinimalShellMode]),
return listenSoftReset(onSoftReset)
}, [onSoftReset, setMinimalShellMode]),
)
return (

View file

@ -50,6 +50,7 @@ import {useModalControls} from '#/state/modals'
import {useSession, SessionAccount} from '#/state/session'
import {useProfileQuery} from '#/state/queries/profile'
import {useUnreadNotifications} from '#/state/queries/notifications/unread'
import {emitSoftReset} from '#/state/events'
export function DrawerProfileCard({
account,
@ -103,7 +104,6 @@ export function DrawerProfileCard({
export const DrawerContent = observer(function DrawerContentImpl() {
const theme = useTheme()
const pal = usePalette('default')
const store = useStores()
const {_} = useLingui()
const setDrawerOpen = useSetDrawerOpen()
const navigation = useNavigation<NavigationProp>()
@ -124,7 +124,7 @@ export const DrawerContent = observer(function DrawerContentImpl() {
if (isWeb) {
// hack because we have flat navigator for web and MyProfile does not exist on the web navigator -ansh
if (tab === 'MyProfile') {
navigation.navigate('Profile', {name: store.me.handle})
navigation.navigate('Profile', {name: currentAccount!.handle})
} else {
// @ts-ignore must be Home, Search, Notifications, or MyProfile
navigation.navigate(tab)
@ -132,7 +132,7 @@ export const DrawerContent = observer(function DrawerContentImpl() {
} else {
const tabState = getTabState(state, tab)
if (tabState === TabState.InsideAtRoot) {
store.emitScreenSoftReset()
emitSoftReset()
} else if (tabState === TabState.Inside) {
navigation.dispatch(StackActions.popToTop())
} else {
@ -141,7 +141,7 @@ export const DrawerContent = observer(function DrawerContentImpl() {
}
}
},
[store, track, navigation, setDrawerOpen],
[track, navigation, setDrawerOpen, currentAccount],
)
const onPressHome = React.useCallback(() => onPressTab('Home'), [onPressTab])

View file

@ -29,6 +29,7 @@ import {msg} from '@lingui/macro'
import {useModalControls} from '#/state/modals'
import {useShellLayout} from '#/state/shell/shell-layout'
import {useUnreadNotifications} from '#/state/queries/notifications/unread'
import {emitSoftReset} from '#/state/events'
type TabOptions = 'Home' | 'Search' | 'Notifications' | 'MyProfile' | 'Feeds'
@ -53,14 +54,14 @@ export const BottomBar = observer(function BottomBarImpl({
const state = navigation.getState()
const tabState = getTabState(state, tab)
if (tabState === TabState.InsideAtRoot) {
store.emitScreenSoftReset()
emitSoftReset()
} else if (tabState === TabState.Inside) {
navigation.dispatch(StackActions.popToTop())
} else {
navigation.navigate(`${tab}Tab`)
}
},
[store, track, navigation],
[track, navigation],
)
const onPressHome = React.useCallback(() => onPressTab('Home'), [onPressTab])
const onPressSearch = React.useCallback(

View file

@ -16,7 +16,6 @@ import {UserAvatar} from 'view/com/util/UserAvatar'
import {Link} from 'view/com/util/Link'
import {LoadingPlaceholder} from 'view/com/util/LoadingPlaceholder'
import {usePalette} from 'lib/hooks/usePalette'
import {useStores} from 'state/index'
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
import {s, colors} from 'lib/styles'
import {
@ -46,6 +45,7 @@ import {useSession} from '#/state/session'
import {useUnreadNotifications} from '#/state/queries/notifications/unread'
import {useComposerControls} from '#/state/shell/composer'
import {useFetchHandle} from '#/state/queries/handle'
import {emitSoftReset} from '#/state/events'
const ProfileCard = observer(function ProfileCardImpl() {
const {currentAccount} = useSession()
@ -126,7 +126,6 @@ const NavItem = observer(function NavItemImpl({
}: NavItemProps) {
const pal = usePalette('default')
const {currentAccount} = useSession()
const store = useStores()
const {isDesktop, isTablet} = useWebMediaQueries()
const [pathName] = React.useMemo(() => router.matchPath(href), [href])
const currentRouteInfo = useNavigationState(state => {
@ -149,12 +148,12 @@ const NavItem = observer(function NavItemImpl({
}
e.preventDefault()
if (isCurrent) {
store.emitScreenSoftReset()
emitSoftReset()
} else {
onPress()
}
},
[onPress, isCurrent, store],
[onPress, isCurrent],
)
return (