my fork of the bluesky client
at main 233 lines 5.3 kB view raw
1import React, {memo} from 'react' 2import {StyleProp, View, ViewStyle} from 'react-native' 3import {msg, Trans} from '@lingui/macro' 4import {useLingui} from '@lingui/react' 5 6import {cleanError} from '#/lib/strings/errors' 7import {CenteredView} from '#/view/com/util/Views' 8import {atoms as a, flatten, useBreakpoints, useTheme} from '#/alf' 9import {Button, ButtonText} from '#/components/Button' 10import {Error} from '#/components/Error' 11import {Loader} from '#/components/Loader' 12import {Text} from '#/components/Typography' 13 14export function ListFooter({ 15 isFetchingNextPage, 16 hasNextPage, 17 error, 18 onRetry, 19 height, 20 style, 21 showEndMessage = false, 22 endMessageText, 23}: { 24 isFetchingNextPage?: boolean 25 hasNextPage?: boolean 26 error?: string 27 onRetry?: () => Promise<unknown> 28 height?: number 29 style?: StyleProp<ViewStyle> 30 showEndMessage?: boolean 31 endMessageText?: string 32}) { 33 const t = useTheme() 34 35 return ( 36 <View 37 style={[ 38 a.w_full, 39 a.align_center, 40 a.border_t, 41 a.pb_lg, 42 t.atoms.border_contrast_low, 43 {height: height ?? 180, paddingTop: 30}, 44 flatten(style), 45 ]}> 46 {isFetchingNextPage ? ( 47 <Loader size="xl" /> 48 ) : error ? ( 49 <ListFooterMaybeError error={error} onRetry={onRetry} /> 50 ) : !hasNextPage && showEndMessage ? ( 51 <Text style={[a.text_sm, t.atoms.text_contrast_low]}> 52 {endMessageText ?? <Trans>You have reached the end</Trans>} 53 </Text> 54 ) : null} 55 </View> 56 ) 57} 58 59function ListFooterMaybeError({ 60 error, 61 onRetry, 62}: { 63 error?: string 64 onRetry?: () => Promise<unknown> 65}) { 66 const t = useTheme() 67 const {_} = useLingui() 68 69 if (!error) return null 70 71 return ( 72 <View style={[a.w_full, a.px_lg]}> 73 <View 74 style={[ 75 a.flex_row, 76 a.gap_md, 77 a.p_md, 78 a.rounded_sm, 79 a.align_center, 80 t.atoms.bg_contrast_25, 81 ]}> 82 <Text 83 style={[a.flex_1, a.text_sm, t.atoms.text_contrast_medium]} 84 numberOfLines={2}> 85 {error ? ( 86 cleanError(error) 87 ) : ( 88 <Trans>Oops, something went wrong!</Trans> 89 )} 90 </Text> 91 <Button 92 variant="gradient" 93 label={_(msg`Press to retry`)} 94 style={[ 95 a.align_center, 96 a.justify_center, 97 a.rounded_sm, 98 a.overflow_hidden, 99 a.px_md, 100 a.py_sm, 101 ]} 102 onPress={onRetry}> 103 <ButtonText> 104 <Trans>Retry</Trans> 105 </ButtonText> 106 </Button> 107 </View> 108 </View> 109 ) 110} 111 112export function ListHeaderDesktop({ 113 title, 114 subtitle, 115}: { 116 title: string 117 subtitle?: string 118}) { 119 const {gtTablet} = useBreakpoints() 120 const t = useTheme() 121 122 if (!gtTablet) return null 123 124 return ( 125 <View 126 style={[ 127 a.w_full, 128 a.py_sm, 129 a.px_xl, 130 a.gap_xs, 131 a.justify_center, 132 {minHeight: 50}, 133 ]}> 134 <Text style={[a.text_2xl, a.font_bold]}>{title}</Text> 135 {subtitle ? ( 136 <Text style={[a.text_md, t.atoms.text_contrast_medium]}> 137 {subtitle} 138 </Text> 139 ) : undefined} 140 </View> 141 ) 142} 143 144let ListMaybePlaceholder = ({ 145 isLoading, 146 noEmpty, 147 isError, 148 emptyTitle, 149 emptyMessage, 150 errorTitle, 151 errorMessage, 152 emptyType = 'page', 153 onRetry, 154 onGoBack, 155 hideBackButton, 156 sideBorders, 157 topBorder = true, 158}: { 159 isLoading: boolean 160 noEmpty?: boolean 161 isError?: boolean 162 emptyTitle?: string 163 emptyMessage?: string 164 errorTitle?: string 165 errorMessage?: string 166 emptyType?: 'page' | 'results' 167 onRetry?: () => Promise<unknown> 168 onGoBack?: () => void 169 hideBackButton?: boolean 170 sideBorders?: boolean 171 topBorder?: boolean 172}): React.ReactNode => { 173 const t = useTheme() 174 const {_} = useLingui() 175 const {gtMobile, gtTablet} = useBreakpoints() 176 177 if (isLoading) { 178 return ( 179 <CenteredView 180 style={[ 181 a.h_full_vh, 182 a.align_center, 183 !gtMobile ? a.justify_between : a.gap_5xl, 184 t.atoms.border_contrast_low, 185 {paddingTop: 175, paddingBottom: 110}, 186 ]} 187 sideBorders={sideBorders ?? gtMobile} 188 topBorder={topBorder && !gtTablet}> 189 <View style={[a.w_full, a.align_center, {top: 100}]}> 190 <Loader size="xl" /> 191 </View> 192 </CenteredView> 193 ) 194 } 195 196 if (isError) { 197 return ( 198 <Error 199 title={errorTitle ?? _(msg`Oops!`)} 200 message={errorMessage ?? _(msg`Something went wrong!`)} 201 onRetry={onRetry} 202 onGoBack={onGoBack} 203 sideBorders={sideBorders} 204 hideBackButton={hideBackButton} 205 /> 206 ) 207 } 208 209 if (!noEmpty) { 210 return ( 211 <Error 212 title={ 213 emptyTitle ?? 214 (emptyType === 'results' 215 ? _(msg`No results found`) 216 : _(msg`Page not found`)) 217 } 218 message={ 219 emptyMessage ?? 220 _(msg`We're sorry! We can't find the page you were looking for.`) 221 } 222 onRetry={onRetry} 223 onGoBack={onGoBack} 224 hideBackButton={hideBackButton} 225 sideBorders={sideBorders} 226 /> 227 ) 228 } 229 230 return null 231} 232ListMaybePlaceholder = memo(ListMaybePlaceholder) 233export {ListMaybePlaceholder}