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 localize-dates 322 lines 9.2 kB view raw
1import React from 'react' 2import { 3 findNodeHandle, 4 ListRenderItemInfo, 5 StyleProp, 6 View, 7 ViewStyle, 8} from 'react-native' 9import {AppBskyGraphDefs, AppBskyGraphGetActorStarterPacks} from '@atproto/api' 10import {msg, Trans} from '@lingui/macro' 11import {useLingui} from '@lingui/react' 12import {useNavigation} from '@react-navigation/native' 13import {InfiniteData, UseInfiniteQueryResult} from '@tanstack/react-query' 14 15import {logger} from '#/logger' 16import {useGenerateStarterPackMutation} from 'lib/generate-starterpack' 17import {useBottomBarOffset} from 'lib/hooks/useBottomBarOffset' 18import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries' 19import {NavigationProp} from 'lib/routes/types' 20import {parseStarterPackUri} from 'lib/strings/starter-pack' 21import {List, ListRef} from 'view/com/util/List' 22import {Text} from 'view/com/util/text/Text' 23import {atoms as a, useTheme} from '#/alf' 24import {Button, ButtonIcon, ButtonText} from '#/components/Button' 25import {useDialogControl} from '#/components/Dialog' 26import {LinearGradientBackground} from '#/components/LinearGradientBackground' 27import {Loader} from '#/components/Loader' 28import * as Prompt from '#/components/Prompt' 29import {Default as StarterPackCard} from '#/components/StarterPack/StarterPackCard' 30import {PlusSmall_Stroke2_Corner0_Rounded as Plus} from '../icons/Plus' 31 32interface SectionRef { 33 scrollToTop: () => void 34} 35 36interface ProfileFeedgensProps { 37 starterPacksQuery: UseInfiniteQueryResult< 38 InfiniteData<AppBskyGraphGetActorStarterPacks.OutputSchema, unknown>, 39 Error 40 > 41 scrollElRef: ListRef 42 headerOffset: number 43 enabled?: boolean 44 style?: StyleProp<ViewStyle> 45 testID?: string 46 setScrollViewTag: (tag: number | null) => void 47 isMe: boolean 48} 49 50function keyExtractor(item: AppBskyGraphDefs.StarterPackView) { 51 return item.uri 52} 53 54export const ProfileStarterPacks = React.forwardRef< 55 SectionRef, 56 ProfileFeedgensProps 57>(function ProfileFeedgensImpl( 58 { 59 starterPacksQuery: query, 60 scrollElRef, 61 headerOffset, 62 enabled, 63 style, 64 testID, 65 setScrollViewTag, 66 isMe, 67 }, 68 ref, 69) { 70 const t = useTheme() 71 const bottomBarOffset = useBottomBarOffset(100) 72 const [isPTRing, setIsPTRing] = React.useState(false) 73 const {data, refetch, isFetching, hasNextPage, fetchNextPage} = query 74 const {isTabletOrDesktop} = useWebMediaQueries() 75 76 const items = data?.pages.flatMap(page => page.starterPacks) 77 78 React.useImperativeHandle(ref, () => ({ 79 scrollToTop: () => {}, 80 })) 81 82 const onRefresh = React.useCallback(async () => { 83 setIsPTRing(true) 84 try { 85 await refetch() 86 } catch (err) { 87 logger.error('Failed to refresh starter packs', {message: err}) 88 } 89 setIsPTRing(false) 90 }, [refetch, setIsPTRing]) 91 92 const onEndReached = React.useCallback(async () => { 93 if (isFetching || !hasNextPage) return 94 95 try { 96 await fetchNextPage() 97 } catch (err) { 98 logger.error('Failed to load more starter packs', {message: err}) 99 } 100 }, [isFetching, hasNextPage, fetchNextPage]) 101 102 React.useEffect(() => { 103 if (enabled && scrollElRef.current) { 104 const nativeTag = findNodeHandle(scrollElRef.current) 105 setScrollViewTag(nativeTag) 106 } 107 }, [enabled, scrollElRef, setScrollViewTag]) 108 109 const renderItem = ({ 110 item, 111 index, 112 }: ListRenderItemInfo<AppBskyGraphDefs.StarterPackView>) => { 113 return ( 114 <View 115 style={[ 116 a.p_lg, 117 (isTabletOrDesktop || index !== 0) && a.border_t, 118 t.atoms.border_contrast_low, 119 ]}> 120 <StarterPackCard starterPack={item} /> 121 </View> 122 ) 123 } 124 125 return ( 126 <View testID={testID} style={style}> 127 <List 128 testID={testID ? `${testID}-flatlist` : undefined} 129 ref={scrollElRef} 130 data={items} 131 renderItem={renderItem} 132 keyExtractor={keyExtractor} 133 refreshing={isPTRing} 134 headerOffset={headerOffset} 135 contentContainerStyle={{paddingBottom: headerOffset + bottomBarOffset}} 136 indicatorStyle={t.name === 'light' ? 'black' : 'white'} 137 removeClippedSubviews={true} 138 desktopFixedHeight 139 onEndReached={onEndReached} 140 onRefresh={onRefresh} 141 ListEmptyComponent={Empty} 142 ListFooterComponent={ 143 items?.length !== 0 && isMe ? CreateAnother : undefined 144 } 145 /> 146 </View> 147 ) 148}) 149 150function CreateAnother() { 151 const {_} = useLingui() 152 const t = useTheme() 153 const navigation = useNavigation<NavigationProp>() 154 155 return ( 156 <View 157 style={[ 158 a.pr_md, 159 a.pt_lg, 160 a.gap_lg, 161 a.border_t, 162 t.atoms.border_contrast_low, 163 ]}> 164 <Button 165 label={_(msg`Create a starter pack`)} 166 variant="solid" 167 color="secondary" 168 size="small" 169 style={[a.self_center]} 170 onPress={() => navigation.navigate('StarterPackWizard')}> 171 <ButtonText> 172 <Trans>Create another</Trans> 173 </ButtonText> 174 <ButtonIcon icon={Plus} position="right" /> 175 </Button> 176 </View> 177 ) 178} 179 180function Empty() { 181 const {_} = useLingui() 182 const t = useTheme() 183 const navigation = useNavigation<NavigationProp>() 184 const confirmDialogControl = useDialogControl() 185 const followersDialogControl = useDialogControl() 186 const errorDialogControl = useDialogControl() 187 188 const [isGenerating, setIsGenerating] = React.useState(false) 189 190 const {mutate: generateStarterPack} = useGenerateStarterPackMutation({ 191 onSuccess: ({uri}) => { 192 const parsed = parseStarterPackUri(uri) 193 if (parsed) { 194 navigation.push('StarterPack', { 195 name: parsed.name, 196 rkey: parsed.rkey, 197 }) 198 } 199 setIsGenerating(false) 200 }, 201 onError: e => { 202 logger.error('Failed to generate starter pack', {safeMessage: e}) 203 setIsGenerating(false) 204 if (e.name === 'NOT_ENOUGH_FOLLOWERS') { 205 followersDialogControl.open() 206 } else { 207 errorDialogControl.open() 208 } 209 }, 210 }) 211 212 const generate = () => { 213 setIsGenerating(true) 214 generateStarterPack() 215 } 216 217 return ( 218 <LinearGradientBackground 219 style={[ 220 a.px_lg, 221 a.py_lg, 222 a.justify_between, 223 a.gap_lg, 224 a.shadow_lg, 225 {marginTop: 2}, 226 ]}> 227 <View style={[a.gap_xs]}> 228 <Text 229 style={[ 230 a.font_bold, 231 a.text_lg, 232 t.atoms.text_contrast_medium, 233 {color: 'white'}, 234 ]}> 235 <Trans>You haven't created a starter pack yet!</Trans> 236 </Text> 237 <Text style={[a.text_md, {color: 'white'}]}> 238 <Trans> 239 Starter packs let you easily share your favorite feeds and people 240 with your friends. 241 </Trans> 242 </Text> 243 </View> 244 <View style={[a.flex_row, a.gap_md, {marginLeft: 'auto'}]}> 245 <Button 246 label={_(msg`Create a starter pack for me`)} 247 variant="ghost" 248 color="primary" 249 size="small" 250 disabled={isGenerating} 251 onPress={confirmDialogControl.open} 252 style={{backgroundColor: 'transparent'}}> 253 <ButtonText style={{color: 'white'}}> 254 <Trans>Make one for me</Trans> 255 </ButtonText> 256 {isGenerating && <Loader size="md" />} 257 </Button> 258 <Button 259 label={_(msg`Create a starter pack`)} 260 variant="ghost" 261 color="primary" 262 size="small" 263 disabled={isGenerating} 264 onPress={() => navigation.navigate('StarterPackWizard')} 265 style={{ 266 backgroundColor: 'white', 267 borderColor: 'white', 268 width: 100, 269 }} 270 hoverStyle={[{backgroundColor: '#dfdfdf'}]}> 271 <ButtonText> 272 <Trans>Create</Trans> 273 </ButtonText> 274 </Button> 275 </View> 276 277 <Prompt.Outer control={confirmDialogControl}> 278 <Prompt.TitleText> 279 <Trans>Generate a starter pack</Trans> 280 </Prompt.TitleText> 281 <Prompt.DescriptionText> 282 <Trans> 283 Bluesky will choose a set of recommended accounts from people in 284 your network. 285 </Trans> 286 </Prompt.DescriptionText> 287 <Prompt.Actions> 288 <Prompt.Action 289 color="primary" 290 cta={_(msg`Choose for me`)} 291 onPress={generate} 292 /> 293 <Prompt.Action 294 color="secondary" 295 cta={_(msg`Let me choose`)} 296 onPress={() => { 297 navigation.navigate('StarterPackWizard') 298 }} 299 /> 300 </Prompt.Actions> 301 </Prompt.Outer> 302 <Prompt.Basic 303 control={followersDialogControl} 304 title={_(msg`Oops!`)} 305 description={_( 306 msg`You must be following at least seven other people to generate a starter pack.`, 307 )} 308 onConfirm={() => {}} 309 showCancel={false} 310 /> 311 <Prompt.Basic 312 control={errorDialogControl} 313 title={_(msg`Oops!`)} 314 description={_( 315 msg`An error occurred while generating your starter pack. Want to try again?`, 316 )} 317 onConfirm={generate} 318 confirmButtonCta={_(msg`Retry`)} 319 /> 320 </LinearGradientBackground> 321 ) 322}