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

Configure Feed

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

at main 223 lines 6.6 kB view raw
1import {memo, useState} from 'react' 2import {View} from 'react-native' 3import {type AppBskyActorDefs, type ChatBskyConvoDefs} from '@atproto/api' 4import {msg} from '@lingui/core/macro' 5import {useLingui} from '@lingui/react' 6import {Trans} from '@lingui/react/macro' 7import {StackActions, useNavigation} from '@react-navigation/native' 8 9import {type NavigationProp} from '#/lib/routes/types' 10import {useProfileShadow} from '#/state/cache/profile-shadow' 11import {useLeaveConvo} from '#/state/queries/messages/leave-conversation' 12import { 13 useProfileBlockMutationQueue, 14 useProfileQuery, 15} from '#/state/queries/profile' 16import {atoms as a, platform, useBreakpoints, useTheme, web} from '#/alf' 17import {Button, ButtonText} from '#/components/Button' 18import * as Dialog from '#/components/Dialog' 19import * as Toggle from '#/components/forms/Toggle' 20import {Loader} from '#/components/Loader' 21import * as Toast from '#/components/Toast' 22import {Text} from '#/components/Typography' 23import {IS_NATIVE} from '#/env' 24 25type ReportDialogParams = { 26 convoId: string 27 message: ChatBskyConvoDefs.MessageView 28} 29 30/** 31 * Dialog shown after a report is submitted, allowing the user to block the 32 * reporter and/or leave the conversation. 33 */ 34export const AfterReportDialog = memo(function BlockOrDeleteDialogInner({ 35 control, 36 params, 37 currentScreen, 38}: { 39 control: Dialog.DialogControlProps 40 params: ReportDialogParams 41 currentScreen: 'list' | 'conversation' 42}): React.ReactNode { 43 const {_} = useLingui() 44 return ( 45 <Dialog.Outer control={control} nativeOptions={{preventExpansion: true}}> 46 <Dialog.Handle /> 47 <Dialog.ScrollableInner 48 label={_( 49 msg`Would you like to block this user and/or delete this conversation?`, 50 )} 51 style={[web({maxWidth: 400})]}> 52 <DialogInner params={params} currentScreen={currentScreen} /> 53 <Dialog.Close /> 54 </Dialog.ScrollableInner> 55 </Dialog.Outer> 56 ) 57}) 58 59function DialogInner({ 60 params, 61 currentScreen, 62}: { 63 params: ReportDialogParams 64 currentScreen: 'list' | 'conversation' 65}) { 66 const t = useTheme() 67 const {_} = useLingui() 68 const control = Dialog.useDialogContext() 69 const { 70 data: profile, 71 isPending, 72 isError, 73 } = useProfileQuery({ 74 did: params.message.sender.did, 75 }) 76 77 return isPending ? ( 78 <View style={[a.w_full, a.py_5xl, a.align_center]}> 79 <Loader size="lg" /> 80 </View> 81 ) : isError || !profile ? ( 82 <View style={[a.w_full, a.gap_lg]}> 83 <View style={[a.justify_center, a.gap_sm]}> 84 <Text style={[a.text_2xl, a.font_semi_bold]}> 85 <Trans>Report submitted</Trans> 86 </Text> 87 <Text style={[a.text_md, t.atoms.text_contrast_medium]}> 88 <Trans>Our moderation team has received your report.</Trans> 89 </Text> 90 </View> 91 92 <Button 93 label={_(msg`Close`)} 94 onPress={() => control.close()} 95 size={platform({native: 'small', web: 'large'})} 96 color="secondary"> 97 <ButtonText> 98 <Trans>Close</Trans> 99 </ButtonText> 100 </Button> 101 </View> 102 ) : ( 103 <DoneStep 104 convoId={params.convoId} 105 currentScreen={currentScreen} 106 profile={profile} 107 /> 108 ) 109} 110 111function DoneStep({ 112 convoId, 113 currentScreen, 114 profile, 115}: { 116 convoId: string 117 currentScreen: 'list' | 'conversation' 118 profile: AppBskyActorDefs.ProfileViewDetailed 119}) { 120 const {_} = useLingui() 121 const navigation = useNavigation<NavigationProp>() 122 const control = Dialog.useDialogContext() 123 const {gtMobile} = useBreakpoints() 124 const t = useTheme() 125 const [actions, setActions] = useState<string[]>(['block', 'leave']) 126 const shadow = useProfileShadow(profile) 127 const [queueBlock] = useProfileBlockMutationQueue(shadow) 128 129 const {mutate: leaveConvo} = useLeaveConvo(convoId, { 130 onMutate: () => { 131 if (currentScreen === 'conversation') { 132 navigation.dispatch( 133 StackActions.replace('Messages', IS_NATIVE ? {animation: 'pop'} : {}), 134 ) 135 } 136 }, 137 onError: () => { 138 Toast.show(_(msg`Could not leave chat`), { 139 type: 'error', 140 }) 141 }, 142 }) 143 144 let btnText = _(msg`Done`) 145 let toastMsg: string | undefined 146 if (actions.includes('leave') && actions.includes('block')) { 147 btnText = _(msg`Block and Delete`) 148 toastMsg = _(msg({message: 'Conversation deleted', context: 'toast'})) 149 } else if (actions.includes('leave')) { 150 btnText = _(msg`Delete Conversation`) 151 toastMsg = _(msg({message: 'Conversation deleted', context: 'toast'})) 152 } else if (actions.includes('block')) { 153 btnText = _(msg`Block User`) 154 toastMsg = _(msg({message: 'User blocked', context: 'toast'})) 155 } 156 157 const onPressPrimaryAction = () => { 158 control.close(() => { 159 if (actions.includes('block')) { 160 queueBlock() 161 } 162 if (actions.includes('leave')) { 163 leaveConvo() 164 } 165 if (toastMsg) { 166 Toast.show(toastMsg, { 167 type: 'success', 168 }) 169 } 170 }) 171 } 172 173 return ( 174 <View style={a.gap_2xl}> 175 <View style={[a.justify_center, gtMobile ? a.gap_sm : a.gap_xs]}> 176 <Text style={[a.text_2xl, a.font_semi_bold]}> 177 <Trans>Report submitted</Trans> 178 </Text> 179 <Text style={[a.text_md, t.atoms.text_contrast_medium]}> 180 <Trans>Our moderation team has received your report.</Trans> 181 </Text> 182 </View> 183 <Toggle.Group 184 label={_(msg`Block user and/or delete this conversation`)} 185 values={actions} 186 onChange={setActions}> 187 <View style={[a.gap_md]}> 188 <Toggle.Item name="block" label={_(msg`Block user`)}> 189 <Toggle.Checkbox /> 190 <Toggle.LabelText style={[a.text_md]}> 191 <Trans>Block user</Trans> 192 </Toggle.LabelText> 193 </Toggle.Item> 194 <Toggle.Item name="leave" label={_(msg`Delete conversation`)}> 195 <Toggle.Checkbox /> 196 <Toggle.LabelText style={[a.text_md]}> 197 <Trans>Delete conversation</Trans> 198 </Toggle.LabelText> 199 </Toggle.Item> 200 </View> 201 </Toggle.Group> 202 203 <View style={[a.gap_sm]}> 204 <Button 205 label={btnText} 206 onPress={onPressPrimaryAction} 207 size="large" 208 color={actions.length > 0 ? 'negative' : 'primary'}> 209 <ButtonText>{btnText}</ButtonText> 210 </Button> 211 <Button 212 label={_(msg`Close`)} 213 onPress={() => control.close()} 214 size="large" 215 color="secondary"> 216 <ButtonText> 217 <Trans>Close</Trans> 218 </ButtonText> 219 </Button> 220 </View> 221 </View> 222 ) 223}