mirror of https://git.lenooby09.tech/LeNooby09/social-app.git
at verify-code 5.7 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 {richTextToString} from '#/lib/strings/rich-text-helpers' 9import {getTranslatorLink} from '#/locale/helpers' 10import {useLanguagePrefs} from '#/state/preferences' 11import {useOpenLink} from '#/state/preferences/in-app-browser' 12import {isWeb} from 'platform/detection' 13import {useConvoActive} from 'state/messages/convo' 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) 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(() => Toast.show(_(msg`Message deleted`))) 73 .catch(() => Toast.show(_(msg`Failed to delete message`))) 74 }, [_, convo, message.id]) 75 76 return ( 77 <> 78 <Menu.Root control={control}> 79 {isWeb && ( 80 <View style={{opacity: triggerOpacity}}> 81 <Menu.Trigger label={_(msg`Chat settings`)}> 82 {({props, state}) => ( 83 <Pressable 84 {...props} 85 style={[ 86 a.p_sm, 87 a.rounded_full, 88 (state.hovered || state.pressed) && t.atoms.bg_contrast_25, 89 ]}> 90 <DotsHorizontal size="md" style={t.atoms.text} /> 91 </Pressable> 92 )} 93 </Menu.Trigger> 94 </View> 95 )} 96 97 <Menu.Outer> 98 {message.text.length > 0 && ( 99 <> 100 <Menu.Group> 101 <Menu.Item 102 testID="messageDropdownTranslateBtn" 103 label={_(msg`Translate`)} 104 onPress={onPressTranslateMessage}> 105 <Menu.ItemText>{_(msg`Translate`)}</Menu.ItemText> 106 <Menu.ItemIcon icon={Translate} position="right" /> 107 </Menu.Item> 108 <Menu.Item 109 testID="messageDropdownCopyBtn" 110 label={_(msg`Copy message text`)} 111 onPress={onCopyMessage}> 112 <Menu.ItemText>{_(msg`Copy message text`)}</Menu.ItemText> 113 <Menu.ItemIcon icon={ClipboardIcon} position="right" /> 114 </Menu.Item> 115 </Menu.Group> 116 <Menu.Divider /> 117 </> 118 )} 119 <Menu.Group> 120 <Menu.Item 121 testID="messageDropdownDeleteBtn" 122 label={_(msg`Delete message for me`)} 123 onPress={deleteControl.open}> 124 <Menu.ItemText>{_(msg`Delete for me`)}</Menu.ItemText> 125 <Menu.ItemIcon icon={Trash} position="right" /> 126 </Menu.Item> 127 {!isFromSelf && ( 128 <Menu.Item 129 testID="messageDropdownReportBtn" 130 label={_(msg`Report message`)} 131 onPress={reportControl.open}> 132 <Menu.ItemText>{_(msg`Report`)}</Menu.ItemText> 133 <Menu.ItemIcon icon={Warning} position="right" /> 134 </Menu.Item> 135 )} 136 </Menu.Group> 137 </Menu.Outer> 138 </Menu.Root> 139 140 <ReportDialog 141 params={{type: 'convoMessage', convoId: convo.convo.id, message}} 142 control={reportControl} 143 /> 144 145 <Prompt.Basic 146 control={deleteControl} 147 title={_(msg`Delete message`)} 148 description={_( 149 msg`Are you sure you want to delete this message? The message will be deleted for you, but not for the other participant.`, 150 )} 151 confirmButtonCta={_(msg`Delete`)} 152 confirmButtonColor="negative" 153 onConfirm={onDelete} 154 /> 155 </> 156 ) 157} 158MessageMenu = React.memo(MessageMenu)