Bluesky app fork with some witchin' additions 馃挮
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

at 82f42e734c50b34de31e8aff1e7ced248ab6e96f 292 lines 9.2 kB view raw
1import React, {useCallback} from 'react' 2import {Keyboard, View} from 'react-native' 3import {type ChatBskyConvoDefs, type ModerationCause} from '@atproto/api' 4import {msg, Trans} from '@lingui/macro' 5import {useLingui} from '@lingui/react' 6import {useNavigation} from '@react-navigation/native' 7 8import {type NavigationProp} from '#/lib/routes/types' 9import {type Shadow} from '#/state/cache/types' 10import { 11 useConvoQuery, 12 useMarkAsReadMutation, 13} from '#/state/queries/messages/conversation' 14import {useMuteConvo} from '#/state/queries/messages/mute-conversation' 15import {useProfileBlockMutationQueue} from '#/state/queries/profile' 16import * as Toast from '#/view/com/util/Toast' 17import {type ViewStyleProp} from '#/alf' 18import {atoms as a} from '#/alf' 19import {Button, ButtonIcon} from '#/components/Button' 20import {AfterReportDialog} from '#/components/dms/AfterReportDialog' 21import {BlockedByListDialog} from '#/components/dms/BlockedByListDialog' 22import {LeaveConvoPrompt} from '#/components/dms/LeaveConvoPrompt' 23import {ReportConversationPrompt} from '#/components/dms/ReportConversationPrompt' 24import {ArrowBoxLeft_Stroke2_Corner0_Rounded as ArrowBoxLeft} from '#/components/icons/ArrowBoxLeft' 25import {Bubble_Stroke2_Corner2_Rounded as Bubble} from '#/components/icons/Bubble' 26import {DotGrid_Stroke2_Corner0_Rounded as DotsHorizontal} from '#/components/icons/DotGrid' 27import {Flag_Stroke2_Corner0_Rounded as Flag} from '#/components/icons/Flag' 28import {Mute_Stroke2_Corner0_Rounded as Mute} from '#/components/icons/Mute' 29import { 30 Person_Stroke2_Corner0_Rounded as Person, 31 PersonCheck_Stroke2_Corner0_Rounded as PersonCheck, 32 PersonX_Stroke2_Corner0_Rounded as PersonX, 33} from '#/components/icons/Person' 34import {SpeakerVolumeFull_Stroke2_Corner0_Rounded as Unmute} from '#/components/icons/Speaker' 35import * as Menu from '#/components/Menu' 36import {ReportDialog} from '#/components/moderation/ReportDialog' 37import * as Prompt from '#/components/Prompt' 38import type * as bsky from '#/types/bsky' 39 40let ConvoMenu = ({ 41 convo, 42 profile, 43 control, 44 currentScreen, 45 showMarkAsRead, 46 hideTrigger, 47 blockInfo, 48 latestReportableMessage, 49 style, 50}: { 51 convo: ChatBskyConvoDefs.ConvoView 52 profile: Shadow<bsky.profile.AnyProfileView> 53 control?: Menu.MenuControlProps 54 currentScreen: 'list' | 'conversation' 55 showMarkAsRead?: boolean 56 hideTrigger?: boolean 57 blockInfo: { 58 listBlocks: ModerationCause[] 59 userBlock?: ModerationCause 60 } 61 latestReportableMessage?: ChatBskyConvoDefs.MessageView 62 style?: ViewStyleProp['style'] 63}): React.ReactNode => { 64 const {_} = useLingui() 65 66 const leaveConvoControl = Prompt.usePromptControl() 67 const reportControl = Prompt.usePromptControl() 68 const blockedByListControl = Prompt.usePromptControl() 69 const blockOrDeleteControl = Prompt.usePromptControl() 70 71 const {listBlocks} = blockInfo 72 73 return ( 74 <> 75 <Menu.Root control={control}> 76 {!hideTrigger && ( 77 <View style={[style]}> 78 <Menu.Trigger label={_(msg`Chat settings`)}> 79 {({props}) => ( 80 <Button 81 label={props.accessibilityLabel} 82 {...props} 83 onPress={() => { 84 Keyboard.dismiss() 85 props.onPress() 86 }} 87 size="small" 88 color="secondary" 89 shape="round" 90 variant="ghost" 91 style={[a.bg_transparent]}> 92 <ButtonIcon icon={DotsHorizontal} size="md" /> 93 </Button> 94 )} 95 </Menu.Trigger> 96 </View> 97 )} 98 99 <Menu.Outer> 100 <MenuContent 101 profile={profile} 102 showMarkAsRead={showMarkAsRead} 103 blockInfo={blockInfo} 104 convo={convo} 105 leaveConvoControl={leaveConvoControl} 106 reportControl={reportControl} 107 blockedByListControl={blockedByListControl} 108 /> 109 </Menu.Outer> 110 </Menu.Root> 111 112 <LeaveConvoPrompt 113 control={leaveConvoControl} 114 convoId={convo.id} 115 currentScreen={currentScreen} 116 /> 117 {latestReportableMessage ? ( 118 <> 119 <ReportDialog 120 subject={{ 121 view: 'convo', 122 convoId: convo.id, 123 message: latestReportableMessage, 124 }} 125 control={reportControl} 126 onAfterSubmit={() => { 127 blockOrDeleteControl.open() 128 }} 129 /> 130 <AfterReportDialog 131 control={blockOrDeleteControl} 132 currentScreen={currentScreen} 133 params={{ 134 convoId: convo.id, 135 message: latestReportableMessage, 136 }} 137 /> 138 </> 139 ) : ( 140 <ReportConversationPrompt control={reportControl} /> 141 )} 142 143 <BlockedByListDialog 144 control={blockedByListControl} 145 listBlocks={listBlocks} 146 /> 147 </> 148 ) 149} 150ConvoMenu = React.memo(ConvoMenu) 151 152function MenuContent({ 153 convo: initialConvo, 154 profile, 155 showMarkAsRead, 156 blockInfo, 157 leaveConvoControl, 158 reportControl, 159 blockedByListControl, 160}: { 161 convo: ChatBskyConvoDefs.ConvoView 162 profile: Shadow<bsky.profile.AnyProfileView> 163 showMarkAsRead?: boolean 164 blockInfo: { 165 listBlocks: ModerationCause[] 166 userBlock?: ModerationCause 167 } 168 leaveConvoControl: Prompt.PromptControlProps 169 reportControl: Prompt.PromptControlProps 170 blockedByListControl: Prompt.PromptControlProps 171}) { 172 const navigation = useNavigation<NavigationProp>() 173 const {_} = useLingui() 174 const {mutate: markAsRead} = useMarkAsReadMutation() 175 176 const {listBlocks, userBlock} = blockInfo 177 const isBlocking = userBlock || !!listBlocks.length 178 const isDeletedAccount = profile.handle === 'missing.invalid' 179 180 const convoId = initialConvo.id 181 const {data: convo} = useConvoQuery(initialConvo) 182 183 const onNavigateToProfile = useCallback(() => { 184 navigation.navigate('Profile', {name: profile.did}) 185 }, [navigation, profile.did]) 186 187 const {mutate: muteConvo} = useMuteConvo(convoId, { 188 onSuccess: data => { 189 if (data.convo.muted) { 190 Toast.show(_(msg({message: 'Chat muted', context: 'toast'}))) 191 } else { 192 Toast.show(_(msg({message: 'Chat unmuted', context: 'toast'}))) 193 } 194 }, 195 onError: () => { 196 Toast.show(_(msg`Could not mute chat`), 'xmark') 197 }, 198 }) 199 200 const [queueBlock, queueUnblock] = useProfileBlockMutationQueue(profile) 201 202 const toggleBlock = React.useCallback(() => { 203 if (listBlocks.length) { 204 blockedByListControl.open() 205 return 206 } 207 208 if (userBlock) { 209 queueUnblock() 210 } else { 211 queueBlock() 212 } 213 }, [userBlock, listBlocks, blockedByListControl, queueBlock, queueUnblock]) 214 215 return isDeletedAccount ? ( 216 <Menu.Item 217 label={_(msg`Leave conversation`)} 218 onPress={() => leaveConvoControl.open()}> 219 <Menu.ItemText> 220 <Trans>Leave conversation</Trans> 221 </Menu.ItemText> 222 <Menu.ItemIcon icon={ArrowBoxLeft} /> 223 </Menu.Item> 224 ) : ( 225 <> 226 <Menu.Group> 227 {showMarkAsRead && ( 228 <Menu.Item 229 label={_(msg`Mark as read`)} 230 onPress={() => markAsRead({convoId})}> 231 <Menu.ItemText> 232 <Trans>Mark as read</Trans> 233 </Menu.ItemText> 234 <Menu.ItemIcon icon={Bubble} /> 235 </Menu.Item> 236 )} 237 <Menu.Item 238 label={_(msg`Go to user's profile`)} 239 onPress={onNavigateToProfile}> 240 <Menu.ItemText> 241 <Trans>Go to profile</Trans> 242 </Menu.ItemText> 243 <Menu.ItemIcon icon={Person} /> 244 </Menu.Item> 245 <Menu.Item 246 label={_(msg`Mute conversation`)} 247 onPress={() => muteConvo({mute: !convo?.muted})}> 248 <Menu.ItemText> 249 {convo?.muted ? ( 250 <Trans>Unmute conversation</Trans> 251 ) : ( 252 <Trans>Mute conversation</Trans> 253 )} 254 </Menu.ItemText> 255 <Menu.ItemIcon icon={convo?.muted ? Unmute : Mute} /> 256 </Menu.Item> 257 </Menu.Group> 258 <Menu.Divider /> 259 <Menu.Group> 260 <Menu.Item 261 label={isBlocking ? _(msg`Unblock account`) : _(msg`Block account`)} 262 onPress={toggleBlock}> 263 <Menu.ItemText> 264 {isBlocking ? _(msg`Unblock account`) : _(msg`Block account`)} 265 </Menu.ItemText> 266 <Menu.ItemIcon icon={isBlocking ? PersonCheck : PersonX} /> 267 </Menu.Item> 268 <Menu.Item 269 label={_(msg`Report conversation`)} 270 onPress={() => reportControl.open()}> 271 <Menu.ItemText> 272 <Trans>Report conversation</Trans> 273 </Menu.ItemText> 274 <Menu.ItemIcon icon={Flag} /> 275 </Menu.Item> 276 </Menu.Group> 277 <Menu.Divider /> 278 <Menu.Group> 279 <Menu.Item 280 label={_(msg`Leave conversation`)} 281 onPress={() => leaveConvoControl.open()}> 282 <Menu.ItemText> 283 <Trans>Leave conversation</Trans> 284 </Menu.ItemText> 285 <Menu.ItemIcon icon={ArrowBoxLeft} /> 286 </Menu.Item> 287 </Menu.Group> 288 </> 289 ) 290} 291 292export {ConvoMenu}