mirror of https://git.lenooby09.tech/LeNooby09/social-app.git
at patch-version 161 lines 5.8 kB view raw
1import React from 'react' 2import {LayoutAnimation, Pressable, View} from 'react-native' 3import * as Clipboard from 'expo-clipboard' 4import {ChatBskyConvoDefs, RichText} from '@atproto/api' 5import {msg} from '@lingui/macro' 6import {useLingui} from '@lingui/react' 7 8import {useOpenLink} from '#/lib/hooks/useOpenLink' 9import {richTextToString} from '#/lib/strings/rich-text-helpers' 10import {getTranslatorLink} from '#/locale/helpers' 11import {isWeb} from '#/platform/detection' 12import {useConvoActive} from '#/state/messages/convo' 13import {useLanguagePrefs} from '#/state/preferences' 14import {useSession} from '#/state/session' 15import * as Toast from '#/view/com/util/Toast' 16import {atoms as a, useTheme} from '#/alf' 17import {ReportDialog} from '#/components/dms/ReportDialog' 18import {BubbleQuestion_Stroke2_Corner0_Rounded as Translate} from '#/components/icons/Bubble' 19import {DotGrid_Stroke2_Corner0_Rounded as DotsHorizontal} from '#/components/icons/DotGrid' 20import {Trash_Stroke2_Corner0_Rounded as Trash} from '#/components/icons/Trash' 21import {Warning_Stroke2_Corner0_Rounded as Warning} from '#/components/icons/Warning' 22import * as Menu from '#/components/Menu' 23import * as Prompt from '#/components/Prompt' 24import {usePromptControl} from '#/components/Prompt' 25import {Clipboard_Stroke2_Corner2_Rounded as ClipboardIcon} from '../icons/Clipboard' 26 27export let MessageMenu = ({ 28 message, 29 control, 30 triggerOpacity, 31}: { 32 triggerOpacity?: number 33 message: ChatBskyConvoDefs.MessageView 34 control: Menu.MenuControlProps 35}): React.ReactNode => { 36 const {_} = useLingui() 37 const t = useTheme() 38 const {currentAccount} = useSession() 39 const convo = useConvoActive() 40 const deleteControl = usePromptControl() 41 const reportControl = usePromptControl() 42 const langPrefs = useLanguagePrefs() 43 const openLink = useOpenLink() 44 45 const isFromSelf = message.sender?.did === currentAccount?.did 46 47 const onCopyMessage = React.useCallback(() => { 48 const str = richTextToString( 49 new RichText({ 50 text: message.text, 51 facets: message.facets, 52 }), 53 true, 54 ) 55 56 Clipboard.setStringAsync(str) 57 Toast.show(_(msg`Copied to clipboard`), 'clipboard-check') 58 }, [_, message.text, message.facets]) 59 60 const onPressTranslateMessage = React.useCallback(() => { 61 const translatorUrl = getTranslatorLink( 62 message.text, 63 langPrefs.primaryLanguage, 64 ) 65 openLink(translatorUrl, true) 66 }, [langPrefs.primaryLanguage, message.text, openLink]) 67 68 const onDelete = React.useCallback(() => { 69 LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut) 70 convo 71 .deleteMessage(message.id) 72 .then(() => 73 Toast.show(_(msg({message: 'Message deleted', context: 'toast'}))), 74 ) 75 .catch(() => Toast.show(_(msg`Failed to delete message`))) 76 }, [_, convo, message.id]) 77 78 return ( 79 <> 80 <Menu.Root control={control}> 81 {isWeb && ( 82 <View style={{opacity: triggerOpacity}}> 83 <Menu.Trigger label={_(msg`Chat settings`)}> 84 {({props, state}) => ( 85 <Pressable 86 {...props} 87 style={[ 88 a.p_sm, 89 a.rounded_full, 90 (state.hovered || state.pressed) && t.atoms.bg_contrast_25, 91 ]}> 92 <DotsHorizontal size="md" style={t.atoms.text} /> 93 </Pressable> 94 )} 95 </Menu.Trigger> 96 </View> 97 )} 98 99 <Menu.Outer> 100 {message.text.length > 0 && ( 101 <> 102 <Menu.Group> 103 <Menu.Item 104 testID="messageDropdownTranslateBtn" 105 label={_(msg`Translate`)} 106 onPress={onPressTranslateMessage}> 107 <Menu.ItemText>{_(msg`Translate`)}</Menu.ItemText> 108 <Menu.ItemIcon icon={Translate} position="right" /> 109 </Menu.Item> 110 <Menu.Item 111 testID="messageDropdownCopyBtn" 112 label={_(msg`Copy message text`)} 113 onPress={onCopyMessage}> 114 <Menu.ItemText>{_(msg`Copy message text`)}</Menu.ItemText> 115 <Menu.ItemIcon icon={ClipboardIcon} position="right" /> 116 </Menu.Item> 117 </Menu.Group> 118 <Menu.Divider /> 119 </> 120 )} 121 <Menu.Group> 122 <Menu.Item 123 testID="messageDropdownDeleteBtn" 124 label={_(msg`Delete message for me`)} 125 onPress={() => deleteControl.open()}> 126 <Menu.ItemText>{_(msg`Delete for me`)}</Menu.ItemText> 127 <Menu.ItemIcon icon={Trash} position="right" /> 128 </Menu.Item> 129 {!isFromSelf && ( 130 <Menu.Item 131 testID="messageDropdownReportBtn" 132 label={_(msg`Report message`)} 133 onPress={() => reportControl.open()}> 134 <Menu.ItemText>{_(msg`Report`)}</Menu.ItemText> 135 <Menu.ItemIcon icon={Warning} position="right" /> 136 </Menu.Item> 137 )} 138 </Menu.Group> 139 </Menu.Outer> 140 </Menu.Root> 141 142 <ReportDialog 143 currentScreen="conversation" 144 params={{type: 'convoMessage', convoId: convo.convo.id, message}} 145 control={reportControl} 146 /> 147 148 <Prompt.Basic 149 control={deleteControl} 150 title={_(msg`Delete message`)} 151 description={_( 152 msg`Are you sure you want to delete this message? The message will be deleted for you, but not for the other participant.`, 153 )} 154 confirmButtonCta={_(msg`Delete`)} 155 confirmButtonColor="negative" 156 onConfirm={onDelete} 157 /> 158 </> 159 ) 160} 161MessageMenu = React.memo(MessageMenu)