add option to mark all chats as read

This commit is contained in:
Samuel Newman
2026-06-12 15:26:34 +03:00
parent ec695a42c5
commit 6741ea35c5
+77 -20
View File
@@ -2,9 +2,8 @@ import {type JSX, useCallback} from 'react'
import {type GestureResponderEvent, View} from 'react-native' import {type GestureResponderEvent, View} from 'react-native'
import Animated from 'react-native-reanimated' import Animated from 'react-native-reanimated'
import {useSafeAreaInsets} from 'react-native-safe-area-context' import {useSafeAreaInsets} from 'react-native-safe-area-context'
import {msg, plural} from '@lingui/core/macro' import {plural} from '@lingui/core/macro'
import {useLingui} from '@lingui/react' import {Trans, useLingui} from '@lingui/react/macro'
import {Trans} from '@lingui/react/macro'
import {type BottomTabBarProps} from '@react-navigation/bottom-tabs' import {type BottomTabBarProps} from '@react-navigation/bottom-tabs'
import {StackActions} from '@react-navigation/native' import {StackActions} from '@react-navigation/native'
@@ -20,6 +19,7 @@ import {getTabState, TabState} from '#/lib/routes/helpers'
import {type SharedNavTab, TAB_TO_NAV_ITEM} from '#/lib/routes/tab-to-nav-item' import {type SharedNavTab, TAB_TO_NAV_ITEM} from '#/lib/routes/tab-to-nav-item'
import {emitSoftReset} from '#/state/events' import {emitSoftReset} from '#/state/events'
import {useUnreadMessageCount} from '#/state/queries/messages/list-conversations' import {useUnreadMessageCount} from '#/state/queries/messages/list-conversations'
import {useUpdateAllRead} from '#/state/queries/messages/update-all-read'
import {useUnreadNotifications} from '#/state/queries/notifications/unread' import {useUnreadNotifications} from '#/state/queries/notifications/unread'
import {useProfileQuery} from '#/state/queries/profile' import {useProfileQuery} from '#/state/queries/profile'
import {useSession} from '#/state/session' import {useSession} from '#/state/session'
@@ -37,10 +37,12 @@ import {
Bell_Filled_Corner0_Rounded as BellFilled, Bell_Filled_Corner0_Rounded as BellFilled,
Bell_Stroke2_Corner0_Rounded as Bell, Bell_Stroke2_Corner0_Rounded as Bell,
} from '#/components/icons/Bell' } from '#/components/icons/Bell'
import {CircleCheck_Stroke2_Corner0_Rounded as CircleCheckIcon} from '#/components/icons/CircleCheck'
import { import {
HomeOpen_Filled_Corner0_Rounded as HomeFilled, HomeOpen_Filled_Corner0_Rounded as HomeFilled,
HomeOpen_Stoke2_Corner0_Rounded as Home, HomeOpen_Stoke2_Corner0_Rounded as Home,
} from '#/components/icons/HomeOpen' } from '#/components/icons/HomeOpen'
import {Inbox_Stroke2_Corner2_Rounded as InboxIcon} from '#/components/icons/Inbox'
import { import {
MagnifyingGlass_Filled_Stroke2_Corner0_Rounded as MagnifyingGlassFilled, MagnifyingGlass_Filled_Stroke2_Corner0_Rounded as MagnifyingGlassFilled,
MagnifyingGlass_Stroke2_Corner0_Rounded as MagnifyingGlass, MagnifyingGlass_Stroke2_Corner0_Rounded as MagnifyingGlass,
@@ -49,6 +51,8 @@ import {
Message_Stroke2_Corner0_Rounded as Message, Message_Stroke2_Corner0_Rounded as Message,
Message_Stroke2_Corner0_Rounded_Filled as MessageFilled, Message_Stroke2_Corner0_Rounded_Filled as MessageFilled,
} from '#/components/icons/Message' } from '#/components/icons/Message'
import * as Menu from '#/components/Menu'
import * as Toast from '#/components/Toast'
import {Text} from '#/components/Typography' import {Text} from '#/components/Typography'
import {useAgeAssurance} from '#/ageAssurance' import {useAgeAssurance} from '#/ageAssurance'
import {useAnalytics} from '#/analytics' import {useAnalytics} from '#/analytics'
@@ -59,7 +63,7 @@ import {styles} from './BottomBarStyles'
export function BottomBar({navigation}: BottomTabBarProps) { export function BottomBar({navigation}: BottomTabBarProps) {
const {hasSession, currentAccount} = useSession() const {hasSession, currentAccount} = useSession()
const t = useTheme() const t = useTheme()
const {_} = useLingui() const {t: l} = useLingui()
const ax = useAnalytics() const ax = useAnalytics()
const safeAreaInsets = useSafeAreaInsets() const safeAreaInsets = useSafeAreaInsets()
const {footerHeight} = useShellLayout() const {footerHeight} = useShellLayout()
@@ -74,6 +78,7 @@ export function BottomBar({navigation}: BottomTabBarProps) {
const closeAllActiveElements = useCloseAllActiveElements() const closeAllActiveElements = useCloseAllActiveElements()
const dedupe = useDedupe() const dedupe = useDedupe()
const accountSwitchControl = useDialogControl() const accountSwitchControl = useDialogControl()
const messagesMenuControl = Menu.useMenuControl()
const playHaptic = useHaptics() const playHaptic = useHaptics()
const hideBorder = useHideBottomBarBorder() const hideBorder = useHideBottomBarBorder()
const iconWidth = 28 const iconWidth = 28
@@ -142,6 +147,12 @@ export function BottomBar({navigation}: BottomTabBarProps) {
accountSwitchControl.open() accountSwitchControl.open()
}, [accountSwitchControl, playHaptic]) }, [accountSwitchControl, playHaptic])
const onLongPressMessages = useCallback(() => {
if (aa.flags.chatDisabled) return
playHaptic()
messagesMenuControl.open()
}, [aa.flags.chatDisabled, messagesMenuControl, playHaptic])
const [demoMode] = useDemoMode() const [demoMode] = useDemoMode()
const {isActive: live} = useActorStatus(profile) const {isActive: live} = useActorStatus(profile)
const isLabeler = profile?.associated?.labeler const isLabeler = profile?.associated?.labeler
@@ -149,7 +160,7 @@ export function BottomBar({navigation}: BottomTabBarProps) {
return ( return (
<> <>
<SwitchAccountDialog control={accountSwitchControl} /> <SwitchAccountDialog control={accountSwitchControl} />
<MessagesTabMenu control={messagesMenuControl} />
<Animated.View <Animated.View
style={[ style={[
styles.bottomBar, styles.bottomBar,
@@ -182,7 +193,7 @@ export function BottomBar({navigation}: BottomTabBarProps) {
} }
onPress={onPressHome} onPress={onPressHome}
accessibilityRole="tab" accessibilityRole="tab"
accessibilityLabel={_(msg`Home`)} accessibilityLabel={l`Home`}
accessibilityHint="" accessibilityHint=""
/> />
<Btn <Btn
@@ -202,7 +213,7 @@ export function BottomBar({navigation}: BottomTabBarProps) {
} }
onPress={onPressSearch} onPress={onPressSearch}
accessibilityRole="search" accessibilityRole="search"
accessibilityLabel={_(msg`Search`)} accessibilityLabel={l`Search`}
accessibilityHint="" accessibilityHint=""
/> />
<Btn <Btn
@@ -221,21 +232,22 @@ export function BottomBar({navigation}: BottomTabBarProps) {
) )
} }
onPress={onPressMessages} onPress={onPressMessages}
onLongPress={onLongPressMessages}
notificationCount={ notificationCount={
aa.flags.chatDisabled ? undefined : numUnreadMessages.numUnread aa.flags.chatDisabled ? undefined : numUnreadMessages.numUnread
} }
hasNew={aa.flags.chatDisabled ? false : numUnreadMessages.hasNew} hasNew={aa.flags.chatDisabled ? false : numUnreadMessages.hasNew}
accessible={true} accessible={true}
accessibilityRole="tab" accessibilityRole="tab"
accessibilityLabel={_(msg`Chat`)} accessibilityLabel={l`Chat`}
accessibilityHint={ accessibilityHint={
!aa.flags.chatDisabled && numUnreadMessages.count > 0 !aa.flags.chatDisabled && numUnreadMessages.count > 0
? _( ? l({
plural(numUnreadMessages.numUnread ?? 0, { message: plural(numUnreadMessages.numUnread ?? 0, {
one: '# unread item', one: '# unread item',
other: '# unread items', other: '# unread items',
}), }),
) })
: '' : ''
} }
/> />
@@ -258,16 +270,16 @@ export function BottomBar({navigation}: BottomTabBarProps) {
notificationCount={numUnreadNotifications} notificationCount={numUnreadNotifications}
accessible={true} accessible={true}
accessibilityRole="tab" accessibilityRole="tab"
accessibilityLabel={_(msg`Notifications`)} accessibilityLabel={l`Notifications`}
accessibilityHint={ accessibilityHint={
numUnreadNotifications === '' numUnreadNotifications === ''
? '' ? ''
: _( : l({
plural(numUnreadNotifications ?? 0, { message: plural(numUnreadNotifications ?? 0, {
one: '# unread item', one: '# unread item',
other: '# unread items', other: '# unread items',
}), }),
) })
} }
/> />
<Btn <Btn
@@ -301,7 +313,7 @@ export function BottomBar({navigation}: BottomTabBarProps) {
onPress={onPressProfile} onPress={onPressProfile}
onLongPress={onLongPressProfile} onLongPress={onLongPressProfile}
accessibilityRole="tab" accessibilityRole="tab"
accessibilityLabel={_(msg`Profile`)} accessibilityLabel={l`Profile`}
accessibilityHint="" accessibilityHint=""
/> />
</> </>
@@ -330,9 +342,8 @@ export function BottomBar({navigation}: BottomTabBarProps) {
<View style={[a.flex_row, a.flex_wrap, a.gap_sm]}> <View style={[a.flex_row, a.flex_wrap, a.gap_sm]}>
<Button <Button
onPress={showCreateAccount} onPress={showCreateAccount}
label={_(msg`Create account`)} label={l`Create account`}
size="small" size="small"
variant="solid"
color="primary"> color="primary">
<ButtonText> <ButtonText>
<Trans>Create account</Trans> <Trans>Create account</Trans>
@@ -340,9 +351,8 @@ export function BottomBar({navigation}: BottomTabBarProps) {
</Button> </Button>
<Button <Button
onPress={showSignIn} onPress={showSignIn}
label={_(msg`Sign in`)} label={l`Sign in`}
size="small" size="small"
variant="solid"
color="secondary"> color="secondary">
<ButtonText> <ButtonText>
<Trans>Sign in</Trans> <Trans>Sign in</Trans>
@@ -419,3 +429,50 @@ function Btn({
</PressableScale> </PressableScale>
) )
} }
function MessagesTabMenu({control}: {control: Menu.MenuControlProps}) {
const {t: l} = useLingui()
const {mutate: markAllChatsRead} = useUpdateAllRead('accepted', {
onMutate: () => {
Toast.show(l`Marked all chats as read`, {type: 'success'})
},
onError: () => {
Toast.show(l`Failed to mark all chats as read`, {type: 'error'})
},
})
const {mutate: markAllRequestsRead} = useUpdateAllRead('request', {
onMutate: () => {
Toast.show(l`Marked all requests as read`, {type: 'success'})
},
onError: () => {
Toast.show(l`Failed to mark all requests as read`, {type: 'error'})
},
})
return (
<Menu.Root control={control}>
<Menu.Outer showCancel>
<Menu.Group>
<Menu.Item
label={l`Mark all chats as read`}
onPress={() => markAllChatsRead()}>
<Menu.ItemIcon icon={CircleCheckIcon} />
<Menu.ItemText>
<Trans>Mark all chats as read</Trans>
</Menu.ItemText>
</Menu.Item>
<Menu.Item
label={l`Mark all requests as read`}
onPress={() => markAllRequestsRead()}>
<Menu.ItemIcon icon={InboxIcon} />
<Menu.ItemText>
<Trans>Mark all requests as read</Trans>
</Menu.ItemText>
</Menu.Item>
</Menu.Group>
</Menu.Outer>
</Menu.Root>
)
}