[Clipclops] Clop menu, leave clop, mute/unmute clop (#3804)

* convo menu

* memoize convomenu

* add convoId to useChat + memoize value

* leave convo

* Create mute-conversation.ts

* add mutes, remove changes to useChat and use chat.convo instead

* add todo comments

* leave convo confirm prompt

* remove dependency on useChat and pass in props instead

* show menu on long press

* optimistic update

* optimistic update leave + add error capture

* don't `popToTop` when unnecessary

---------

Co-authored-by: Hailey <me@haileyok.com>
This commit is contained in:
Samuel Newman 2024-05-02 00:15:10 +01:00 committed by GitHub
parent d3fafdc066
commit e19f882450
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
11 changed files with 420 additions and 57 deletions

View file

@ -1,6 +1,7 @@
import React from 'react'
import React, {useCallback} from 'react'
import {TouchableOpacity, View} from 'react-native'
import {AppBskyActorDefs} from '@atproto/api'
import {ChatBskyConvoDefs} from '@atproto-labs/api'
import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
import {msg, Trans} from '@lingui/macro'
import {useLingui} from '@lingui/react'
@ -14,12 +15,11 @@ import {isWeb} from 'platform/detection'
import {ChatProvider, useChat} from 'state/messages'
import {ConvoStatus} from 'state/messages/convo'
import {useSession} from 'state/session'
import {UserAvatar} from 'view/com/util/UserAvatar'
import {PreviewableUserAvatar} from 'view/com/util/UserAvatar'
import {CenteredView} from 'view/com/util/Views'
import {MessagesList} from '#/screens/Messages/Conversation/MessagesList'
import {atoms as a, useBreakpoints, useTheme} from '#/alf'
import {Button, ButtonIcon} from '#/components/Button'
import {DotGrid_Stroke2_Corner0_Rounded} from '#/components/icons/DotGrid'
import {ConvoMenu} from '#/components/dms/ConvoMenu'
import {ListMaybePlaceholder} from '#/components/Lists'
import {Text} from '#/components/Typography'
import {ClipClopGate} from '../gate'
@ -78,8 +78,9 @@ let Header = ({
const {_} = useLingui()
const {gtTablet} = useBreakpoints()
const navigation = useNavigation<NavigationProp>()
const {service} = useChat()
const onPressBack = React.useCallback(() => {
const onPressBack = useCallback(() => {
if (isWeb) {
navigation.replace('MessagesList')
} else {
@ -87,6 +88,13 @@ let Header = ({
}
}, [navigation])
const onUpdateConvo = useCallback(
(convo: ChatBskyConvoDefs.ConvoView) => {
service.convo = convo
},
[service],
)
return (
<View
style={[
@ -95,22 +103,20 @@ let Header = ({
a.border_b,
a.flex_row,
a.justify_between,
a.align_start,
a.gap_lg,
a.px_lg,
a.py_sm,
]}>
{!gtTablet ? (
<TouchableOpacity
testID="viewHeaderDrawerBtn"
testID="conversationHeaderBackBtn"
onPress={onPressBack}
hitSlop={BACK_HITSLOP}
style={{
width: 30,
height: 30,
}}
style={{width: 30, height: 30}}
accessibilityRole="button"
accessibilityLabel={_(msg`Back`)}
accessibilityHint={_(msg`Access navigation links and settings`)}>
accessibilityHint="">
<FontAwesomeIcon
size={18}
icon="angle-left"
@ -124,24 +130,22 @@ let Header = ({
<View style={{width: 30}} />
)}
<View style={[a.align_center, a.gap_sm]}>
<UserAvatar size={32} avatar={profile.avatar} />
<PreviewableUserAvatar size={32} profile={profile} />
<Text style={[a.text_lg, a.font_bold]}>
<Trans>{profile.displayName}</Trans>
</Text>
</View>
<View>
<Button
label={_(msg`Chat settings`)}
color="secondary"
size="large"
variant="ghost"
style={[{height: 'auto', width: 'auto'}, a.px_sm, a.py_sm]}
onPress={() => {}}>
<ButtonIcon icon={DotGrid_Stroke2_Corner0_Rounded} />
</Button>
</View>
{service.convo ? (
<ConvoMenu
convo={service.convo}
profile={profile}
onUpdateConvo={onUpdateConvo}
currentScreen="conversation"
/>
) : (
<View style={{width: 30}} />
)}
</View>
)
}
Header = React.memo(Header)

View file

@ -12,6 +12,7 @@ import {MessagesTabNavigatorParams} from '#/lib/routes/types'
import {useGate} from '#/lib/statsig/statsig'
import {cleanError} from '#/lib/strings/errors'
import {logger} from '#/logger'
import {isNative} from '#/platform/detection'
import {useListConvos} from '#/state/queries/messages/list-converations'
import {useSession} from '#/state/session'
import {List} from '#/view/com/util/List'
@ -22,11 +23,13 @@ import {CenteredView} from '#/view/com/util/Views'
import {atoms as a, useBreakpoints, useTheme} from '#/alf'
import {Button, ButtonIcon, ButtonText} from '#/components/Button'
import {DialogControlProps, useDialogControl} from '#/components/Dialog'
import {ConvoMenu} from '#/components/dms/ConvoMenu'
import {NewChat} from '#/components/dms/NewChat'
import {PlusLarge_Stroke2_Corner0_Rounded as Plus} from '#/components/icons/Plus'
import {SettingsSliderVertical_Stroke2_Corner0_Rounded as SettingsSlider} from '#/components/icons/SettingsSlider'
import {Link} from '#/components/Link'
import {ListFooter, ListMaybePlaceholder} from '#/components/Lists'
import {useMenuControl} from '#/components/Menu'
import {Text} from '#/components/Typography'
import {ClipClopGate} from '../gate'
@ -190,6 +193,7 @@ function ChatListItem({convo}: {convo: ChatBskyConvoDefs.ConvoView}) {
const t = useTheme()
const {_} = useLingui()
const {currentAccount} = useSession()
const menuControl = useMenuControl()
let lastMessage = _(msg`No messages yet`)
let lastMessageSentAt: string | null = null
@ -214,7 +218,10 @@ function ChatListItem({convo}: {convo: ChatBskyConvoDefs.ConvoView}) {
}
return (
<Link to={`/messages/${convo.id}`} style={a.flex_1}>
<Link
to={`/messages/${convo.id}`}
style={a.flex_1}
onLongPress={isNative ? menuControl.open : undefined}>
{({hovered, pressed}) => (
<View
style={[
@ -267,12 +274,26 @@ function ChatListItem({convo}: {convo: ChatBskyConvoDefs.ConvoView}) {
a.flex_0,
a.ml_md,
a.mt_sm,
{backgroundColor: t.palette.primary_500},
a.rounded_full,
{height: 7, width: 7},
{
backgroundColor: convo.muted
? t.palette.contrast_200
: t.palette.primary_500,
height: 7,
width: 7,
},
]}
/>
)}
<ConvoMenu
convo={convo}
profile={otherUser}
control={menuControl}
// TODO(sam) show on hover on web
// tricky because it captures the mouse event
hideTrigger
currentScreen="list"
/>
</View>
)}
</Link>