Bluesky app fork with some witchin' additions 💫

resolve did before loading feed (#1092)

* resolve did before loading feed

* add loader

* wrap with authRequired, handle errors

authored by

Eric Bailey and committed by
GitHub
1211c353 a63f97ae

+98 -10
+98 -10
src/view/screens/CustomFeed.tsx
··· 1 1 import React, {useMemo, useRef} from 'react' 2 2 import {NativeStackScreenProps} from '@react-navigation/native-stack' 3 3 import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' 4 + import {useNavigation} from '@react-navigation/native' 4 5 import {usePalette} from 'lib/hooks/usePalette' 5 6 import {HeartIcon, HeartIconSolid} from 'lib/icons' 6 7 import {CommonNavigatorParams} from 'lib/routes/types' 7 8 import {makeRecordUri} from 'lib/strings/url-helpers' 8 9 import {colors, s} from 'lib/styles' 9 10 import {observer} from 'mobx-react-lite' 10 - import {FlatList, StyleSheet, View} from 'react-native' 11 + import {FlatList, StyleSheet, View, ActivityIndicator} from 'react-native' 11 12 import {useStores} from 'state/index' 12 13 import {PostsFeedModel} from 'state/models/feeds/posts' 13 14 import {useCustomFeed} from 'lib/hooks/useCustomFeed' ··· 34 35 import {useAnalytics} from 'lib/analytics/analytics' 35 36 import {NativeDropdown, DropdownItem} from 'view/com/util/forms/NativeDropdown' 36 37 import {makeProfileLink} from 'lib/routes/links' 38 + import {resolveName} from 'lib/api' 39 + import {CenteredView} from 'view/com/util/Views' 40 + import {NavigationProp} from 'lib/routes/types' 37 41 38 42 type Props = NativeStackScreenProps<CommonNavigatorParams, 'CustomFeed'> 43 + 39 44 export const CustomFeedScreen = withAuthRequired( 40 - observer(({route}: Props) => { 45 + observer((props: Props) => { 46 + const pal = usePalette('default') 47 + const store = useStores() 48 + const navigation = useNavigation<NavigationProp>() 49 + 50 + const {name: handleOrDid} = props.route.params 51 + 52 + const [feedOwnerDid, setFeedOwnerDid] = React.useState<string | undefined>() 53 + const [error, setError] = React.useState<string | undefined>() 54 + 55 + const onPressBack = React.useCallback(() => { 56 + if (navigation.canGoBack()) { 57 + navigation.goBack() 58 + } else { 59 + navigation.navigate('Home') 60 + } 61 + }, [navigation]) 62 + 63 + React.useEffect(() => { 64 + /* 65 + * We must resolve the DID of the feed owner before we can fetch the feed. 66 + */ 67 + async function fetchDid() { 68 + try { 69 + const did = await resolveName(store, handleOrDid) 70 + setFeedOwnerDid(did) 71 + } catch (e) { 72 + setError( 73 + `We're sorry, but we were unable to resolve this feed. If this persists, please contact the feed creator, @${handleOrDid}.`, 74 + ) 75 + } 76 + } 77 + 78 + fetchDid() 79 + }, [store, handleOrDid, setFeedOwnerDid]) 80 + 81 + if (error) { 82 + return ( 83 + <CenteredView> 84 + <View style={[pal.view, pal.border, styles.notFoundContainer]}> 85 + <Text type="title-lg" style={[pal.text, s.mb10]}> 86 + Could not load feed 87 + </Text> 88 + <Text type="md" style={[pal.text, s.mb20]}> 89 + {error} 90 + </Text> 91 + 92 + <View style={{flexDirection: 'row'}}> 93 + <Button 94 + type="default" 95 + accessibilityLabel="Go Back" 96 + accessibilityHint="Return to previous page" 97 + onPress={onPressBack} 98 + style={{flexShrink: 1}}> 99 + <Text type="button" style={pal.text}> 100 + Go Back 101 + </Text> 102 + </Button> 103 + </View> 104 + </View> 105 + </CenteredView> 106 + ) 107 + } 108 + 109 + return feedOwnerDid ? ( 110 + <CustomFeedScreenInner {...props} feedOwnerDid={feedOwnerDid} /> 111 + ) : ( 112 + <CenteredView> 113 + <View style={s.p20}> 114 + <ActivityIndicator size="large" /> 115 + </View> 116 + </CenteredView> 117 + ) 118 + }), 119 + ) 120 + 121 + export const CustomFeedScreenInner = observer( 122 + ({route, feedOwnerDid}: Props & {feedOwnerDid: string}) => { 41 123 const store = useStores() 42 124 const pal = usePalette('default') 43 125 const {track} = useAnalytics() 44 - const {rkey, name} = route.params 126 + const {rkey, name: handleOrDid} = route.params 45 127 const uri = useMemo( 46 - () => makeRecordUri(name, 'app.bsky.feed.generator', rkey), 47 - [rkey, name], 128 + () => makeRecordUri(feedOwnerDid, 'app.bsky.feed.generator', rkey), 129 + [rkey, feedOwnerDid], 48 130 ) 49 131 const scrollElRef = useRef<FlatList>(null) 50 132 const currentFeed = useCustomFeed(uri) ··· 101 183 }, [store, currentFeed]) 102 184 103 185 const onPressShare = React.useCallback(() => { 104 - const url = toShareUrl(`/profile/${name}/feed/${rkey}`) 186 + const url = toShareUrl(`/profile/${handleOrDid}/feed/${rkey}`) 105 187 shareUrl(url) 106 188 track('CustomFeed:Share') 107 - }, [name, rkey, track]) 189 + }, [handleOrDid, rkey, track]) 108 190 109 191 const onScrollToTop = React.useCallback(() => { 110 192 scrollElRef.current?.scrollToOffset({offset: 0, animated: true}) ··· 310 392 <TextLink 311 393 type="md-medium" 312 394 style={pal.textLight} 313 - href={`/profile/${name}/feed/${rkey}/liked-by`} 395 + href={`/profile/${handleOrDid}/feed/${rkey}/liked-by`} 314 396 text={`Liked by ${currentFeed.data.likeCount} ${pluralize( 315 397 currentFeed?.data.likeCount || 0, 316 398 'user', ··· 336 418 onToggleSaved, 337 419 onToggleLiked, 338 420 onPressShare, 339 - name, 421 + handleOrDid, 340 422 rkey, 341 423 isPinned, 342 424 onTogglePinned, ··· 375 457 /> 376 458 </View> 377 459 ) 378 - }), 460 + }, 379 461 ) 380 462 381 463 const styles = StyleSheet.create({ ··· 429 511 top2: { 430 512 position: 'relative', 431 513 top: 2, 514 + }, 515 + notFoundContainer: { 516 + margin: 10, 517 + paddingHorizontal: 18, 518 + paddingVertical: 14, 519 + borderRadius: 6, 432 520 }, 433 521 })