Bluesky app fork with some witchin' additions 💫
0
fork

Configure Feed

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

cleanup mod screens (#8199)

authored by

Samuel Newman and committed by
GitHub
69f656f2 278a5485

+170 -198
+86 -100
src/view/screens/ModerationBlockedAccounts.tsx
··· 1 - import React from 'react' 2 - import { 3 - ActivityIndicator, 4 - FlatList, 5 - RefreshControl, 6 - StyleSheet, 7 - View, 8 - } from 'react-native' 1 + import {useCallback, useMemo, useState} from 'react' 2 + import {type StyleProp, View, type ViewStyle} from 'react-native' 9 3 import {type AppBskyActorDefs as ActorDefs} from '@atproto/api' 10 - import {msg, Trans} from '@lingui/macro' 11 - import {useLingui} from '@lingui/react' 4 + import {Trans} from '@lingui/macro' 12 5 import {useFocusEffect} from '@react-navigation/native' 13 6 import {type NativeStackScreenProps} from '@react-navigation/native-stack' 14 7 15 - import {usePalette} from '#/lib/hooks/usePalette' 16 - import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries' 17 8 import {type CommonNavigatorParams} from '#/lib/routes/types' 18 9 import {cleanError} from '#/lib/strings/errors' 19 10 import {logger} from '#/logger' ··· 21 12 import {useMyBlockedAccountsQuery} from '#/state/queries/my-blocked-accounts' 22 13 import {useSetMinimalShellMode} from '#/state/shell' 23 14 import {ErrorScreen} from '#/view/com/util/error/ErrorScreen' 24 - import {Text} from '#/view/com/util/text/Text' 25 - import {ViewHeader} from '#/view/com/util/ViewHeader' 15 + import {List} from '#/view/com/util/List' 26 16 import {atoms as a, useTheme} from '#/alf' 27 17 import * as Layout from '#/components/Layout' 18 + import {ListFooter} from '#/components/Lists' 28 19 import * as ProfileCard from '#/components/ProfileCard' 20 + import {Text} from '#/components/Typography' 29 21 30 22 type Props = NativeStackScreenProps< 31 23 CommonNavigatorParams, ··· 33 25 > 34 26 export function ModerationBlockedAccounts({}: Props) { 35 27 const t = useTheme() 36 - const pal = usePalette('default') 37 - const {_} = useLingui() 38 28 const setMinimalShellMode = useSetMinimalShellMode() 39 - const {isTabletOrDesktop} = useWebMediaQueries() 40 29 const moderationOpts = useModerationOpts() 41 30 42 - const [isPTRing, setIsPTRing] = React.useState(false) 31 + const [isPTRing, setIsPTRing] = useState(false) 43 32 const { 44 33 data, 45 34 isFetching, ··· 51 40 isFetchingNextPage, 52 41 } = useMyBlockedAccountsQuery() 53 42 const isEmpty = !isFetching && !data?.pages[0]?.blocks.length 54 - const profiles = React.useMemo(() => { 43 + const profiles = useMemo(() => { 55 44 if (data?.pages) { 56 45 return data.pages.flatMap(page => page.blocks) 57 46 } ··· 59 48 }, [data]) 60 49 61 50 useFocusEffect( 62 - React.useCallback(() => { 51 + useCallback(() => { 63 52 setMinimalShellMode(false) 64 53 }, [setMinimalShellMode]), 65 54 ) 66 55 67 - const onRefresh = React.useCallback(async () => { 56 + const onRefresh = useCallback(async () => { 68 57 setIsPTRing(true) 69 58 try { 70 59 await refetch() ··· 74 63 setIsPTRing(false) 75 64 }, [refetch, setIsPTRing]) 76 65 77 - const onEndReached = React.useCallback(async () => { 66 + const onEndReached = useCallback(async () => { 78 67 if (isFetching || !hasNextPage || isError) return 79 68 80 69 try { ··· 104 93 </View> 105 94 ) 106 95 } 96 + 107 97 return ( 108 98 <Layout.Screen testID="blockedAccountsScreen"> 109 - <Layout.Center style={[a.flex_1, {paddingBottom: 100}]}> 110 - <ViewHeader title={_(msg`Blocked Accounts`)} showOnDesktop /> 111 - <Text 112 - type="sm" 113 - style={[ 114 - styles.description, 115 - pal.text, 116 - isTabletOrDesktop && styles.descriptionDesktop, 117 - { 118 - marginTop: 20, 119 - }, 120 - ]}> 121 - <Trans> 122 - Blocked accounts cannot reply in your threads, mention you, or 123 - otherwise interact with you. You will not see their content and they 124 - will be prevented from seeing yours. 125 - </Trans> 126 - </Text> 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> 127 109 {isEmpty ? ( 128 - <View style={[pal.border]}> 110 + <View> 111 + <Info style={[a.border_b]} /> 129 112 {isError ? ( 130 113 <ErrorScreen 131 114 title="Oops!" ··· 133 116 onPressTryAgain={refetch} 134 117 /> 135 118 ) : ( 136 - <View style={[styles.empty, pal.viewLight]}> 137 - <Text type="lg" style={[pal.text, styles.emptyText]}> 138 - <Trans> 139 - You have not blocked any accounts yet. To block an account, 140 - go to their profile and select "Block account" from the menu 141 - on their account. 142 - </Trans> 143 - </Text> 144 - </View> 119 + <Empty /> 145 120 )} 146 121 </View> 147 122 ) : ( 148 - <FlatList 149 - style={[!isTabletOrDesktop && styles.flex1]} 123 + <List 150 124 data={profiles} 151 125 keyExtractor={(item: ActorDefs.ProfileView) => item.did} 152 - refreshControl={ 153 - <RefreshControl 154 - refreshing={isPTRing} 155 - onRefresh={onRefresh} 156 - tintColor={pal.colors.text} 157 - titleColor={pal.colors.text} 158 - /> 159 - } 126 + refreshing={isPTRing} 127 + onRefresh={onRefresh} 160 128 onEndReached={onEndReached} 161 129 renderItem={renderItem} 162 130 initialNumToRender={15} 163 131 // FIXME(dan) 164 132 165 - ListFooterComponent={() => ( 166 - <View style={styles.footer}> 167 - {(isFetching || isFetchingNextPage) && <ActivityIndicator />} 168 - </View> 169 - )} 170 - // @ts-ignore our .web version only -prf 171 - desktopFixedHeight 133 + ListHeaderComponent={Info} 134 + ListFooterComponent={ 135 + <ListFooter 136 + isFetchingNextPage={isFetchingNextPage} 137 + hasNextPage={hasNextPage} 138 + error={cleanError(error)} 139 + onRetry={fetchNextPage} 140 + /> 141 + } 172 142 /> 173 143 )} 174 144 </Layout.Center> ··· 176 146 ) 177 147 } 178 148 179 - const styles = StyleSheet.create({ 180 - title: { 181 - textAlign: 'center', 182 - marginTop: 12, 183 - marginBottom: 12, 184 - }, 185 - description: { 186 - textAlign: 'center', 187 - paddingHorizontal: 30, 188 - marginBottom: 14, 189 - }, 190 - descriptionDesktop: { 191 - marginTop: 14, 192 - }, 149 + function 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 + } 193 174 194 - flex1: { 195 - flex: 1, 196 - }, 197 - empty: { 198 - paddingHorizontal: 20, 199 - paddingVertical: 20, 200 - borderRadius: 16, 201 - marginHorizontal: 24, 202 - marginTop: 10, 203 - }, 204 - emptyText: { 205 - textAlign: 'center', 206 - }, 207 - 208 - footer: { 209 - height: 200, 210 - paddingTop: 20, 211 - }, 212 - }) 175 + function 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 + }
+84 -98
src/view/screens/ModerationMutedAccounts.tsx
··· 1 - import React from 'react' 2 - import { 3 - ActivityIndicator, 4 - FlatList, 5 - RefreshControl, 6 - StyleSheet, 7 - View, 8 - } from 'react-native' 1 + import {useCallback, useMemo, useState} from 'react' 2 + import {type StyleProp, View, type ViewStyle} from 'react-native' 9 3 import {type AppBskyActorDefs as ActorDefs} from '@atproto/api' 10 - import {msg, Trans} from '@lingui/macro' 4 + import {Trans} from '@lingui/macro' 11 5 import {useLingui} from '@lingui/react' 12 6 import {useFocusEffect} from '@react-navigation/native' 13 7 import {type NativeStackScreenProps} from '@react-navigation/native-stack' 14 8 15 - import {usePalette} from '#/lib/hooks/usePalette' 16 - import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries' 17 9 import {type CommonNavigatorParams} from '#/lib/routes/types' 18 10 import {cleanError} from '#/lib/strings/errors' 19 11 import {logger} from '#/logger' ··· 21 13 import {useMyMutedAccountsQuery} from '#/state/queries/my-muted-accounts' 22 14 import {useSetMinimalShellMode} from '#/state/shell' 23 15 import {ErrorScreen} from '#/view/com/util/error/ErrorScreen' 24 - import {Text} from '#/view/com/util/text/Text' 25 - import {ViewHeader} from '#/view/com/util/ViewHeader' 16 + import {List} from '#/view/com/util/List' 26 17 import {atoms as a, useTheme} from '#/alf' 27 18 import * as Layout from '#/components/Layout' 19 + import {ListFooter} from '#/components/Lists' 28 20 import * as ProfileCard from '#/components/ProfileCard' 21 + import {Text} from '#/components/Typography' 29 22 30 23 type Props = NativeStackScreenProps< 31 24 CommonNavigatorParams, ··· 33 26 > 34 27 export function ModerationMutedAccounts({}: Props) { 35 28 const t = useTheme() 36 - const pal = usePalette('default') 29 + const moderationOpts = useModerationOpts() 37 30 const {_} = useLingui() 38 31 const setMinimalShellMode = useSetMinimalShellMode() 39 - const {isTabletOrDesktop} = useWebMediaQueries() 40 - const moderationOpts = useModerationOpts() 41 32 42 - const [isPTRing, setIsPTRing] = React.useState(false) 33 + const [isPTRing, setIsPTRing] = useState(false) 43 34 const { 44 35 data, 45 36 isFetching, ··· 51 42 isFetchingNextPage, 52 43 } = useMyMutedAccountsQuery() 53 44 const isEmpty = !isFetching && !data?.pages[0]?.mutes.length 54 - const profiles = React.useMemo(() => { 45 + const profiles = useMemo(() => { 55 46 if (data?.pages) { 56 47 return data.pages.flatMap(page => page.mutes) 57 48 } ··· 59 50 }, [data]) 60 51 61 52 useFocusEffect( 62 - React.useCallback(() => { 53 + useCallback(() => { 63 54 setMinimalShellMode(false) 64 55 }, [setMinimalShellMode]), 65 56 ) 66 57 67 - const onRefresh = React.useCallback(async () => { 58 + const onRefresh = useCallback(async () => { 68 59 setIsPTRing(true) 69 60 try { 70 61 await refetch() ··· 74 65 setIsPTRing(false) 75 66 }, [refetch, setIsPTRing]) 76 67 77 - const onEndReached = React.useCallback(async () => { 68 + const onEndReached = useCallback(async () => { 78 69 if (isFetching || !hasNextPage || isError) return 79 70 80 71 try { ··· 120 111 } 121 112 return ( 122 113 <Layout.Screen testID="mutedAccountsScreen"> 123 - <ViewHeader title={_(msg`Muted Accounts`)} showOnDesktop /> 124 - <Layout.Center style={[a.flex_1, {paddingBottom: 100}]}> 125 - <Text 126 - type="sm" 127 - style={[ 128 - styles.description, 129 - pal.text, 130 - isTabletOrDesktop && styles.descriptionDesktop, 131 - { 132 - marginTop: 20, 133 - }, 134 - ]}> 135 - <Trans> 136 - Muted accounts have their posts removed from your feed and from your 137 - notifications. Mutes are completely private. 138 - </Trans> 139 - </Text> 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> 140 124 {isEmpty ? ( 141 - <View style={[pal.border]}> 125 + <View> 126 + <Info style={[a.border_b]} /> 142 127 {isError ? ( 143 128 <ErrorScreen 144 129 title="Oops!" ··· 146 131 onPressTryAgain={refetch} 147 132 /> 148 133 ) : ( 149 - <View style={[styles.empty, pal.viewLight]}> 150 - <Text type="lg" style={[pal.text, styles.emptyText]}> 151 - <Trans> 152 - You have not muted any accounts yet. To mute an account, go 153 - to their profile and select "Mute account" from the menu on 154 - their account. 155 - </Trans> 156 - </Text> 157 - </View> 134 + <Empty /> 158 135 )} 159 136 </View> 160 137 ) : ( 161 - <FlatList 162 - style={[!isTabletOrDesktop && styles.flex1]} 138 + <List 163 139 data={profiles} 164 140 keyExtractor={item => item.did} 165 - refreshControl={ 166 - <RefreshControl 167 - refreshing={isPTRing} 168 - onRefresh={onRefresh} 169 - tintColor={pal.colors.text} 170 - titleColor={pal.colors.text} 171 - /> 172 - } 141 + refreshing={isPTRing} 142 + onRefresh={onRefresh} 173 143 onEndReached={onEndReached} 174 144 renderItem={renderItem} 175 145 initialNumToRender={15} 176 146 // FIXME(dan) 177 147 178 - ListFooterComponent={() => ( 179 - <View style={styles.footer}> 180 - {(isFetching || isFetchingNextPage) && <ActivityIndicator />} 181 - </View> 182 - )} 183 - // @ts-ignore our .web version only -prf 184 - desktopFixedHeight 148 + ListHeaderComponent={Info} 149 + ListFooterComponent={ 150 + <ListFooter 151 + isFetchingNextPage={isFetchingNextPage} 152 + hasNextPage={hasNextPage} 153 + error={cleanError(error)} 154 + onRetry={fetchNextPage} 155 + /> 156 + } 185 157 /> 186 158 )} 187 159 </Layout.Center> ··· 189 161 ) 190 162 } 191 163 192 - const styles = StyleSheet.create({ 193 - title: { 194 - textAlign: 'center', 195 - marginTop: 12, 196 - marginBottom: 12, 197 - }, 198 - description: { 199 - textAlign: 'center', 200 - paddingHorizontal: 30, 201 - marginBottom: 14, 202 - }, 203 - descriptionDesktop: { 204 - marginTop: 14, 205 - }, 164 + function 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 + } 206 188 207 - flex1: { 208 - flex: 1, 209 - }, 210 - empty: { 211 - paddingHorizontal: 20, 212 - paddingVertical: 20, 213 - borderRadius: 16, 214 - marginHorizontal: 24, 215 - marginTop: 10, 216 - }, 217 - emptyText: { 218 - textAlign: 'center', 219 - }, 220 - 221 - footer: { 222 - height: 200, 223 - paddingTop: 20, 224 - }, 225 - }) 189 + function 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 + }