diff options
author | Ansh Nanda <anshnanda10@gmail.com> | 2023-05-15 17:59:36 -0700 |
---|---|---|
committer | Ansh Nanda <anshnanda10@gmail.com> | 2023-05-15 17:59:36 -0700 |
commit | c4a666c2210c2e9c14812ddc5a0f797dd538014c (patch) | |
tree | 84514dde919368b88d7adccde08712c85f6ff53f /src | |
parent | 6249bb16cad1dbcf1930282e564595817735932f (diff) | |
download | voidsky-c4a666c2210c2e9c14812ddc5a0f797dd538014c.tar.zst |
new design for custom feed preview
Diffstat (limited to 'src')
-rw-r--r-- | src/lib/icons.tsx | 4 | ||||
-rw-r--r-- | src/lib/routes/types.ts | 2 | ||||
-rw-r--r-- | src/state/models/feeds/algo/algo-item.ts | 77 | ||||
-rw-r--r-- | src/view/com/algos/AlgoItem.tsx | 18 | ||||
-rw-r--r-- | src/view/com/algos/useCustomFeed.ts | 27 | ||||
-rw-r--r-- | src/view/com/util/PostCtrls.tsx | 2 | ||||
-rw-r--r-- | src/view/screens/CustomFeed.tsx | 103 |
7 files changed, 199 insertions, 34 deletions
diff --git a/src/lib/icons.tsx b/src/lib/icons.tsx index 4cb491e46..960090ad7 100644 --- a/src/lib/icons.tsx +++ b/src/lib/icons.tsx @@ -443,7 +443,7 @@ export function HeartIcon({ size = 24, strokeWidth = 1.5, }: { - style?: StyleProp<ViewStyle> + style?: StyleProp<TextStyle> size?: string | number strokeWidth: number }) { @@ -464,7 +464,7 @@ export function HeartIconSolid({ style, size = 24, }: { - style?: StyleProp<ViewStyle> + style?: StyleProp<TextStyle> size?: string | number }) { return ( diff --git a/src/lib/routes/types.ts b/src/lib/routes/types.ts index 29fadd709..77ed58cc3 100644 --- a/src/lib/routes/types.ts +++ b/src/lib/routes/types.ts @@ -21,7 +21,7 @@ export type CommonNavigatorParams = { CopyrightPolicy: undefined AppPasswords: undefined SavedFeeds: undefined - CustomFeed: {name: string; rkey: string} + CustomFeed: {name?: string; rkey: string} MutedAccounts: undefined BlockedAccounts: undefined } diff --git a/src/state/models/feeds/algo/algo-item.ts b/src/state/models/feeds/algo/algo-item.ts index 3dee5e2bf..41fe6a976 100644 --- a/src/state/models/feeds/algo/algo-item.ts +++ b/src/state/models/feeds/algo/algo-item.ts @@ -33,6 +33,20 @@ export class AlgoItemModel { return this.data.uri } + get isSaved() { + return this.data.viewer?.saved + } + + get isLiked() { + return this.data.viewer?.liked + } + + set toggleLiked(value: boolean) { + if (this.data.viewer) { + this.data.viewer.liked = value + } + } + // public apis // = async save() { @@ -57,19 +71,52 @@ export class AlgoItemModel { } } - // async getFeedSkeleton() { - // const res = await this.rootStore.agent.app.bsky.feed.getFeedSkeleton({ - // feed: this.data.uri, - // }) - // const skeleton = res.data.feed - // console.log('skeleton', skeleton) - // return skeleton - // } - // async getFeed() { - // const feed = await this.rootStore.agent.app.bsky.feed.getFeed({ - // feed: this.data.uri, - // }) - // console.log('feed', feed) - // return feed - // } + async like() { + try { + this.toggleLiked = true + await this.rootStore.agent.app.bsky.feed.like.create( + { + repo: this.rootStore.me.did, + }, + { + subject: { + uri: this.data.uri, + cid: this.data.cid, + }, + createdAt: new Date().toString(), + }, + ) + } catch (e: any) { + this.rootStore.log.error('Failed to like feed', e) + } + } + + static async getView(store: RootStoreModel, uri: string) { + const res = await store.agent.app.bsky.feed.getFeedGenerator({ + feed: uri, + }) + const view = res.data.view + return view + } + + async checkIsValid() { + const res = await this.rootStore.agent.app.bsky.feed.getFeedGenerator({ + feed: this.data.uri, + }) + return res.data.isValid + } + + async checkIsOnline() { + const res = await this.rootStore.agent.app.bsky.feed.getFeedGenerator({ + feed: this.data.uri, + }) + return res.data.isOnline + } + + async reload() { + const res = await this.rootStore.agent.app.bsky.feed.getFeedGenerator({ + feed: this.data.uri, + }) + this.data = res.data.view + } } diff --git a/src/view/com/algos/AlgoItem.tsx b/src/view/com/algos/AlgoItem.tsx index 04117e589..f2fb075da 100644 --- a/src/view/com/algos/AlgoItem.tsx +++ b/src/view/com/algos/AlgoItem.tsx @@ -29,7 +29,7 @@ const AlgoItem = observer( style={[styles.container, style]} onPress={() => { navigation.navigate('CustomFeed', { - name: item.data.creator.did, + name: item.data.displayName, rkey: item.data.uri, }) }} @@ -40,25 +40,27 @@ const AlgoItem = observer( </View> <View style={[styles.headerTextContainer]}> <Text style={[pal.text, s.bold]}> - {item.data.displayName ? item.data.displayName : 'Feed name'} + {item.data.displayName ?? 'Feed name'} </Text> <Text style={[pal.textLight, styles.description]}> - {item.data.description ?? - 'THIS IS A FEED DESCRIPTION, IT WILL TELL YOU WHAT THE FEED IS ABOUT. THIS IS A COOL FEED ABOUT COOL PEOPLE.'} + {item.data.description ?? 'Feed description'} </Text> </View> </View> - {/* TODO: this feed is like by *3* people UserAvatars and others */} <View style={styles.bottomContainer}> <View style={styles.likedByContainer}> - <View style={styles.likedByAvatars}> + {/* <View style={styles.likedByAvatars}> <UserAvatar size={24} avatar={item.data.avatar} /> <UserAvatar size={24} avatar={item.data.avatar} /> <UserAvatar size={24} avatar={item.data.avatar} /> - </View> + </View> */} - <Text style={[pal.text, pal.textLight]}>Liked by 3 others</Text> + <Text style={[pal.text, pal.textLight]}> + {item.data.likeCount && item.data.likeCount > 1 + ? `Liked by ${item.data.likeCount} others` + : 'Be the first to like this'} + </Text> </View> <View> <Button diff --git a/src/view/com/algos/useCustomFeed.ts b/src/view/com/algos/useCustomFeed.ts new file mode 100644 index 000000000..cea9c1cea --- /dev/null +++ b/src/view/com/algos/useCustomFeed.ts @@ -0,0 +1,27 @@ +import {useEffect, useState} from 'react' +import {useStores} from 'state/index' +import {AlgoItemModel} from 'state/models/feeds/algo/algo-item' + +export function useCustomFeed(uri: string) { + const store = useStores() + const [item, setItem] = useState<AlgoItemModel>() + useEffect(() => { + async function fetchView() { + const res = await store.agent.app.bsky.feed.getFeedGenerator({ + feed: uri, + }) + const view = res.data.view + return view + } + async function buildFeedItem() { + const view = await fetchView() + if (view) { + const temp = new AlgoItemModel(store, view) + setItem(temp) + } + } + buildFeedItem() + }, [store, uri]) + + return item +} diff --git a/src/view/com/util/PostCtrls.tsx b/src/view/com/util/PostCtrls.tsx index 11b73cea0..3be6b59f1 100644 --- a/src/view/com/util/PostCtrls.tsx +++ b/src/view/com/util/PostCtrls.tsx @@ -240,7 +240,7 @@ export function PostCtrls(opts: PostCtrlsOpts) { }> {opts.isLiked ? ( <HeartIconSolid - style={styles.ctrlIconLiked as StyleProp<ViewStyle>} + style={styles.ctrlIconLiked} size={opts.big ? 22 : 16} /> ) : ( diff --git a/src/view/screens/CustomFeed.tsx b/src/view/screens/CustomFeed.tsx index 1d4343b29..070db8c07 100644 --- a/src/view/screens/CustomFeed.tsx +++ b/src/view/screens/CustomFeed.tsx @@ -1,23 +1,30 @@ import {NativeStackScreenProps} from '@react-navigation/native-stack' +import {usePalette} from 'lib/hooks/usePalette' +import {HeartIcon} from 'lib/icons' import {CommonNavigatorParams} from 'lib/routes/types' +import {colors, s} from 'lib/styles' import {observer} from 'mobx-react-lite' -import React, {useEffect, useMemo, useRef} from 'react' -import {FlatList, StyleSheet, View} from 'react-native' +import React, {useMemo, useRef} from 'react' +import {FlatList, StyleSheet, TouchableOpacity, View} from 'react-native' import {useStores} from 'state/index' -import {AlgoItemModel} from 'state/models/feeds/algo/algo-item' import {PostsFeedModel} from 'state/models/feeds/posts' +import {useCustomFeed} from 'view/com/algos/useCustomFeed' import {withAuthRequired} from 'view/com/auth/withAuthRequired' import {Feed} from 'view/com/posts/Feed' +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' type Props = NativeStackScreenProps<CommonNavigatorParams, 'CustomFeed'> export const CustomFeed = withAuthRequired( - observer(({route}: Props) => { + observer(({route, navigation}: Props) => { const rootStore = useStores() - const scrollElRef = useRef<FlatList>(null) - const {rkey, name} = route.params + const currentFeed = useCustomFeed(rkey) + const pal = usePalette('default') + + const scrollElRef = useRef<FlatList>(null) const algoFeed: PostsFeedModel = useMemo(() => { const feed = new PostsFeedModel(rootStore, 'custom', { @@ -29,13 +36,62 @@ export const CustomFeed = withAuthRequired( return ( <View style={[styles.container]}> - <ViewHeader title={'Custom Feed'} showOnDesktop /> + <View> + <ViewHeader + title={name ?? `${currentFeed?.data.creator.displayName}'s feed`} + showOnDesktop + /> + <View style={[styles.center]}> + <View style={[styles.header]}> + <View style={styles.avatarContainer}> + <UserAvatar + size={30} + avatar={currentFeed?.data.creator.avatar} + /> + <Text style={[pal.textLight]}> + @{currentFeed?.data.creator.handle} + </Text> + </View> + <Text style={[pal.text]}>{currentFeed?.data.description}</Text> + </View> + + <View style={[styles.buttonsContainer]}> + <Button + type={currentFeed?.isSaved ? 'default' : 'inverted'} + style={[styles.saveButton]} + onPress={() => { + if (currentFeed?.data.viewer?.saved) { + currentFeed?.unsave() + rootStore.me.savedFeeds.removeFeed(currentFeed!.data.uri) + } else { + currentFeed!.save() + rootStore.me.savedFeeds.addFeed(currentFeed!) + } + }} + label={currentFeed?.data.viewer?.saved ? 'Unsave' : 'Save'} + /> + + <TouchableOpacity + accessibilityRole="button" + onPress={() => { + currentFeed?.like() + }} + style={[styles.likeButton]}> + <Text style={[pal.text, s.semiBold]}> + {currentFeed?.data.likeCount} + </Text> + <HeartIcon strokeWidth={3} size={18} style={styles.liked} /> + </TouchableOpacity> + </View> + </View> + </View> <Feed scrollElRef={scrollElRef} testID={'test-feed'} key="default" feed={algoFeed} + headerOffset={12} /> </View> ) @@ -47,4 +103,37 @@ const styles = StyleSheet.create({ flex: 1, height: '100%', }, + center: {alignItems: 'center', justifyContent: 'center', gap: 8}, + header: { + alignItems: 'center', + gap: 8, + }, + avatarContainer: { + flexDirection: 'row', + gap: 8, + }, + buttonsContainer: { + flexDirection: 'row', + gap: 8, + }, + saveButton: { + minWidth: 100, + alignItems: 'center', + }, + liked: { + color: colors.red3, + }, + notLiked: { + color: colors.gray3, + }, + likeButton: { + flexDirection: 'row', + alignItems: 'center', + justifyContent: 'center', + paddingVertical: 4, + paddingHorizontal: 8, + borderRadius: 24, + backgroundColor: colors.gray1, + gap: 4, + }, }) |