From 1ac307bc42bdf370c2011e7e7f2c76baea3441d2 Mon Sep 17 00:00:00 2001 From: dan Date: Sun, 15 Dec 2024 20:30:17 +0000 Subject: [Experiment] Remove "Load Latest" button (#7120) * Remove "show latest" behind the gate * Add HomeBadgeProvider * Update provider state from home feed tabs * Add Home badge to native * Add Home badge to mobile web * Add Home badge to desktop web --- src/App.native.tsx | 31 ++++++++++++++----------- src/App.web.tsx | 27 +++++++++++---------- src/lib/statsig/gates.ts | 1 + src/state/home-badge.tsx | 24 +++++++++++++++++++ src/view/com/feeds/FeedPage.tsx | 8 +++++++ src/view/com/util/load-latest/LoadLatestBtn.tsx | 6 +++++ src/view/shell/bottom-bar/BottomBar.tsx | 11 ++++++++- src/view/shell/bottom-bar/BottomBarStyles.tsx | 11 +++++++++ src/view/shell/bottom-bar/BottomBarWeb.tsx | 28 +++++++++++++++------- src/view/shell/desktop/LeftNav.tsx | 26 ++++++++++++++++++++- 10 files changed, 136 insertions(+), 37 deletions(-) create mode 100644 src/state/home-badge.tsx (limited to 'src') diff --git a/src/App.native.tsx b/src/App.native.tsx index f985b96a5..39ab7ca92 100644 --- a/src/App.native.tsx +++ b/src/App.native.tsx @@ -32,6 +32,7 @@ import { ensureGeolocationResolved, Provider as GeolocationProvider, } from '#/state/geolocation' +import {Provider as HomeBadgeProvider} from '#/state/home-badge' import {Provider as InvitesStateProvider} from '#/state/invites' import {Provider as LightboxStateProvider} from '#/state/lightbox' import {MessagesProvider} from '#/state/messages' @@ -137,20 +138,22 @@ function InnerApp() { - - - - - - - - - - - - - + + + + + + + + + + + + + + + diff --git a/src/App.web.tsx b/src/App.web.tsx index b7c5a5633..8d13a826e 100644 --- a/src/App.web.tsx +++ b/src/App.web.tsx @@ -22,6 +22,7 @@ import { ensureGeolocationResolved, Provider as GeolocationProvider, } from '#/state/geolocation' +import {Provider as HomeBadgeProvider} from '#/state/home-badge' import {Provider as InvitesStateProvider} from '#/state/invites' import {Provider as LightboxStateProvider} from '#/state/lightbox' import {MessagesProvider} from '#/state/messages' @@ -120,18 +121,20 @@ function InnerApp() { - - - - - - - - - - - - + + + + + + + + + + + + + + diff --git a/src/lib/statsig/gates.ts b/src/lib/statsig/gates.ts index 3cec5d5b2..3767ec1e5 100644 --- a/src/lib/statsig/gates.ts +++ b/src/lib/statsig/gates.ts @@ -2,3 +2,4 @@ export type Gate = // Keep this alphabetic please. | 'debug_show_feedcontext' // DISABLED DUE TO EME | 'post_feed_lang_window' // DISABLED DUE TO EME + | 'remove_show_latest_button' diff --git a/src/state/home-badge.tsx b/src/state/home-badge.tsx new file mode 100644 index 000000000..59a9276ce --- /dev/null +++ b/src/state/home-badge.tsx @@ -0,0 +1,24 @@ +import React from 'react' + +type StateContext = boolean +type ApiContext = (hasNew: boolean) => void + +const stateContext = React.createContext(false) +const apiContext = React.createContext((_: boolean) => {}) + +export function Provider({children}: React.PropsWithChildren<{}>) { + const [state, setState] = React.useState(false) + return ( + + {children} + + ) +} + +export function useHomeBadge() { + return React.useContext(stateContext) +} + +export function useSetHomeBadge() { + return React.useContext(apiContext) +} diff --git a/src/view/com/feeds/FeedPage.tsx b/src/view/com/feeds/FeedPage.tsx index e766b589b..10ed60212 100644 --- a/src/view/com/feeds/FeedPage.tsx +++ b/src/view/com/feeds/FeedPage.tsx @@ -14,6 +14,7 @@ import {s} from '#/lib/styles' import {isNative} from '#/platform/detection' import {listenSoftReset} from '#/state/events' import {FeedFeedbackProvider, useFeedFeedback} from '#/state/feed-feedback' +import {useSetHomeBadge} from '#/state/home-badge' import {RQKEY as FEED_RQKEY} from '#/state/queries/post-feed' import {FeedDescriptor, FeedParams} from '#/state/queries/post-feed' import {truncateAndInvalidate} from '#/state/queries/util' @@ -59,6 +60,13 @@ export function FeedPage({ const feedFeedback = useFeedFeedback(feed, hasSession) const scrollElRef = React.useRef(null) const [hasNew, setHasNew] = React.useState(false) + const setHomeBadge = useSetHomeBadge() + + React.useEffect(() => { + if (isPageFocused) { + setHomeBadge(hasNew) + } + }, [isPageFocused, hasNew, setHomeBadge]) const scrollToTop = React.useCallback(() => { scrollElRef.current?.scrollToOffset({ diff --git a/src/view/com/util/load-latest/LoadLatestBtn.tsx b/src/view/com/util/load-latest/LoadLatestBtn.tsx index d98aa0fa7..b502f0b68 100644 --- a/src/view/com/util/load-latest/LoadLatestBtn.tsx +++ b/src/view/com/util/load-latest/LoadLatestBtn.tsx @@ -9,6 +9,7 @@ import {useMinimalShellFabTransform} from '#/lib/hooks/useMinimalShellTransform' import {usePalette} from '#/lib/hooks/usePalette' import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries' import {clamp} from '#/lib/numbers' +import {useGate} from '#/lib/statsig/statsig' import {colors} from '#/lib/styles' import {isWeb} from '#/platform/detection' import {useSession} from '#/state/session' @@ -34,6 +35,11 @@ export function LoadLatestBtn({ // move button inline if it starts overlapping the left nav const isTallViewport = useMediaQuery({minHeight: 700}) + const gate = useGate() + if (gate('remove_show_latest_button')) { + return null + } + // Adjust height of the fab if we have a session only on mobile web. If we don't have a session, we want to adjust // it on both tablet and mobile since we are showing the bottom bar (see createNativeStackNavigatorWithAuth) const showBottomBar = hasSession ? isMobile : isTabletOrMobile diff --git a/src/view/shell/bottom-bar/BottomBar.tsx b/src/view/shell/bottom-bar/BottomBar.tsx index db9c04965..47a525c04 100644 --- a/src/view/shell/bottom-bar/BottomBar.tsx +++ b/src/view/shell/bottom-bar/BottomBar.tsx @@ -15,8 +15,10 @@ import {useNavigationTabState} from '#/lib/hooks/useNavigationTabState' import {usePalette} from '#/lib/hooks/usePalette' import {clamp} from '#/lib/numbers' import {getTabState, TabState} from '#/lib/routes/helpers' +import {useGate} from '#/lib/statsig/statsig' import {s} from '#/lib/styles' import {emitSoftReset} from '#/state/events' +import {useHomeBadge} from '#/state/home-badge' import {useUnreadMessageCount} from '#/state/queries/messages/list-conversations' import {useUnreadNotifications} from '#/state/queries/notifications/unread' import {useProfileQuery} from '#/state/queries/profile' @@ -73,6 +75,8 @@ export function BottomBar({navigation}: BottomTabBarProps) { const dedupe = useDedupe() const accountSwitchControl = useDialogControl() const playHaptic = useHaptics() + const hasHomeBadge = useHomeBadge() + const gate = useGate() const iconWidth = 28 const showSignIn = React.useCallback(() => { @@ -153,6 +157,7 @@ export function BottomBar({navigation}: BottomTabBarProps) { /> ) } + hasNew={hasHomeBadge && gate('remove_show_latest_button')} onPress={onPressHome} accessibilityRole="tab" accessibilityLabel={_(msg`Home`)} @@ -334,6 +339,7 @@ interface BtnProps testID?: string icon: JSX.Element notificationCount?: string + hasNew?: boolean onPress?: (event: GestureResponderEvent) => void onLongPress?: (event: GestureResponderEvent) => void } @@ -341,6 +347,7 @@ interface BtnProps function Btn({ testID, icon, + hasNew, notificationCount, onPress, onLongPress, @@ -363,7 +370,9 @@ function Btn({ {notificationCount} - ) : undefined} + ) : hasNew ? ( + + ) : null} ) } diff --git a/src/view/shell/bottom-bar/BottomBarStyles.tsx b/src/view/shell/bottom-bar/BottomBarStyles.tsx index 9255957cb..d80914d09 100644 --- a/src/view/shell/bottom-bar/BottomBarStyles.tsx +++ b/src/view/shell/bottom-bar/BottomBarStyles.tsx @@ -44,6 +44,17 @@ export const styles = StyleSheet.create({ color: colors.white, fontVariant: ['tabular-nums'], }, + hasNewBadge: { + position: 'absolute', + left: '52%', + marginLeft: 4, + top: 10, + width: 8, + height: 8, + backgroundColor: colors.blue3, + borderRadius: 6, + zIndex: 1, + }, ctrlIcon: { marginLeft: 'auto', marginRight: 'auto', diff --git a/src/view/shell/bottom-bar/BottomBarWeb.tsx b/src/view/shell/bottom-bar/BottomBarWeb.tsx index 127ff2b26..1855969cc 100644 --- a/src/view/shell/bottom-bar/BottomBarWeb.tsx +++ b/src/view/shell/bottom-bar/BottomBarWeb.tsx @@ -9,6 +9,8 @@ import {useMinimalShellFooterTransform} from '#/lib/hooks/useMinimalShellTransfo import {getCurrentRoute, isTab} from '#/lib/routes/helpers' import {makeProfileLink} from '#/lib/routes/links' import {CommonNavigatorParams} from '#/lib/routes/types' +import {useGate} from '#/lib/statsig/statsig' +import {useHomeBadge} from '#/state/home-badge' import {useUnreadMessageCount} from '#/state/queries/messages/list-conversations' import {useUnreadNotifications} from '#/state/queries/notifications/unread' import {useSession} from '#/state/session' @@ -51,6 +53,8 @@ export function BottomBarWeb() { const unreadMessageCount = useUnreadMessageCount() const notificationCountStr = useUnreadNotifications() + const hasHomeBadge = useHomeBadge() + const gate = useGate() const showSignIn = React.useCallback(() => { closeAllActiveElements() @@ -75,7 +79,10 @@ export function BottomBarWeb() { ]}> {hasSession ? ( <> - + {({isActive}) => { const Icon = isActive ? HomeFilled : Home return ( @@ -105,7 +112,7 @@ export function BottomBarWeb() { 0 ? unreadMessageCount.numUnread : undefined @@ -128,7 +135,7 @@ export function BottomBarWeb() { + notificationCount={notificationCountStr}> {({isActive}) => { const Icon = isActive ? BellFilled : Bell return ( @@ -220,8 +227,9 @@ const NavItem: React.FC<{ children: (props: {isActive: boolean}) => React.ReactChild href: string routeName: string - badge?: string -}> = ({children, href, routeName, badge}) => { + hasNew?: boolean + notificationCount?: string +}> = ({children, href, routeName, hasNew, notificationCount}) => { const {_} = useLingui() const {currentAccount} = useSession() const currentRoute = useNavigationState(state => { @@ -246,13 +254,15 @@ const NavItem: React.FC<{ aria-label={routeName} accessible={true}> {children({isActive})} - {!!badge && ( + {notificationCount ? ( - {badge} + aria-label={_(msg`${notificationCount} unread items`)}> + {notificationCount} - )} + ) : hasNew ? ( + + ) : null} ) } diff --git a/src/view/shell/desktop/LeftNav.tsx b/src/view/shell/desktop/LeftNav.tsx index 6655572f8..d367e1b98 100644 --- a/src/view/shell/desktop/LeftNav.tsx +++ b/src/view/shell/desktop/LeftNav.tsx @@ -14,8 +14,10 @@ import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries' import {getCurrentRoute, isTab} from '#/lib/routes/helpers' import {makeProfileLink} from '#/lib/routes/links' import {CommonNavigatorParams} from '#/lib/routes/types' +import {useGate} from '#/lib/statsig/statsig' import {isInvalidHandle} from '#/lib/strings/handles' import {emitSoftReset} from '#/state/events' +import {useHomeBadge} from '#/state/home-badge' import {useFetchHandle} from '#/state/queries/handle' import {useUnreadMessageCount} from '#/state/queries/messages/list-conversations' import {useUnreadNotifications} from '#/state/queries/notifications/unread' @@ -100,12 +102,13 @@ function ProfileCard() { interface NavItemProps { count?: string + hasNew?: boolean href: string icon: JSX.Element iconFilled: JSX.Element label: string } -function NavItem({count, href, icon, iconFilled, label}: NavItemProps) { +function NavItem({count, hasNew, href, icon, iconFilled, label}: NavItemProps) { const t = useTheme() const {_} = useLingui() const {currentAccount} = useSession() @@ -214,6 +217,24 @@ function NavItem({count, href, icon, iconFilled, label}: NavItemProps) { {count} + ) : hasNew ? ( + ) : null} {gtTablet && ( @@ -322,6 +343,8 @@ export function DesktopLeftNav() { const {_} = useLingui() const {isDesktop, isTablet} = useWebMediaQueries() const numUnreadNotifications = useUnreadNotifications() + const hasHomeBadge = useHomeBadge() + const gate = useGate() if (!hasSession && !isDesktop) { return null @@ -348,6 +371,7 @@ export function DesktopLeftNav() { <>