diff options
author | Samuel Newman <mozzius@protonmail.com> | 2025-03-04 13:54:19 +0000 |
---|---|---|
committer | GitHub <noreply@github.com> | 2025-03-04 05:54:19 -0800 |
commit | c995eb2f2fa3e73dcc6943078c85cd6a68f5370b (patch) | |
tree | 2dfea8ae6e4d86a77a90c72663b22441ca407159 /src/screens/Messages/components/RequestButtons.tsx | |
parent | 5c14f695660dcbf815a584d9d3bb037171dd0c14 (diff) | |
download | voidsky-c995eb2f2fa3e73dcc6943078c85cd6a68f5370b.tar.zst |
DMs inbox (#7778)
* improve error screen * add chat request prompt * mock up inbox * bigger button * use two-button layout * get inbox working somewhat * fix type errors * fetch both pages for badge * don't include read convos in preview * in-chat ui for non-accepted convos (part 1) * add chatstatusinfo * fix status info not disappearing * get chat status info working * change min item height * move files around * add updated sdk * improve badge behaviour * mock up mark all as read * update sdk to 0.14.4 * hide chat status info if initiating convo * fix unread count for deleted accounts * add toasts after rejection * add prompt to delete * adjust badge on desktop * requests -> chat requests * fix height flicker * add mark as read button to header * add mark all as read APIs * separate avatarstack into two components (#7845) * fix messages being hidden behind chatstatusinfo * show inbox preview on empty state * fix empty state again * Use new convo availability API (#7812) * [Inbox] Accept button on convo screen (#7795) * accept button on convo screen * fix types * fix type error * improve spacing * [DMs] Implement new log types (#7835) * optimise badge state * add read message log * add isLogAcceptConvo * mute/unmute convo logs * use setqueriesdata * always show label on button * optimistically update badge * change incorrect unread count change * Update src/screens/Messages/Inbox.tsx Co-authored-by: surfdude29 <149612116+surfdude29@users.noreply.github.com> * Update src/screens/Messages/components/RequestButtons.tsx Co-authored-by: surfdude29 <149612116+surfdude29@users.noreply.github.com> * Update src/screens/Messages/components/RequestButtons.tsx Co-authored-by: surfdude29 <149612116+surfdude29@users.noreply.github.com> * Update src/screens/Messages/components/RequestListItem.tsx Co-authored-by: surfdude29 <149612116+surfdude29@users.noreply.github.com> * fix race condition with accepting convo * fix back button on web * filter left convos from badge * update atproto to fix CI * Add accept override external to convo (#7891) * Add accept override external to convo * rm log --------- Co-authored-by: Samuel Newman <mozzius@protonmail.com> --------- Co-authored-by: surfdude29 <149612116+surfdude29@users.noreply.github.com> Co-authored-by: Eric Bailey <git@esb.lol>
Diffstat (limited to 'src/screens/Messages/components/RequestButtons.tsx')
-rw-r--r-- | src/screens/Messages/components/RequestButtons.tsx | 254 |
1 files changed, 254 insertions, 0 deletions
diff --git a/src/screens/Messages/components/RequestButtons.tsx b/src/screens/Messages/components/RequestButtons.tsx new file mode 100644 index 000000000..023cbff2d --- /dev/null +++ b/src/screens/Messages/components/RequestButtons.tsx @@ -0,0 +1,254 @@ +import {useCallback} from 'react' +import {ChatBskyActorDefs, ChatBskyConvoDefs} from '@atproto/api' +import {msg, Trans} from '@lingui/macro' +import {useLingui} from '@lingui/react' +import {StackActions, useNavigation} from '@react-navigation/native' +import {useQueryClient} from '@tanstack/react-query' + +import {NavigationProp} from '#/lib/routes/types' +import {useProfileShadow} from '#/state/cache/profile-shadow' +import {useAcceptConversation} from '#/state/queries/messages/accept-conversation' +import {precacheConvoQuery} from '#/state/queries/messages/conversation' +import {useLeaveConvo} from '#/state/queries/messages/leave-conversation' +import {useProfileBlockMutationQueue} from '#/state/queries/profile' +import * as Toast from '#/view/com/util/Toast' +import {atoms as a} from '#/alf' +import {Button, ButtonIcon, ButtonProps, ButtonText} from '#/components/Button' +import {useDialogControl} from '#/components/Dialog' +import {ReportDialog} from '#/components/dms/ReportDialog' +import {CircleX_Stroke2_Corner0_Rounded} from '#/components/icons/CircleX' +import {Flag_Stroke2_Corner0_Rounded as FlagIcon} from '#/components/icons/Flag' +import {PersonX_Stroke2_Corner0_Rounded as PersonXIcon} from '#/components/icons/Person' +import {Loader} from '#/components/Loader' +import * as Menu from '#/components/Menu' + +export function RejectMenu({ + convo, + profile, + size = 'tiny', + variant = 'outline', + color = 'secondary', + label, + showDeleteConvo, + currentScreen, + ...props +}: Omit<ButtonProps, 'onPress' | 'children' | 'label'> & { + label?: string + convo: ChatBskyConvoDefs.ConvoView + profile: ChatBskyActorDefs.ProfileViewBasic + showDeleteConvo?: boolean + currentScreen: 'list' | 'conversation' +}) { + const {_} = useLingui() + const shadowedProfile = useProfileShadow(profile) + const navigation = useNavigation<NavigationProp>() + const {mutate: leaveConvo} = useLeaveConvo(convo.id, { + onMutate: () => { + if (currentScreen === 'conversation') { + navigation.dispatch(StackActions.pop()) + } + }, + onError: () => { + Toast.show(_('Failed to delete chat'), 'xmark') + }, + }) + const [queueBlock] = useProfileBlockMutationQueue(shadowedProfile) + + const onPressDelete = useCallback(() => { + Toast.show(_('Chat deleted'), 'check') + leaveConvo() + }, [leaveConvo, _]) + + const onPressBlock = useCallback(() => { + Toast.show(_('Account blocked'), 'check') + // block and also delete convo + queueBlock() + leaveConvo() + }, [queueBlock, leaveConvo, _]) + + const reportControl = useDialogControl() + + const lastMessage = ChatBskyConvoDefs.isMessageView(convo.lastMessage) + ? convo.lastMessage + : null + + return ( + <> + <Menu.Root> + <Menu.Trigger label={_(msg`Reject chat request`)}> + {({props: triggerProps}) => ( + <Button + {...triggerProps} + {...props} + label={triggerProps.accessibilityLabel} + style={[a.flex_1]} + color={color} + variant={variant} + size={size}> + <ButtonText> + {label || ( + <Trans comment="Reject a chat request, this opens a menu with options"> + Reject + </Trans> + )} + </ButtonText> + </Button> + )} + </Menu.Trigger> + <Menu.Outer> + <Menu.Group> + {showDeleteConvo && ( + <Menu.Item + label={_(msg`Delete conversation`)} + onPress={onPressDelete}> + <Menu.ItemText> + <Trans>Delete conversation</Trans> + </Menu.ItemText> + <Menu.ItemIcon icon={CircleX_Stroke2_Corner0_Rounded} /> + </Menu.Item> + )} + <Menu.Item label={_(msg`Block account`)} onPress={onPressBlock}> + <Menu.ItemText> + <Trans>Block account</Trans> + </Menu.ItemText> + <Menu.ItemIcon icon={PersonXIcon} /> + </Menu.Item> + {/* note: last message will almost certainly be defined, since you can't + delete messages for other people andit's impossible for a convo on this + screen to have a message sent by you */} + {lastMessage && ( + <Menu.Item + label={_(msg`Report conversation`)} + onPress={reportControl.open}> + <Menu.ItemText> + <Trans>Report conversation</Trans> + </Menu.ItemText> + <Menu.ItemIcon icon={FlagIcon} /> + </Menu.Item> + )} + </Menu.Group> + </Menu.Outer> + </Menu.Root> + {lastMessage && ( + <ReportDialog + currentScreen={currentScreen} + params={{ + type: 'convoMessage', + convoId: convo.id, + message: lastMessage, + }} + control={reportControl} + /> + )} + </> + ) +} + +export function AcceptChatButton({ + convo, + size = 'tiny', + variant = 'solid', + color = 'secondary_inverted', + label, + currentScreen, + onAcceptConvo, + ...props +}: Omit<ButtonProps, 'onPress' | 'children' | 'label'> & { + label?: string + convo: ChatBskyConvoDefs.ConvoView + onAcceptConvo?: () => void + currentScreen: 'list' | 'conversation' +}) { + const {_} = useLingui() + const queryClient = useQueryClient() + const navigation = useNavigation<NavigationProp>() + + const {mutate: acceptConvo, isPending} = useAcceptConversation(convo.id, { + onMutate: () => { + onAcceptConvo?.() + if (currentScreen === 'list') { + precacheConvoQuery(queryClient, {...convo, status: 'accepted'}) + navigation.navigate('MessagesConversation', { + conversation: convo.id, + accept: true, + }) + } + }, + onError: () => { + // Should we show a toast here? They'll be on the convo screen, and it'll make + // no difference if the request failed - when they send a message, the convo will be accepted + // automatically. The only difference is that when they back out of the convo (without sending a message), the conversation will be rejected. + // the list will still have this chat in it -sfn + Toast.show(_('Failed to accept chat'), 'xmark') + }, + }) + + const onPressAccept = useCallback(() => { + acceptConvo() + }, [acceptConvo]) + + return ( + <Button + {...props} + label={label || _(msg`Accept chat request`)} + size={size} + variant={variant} + color={color} + style={a.flex_1} + onPress={onPressAccept}> + {isPending ? ( + <ButtonIcon icon={Loader} /> + ) : ( + <ButtonText> + {label || <Trans comment="Accept a chat request">Accept</Trans>} + </ButtonText> + )} + </Button> + ) +} + +export function DeleteChatButton({ + convo, + size = 'tiny', + variant = 'outline', + color = 'secondary', + label, + currentScreen, + ...props +}: Omit<ButtonProps, 'children' | 'label'> & { + label?: string + convo: ChatBskyConvoDefs.ConvoView + currentScreen: 'list' | 'conversation' +}) { + const {_} = useLingui() + const navigation = useNavigation<NavigationProp>() + + const {mutate: leaveConvo} = useLeaveConvo(convo.id, { + onMutate: () => { + if (currentScreen === 'conversation') { + navigation.dispatch(StackActions.pop()) + } + }, + onError: () => { + Toast.show(_('Failed to delete chat'), 'xmark') + }, + }) + + const onPressDelete = useCallback(() => { + Toast.show(_('Chat deleted'), 'check') + leaveConvo() + }, [leaveConvo, _]) + + return ( + <Button + label={label || _(msg`Delete chat`)} + size={size} + variant={variant} + color={color} + style={a.flex_1} + onPress={onPressDelete} + {...props}> + <ButtonText>{label || <Trans>Delete chat</Trans>}</ButtonText> + </Button> + ) +} |