mirror of https://git.lenooby09.tech/LeNooby09/social-app.git
0
fork

Configure Feed

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

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