diff options
author | Paul Frazee <pfrazee@gmail.com> | 2023-05-17 22:12:14 -0500 |
---|---|---|
committer | Paul Frazee <pfrazee@gmail.com> | 2023-05-17 22:12:14 -0500 |
commit | a2c89b47a135d83057b34d0b0b7a1fa7551e0ad2 (patch) | |
tree | 672266ccf713b38ec050aa42d50ff497c5245a04 /src/view/screens/CustomFeed.tsx | |
parent | 998879d6d60b59e65250af395fd6ce389c89189b (diff) | |
download | voidsky-a2c89b47a135d83057b34d0b0b7a1fa7551e0ad2.tar.zst |
Add custom feed liked by screen
Diffstat (limited to 'src/view/screens/CustomFeed.tsx')
-rw-r--r-- | src/view/screens/CustomFeed.tsx | 235 |
1 files changed, 235 insertions, 0 deletions
diff --git a/src/view/screens/CustomFeed.tsx b/src/view/screens/CustomFeed.tsx new file mode 100644 index 000000000..9f7f81691 --- /dev/null +++ b/src/view/screens/CustomFeed.tsx @@ -0,0 +1,235 @@ +import React, {useMemo, useRef} from 'react' +import {NativeStackScreenProps} from '@react-navigation/native-stack' +import {usePalette} from 'lib/hooks/usePalette' +import {HeartIcon, HeartIconSolid} from 'lib/icons' +import {CommonNavigatorParams} from 'lib/routes/types' +import {makeRecordUri} from 'lib/strings/url-helpers' +import {colors, s} from 'lib/styles' +import {observer} from 'mobx-react-lite' +import {FlatList, StyleSheet, View} from 'react-native' +import {useStores} from 'state/index' +import {PostsFeedModel} from 'state/models/feeds/posts' +import {useCustomFeed} from 'lib/hooks/useCustomFeed' +import {withAuthRequired} from 'view/com/auth/withAuthRequired' +import {Feed} from 'view/com/posts/Feed' +import {pluralize} from 'lib/strings/helpers' +import {TextLink} from 'view/com/util/Link' +import {UserAvatar} from 'view/com/util/UserAvatar' +import {ViewHeader} from 'view/com/util/ViewHeader' +import {Button} from 'view/com/util/forms/Button' +import {Text} from 'view/com/util/text/Text' +import * as Toast from 'view/com/util/Toast' +import {isDesktopWeb} from 'platform/detection' + +type Props = NativeStackScreenProps<CommonNavigatorParams, 'CustomFeed'> +export const CustomFeedScreen = withAuthRequired( + observer(({route}: Props) => { + const store = useStores() + const pal = usePalette('default') + const {rkey, name} = route.params + const uri = useMemo( + () => makeRecordUri(name, 'app.bsky.feed.generator', rkey), + [rkey, name], + ) + const scrollElRef = useRef<FlatList>(null) + const currentFeed = useCustomFeed(uri) + const algoFeed: PostsFeedModel = useMemo(() => { + const feed = new PostsFeedModel(store, 'custom', { + feed: uri, + }) + feed.setup() + return feed + }, [store, uri]) + + const onToggleSaved = React.useCallback(async () => { + try { + if (currentFeed?.isSaved) { + await currentFeed?.unsave() + } else { + await currentFeed?.save() + } + } catch (err) { + Toast.show( + 'There was an an issue updating your feeds, please check your internet connection and try again.', + ) + store.log.error('Failed up update feeds', {err}) + } + }, [store, currentFeed]) + + const onToggleLiked = React.useCallback(async () => { + try { + if (currentFeed?.isLiked) { + await currentFeed?.unlike() + } else { + await currentFeed?.like() + } + } catch (err) { + Toast.show( + 'There was an an issue contacting the server, please check your internet connection and try again.', + ) + store.log.error('Failed up toggle like', {err}) + } + }, [store, currentFeed]) + + const renderHeaderBtns = React.useCallback(() => { + return ( + <View style={styles.headerBtns}> + <Button + type="default" + testID="toggleLikeBtn" + accessibilityLabel="Like this feed" + accessibilityHint="" + onPress={onToggleLiked}> + {currentFeed?.isLiked ? ( + <HeartIconSolid size={18} style={styles.liked} /> + ) : ( + <HeartIcon strokeWidth={3} size={18} style={pal.textLight} /> + )} + </Button> + <Button + type={currentFeed?.isSaved ? 'default' : 'inverted'} + onPress={onToggleSaved} + accessibilityLabel={ + currentFeed?.isSaved ? 'Remove from my feeds' : 'Add to my feeds' + } + accessibilityHint="" + label={ + currentFeed?.isSaved ? 'Remove from My Feeds' : 'Add to My Feeds' + } + /> + </View> + ) + }, [ + pal, + currentFeed?.isSaved, + currentFeed?.isLiked, + onToggleSaved, + onToggleLiked, + ]) + + const renderListHeaderComponent = React.useCallback(() => { + return ( + <> + <View style={[styles.header, pal.border]}> + <View style={s.flex1}> + <Text + testID="feedName" + type="title-xl" + style={[pal.text, s.bold]}> + {currentFeed?.displayName} + </Text> + {currentFeed && ( + <Text type="md" style={[pal.textLight]} numberOfLines={1}> + by{' '} + {currentFeed.data.creator.did === store.me.did ? ( + 'you' + ) : ( + <TextLink + text={`@${currentFeed.data.creator.handle}`} + href={`/profile/${currentFeed.data.creator.did}`} + /> + )} + </Text> + )} + </View> + <View> + <UserAvatar + type="algo" + avatar={currentFeed?.data.avatar} + size={64} + /> + </View> + </View> + <View style={styles.headerDetails}> + {currentFeed?.data.description ? ( + <Text style={[pal.text, s.mb10]} numberOfLines={6}> + {currentFeed.data.description} + </Text> + ) : null} + <TextLink + type="md-medium" + style={pal.textLight} + href={`/profile/${name}/feed/${rkey}/liked-by`} + text={`Liked by ${currentFeed?.data.likeCount} ${pluralize( + currentFeed?.data.likeCount || 0, + 'user', + )}`} + /> + {isDesktopWeb && ( + <View style={styles.headerBtns}> + <Button + type={currentFeed?.isSaved ? 'default' : 'inverted'} + onPress={onToggleSaved} + accessibilityLabel={ + currentFeed?.isSaved ? 'Unsave this feed' : 'Save this feed' + } + accessibilityHint="" + label={currentFeed?.isSaved ? 'Unsave' : 'Save'} + /> + + <Button type="default" onPress={onToggleLiked}> + {currentFeed?.isLiked ? ( + <HeartIconSolid size={18} style={styles.liked} /> + ) : ( + <HeartIcon strokeWidth={3} size={18} style={pal.icon} /> + )} + </Button> + </View> + )} + </View> + <View style={[styles.fakeSelector, pal.border]}> + <View + style={[styles.fakeSelectorItem, {borderColor: pal.colors.link}]}> + <Text type="md-medium" style={[pal.text]}> + Feed + </Text> + </View> + </View> + </> + ) + }, [store.me.did, pal, currentFeed, onToggleLiked, onToggleSaved]) + + return ( + <View style={s.hContentRegion}> + <ViewHeader title="" renderButton={renderHeaderBtns} /> + <Feed + scrollElRef={scrollElRef} + feed={algoFeed} + ListHeaderComponent={renderListHeaderComponent} + extraData={uri} + /> + </View> + ) + }), +) + +const styles = StyleSheet.create({ + headerBtns: { + flexDirection: 'row', + gap: 8, + }, + header: { + flexDirection: 'row', + gap: 12, + paddingHorizontal: 16, + paddingTop: 12, + paddingBottom: 16, + borderTopWidth: 1, + }, + headerDetails: { + paddingHorizontal: 16, + paddingBottom: 16, + }, + fakeSelector: { + flexDirection: 'row', + paddingHorizontal: isDesktopWeb ? 16 : 6, + }, + fakeSelectorItem: { + paddingHorizontal: 12, + paddingBottom: 8, + borderBottomWidth: 3, + }, + liked: { + color: colors.red3, + }, +}) |