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

Configure Feed

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

at main 198 lines 5.7 kB view raw
1import {useCallback, useMemo, useState} from 'react' 2import {type StyleProp, View, type ViewStyle} from 'react-native' 3import {type AppBskyActorDefs as ActorDefs} from '@atproto/api' 4import {Trans} from '@lingui/macro' 5import {useFocusEffect} from '@react-navigation/native' 6import {type NativeStackScreenProps} from '@react-navigation/native-stack' 7 8import {type CommonNavigatorParams} from '#/lib/routes/types' 9import {cleanError} from '#/lib/strings/errors' 10import {logger} from '#/logger' 11import {useModerationOpts} from '#/state/preferences/moderation-opts' 12import {useMyBlockedAccountsQuery} from '#/state/queries/my-blocked-accounts' 13import {useSetMinimalShellMode} from '#/state/shell' 14import {ErrorScreen} from '#/view/com/util/error/ErrorScreen' 15import {List} from '#/view/com/util/List' 16import {atoms as a, useTheme} from '#/alf' 17import * as Layout from '#/components/Layout' 18import {ListFooter} from '#/components/Lists' 19import * as ProfileCard from '#/components/ProfileCard' 20import {Text} from '#/components/Typography' 21 22type Props = NativeStackScreenProps< 23 CommonNavigatorParams, 24 'ModerationBlockedAccounts' 25> 26export function ModerationBlockedAccounts({}: Props) { 27 const t = useTheme() 28 const setMinimalShellMode = useSetMinimalShellMode() 29 const moderationOpts = useModerationOpts() 30 31 const [isPTRing, setIsPTRing] = useState(false) 32 const { 33 data, 34 isFetching, 35 isError, 36 error, 37 refetch, 38 hasNextPage, 39 fetchNextPage, 40 isFetchingNextPage, 41 } = useMyBlockedAccountsQuery() 42 const isEmpty = !isFetching && !data?.pages[0]?.blocks.length 43 const profiles = useMemo(() => { 44 if (data?.pages) { 45 return data.pages.flatMap(page => page.blocks) 46 } 47 return [] 48 }, [data]) 49 50 useFocusEffect( 51 useCallback(() => { 52 setMinimalShellMode(false) 53 }, [setMinimalShellMode]), 54 ) 55 56 const onRefresh = useCallback(async () => { 57 setIsPTRing(true) 58 try { 59 await refetch() 60 } catch (err) { 61 logger.error('Failed to refresh my muted accounts', {message: err}) 62 } 63 setIsPTRing(false) 64 }, [refetch, setIsPTRing]) 65 66 const onEndReached = useCallback(async () => { 67 if (isFetching || !hasNextPage || isError) return 68 69 try { 70 await fetchNextPage() 71 } catch (err) { 72 logger.error('Failed to load more of my muted accounts', {message: err}) 73 } 74 }, [isFetching, hasNextPage, isError, fetchNextPage]) 75 76 const renderItem = ({ 77 item, 78 index, 79 }: { 80 item: ActorDefs.ProfileView 81 index: number 82 }) => { 83 if (!moderationOpts) return null 84 return ( 85 <View 86 style={[a.py_md, a.px_xl, a.border_t, t.atoms.border_contrast_low]} 87 key={item.did}> 88 <ProfileCard.Default 89 testID={`blockedAccount-${index}`} 90 profile={item} 91 moderationOpts={moderationOpts} 92 /> 93 </View> 94 ) 95 } 96 97 return ( 98 <Layout.Screen testID="blockedAccountsScreen"> 99 <Layout.Center> 100 <Layout.Header.Outer> 101 <Layout.Header.BackButton /> 102 <Layout.Header.Content> 103 <Layout.Header.TitleText> 104 <Trans>Blocked Accounts</Trans> 105 </Layout.Header.TitleText> 106 </Layout.Header.Content> 107 <Layout.Header.Slot /> 108 </Layout.Header.Outer> 109 {isEmpty ? ( 110 <View> 111 <Info style={[a.border_b]} /> 112 {isError ? ( 113 <ErrorScreen 114 title="Oops!" 115 message={cleanError(error)} 116 onPressTryAgain={refetch} 117 /> 118 ) : ( 119 <Empty /> 120 )} 121 </View> 122 ) : ( 123 <List 124 data={profiles} 125 keyExtractor={(item: ActorDefs.ProfileView) => item.did} 126 refreshing={isPTRing} 127 onRefresh={onRefresh} 128 onEndReached={onEndReached} 129 renderItem={renderItem} 130 initialNumToRender={15} 131 // FIXME(dan) 132 133 ListHeaderComponent={Info} 134 ListFooterComponent={ 135 <ListFooter 136 isFetchingNextPage={isFetchingNextPage} 137 hasNextPage={hasNextPage} 138 error={cleanError(error)} 139 onRetry={fetchNextPage} 140 /> 141 } 142 /> 143 )} 144 </Layout.Center> 145 </Layout.Screen> 146 ) 147} 148 149function Empty() { 150 const t = useTheme() 151 return ( 152 <View style={[a.pt_2xl, a.px_xl, a.align_center]}> 153 <View 154 style={[ 155 a.py_md, 156 a.px_lg, 157 a.rounded_sm, 158 t.atoms.bg_contrast_25, 159 a.border, 160 t.atoms.border_contrast_low, 161 {maxWidth: 400}, 162 ]}> 163 <Text style={[a.text_sm, a.text_center, t.atoms.text_contrast_high]}> 164 <Trans> 165 You have not blocked any accounts yet. To block an account, go to 166 their profile and select "Block account" from the menu on their 167 account. 168 </Trans> 169 </Text> 170 </View> 171 </View> 172 ) 173} 174 175function Info({style}: {style?: StyleProp<ViewStyle>}) { 176 const t = useTheme() 177 return ( 178 <View 179 style={[ 180 a.w_full, 181 t.atoms.bg_contrast_25, 182 a.py_md, 183 a.px_xl, 184 a.border_t, 185 {marginTop: a.border.borderWidth * -1}, 186 t.atoms.border_contrast_low, 187 style, 188 ]}> 189 <Text style={[a.text_center, a.text_sm, t.atoms.text_contrast_high]}> 190 <Trans> 191 Blocked accounts cannot reply in your threads, mention you, or 192 otherwise interact with you. You will not see their content and they 193 will be prevented from seeing yours. 194 </Trans> 195 </Text> 196 </View> 197 ) 198}