An ATproto social media client -- with an independent Appview.
6
fork

Configure Feed

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

Add WIP 'report account' modal

+127 -4
+11 -1
src/state/models/shell-ui.ts
··· 59 59 } 60 60 } 61 61 62 + export class ReportAccountModal { 63 + name = 'report-account' 64 + 65 + constructor(public did: string) { 66 + makeAutoObservable(this) 67 + } 68 + } 69 + 62 70 interface LightboxModel { 63 71 canSwipeLeft: boolean 64 72 canSwipeRight: boolean ··· 136 144 | CreateSceneModal 137 145 | ServerInputModal 138 146 | ReportPostModal 147 + | ReportAccountModal 139 148 | undefined 140 149 isLightboxActive = false 141 150 activeLightbox: ··· 164 173 | EditProfileModal 165 174 | CreateSceneModal 166 175 | ServerInputModal 167 - | ReportPostModal, 176 + | ReportPostModal 177 + | ReportAccountModal, 168 178 ) { 169 179 this.isModalActive = true 170 180 this.activeModal = modal
+8
src/view/com/modals/Modal.tsx
··· 13 13 import * as InviteToSceneModal from './InviteToScene' 14 14 import * as ServerInputModal from './ServerInput' 15 15 import * as ReportPostModal from './ReportPost' 16 + import * as ReportAccountModal from './ReportAccount' 16 17 17 18 const CLOSED_SNAPPOINTS = ['10%'] 18 19 ··· 76 77 element = ( 77 78 <ReportPostModal.Component 78 79 {...(store.shell.activeModal as models.ReportPostModal)} 80 + /> 81 + ) 82 + } else if (store.shell.activeModal?.name === 'report-account') { 83 + snapPoints = ReportAccountModal.snapPoints 84 + element = ( 85 + <ReportAccountModal.Component 86 + {...(store.shell.activeModal as models.ReportAccountModal)} 79 87 /> 80 88 ) 81 89 } else {
+95
src/view/com/modals/ReportAccount.tsx
··· 1 + import React, {useState} from 'react' 2 + import { 3 + ActivityIndicator, 4 + StyleSheet, 5 + Text, 6 + TouchableOpacity, 7 + View, 8 + } from 'react-native' 9 + import LinearGradient from 'react-native-linear-gradient' 10 + import {useStores} from '../../../state' 11 + import {s, colors, gradients} from '../../lib/styles' 12 + import {RadioGroup, RadioGroupItem} from '../util/forms/RadioGroup' 13 + import {ErrorMessage} from '../util/ErrorMessage' 14 + 15 + const ITEMS: RadioGroupItem[] = [ 16 + {key: 'spam', label: 'Spam or excessive repeat posts'}, 17 + {key: 'abuse', label: 'Abusive, rude, or hateful'}, 18 + {key: 'illegal', label: 'Posts illegal content'}, 19 + ] 20 + 21 + export const snapPoints = ['50%'] 22 + 23 + export function Component({did}: {did: string}) { 24 + const store = useStores() 25 + const [isProcessing, setIsProcessing] = useState<boolean>(false) 26 + const [error, setError] = useState<string>('') 27 + const [issue, setIssue] = useState<string>('') 28 + const onSelectIssue = (v: string) => setIssue(v) 29 + const onPress = async () => { 30 + setError('') 31 + setIsProcessing(true) 32 + try { 33 + // TODO 34 + store.shell.closeModal() 35 + return 36 + } catch (e: any) { 37 + setError(e.toString()) 38 + setIsProcessing(false) 39 + } 40 + } 41 + return ( 42 + <View style={[s.flex1, s.pl10, s.pr10]}> 43 + <Text style={styles.title}>Report account</Text> 44 + <Text style={styles.description}> 45 + What is the issue with this account? 46 + </Text> 47 + <RadioGroup items={ITEMS} onSelect={onSelectIssue} /> 48 + {error ? ( 49 + <View style={s.mt10}> 50 + <ErrorMessage message={error} /> 51 + </View> 52 + ) : undefined} 53 + {isProcessing ? ( 54 + <View style={[styles.btn, s.mt10]}> 55 + <ActivityIndicator /> 56 + </View> 57 + ) : issue ? ( 58 + <TouchableOpacity style={s.mt10} onPress={onPress}> 59 + <LinearGradient 60 + colors={[gradients.primary.start, gradients.primary.end]} 61 + start={{x: 0, y: 0}} 62 + end={{x: 1, y: 1}} 63 + style={[styles.btn]}> 64 + <Text style={[s.white, s.bold, s.f18]}>Send Report</Text> 65 + </LinearGradient> 66 + </TouchableOpacity> 67 + ) : undefined} 68 + </View> 69 + ) 70 + } 71 + 72 + const styles = StyleSheet.create({ 73 + title: { 74 + textAlign: 'center', 75 + fontWeight: 'bold', 76 + fontSize: 24, 77 + marginBottom: 12, 78 + }, 79 + description: { 80 + textAlign: 'center', 81 + fontSize: 17, 82 + paddingHorizontal: 22, 83 + color: colors.gray5, 84 + marginBottom: 10, 85 + }, 86 + btn: { 87 + flexDirection: 'row', 88 + alignItems: 'center', 89 + justifyContent: 'center', 90 + width: '100%', 91 + borderRadius: 32, 92 + padding: 14, 93 + backgroundColor: colors.gray1, 94 + }, 95 + })
+13 -3
src/view/com/profile/ProfileHeader.tsx
··· 10 10 ConfirmModal, 11 11 EditProfileModal, 12 12 InviteToSceneModal, 13 + ReportAccountModal, 13 14 ProfileImageLightbox, 14 15 } from '../../../state/models/shell-ui' 15 16 import {pluralize} from '../../../lib/strings' ··· 85 86 } 86 87 onRefreshAll() 87 88 } 89 + const onPressReportAccount = () => { 90 + store.shell.openModal(new ReportAccountModal(view.did)) 91 + } 88 92 89 93 // loading 90 94 // = ··· 133 137 const isMe = store.me.did === view.did 134 138 const isCreator = view.isScene && view.creator === store.me.did 135 139 let dropdownItems: DropdownItem[] | undefined 140 + if (!isMe) { 141 + dropdownItems = dropdownItems || [] 142 + dropdownItems.push({ 143 + label: 'Report Account', 144 + onPress: onPressReportAccount, 145 + }) 146 + } 136 147 if (isCreator || isMember) { 137 - dropdownItems = [] 148 + dropdownItems = dropdownItems || [] 138 149 if (isCreator) { 139 150 dropdownItems.push({ 140 151 label: 'Edit Profile', ··· 182 193 )} 183 194 </> 184 195 )} 185 - {view.isScene && 186 - (view.myState.member || view.creator === store.me.did) ? ( 196 + {dropdownItems?.length ? ( 187 197 <DropdownBtn 188 198 items={dropdownItems} 189 199 style={[styles.btn, styles.secondaryBtn]}>