From 370d52bd1f4ba3b5effb7a48cd1c8b14aea88781 Mon Sep 17 00:00:00 2001 From: Ansh Nanda Date: Fri, 12 May 2023 14:40:58 -0700 Subject: add custom algorithm screen to settings under moderation --- src/Navigation.tsx | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/Navigation.tsx') diff --git a/src/Navigation.tsx b/src/Navigation.tsx index afc7b39b8..8b6e1b453 100644 --- a/src/Navigation.tsx +++ b/src/Navigation.tsx @@ -52,6 +52,7 @@ import {AppPasswords} from 'view/screens/AppPasswords' import {MutedAccounts} from 'view/screens/MutedAccounts' import {BlockedAccounts} from 'view/screens/BlockedAccounts' import {getRoutingInstrumentation} from 'lib/sentry' +import CustomAlgorithms from 'view/screens/CustomAlgorithms' const navigationRef = createNavigationContainerRef() @@ -91,6 +92,7 @@ function commonScreens(Stack: typeof HomeTab) { /> + -- cgit 1.4.1 From 61ea37ff818ca64cec8e10b3a423ecd22dcf0141 Mon Sep 17 00:00:00 2001 From: Ansh Nanda Date: Sun, 14 May 2023 18:37:18 -0700 Subject: renamed page to savedfeeds --- src/Navigation.tsx | 4 +- src/lib/routes/types.ts | 2 +- src/routes.ts | 2 +- src/view/screens/CustomAlgorithms.tsx | 105 ---------------------------------- src/view/screens/SavedFeeds.tsx | 103 +++++++++++++++++++++++++++++++++ 5 files changed, 107 insertions(+), 109 deletions(-) delete mode 100644 src/view/screens/CustomAlgorithms.tsx create mode 100644 src/view/screens/SavedFeeds.tsx (limited to 'src/Navigation.tsx') diff --git a/src/Navigation.tsx b/src/Navigation.tsx index 8b6e1b453..26dc9f7ad 100644 --- a/src/Navigation.tsx +++ b/src/Navigation.tsx @@ -52,7 +52,7 @@ import {AppPasswords} from 'view/screens/AppPasswords' import {MutedAccounts} from 'view/screens/MutedAccounts' import {BlockedAccounts} from 'view/screens/BlockedAccounts' import {getRoutingInstrumentation} from 'lib/sentry' -import CustomAlgorithms from 'view/screens/CustomAlgorithms' +import {SavedFeeds} from './view/screens/SavedFeeds' const navigationRef = createNavigationContainerRef() @@ -92,7 +92,7 @@ function commonScreens(Stack: typeof HomeTab) { /> - + diff --git a/src/lib/routes/types.ts b/src/lib/routes/types.ts index b91495640..5a4126f6a 100644 --- a/src/lib/routes/types.ts +++ b/src/lib/routes/types.ts @@ -20,7 +20,7 @@ export type CommonNavigatorParams = { CommunityGuidelines: undefined CopyrightPolicy: undefined AppPasswords: undefined - CustomAlgorithms: undefined + SavedFeeds: undefined MutedAccounts: undefined BlockedAccounts: undefined } diff --git a/src/routes.ts b/src/routes.ts index c1b441984..b510f66f6 100644 --- a/src/routes.ts +++ b/src/routes.ts @@ -14,7 +14,7 @@ export const router = new Router({ Debug: '/sys/debug', Log: '/sys/log', AppPasswords: '/settings/app-passwords', - CustomAlgorithms: '/settings/custom-algorithms', + SavedFeeds: '/settings/saved-feeds', MutedAccounts: '/settings/muted-accounts', BlockedAccounts: '/settings/blocked-accounts', Support: '/support', diff --git a/src/view/screens/CustomAlgorithms.tsx b/src/view/screens/CustomAlgorithms.tsx deleted file mode 100644 index b838660df..000000000 --- a/src/view/screens/CustomAlgorithms.tsx +++ /dev/null @@ -1,105 +0,0 @@ -import React, {useCallback, useMemo} from 'react' -import { - RefreshControl, - StyleSheet, - View, - FlatList, - ActivityIndicator, -} from 'react-native' -import {useFocusEffect} from '@react-navigation/native' -import {NativeStackScreenProps} from '@react-navigation/native-stack' -import {useAnalytics} from 'lib/analytics' -import {usePalette} from 'lib/hooks/usePalette' -import {CommonNavigatorParams} from 'lib/routes/types' -import {observer} from 'mobx-react-lite' -import {useStores} from 'state/index' -import {SavedFeedsModel} from 'state/models/feeds/algo/saved' -import AlgoItem from 'view/com/algos/AlgoItem' -import {withAuthRequired} from 'view/com/auth/withAuthRequired' -import {ViewHeader} from 'view/com/util/ViewHeader' -import {CenteredView} from 'view/com/util/Views' -import {Text} from 'view/com/util/text/Text' -import {isDesktopWeb} from 'platform/detection' -import {s} from 'lib/styles' - -type Props = NativeStackScreenProps - -const CustomAlgorithms = withAuthRequired( - observer(({}: Props) => { - const pal = usePalette('default') - const rootStore = useStores() - const {screen} = useAnalytics() - - const savedFeeds = useMemo( - () => new SavedFeedsModel(rootStore), - [rootStore], - ) - - useFocusEffect( - useCallback(() => { - screen('SavedFeeds') - rootStore.shell.setMinimalShellMode(false) - savedFeeds.refresh() - }, [screen, rootStore, savedFeeds]), - ) - - return ( - - - item.data.uri} - refreshControl={ - savedFeeds.refresh()} - tintColor={pal.colors.text} - titleColor={pal.colors.text} - /> - } - onEndReached={() => savedFeeds.loadMore()} - renderItem={({item}) => } - initialNumToRender={15} - ListFooterComponent={() => ( - - {savedFeeds.isLoading && } - - )} - ListEmptyComponent={() => ( - - - You don't have any saved feeds. To save a feed, click the save - button when a custom feed or algorithm shows up. - - - )} - extraData={savedFeeds.isLoading} - // @ts-ignore our .web version only -prf - desktopFixedHeight - /> - - ) - }), -) - -export default CustomAlgorithms - -const styles = StyleSheet.create({ - footer: { - paddingVertical: 20, - }, - empty: { - paddingHorizontal: 20, - paddingVertical: 20, - borderRadius: 16, - marginHorizontal: 24, - marginTop: 10, - }, -}) diff --git a/src/view/screens/SavedFeeds.tsx b/src/view/screens/SavedFeeds.tsx new file mode 100644 index 000000000..7b04a6474 --- /dev/null +++ b/src/view/screens/SavedFeeds.tsx @@ -0,0 +1,103 @@ +import React, {useCallback, useMemo} from 'react' +import { + RefreshControl, + StyleSheet, + View, + FlatList, + ActivityIndicator, +} from 'react-native' +import {useFocusEffect} from '@react-navigation/native' +import {NativeStackScreenProps} from '@react-navigation/native-stack' +import {useAnalytics} from 'lib/analytics' +import {usePalette} from 'lib/hooks/usePalette' +import {CommonNavigatorParams} from 'lib/routes/types' +import {observer} from 'mobx-react-lite' +import {useStores} from 'state/index' +import {SavedFeedsModel} from 'state/models/feeds/algo/saved' +import AlgoItem from 'view/com/algos/AlgoItem' +import {withAuthRequired} from 'view/com/auth/withAuthRequired' +import {ViewHeader} from 'view/com/util/ViewHeader' +import {CenteredView} from 'view/com/util/Views' +import {Text} from 'view/com/util/text/Text' +import {isDesktopWeb} from 'platform/detection' +import {s} from 'lib/styles' + +type Props = NativeStackScreenProps + +export const SavedFeeds = withAuthRequired( + observer(({}: Props) => { + const pal = usePalette('default') + const rootStore = useStores() + const {screen} = useAnalytics() + + const savedFeeds = useMemo( + () => new SavedFeedsModel(rootStore), + [rootStore], + ) + + useFocusEffect( + useCallback(() => { + screen('SavedFeeds') + rootStore.shell.setMinimalShellMode(false) + savedFeeds.refresh() + }, [screen, rootStore, savedFeeds]), + ) + + return ( + + + item.data.uri} + refreshControl={ + savedFeeds.refresh()} + tintColor={pal.colors.text} + titleColor={pal.colors.text} + /> + } + onEndReached={() => savedFeeds.loadMore()} + renderItem={({item}) => } + initialNumToRender={15} + ListFooterComponent={() => ( + + {savedFeeds.isLoading && } + + )} + ListEmptyComponent={() => ( + + + You don't have any saved feeds. To save a feed, click the save + button when a custom feed or algorithm shows up. + + + )} + extraData={savedFeeds.isLoading} + // @ts-ignore our .web version only -prf + desktopFixedHeight + /> + + ) + }), +) + +const styles = StyleSheet.create({ + footer: { + paddingVertical: 20, + }, + empty: { + paddingHorizontal: 20, + paddingVertical: 20, + borderRadius: 16, + marginHorizontal: 24, + marginTop: 10, + }, +}) -- cgit 1.4.1 From 50108611600ae2addb3bff4f00562b02f9c35fdf Mon Sep 17 00:00:00 2001 From: Ansh Nanda Date: Mon, 15 May 2023 10:42:23 -0700 Subject: custom feed screen --- src/Navigation.tsx | 2 ++ src/lib/routes/types.ts | 1 + src/routes.ts | 1 + src/state/models/feeds/algo/algo-item.ts | 20 +++++++++++++ src/state/models/feeds/posts.ts | 31 +++++++++++++++----- src/view/com/algos/AlgoItem.tsx | 24 +++++++++++++-- src/view/screens/CustomFeed.tsx | 50 ++++++++++++++++++++++++++++++++ 7 files changed, 119 insertions(+), 10 deletions(-) create mode 100644 src/view/screens/CustomFeed.tsx (limited to 'src/Navigation.tsx') diff --git a/src/Navigation.tsx b/src/Navigation.tsx index 26dc9f7ad..d4c992eb6 100644 --- a/src/Navigation.tsx +++ b/src/Navigation.tsx @@ -53,6 +53,7 @@ import {MutedAccounts} from 'view/screens/MutedAccounts' import {BlockedAccounts} from 'view/screens/BlockedAccounts' import {getRoutingInstrumentation} from 'lib/sentry' import {SavedFeeds} from './view/screens/SavedFeeds' +import {CustomFeed} from './view/screens/CustomFeed' const navigationRef = createNavigationContainerRef() @@ -93,6 +94,7 @@ function commonScreens(Stack: typeof HomeTab) { + diff --git a/src/lib/routes/types.ts b/src/lib/routes/types.ts index 5a4126f6a..29fadd709 100644 --- a/src/lib/routes/types.ts +++ b/src/lib/routes/types.ts @@ -21,6 +21,7 @@ export type CommonNavigatorParams = { CopyrightPolicy: undefined AppPasswords: undefined SavedFeeds: undefined + CustomFeed: {name: string; rkey: string} MutedAccounts: undefined BlockedAccounts: undefined } diff --git a/src/routes.ts b/src/routes.ts index b510f66f6..2cdaa28bb 100644 --- a/src/routes.ts +++ b/src/routes.ts @@ -15,6 +15,7 @@ export const router = new Router({ Log: '/sys/log', AppPasswords: '/settings/app-passwords', SavedFeeds: '/settings/saved-feeds', + CustomFeed: '/profile/:name/feed/:rkey', MutedAccounts: '/settings/muted-accounts', BlockedAccounts: '/settings/blocked-accounts', Support: '/support', diff --git a/src/state/models/feeds/algo/algo-item.ts b/src/state/models/feeds/algo/algo-item.ts index 88e9c0662..3dee5e2bf 100644 --- a/src/state/models/feeds/algo/algo-item.ts +++ b/src/state/models/feeds/algo/algo-item.ts @@ -29,6 +29,10 @@ export class AlgoItemModel { } } + get getUri() { + return this.data.uri + } + // public apis // = async save() { @@ -52,4 +56,20 @@ export class AlgoItemModel { this.rootStore.log.error('Failed to unsanve feed', e) } } + + // 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 + // } } diff --git a/src/state/models/feeds/posts.ts b/src/state/models/feeds/posts.ts index 44cec3af7..7adc1cb1c 100644 --- a/src/state/models/feeds/posts.ts +++ b/src/state/models/feeds/posts.ts @@ -4,6 +4,7 @@ import { AppBskyFeedDefs, AppBskyFeedPost, AppBskyFeedGetAuthorFeed as GetAuthorFeed, + AppBskyFeedGetFeed as GetCustomFeed, RichText, jsonToLex, } from '@atproto/api' @@ -305,8 +306,11 @@ export class PostsFeedModel { constructor( public rootStore: RootStoreModel, - public feedType: 'home' | 'author' | 'suggested' | 'goodstuff', - params: GetTimeline.QueryParams | GetAuthorFeed.QueryParams, + public feedType: 'home' | 'author' | 'suggested' | 'goodstuff' | 'custom', + params: + | GetTimeline.QueryParams + | GetAuthorFeed.QueryParams + | GetCustomFeed.QueryParams, ) { makeAutoObservable( this, @@ -595,13 +599,15 @@ export class PostsFeedModel { // helper functions // = - async _replaceAll(res: GetTimeline.Response | GetAuthorFeed.Response) { + async _replaceAll( + res: GetTimeline.Response | GetAuthorFeed.Response | GetCustomFeed.Response, + ) { this.pollCursor = res.data.feed[0]?.post.uri return this._appendAll(res, true) } async _appendAll( - res: GetTimeline.Response | GetAuthorFeed.Response, + res: GetTimeline.Response | GetAuthorFeed.Response | GetCustomFeed.Response, replace = false, ) { this.loadMoreCursor = res.data.cursor @@ -640,7 +646,9 @@ export class PostsFeedModel { }) } - _updateAll(res: GetTimeline.Response | GetAuthorFeed.Response) { + _updateAll( + res: GetTimeline.Response | GetAuthorFeed.Response | GetCustomFeed.Response, + ) { for (const item of res.data.feed) { const existingSlice = this.slices.find(slice => slice.containsUri(item.post.uri), @@ -657,8 +665,13 @@ export class PostsFeedModel { } protected async _getFeed( - params: GetTimeline.QueryParams | GetAuthorFeed.QueryParams = {}, - ): Promise { + params: + | GetTimeline.QueryParams + | GetAuthorFeed.QueryParams + | GetCustomFeed.QueryParams, + ): Promise< + GetTimeline.Response | GetAuthorFeed.Response | GetCustomFeed.Response + > { params = Object.assign({}, this.params, params) if (this.feedType === 'suggested') { const responses = await getMultipleAuthorsPosts( @@ -680,6 +693,10 @@ export class PostsFeedModel { } } else if (this.feedType === 'home') { return this.rootStore.agent.getTimeline(params as GetTimeline.QueryParams) + } else if (this.feedType === 'custom') { + return this.rootStore.agent.app.bsky.feed.getFeed( + params as GetCustomFeed.QueryParams, + ) } else if (this.feedType === 'goodstuff') { const res = await getGoodStuff( this.rootStore.session.currentSession?.accessJwt || '', diff --git a/src/view/com/algos/AlgoItem.tsx b/src/view/com/algos/AlgoItem.tsx index e475624c5..51de89bd6 100644 --- a/src/view/com/algos/AlgoItem.tsx +++ b/src/view/com/algos/AlgoItem.tsx @@ -1,5 +1,11 @@ import React from 'react' -import {StyleProp, StyleSheet, View, ViewStyle} from 'react-native' +import { + StyleProp, + StyleSheet, + View, + ViewStyle, + TouchableOpacity, +} from 'react-native' import {Text} from '../util/text/Text' import {usePalette} from 'lib/hooks/usePalette' import {s} from 'lib/styles' @@ -7,13 +13,25 @@ import {UserAvatar} from '../util/UserAvatar' import {Button} from '../util/forms/Button' import {observer} from 'mobx-react-lite' import {AlgoItemModel} from 'state/models/feeds/algo/algo-item' +import {useNavigation} from '@react-navigation/native' +import {NavigationProp} from 'lib/routes/types' const AlgoItem = observer( ({item, style}: {item: AlgoItemModel; style?: StyleProp}) => { const pal = usePalette('default') + const navigation = useNavigation() return ( - + { + navigation.navigate('CustomFeed', { + name: item.data.creator.did, + rkey: item.data.uri, + }) + }} + key={item.data.uri}> @@ -54,7 +72,7 @@ const AlgoItem = observer( /> - + ) }, ) diff --git a/src/view/screens/CustomFeed.tsx b/src/view/screens/CustomFeed.tsx new file mode 100644 index 000000000..1d4343b29 --- /dev/null +++ b/src/view/screens/CustomFeed.tsx @@ -0,0 +1,50 @@ +import {NativeStackScreenProps} from '@react-navigation/native-stack' +import {CommonNavigatorParams} from 'lib/routes/types' +import {observer} from 'mobx-react-lite' +import React, {useEffect, useMemo, useRef} from 'react' +import {FlatList, StyleSheet, 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 {withAuthRequired} from 'view/com/auth/withAuthRequired' +import {Feed} from 'view/com/posts/Feed' +import {ViewHeader} from 'view/com/util/ViewHeader' +import {Text} from 'view/com/util/text/Text' + +type Props = NativeStackScreenProps +export const CustomFeed = withAuthRequired( + observer(({route}: Props) => { + const rootStore = useStores() + const scrollElRef = useRef(null) + + const {rkey, name} = route.params + + const algoFeed: PostsFeedModel = useMemo(() => { + const feed = new PostsFeedModel(rootStore, 'custom', { + feed: rkey, + }) + feed.setup() + return feed + }, [rkey, rootStore]) + + return ( + + + + + + ) + }), +) + +const styles = StyleSheet.create({ + container: { + flex: 1, + height: '100%', + }, +}) -- cgit 1.4.1 From 53ca0cd626cc71fea38fb0f59f68092ab406d143 Mon Sep 17 00:00:00 2001 From: Ansh Nanda Date: Tue, 16 May 2023 18:28:44 -0700 Subject: drag to rearrange pinned items --- src/Navigation.tsx | 2 + src/lib/routes/types.ts | 1 + src/routes.ts | 1 + src/view/com/algos/AlgoItem.tsx | 8 +-- src/view/com/algos/SavedFeedItem.tsx | 50 +++++++++++++ src/view/screens/PinnedFeeds.tsx | 134 +++++++++++++++++++++++++++++++++++ src/view/screens/SavedFeeds.tsx | 99 +++++++++++++++++--------- 7 files changed, 256 insertions(+), 39 deletions(-) create mode 100644 src/view/com/algos/SavedFeedItem.tsx create mode 100644 src/view/screens/PinnedFeeds.tsx (limited to 'src/Navigation.tsx') diff --git a/src/Navigation.tsx b/src/Navigation.tsx index d4c992eb6..17d80dfdc 100644 --- a/src/Navigation.tsx +++ b/src/Navigation.tsx @@ -54,6 +54,7 @@ import {BlockedAccounts} from 'view/screens/BlockedAccounts' import {getRoutingInstrumentation} from 'lib/sentry' import {SavedFeeds} from './view/screens/SavedFeeds' import {CustomFeed} from './view/screens/CustomFeed' +import {PinnedFeeds} from 'view/screens/PinnedFeeds' const navigationRef = createNavigationContainerRef() @@ -94,6 +95,7 @@ function commonScreens(Stack: typeof HomeTab) { + diff --git a/src/lib/routes/types.ts b/src/lib/routes/types.ts index 77ed58cc3..12ff27070 100644 --- a/src/lib/routes/types.ts +++ b/src/lib/routes/types.ts @@ -21,6 +21,7 @@ export type CommonNavigatorParams = { CopyrightPolicy: undefined AppPasswords: undefined SavedFeeds: undefined + PinnedFeeds: undefined CustomFeed: {name?: string; rkey: string} MutedAccounts: undefined BlockedAccounts: undefined diff --git a/src/routes.ts b/src/routes.ts index 2cdaa28bb..6998226c0 100644 --- a/src/routes.ts +++ b/src/routes.ts @@ -15,6 +15,7 @@ export const router = new Router({ Log: '/sys/log', AppPasswords: '/settings/app-passwords', SavedFeeds: '/settings/saved-feeds', + PinnedFeeds: '/settings/pinned-feeds', CustomFeed: '/profile/:name/feed/:rkey', MutedAccounts: '/settings/muted-accounts', BlockedAccounts: '/settings/blocked-accounts', diff --git a/src/view/com/algos/AlgoItem.tsx b/src/view/com/algos/AlgoItem.tsx index 6fbbd0df1..b28545c17 100644 --- a/src/view/com/algos/AlgoItem.tsx +++ b/src/view/com/algos/AlgoItem.tsx @@ -24,13 +24,11 @@ const AlgoItem = observer( item, style, showBottom = true, - onLongPress, reloadOnFocus = false, }: { item: AlgoItemModel style?: StyleProp showBottom?: boolean - onLongPress?: () => void reloadOnFocus?: boolean }) => { const store = useStores() @@ -54,7 +52,6 @@ const AlgoItem = observer( rkey: item.data.uri, }) }} - onLongPress={onLongPress} key={item.data.uri}> @@ -64,8 +61,9 @@ const AlgoItem = observer( {item.data.displayName ?? 'Feed name'} - - {item.data.description ?? 'Feed description'} + + {item.data.description ?? + "Explore our Feed for the latest updates and insights! Dive into a world of intriguing articles, trending news, and exciting stories that cover a wide range of topics. From technology breakthroughs to lifestyle tips, there's something here for everyone. Stay informed and get inspired with us. Join the conversation now!"} diff --git a/src/view/com/algos/SavedFeedItem.tsx b/src/view/com/algos/SavedFeedItem.tsx new file mode 100644 index 000000000..bb4ec10b3 --- /dev/null +++ b/src/view/com/algos/SavedFeedItem.tsx @@ -0,0 +1,50 @@ +import React from 'react' +import {View, TouchableOpacity, StyleSheet} from 'react-native' +import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' +import {colors} from 'lib/styles' +import {observer} from 'mobx-react-lite' +import {AlgoItemModel} from 'state/models/feeds/algo/algo-item' +import {SavedFeedsModel} from 'state/models/feeds/algo/saved' +import AlgoItem from './AlgoItem' + +export const SavedFeedItem = observer( + ({item, savedFeeds}: {item: AlgoItemModel; savedFeeds: SavedFeedsModel}) => { + const isPinned = savedFeeds.isPinned(item) + + return ( + + + { + savedFeeds.togglePinnedFeed(item) + console.log('pinned', savedFeeds.pinned) + console.log('isPinned', savedFeeds.isPinned(item)) + }}> + + + + ) + }, +) + +const styles = StyleSheet.create({ + itemContainer: { + flex: 1, + flexDirection: 'row', + alignItems: 'center', + marginRight: 18, + }, + item: { + borderTopWidth: 0, + }, +}) diff --git a/src/view/screens/PinnedFeeds.tsx b/src/view/screens/PinnedFeeds.tsx new file mode 100644 index 000000000..2a0e3deff --- /dev/null +++ b/src/view/screens/PinnedFeeds.tsx @@ -0,0 +1,134 @@ +import React, {useCallback, useMemo} from 'react' +import { + RefreshControl, + StyleSheet, + View, + ActivityIndicator, + Pressable, +} from 'react-native' +import {useFocusEffect} from '@react-navigation/native' +import {NativeStackScreenProps} from '@react-navigation/native-stack' +import {useAnalytics} from 'lib/analytics' +import {usePalette} from 'lib/hooks/usePalette' +import {CommonNavigatorParams} from 'lib/routes/types' +import {observer} from 'mobx-react-lite' +import {useStores} from 'state/index' +import {withAuthRequired} from 'view/com/auth/withAuthRequired' +import {ViewHeader} from 'view/com/util/ViewHeader' +import {CenteredView} from 'view/com/util/Views' +import {Text} from 'view/com/util/text/Text' +import {isDesktopWeb} from 'platform/detection' +import {s} from 'lib/styles' +import DraggableFlatList, { + ShadowDecorator, + ScaleDecorator, +} from 'react-native-draggable-flatlist' +import {SavedFeedItem} from 'view/com/algos/SavedFeedItem' +import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' + +type Props = NativeStackScreenProps + +export const PinnedFeeds = withAuthRequired( + observer(({}: Props) => { + // hooks for global items + const pal = usePalette('default') + const rootStore = useStores() + const {screen} = useAnalytics() + + // hooks for local + const savedFeeds = useMemo(() => rootStore.me.savedFeeds, [rootStore]) + useFocusEffect( + useCallback(() => { + screen('SavedFeeds') + rootStore.shell.setMinimalShellMode(false) + savedFeeds.refresh() + }, [screen, rootStore, savedFeeds]), + ) + const _ListEmptyComponent = () => { + return ( + + + You don't have any pinned feeds. To pin a feed, go back to the Saved + Feeds screen and click the pin icon! + + + ) + } + const _ListFooterComponent = () => { + return ( + + {savedFeeds.isLoading && } + + ) + } + + return ( + + + item.data.uri} + refreshing={savedFeeds.isRefreshing} + refreshControl={ + savedFeeds.refresh()} + tintColor={pal.colors.text} + titleColor={pal.colors.text} + /> + } + renderItem={({item, drag}) => ( + + + + + + + + + )} + initialNumToRender={10} + ListFooterComponent={_ListFooterComponent} + ListEmptyComponent={_ListEmptyComponent} + extraData={savedFeeds.isLoading} + onDragEnd={({data}) => savedFeeds.reorderPinnedFeeds(data)} + // @ts-ignore our .web version only -prf + desktopFixedHeight + /> + + ) + }), +) + +const styles = StyleSheet.create({ + footer: { + paddingVertical: 20, + }, + empty: { + paddingHorizontal: 20, + paddingVertical: 20, + borderRadius: 16, + marginHorizontal: 24, + marginTop: 10, + }, + itemContainer: { + flex: 1, + flexDirection: 'row', + alignItems: 'center', + marginLeft: 18, + }, + item: { + borderTopWidth: 0, + }, + icon: {marginRight: 10}, +}) diff --git a/src/view/screens/SavedFeeds.tsx b/src/view/screens/SavedFeeds.tsx index 65ffdb233..8403efc6e 100644 --- a/src/view/screens/SavedFeeds.tsx +++ b/src/view/screens/SavedFeeds.tsx @@ -6,6 +6,7 @@ import { ActivityIndicator, FlatList, TouchableOpacity, + ScrollView, } from 'react-native' import {useFocusEffect} from '@react-navigation/native' import {NativeStackScreenProps} from '@react-navigation/native-stack' @@ -14,21 +15,21 @@ import {usePalette} from 'lib/hooks/usePalette' import {CommonNavigatorParams} from 'lib/routes/types' import {observer} from 'mobx-react-lite' import {useStores} from 'state/index' -import AlgoItem from 'view/com/algos/AlgoItem' import {withAuthRequired} from 'view/com/auth/withAuthRequired' import {ViewHeader} from 'view/com/util/ViewHeader' import {CenteredView} from 'view/com/util/Views' import {Text} from 'view/com/util/text/Text' import {isDesktopWeb} from 'platform/detection' -import {colors, s} from 'lib/styles' -import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' -import {AlgoItemModel} from 'state/models/feeds/algo/algo-item' +import {s} from 'lib/styles' import {SavedFeedsModel} from 'state/models/feeds/algo/saved' +import {Link} from 'view/com/util/Link' +import {UserAvatar} from 'view/com/util/UserAvatar' +import {SavedFeedItem} from 'view/com/algos/SavedFeedItem' type Props = NativeStackScreenProps export const SavedFeeds = withAuthRequired( - observer(({}: Props) => { + observer(({navigation}: Props) => { // hooks for global items const pal = usePalette('default') const rootStore = useStores() @@ -87,6 +88,12 @@ export const SavedFeeds = withAuthRequired( )} initialNumToRender={10} + ListHeaderComponent={() => ( + + )} ListFooterComponent={_ListFooterComponent} ListEmptyComponent={_ListEmptyComponent} extraData={savedFeeds.isLoading} @@ -98,31 +105,53 @@ export const SavedFeeds = withAuthRequired( }), ) -const SavedFeedItem = observer( - ({item, savedFeeds}: {item: AlgoItemModel; savedFeeds: SavedFeedsModel}) => { - const isPinned = savedFeeds.isPinned(item) - +const ListHeaderComponent = observer( + ({ + savedFeeds, + navigation, + }: { + savedFeeds: SavedFeedsModel + navigation: Props['navigation'] + }) => { return ( - - - { - savedFeeds.togglePinnedFeed(item) - console.log('pinned', savedFeeds.pinned) - console.log('isPinned', savedFeeds.isPinned(item)) - }}> - - + + {savedFeeds.pinned.length > 0 ? ( + + + Pinned Feeds + + Edit + + + + + {savedFeeds.pinned.map(item => { + return ( + { + navigation.navigate('CustomFeed', { + rkey: item.data.uri, + name: item.data.displayName, + }) + }} + style={styles.pinnedItem}> + + + {item.data.displayName ?? + `${item.data.creator.displayName}'s feed`} + + + ) + })} + + + ) : null} + + All Saved Feeds ) }, @@ -139,12 +168,14 @@ const styles = StyleSheet.create({ marginHorizontal: 24, marginTop: 10, }, - itemContainer: { - flexDirection: 'row', + headerContainer: {paddingHorizontal: 18}, + pinnedContainer: {marginBottom: 18, gap: 18}, + pinnedHeader: {flexDirection: 'row', justifyContent: 'space-between'}, + pinnedItem: { + flex: 1, alignItems: 'center', marginRight: 18, + maxWidth: 100, }, - item: { - borderTopWidth: 0, - }, + editPinned: {textDecorationLine: 'underline'}, }) -- cgit 1.4.1 From 85910cdbea73352a0512218d4a3851ad52e7c8b3 Mon Sep 17 00:00:00 2001 From: Paul Frazee Date: Wed, 17 May 2023 13:42:16 -0500 Subject: Fix missed change in merge --- src/Navigation.tsx | 2 -- 1 file changed, 2 deletions(-) (limited to 'src/Navigation.tsx') diff --git a/src/Navigation.tsx b/src/Navigation.tsx index 45ab439b6..4521068f2 100644 --- a/src/Navigation.tsx +++ b/src/Navigation.tsx @@ -187,8 +187,6 @@ function commonScreens(Stack: typeof HomeTab, unreadCountLabel?: string) { component={AppPasswords} options={{title: title('App Passwords')}} /> - - -- cgit 1.4.1 From c55ce6de020d6c9df86c71158251caddf12da777 Mon Sep 17 00:00:00 2001 From: Paul Frazee Date: Wed, 17 May 2023 15:52:11 -0500 Subject: Rework the UI for the custom feed view --- src/Navigation.tsx | 4 +- src/lib/hooks/useCustomFeed.ts | 4 +- src/lib/routes/types.ts | 2 +- src/view/com/feeds/CustomFeed.tsx | 3 +- src/view/screens/CustomFeed.tsx | 164 ------------------- src/view/screens/ProfileCustomFeed.tsx | 291 +++++++++++++++++++++++++++++++++ src/view/screens/ProfileList.tsx | 4 +- src/view/screens/SavedFeeds.tsx | 5 +- 8 files changed, 300 insertions(+), 177 deletions(-) delete mode 100644 src/view/screens/CustomFeed.tsx create mode 100644 src/view/screens/ProfileCustomFeed.tsx (limited to 'src/Navigation.tsx') diff --git a/src/Navigation.tsx b/src/Navigation.tsx index 4521068f2..025020afa 100644 --- a/src/Navigation.tsx +++ b/src/Navigation.tsx @@ -40,6 +40,7 @@ import {SettingsScreen} from './view/screens/Settings' import {ProfileScreen} from './view/screens/Profile' import {ProfileFollowersScreen} from './view/screens/ProfileFollowers' import {ProfileFollowsScreen} from './view/screens/ProfileFollows' +import {ProfileCustomFeed} from './view/screens/ProfileCustomFeed' import {ProfileListScreen} from './view/screens/ProfileList' import {PostThreadScreen} from './view/screens/PostThread' import {PostLikedByScreen} from './view/screens/PostLikedBy' @@ -56,7 +57,6 @@ import {ModerationMutedAccounts} from 'view/screens/ModerationMutedAccounts' import {ModerationBlockedAccounts} from 'view/screens/ModerationBlockedAccounts' import {getRoutingInstrumentation} from 'lib/sentry' import {SavedFeeds} from './view/screens/SavedFeeds' -import {CustomFeed} from './view/screens/CustomFeed' import {PinnedFeeds} from 'view/screens/PinnedFeeds' import {bskyTitle} from 'lib/strings/headings' @@ -127,6 +127,7 @@ function commonScreens(Stack: typeof HomeTab, unreadCountLabel?: string) { title: title(`People followed by @${route.params.name}`), })} /> + - ) } diff --git a/src/lib/hooks/useCustomFeed.ts b/src/lib/hooks/useCustomFeed.ts index ee40cf49e..d7a27050d 100644 --- a/src/lib/hooks/useCustomFeed.ts +++ b/src/lib/hooks/useCustomFeed.ts @@ -2,9 +2,9 @@ import {useEffect, useState} from 'react' import {useStores} from 'state/index' import {CustomFeedModel} from 'state/models/feeds/custom-feed' -export function useCustomFeed(uri: string) { +export function useCustomFeed(uri: string): CustomFeedModel | undefined { const store = useStores() - const [item, setItem] = useState() + const [item, setItem] = useState() useEffect(() => { async function fetchView() { const res = await store.agent.app.bsky.feed.getFeedGenerator({ diff --git a/src/lib/routes/types.ts b/src/lib/routes/types.ts index 8b96aaad7..52d0e9af2 100644 --- a/src/lib/routes/types.ts +++ b/src/lib/routes/types.ts @@ -13,11 +13,11 @@ export type CommonNavigatorParams = { Profile: {name: string; hideBackButton?: boolean} ProfileFollowers: {name: string} ProfileFollows: {name: string} + ProfileCustomFeed: {name: string; rkey: string} ProfileList: {name: string; rkey: string} PostThread: {name: string; rkey: string} PostLikedBy: {name: string; rkey: string} PostRepostedBy: {name: string; rkey: string} - CustomFeed: {name: string; rkey: string; displayName?: string} Debug: undefined Log: undefined Support: undefined diff --git a/src/view/com/feeds/CustomFeed.tsx b/src/view/com/feeds/CustomFeed.tsx index 8e1a78453..5a93020a0 100644 --- a/src/view/com/feeds/CustomFeed.tsx +++ b/src/view/com/feeds/CustomFeed.tsx @@ -40,10 +40,9 @@ export const CustomFeed = observer( accessibilityRole="button" style={[styles.container, pal.border, style]} onPress={() => { - navigation.navigate('CustomFeed', { + navigation.navigate('ProfileCustomFeed', { name: item.data.creator.did, rkey: new AtUri(item.data.uri).rkey, - displayName: item.displayName, }) }} key={item.data.uri}> diff --git a/src/view/screens/CustomFeed.tsx b/src/view/screens/CustomFeed.tsx deleted file mode 100644 index 76125fa5c..000000000 --- a/src/view/screens/CustomFeed.tsx +++ /dev/null @@ -1,164 +0,0 @@ -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 React, {useMemo, useRef} from 'react' -import {FlatList, StyleSheet, TouchableOpacity, 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 {Link} 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' - -type Props = NativeStackScreenProps -export const CustomFeed = withAuthRequired( - observer(({route}: Props) => { - const rootStore = useStores() - const {rkey, name, displayName} = route.params - const uri = useMemo( - () => makeRecordUri(name, 'app.bsky.feed.generator', rkey), - [rkey, name], - ) - const currentFeed = useCustomFeed(uri) - const scrollElRef = useRef(null) - const algoFeed: PostsFeedModel = useMemo(() => { - const feed = new PostsFeedModel(rootStore, 'custom', { - feed: uri, - }) - feed.setup() - return feed - }, [rootStore, uri]) - - return ( - - - } - extraData={uri} - /> - - ) - }), -) - -const ListHeaderComponent = observer(({uri}: {uri: string}) => { - const currentFeed = useCustomFeed(uri) - const pal = usePalette('default') - const rootStore = useStores() - return ( - - - - - - - @{currentFeed?.data.creator.handle} - - - - {currentFeed?.data.description} - - - - + + + )} + + + + + Feed + + + + + ) + }, [store.me.did, pal, currentFeed, onToggleLiked, onToggleSaved]) + + return ( + + + + + ) + }), +) + +/* + + + + + + + + @{currentFeed?.data.creator.handle} + + + + {currentFeed?.data.description} + + + + + + */ + +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, + }, + + /* headerContainer: { + alignItems: 'center', + justifyContent: 'center', + gap: 8, + marginBottom: 12, + }, + header: { + alignItems: 'center', + gap: 4, + }, + avatarContainer: { + flexDirection: 'row', + alignItems: 'center', + 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, + gap: 4, + },*/ +}) diff --git a/src/view/screens/ProfileList.tsx b/src/view/screens/ProfileList.tsx index 01f27bae1..7c3ed831c 100644 --- a/src/view/screens/ProfileList.tsx +++ b/src/view/screens/ProfileList.tsx @@ -87,7 +87,7 @@ export const ProfileListScreen = withAuthRequired( return }, []) - const renderHeaderBtn = React.useCallback(() => { + const renderHeaderBtns = React.useCallback(() => { return ( {list?.isOwner && ( @@ -148,7 +148,7 @@ export const ProfileListScreen = withAuthRequired( pal.border, ]} testID="moderationMutelistsScreen"> - + { - navigation.navigate('CustomFeed', { + navigation.navigate('ProfileCustomFeed', { name: item.data.creator.did, rkey: new AtUri(item.data.uri).rkey, - displayName: - item.data.displayName ?? - `${item.data.creator.displayName}'s feed`, }) }} style={styles.pinnedItem}> -- cgit 1.4.1 From 998879d6d60b59e65250af395fd6ce389c89189b Mon Sep 17 00:00:00 2001 From: Paul Frazee Date: Wed, 17 May 2023 22:04:01 -0500 Subject: Remove redundant feed-settings page --- src/Navigation.tsx | 4 +- src/lib/routes/types.ts | 1 - src/routes.ts | 1 - src/view/com/feeds/SavedFeeds.tsx | 2 +- src/view/index.ts | 12 +-- src/view/screens/PinnedFeeds.tsx | 179 -------------------------------------- src/view/screens/SavedFeeds.tsx | 158 +++++++++++++++------------------ src/view/screens/Settings.tsx | 27 +++--- 8 files changed, 94 insertions(+), 290 deletions(-) delete mode 100644 src/view/screens/PinnedFeeds.tsx (limited to 'src/Navigation.tsx') diff --git a/src/Navigation.tsx b/src/Navigation.tsx index 025020afa..ea36b0f20 100644 --- a/src/Navigation.tsx +++ b/src/Navigation.tsx @@ -55,9 +55,8 @@ import {CopyrightPolicyScreen} from './view/screens/CopyrightPolicy' import {AppPasswords} from 'view/screens/AppPasswords' import {ModerationMutedAccounts} from 'view/screens/ModerationMutedAccounts' import {ModerationBlockedAccounts} from 'view/screens/ModerationBlockedAccounts' +import {SavedFeeds} from 'view/screens/SavedFeeds' import {getRoutingInstrumentation} from 'lib/sentry' -import {SavedFeeds} from './view/screens/SavedFeeds' -import {PinnedFeeds} from 'view/screens/PinnedFeeds' import {bskyTitle} from 'lib/strings/headings' const navigationRef = createNavigationContainerRef() @@ -189,7 +188,6 @@ function commonScreens(Stack: typeof HomeTab, unreadCountLabel?: string) { options={{title: title('App Passwords')}} /> - ) } diff --git a/src/lib/routes/types.ts b/src/lib/routes/types.ts index 52d0e9af2..5c5185602 100644 --- a/src/lib/routes/types.ts +++ b/src/lib/routes/types.ts @@ -27,7 +27,6 @@ export type CommonNavigatorParams = { CopyrightPolicy: undefined AppPasswords: undefined SavedFeeds: undefined - PinnedFeeds: undefined } export type BottomTabNavigatorParams = CommonNavigatorParams & { diff --git a/src/routes.ts b/src/routes.ts index 7501e7abf..c5dc4fb5b 100644 --- a/src/routes.ts +++ b/src/routes.ts @@ -21,7 +21,6 @@ export const router = new Router({ Log: '/sys/log', AppPasswords: '/settings/app-passwords', SavedFeeds: '/settings/saved-feeds', - PinnedFeeds: '/settings/pinned-feeds', Support: '/support', PrivacyPolicy: '/support/privacy', TermsOfService: '/support/tos', diff --git a/src/view/com/feeds/SavedFeeds.tsx b/src/view/com/feeds/SavedFeeds.tsx index 06c47d114..2d0057cfb 100644 --- a/src/view/com/feeds/SavedFeeds.tsx +++ b/src/view/com/feeds/SavedFeeds.tsx @@ -50,7 +50,7 @@ export const SavedFeeds = observer( return ( + href="/settings/saved-feeds"> Settings diff --git a/src/view/index.ts b/src/view/index.ts index 84fc3f315..f06bdaccc 100644 --- a/src/view/index.ts +++ b/src/view/index.ts @@ -60,14 +60,17 @@ import {faPenNib} from '@fortawesome/free-solid-svg-icons/faPenNib' import {faPenToSquare} from '@fortawesome/free-solid-svg-icons/faPenToSquare' import {faPlus} from '@fortawesome/free-solid-svg-icons/faPlus' import {faQuoteLeft} from '@fortawesome/free-solid-svg-icons/faQuoteLeft' +import {faReply} from '@fortawesome/free-solid-svg-icons/faReply' +import {faRetweet} from '@fortawesome/free-solid-svg-icons/faRetweet' +import {faRss} from '@fortawesome/free-solid-svg-icons/faRss' +import {faSatelliteDish} from '@fortawesome/free-solid-svg-icons/faSatelliteDish' import {faShare} from '@fortawesome/free-solid-svg-icons/faShare' import {faShareFromSquare} from '@fortawesome/free-solid-svg-icons/faShareFromSquare' import {faShield} from '@fortawesome/free-solid-svg-icons/faShield' import {faSquarePlus} from '@fortawesome/free-regular-svg-icons/faSquarePlus' import {faSignal} from '@fortawesome/free-solid-svg-icons/faSignal' -import {faReply} from '@fortawesome/free-solid-svg-icons/faReply' -import {faRetweet} from '@fortawesome/free-solid-svg-icons/faRetweet' -import {faRss} from '@fortawesome/free-solid-svg-icons/faRss' +import {faTicket} from '@fortawesome/free-solid-svg-icons/faTicket' +import {faTrashCan} from '@fortawesome/free-regular-svg-icons/faTrashCan' import {faUser} from '@fortawesome/free-regular-svg-icons/faUser' import {faUsers} from '@fortawesome/free-solid-svg-icons/faUsers' import {faUserCheck} from '@fortawesome/free-solid-svg-icons/faUserCheck' @@ -75,8 +78,6 @@ import {faUserSlash} from '@fortawesome/free-solid-svg-icons/faUserSlash' import {faUserPlus} from '@fortawesome/free-solid-svg-icons/faUserPlus' import {faUserXmark} from '@fortawesome/free-solid-svg-icons/faUserXmark' import {faUsersSlash} from '@fortawesome/free-solid-svg-icons/faUsersSlash' -import {faTicket} from '@fortawesome/free-solid-svg-icons/faTicket' -import {faTrashCan} from '@fortawesome/free-regular-svg-icons/faTrashCan' import {faX} from '@fortawesome/free-solid-svg-icons/faX' import {faXmark} from '@fortawesome/free-solid-svg-icons/faXmark' import {faPlay} from '@fortawesome/free-solid-svg-icons/faPlay' @@ -148,6 +149,7 @@ export function setup() { faReply, faRetweet, faRss, + faSatelliteDish, faShare, faShareFromSquare, faShield, diff --git a/src/view/screens/PinnedFeeds.tsx b/src/view/screens/PinnedFeeds.tsx deleted file mode 100644 index a90012093..000000000 --- a/src/view/screens/PinnedFeeds.tsx +++ /dev/null @@ -1,179 +0,0 @@ -import React, {useCallback, useMemo} from 'react' -import { - RefreshControl, - StyleSheet, - View, - ActivityIndicator, - Pressable, - TouchableOpacity, -} from 'react-native' -import {useFocusEffect} from '@react-navigation/native' -import {NativeStackScreenProps} from '@react-navigation/native-stack' -import {useAnalytics} from 'lib/analytics' -import {usePalette} from 'lib/hooks/usePalette' -import {CommonNavigatorParams} from 'lib/routes/types' -import {observer} from 'mobx-react-lite' -import {useStores} from 'state/index' -import {withAuthRequired} from 'view/com/auth/withAuthRequired' -import {ViewHeader} from 'view/com/util/ViewHeader' -import {CenteredView} from 'view/com/util/Views' -import {Text} from 'view/com/util/text/Text' -import {isDesktopWeb, isWeb} from 'platform/detection' -import {s} from 'lib/styles' -import DraggableFlatList, { - ShadowDecorator, - ScaleDecorator, -} from 'react-native-draggable-flatlist' -import {SavedFeedItem} from 'view/com/feeds/SavedFeedItem' -import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' -import {CustomFeedModel} from 'state/models/feeds/custom-feed' - -type Props = NativeStackScreenProps - -export const PinnedFeeds = withAuthRequired( - observer(({}: Props) => { - // hooks for global items - const pal = usePalette('default') - const rootStore = useStores() - const {screen} = useAnalytics() - - // hooks for local - const savedFeeds = useMemo(() => rootStore.me.savedFeeds, [rootStore]) - useFocusEffect( - useCallback(() => { - screen('SavedFeeds') - rootStore.shell.setMinimalShellMode(false) - savedFeeds.refresh() - }, [screen, rootStore, savedFeeds]), - ) - const _ListEmptyComponent = () => { - return ( - - - You don't have any pinned feeds. To pin a feed, go back to the Saved - Feeds screen and click the pin icon! - - - ) - } - const _ListFooterComponent = () => { - return ( - - {savedFeeds.isLoading && } - - ) - } - - return ( - - - item.data.uri} - refreshing={savedFeeds.isRefreshing} - refreshControl={ - savedFeeds.refresh()} - tintColor={pal.colors.text} - titleColor={pal.colors.text} - /> - } - renderItem={({item, drag}) => } - initialNumToRender={10} - ListFooterComponent={_ListFooterComponent} - ListEmptyComponent={_ListEmptyComponent} - extraData={savedFeeds.isLoading} - onDragEnd={({data}) => savedFeeds.reorderPinnedFeeds(data)} - // @ts-ignore our .web version only -prf - desktopFixedHeight - /> - - ) - }), -) - -const ListItem = observer( - ({item, drag}: {item: CustomFeedModel; drag: () => void}) => { - const pal = usePalette('default') - const rootStore = useStores() - const savedFeeds = useMemo(() => rootStore.me.savedFeeds, [rootStore]) - const isPinned = savedFeeds.isPinned(item) - return ( - - - - {isPinned && isWeb ? ( - - { - savedFeeds.movePinnedItem(item, 'up') - }}> - - - { - savedFeeds.movePinnedItem(item, 'down') - }}> - - - - ) : isPinned ? ( - - ) : null} - - - - - ) - }, -) - -const styles = StyleSheet.create({ - footer: { - paddingVertical: 20, - }, - empty: { - paddingHorizontal: 20, - paddingVertical: 20, - borderRadius: 16, - marginHorizontal: 24, - marginTop: 10, - }, - itemContainer: { - flex: 1, - flexDirection: 'row', - alignItems: 'center', - borderTopWidth: 1, - }, - webArrowButtonsContainer: { - flexDirection: 'column', - justifyContent: 'space-around', - }, - webArrowUpButton: {marginBottom: 10}, -}) diff --git a/src/view/screens/SavedFeeds.tsx b/src/view/screens/SavedFeeds.tsx index c32639889..b1ea27af8 100644 --- a/src/view/screens/SavedFeeds.tsx +++ b/src/view/screens/SavedFeeds.tsx @@ -4,9 +4,8 @@ import { StyleSheet, View, ActivityIndicator, - FlatList, + Pressable, TouchableOpacity, - ScrollView, } from 'react-native' import {useFocusEffect} from '@react-navigation/native' import {NativeStackScreenProps} from '@react-navigation/native-stack' @@ -21,16 +20,18 @@ import {CenteredView} from 'view/com/util/Views' import {Text} from 'view/com/util/text/Text' import {isDesktopWeb, isWeb} from 'platform/detection' import {s} from 'lib/styles' -import {SavedFeedsModel} from 'state/models/ui/saved-feeds' -import {Link} from 'view/com/util/Link' -import {UserAvatar} from 'view/com/util/UserAvatar' +import DraggableFlatList, { + ShadowDecorator, + ScaleDecorator, +} from 'react-native-draggable-flatlist' import {SavedFeedItem} from 'view/com/feeds/SavedFeedItem' -import {AtUri} from '@atproto/api' +import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome' +import {CustomFeedModel} from 'state/models/feeds/custom-feed' type Props = NativeStackScreenProps export const SavedFeeds = withAuthRequired( - observer(({navigation}: Props) => { + observer(({}: Props) => { // hooks for global items const pal = usePalette('default') const rootStore = useStores() @@ -55,8 +56,8 @@ export const SavedFeeds = withAuthRequired( styles.empty, ]}> - You don't have any saved feeds. To save a feed, click the save - button when a custom feed or algorithm shows up. + You don't have any pinned feeds. To pin a feed, go back to the Saved + Feeds screen and click the pin icon! ) @@ -71,10 +72,10 @@ export const SavedFeeds = withAuthRequired( return ( - - + item.data.uri} refreshing={savedFeeds.isRefreshing} refreshControl={ @@ -85,19 +86,12 @@ export const SavedFeeds = withAuthRequired( titleColor={pal.colors.text} /> } - renderItem={({item}) => ( - - )} + renderItem={({item, drag}) => } initialNumToRender={10} - ListHeaderComponent={() => ( - - )} ListFooterComponent={_ListFooterComponent} ListEmptyComponent={_ListEmptyComponent} extraData={savedFeeds.isLoading} + onDragEnd={({data}) => savedFeeds.reorderPinnedFeeds(data)} // @ts-ignore our .web version only -prf desktopFixedHeight /> @@ -106,64 +100,56 @@ export const SavedFeeds = withAuthRequired( }), ) -const ListHeaderComponent = observer( - ({ - savedFeeds, - navigation, - }: { - savedFeeds: SavedFeedsModel - navigation: Props['navigation'] - }) => { +const ListItem = observer( + ({item, drag}: {item: CustomFeedModel; drag: () => void}) => { const pal = usePalette('default') + const rootStore = useStores() + const savedFeeds = useMemo(() => rootStore.me.savedFeeds, [rootStore]) + const isPinned = savedFeeds.isPinned(item) return ( - - {savedFeeds.pinned.length > 0 ? ( - - - - Pinned Feeds - - - Edit - - - - - {savedFeeds.pinned.map(item => { - return ( - { - navigation.navigate('ProfileCustomFeed', { - name: item.data.creator.did, - rkey: new AtUri(item.data.uri).rkey, - }) - }} - style={styles.pinnedItem}> - - - {item.data.displayName ?? - `${item.data.creator.displayName}'s feed`} - - - ) - })} - - - ) : null} - - All Saved Feeds - + + + + {isPinned && isWeb ? ( + + { + savedFeeds.movePinnedItem(item, 'up') + }}> + + + { + savedFeeds.movePinnedItem(item, 'down') + }}> + + + + ) : isPinned ? ( + + ) : null} + + + + ) }, ) @@ -179,15 +165,15 @@ const styles = StyleSheet.create({ marginHorizontal: 24, marginTop: 10, }, - headerContainer: {paddingHorizontal: 18, paddingTop: 18}, - pinnedContainer: {marginBottom: 18, gap: 18}, - pinnedHeader: {flexDirection: 'row', justifyContent: 'space-between'}, - pinnedItem: { + itemContainer: { flex: 1, + flexDirection: 'row', alignItems: 'center', - marginRight: 18, - maxWidth: 100, + borderTopWidth: 1, + }, + webArrowButtonsContainer: { + flexDirection: 'column', + justifyContent: 'space-around', }, - pinnedItemName: {marginTop: 8, textAlign: 'center'}, - editPinned: {textDecorationLine: 'underline'}, + webArrowUpButton: {marginBottom: 10}, }) diff --git a/src/view/screens/Settings.tsx b/src/view/screens/Settings.tsx index a919f11b0..3ce41f8c0 100644 --- a/src/view/screens/Settings.tsx +++ b/src/view/screens/Settings.tsx @@ -284,38 +284,37 @@ export const SettingsScreen = withAuthRequired( + + Advanced + + href="/settings/app-passwords"> - Custom Algorithms + App passwords - - - Advanced - + accessibilityHint="Saved Feeds" + accessibilityLabel="Opens screen with all saved feeds" + href="/settings/saved-feeds"> - App passwords + Saved Feeds Date: Wed, 17 May 2023 22:12:14 -0500 Subject: Add custom feed liked by screen --- src/Navigation.tsx | 9 +- src/lib/routes/types.ts | 3 +- src/routes.ts | 1 + src/view/com/feeds/CustomFeed.tsx | 2 +- src/view/screens/CustomFeed.tsx | 235 ++++++++++++++++++++++++++ src/view/screens/CustomFeedLikedBy.tsx | 29 ++++ src/view/screens/ProfileCustomFeed.tsx | 293 --------------------------------- 7 files changed, 275 insertions(+), 297 deletions(-) create mode 100644 src/view/screens/CustomFeed.tsx create mode 100644 src/view/screens/CustomFeedLikedBy.tsx delete mode 100644 src/view/screens/ProfileCustomFeed.tsx (limited to 'src/Navigation.tsx') diff --git a/src/Navigation.tsx b/src/Navigation.tsx index ea36b0f20..d84167d63 100644 --- a/src/Navigation.tsx +++ b/src/Navigation.tsx @@ -40,7 +40,8 @@ import {SettingsScreen} from './view/screens/Settings' import {ProfileScreen} from './view/screens/Profile' import {ProfileFollowersScreen} from './view/screens/ProfileFollowers' import {ProfileFollowsScreen} from './view/screens/ProfileFollows' -import {ProfileCustomFeed} from './view/screens/ProfileCustomFeed' +import {CustomFeedScreen} from './view/screens/CustomFeed' +import {CustomFeedLikedByScreen} from './view/screens/CustomFeedLikedBy' import {ProfileListScreen} from './view/screens/ProfileList' import {PostThreadScreen} from './view/screens/PostThread' import {PostLikedByScreen} from './view/screens/PostLikedBy' @@ -126,7 +127,6 @@ function commonScreens(Stack: typeof HomeTab, unreadCountLabel?: string) { title: title(`People followed by @${route.params.name}`), })} /> - ({title: title(`Post by @${route.params.name}`)})} /> + + { - navigation.navigate('ProfileCustomFeed', { + navigation.navigate('CustomFeed', { name: item.data.creator.did, rkey: new AtUri(item.data.uri).rkey, }) 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 +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(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 ( + + + + + )} + + + + + Feed + + + + + ) + }, [store.me.did, pal, currentFeed, onToggleLiked, onToggleSaved]) + + return ( + + + + + ) + }), +) + +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, + }, +}) diff --git a/src/view/screens/CustomFeedLikedBy.tsx b/src/view/screens/CustomFeedLikedBy.tsx new file mode 100644 index 000000000..49d0d0482 --- /dev/null +++ b/src/view/screens/CustomFeedLikedBy.tsx @@ -0,0 +1,29 @@ +import React from 'react' +import {View} from 'react-native' +import {useFocusEffect} from '@react-navigation/native' +import {NativeStackScreenProps, CommonNavigatorParams} from 'lib/routes/types' +import {withAuthRequired} from 'view/com/auth/withAuthRequired' +import {ViewHeader} from '../com/util/ViewHeader' +import {PostLikedBy as PostLikedByComponent} from '../com/post-thread/PostLikedBy' +import {useStores} from 'state/index' +import {makeRecordUri} from 'lib/strings/url-helpers' + +type Props = NativeStackScreenProps +export const CustomFeedLikedByScreen = withAuthRequired(({route}: Props) => { + const store = useStores() + const {name, rkey} = route.params + const uri = makeRecordUri(name, 'app.bsky.feed.generator', rkey) + + useFocusEffect( + React.useCallback(() => { + store.shell.setMinimalShellMode(false) + }, [store]), + ) + + return ( + + + + + ) +}) diff --git a/src/view/screens/ProfileCustomFeed.tsx b/src/view/screens/ProfileCustomFeed.tsx deleted file mode 100644 index 681798308..000000000 --- a/src/view/screens/ProfileCustomFeed.tsx +++ /dev/null @@ -1,293 +0,0 @@ -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 -export const ProfileCustomFeed = 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(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 ( - - - - - )} - - - - - Feed - - - - - ) - }, [store.me.did, pal, currentFeed, onToggleLiked, onToggleSaved]) - - return ( - - - - - ) - }), -) - -/* - - - - - - - - @{currentFeed?.data.creator.handle} - - - - {currentFeed?.data.description} - - - - - - */ - -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, - }, - - /* headerContainer: { - alignItems: 'center', - justifyContent: 'center', - gap: 8, - marginBottom: 12, - }, - header: { - alignItems: 'center', - gap: 4, - }, - avatarContainer: { - flexDirection: 'row', - alignItems: 'center', - 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, - gap: 4, - },*/ -}) -- cgit 1.4.1 From 5a20e0fafa2bdbfd57a69da42448a687c7d812c1 Mon Sep 17 00:00:00 2001 From: Paul Frazee Date: Wed, 17 May 2023 23:33:59 -0500 Subject: Add web titles --- src/Navigation.tsx | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) (limited to 'src/Navigation.tsx') diff --git a/src/Navigation.tsx b/src/Navigation.tsx index d84167d63..46189c5f0 100644 --- a/src/Navigation.tsx +++ b/src/Navigation.tsx @@ -147,10 +147,19 @@ function commonScreens(Stack: typeof HomeTab, unreadCountLabel?: string) { component={PostRepostedByScreen} options={({route}) => ({title: title(`Post by @${route.params.name}`)})} /> - + ({ + title: title(`Custom feed by @${route.params.name}`), + })} + /> ({ + title: title(`Custom feed by @${route.params.name}`), + })} /> - + ) } -- cgit 1.4.1 From d88c27a41995c181a38c01248fe01f853ba83366 Mon Sep 17 00:00:00 2001 From: Paul Frazee Date: Wed, 17 May 2023 23:36:36 -0500 Subject: Improve web titles --- src/Navigation.tsx | 8 ++------ src/view/screens/CustomFeed.tsx | 3 +++ 2 files changed, 5 insertions(+), 6 deletions(-) (limited to 'src/Navigation.tsx') diff --git a/src/Navigation.tsx b/src/Navigation.tsx index 46189c5f0..ff7a5f5c2 100644 --- a/src/Navigation.tsx +++ b/src/Navigation.tsx @@ -150,16 +150,12 @@ function commonScreens(Stack: typeof HomeTab, unreadCountLabel?: string) { ({ - title: title(`Custom feed by @${route.params.name}`), - })} + options={{title: title('Feed')}} /> ({ - title: title(`Custom feed by @${route.params.name}`), - })} + options={{title: title('Liked by')}} /> export const CustomFeedScreen = withAuthRequired( @@ -41,6 +42,8 @@ export const CustomFeedScreen = withAuthRequired( return feed }, [store, uri]) + useSetTitle(currentFeed?.displayName) + const onToggleSaved = React.useCallback(async () => { try { if (currentFeed?.isSaved) { -- cgit 1.4.1 From dfb39e7c4fcaff3effcc82b412191177fdfdaf22 Mon Sep 17 00:00:00 2001 From: Paul Frazee Date: Wed, 24 May 2023 22:09:39 -0500 Subject: Add feed discovery page --- bskyweb/cmd/bskyweb/server.go | 1 + package.json | 2 +- src/Navigation.tsx | 6 ++ src/lib/routes/types.ts | 1 + src/routes.ts | 1 + src/state/models/discovery/feeds.ts | 97 ++++++++++++++++++++++++++++++++ src/view/com/feeds/SavedFeeds.tsx | 41 +++++++++----- src/view/screens/CustomFeed.tsx | 6 +- src/view/screens/DiscoverFeeds.tsx | 109 ++++++++++++++++++++++++++++++++++++ yarn.lock | 8 +-- 10 files changed, 252 insertions(+), 20 deletions(-) create mode 100644 src/state/models/discovery/feeds.ts create mode 100644 src/view/screens/DiscoverFeeds.tsx (limited to 'src/Navigation.tsx') diff --git a/bskyweb/cmd/bskyweb/server.go b/bskyweb/cmd/bskyweb/server.go index 07df85146..462740f54 100644 --- a/bskyweb/cmd/bskyweb/server.go +++ b/bskyweb/cmd/bskyweb/server.go @@ -105,6 +105,7 @@ func serve(cctx *cli.Context) error { // generic routes e.GET("/search", server.WebGeneric) + e.GET("/search/feeds", server.WebGeneric) e.GET("/notifications", server.WebGeneric) e.GET("/moderation", server.WebGeneric) e.GET("/moderation/mute-lists", server.WebGeneric) diff --git a/package.json b/package.json index 5c30a0cae..253c3b782 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,7 @@ "e2e:run": "detox test --configuration ios.sim.debug --take-screenshots all" }, "dependencies": { - "@atproto/api": "0.3.7", + "@atproto/api": "0.3.8", "@bam.tech/react-native-image-resizer": "^3.0.4", "@braintree/sanitize-url": "^6.0.2", "@expo/webpack-config": "^18.0.1", diff --git a/src/Navigation.tsx b/src/Navigation.tsx index ff7a5f5c2..0664ac526 100644 --- a/src/Navigation.tsx +++ b/src/Navigation.tsx @@ -35,6 +35,7 @@ import {SearchScreen} from './view/screens/Search' import {NotificationsScreen} from './view/screens/Notifications' import {ModerationScreen} from './view/screens/Moderation' import {ModerationMuteListsScreen} from './view/screens/ModerationMuteLists' +import {DiscoverFeedsScreen} from 'view/screens/DiscoverFeeds' import {NotFoundScreen} from './view/screens/NotFound' import {SettingsScreen} from './view/screens/Settings' import {ProfileScreen} from './view/screens/Profile' @@ -103,6 +104,11 @@ function commonScreens(Stack: typeof HomeTab, unreadCountLabel?: string) { component={ModerationBlockedAccounts} options={{title: title('Blocked Accounts')}} /> + 0 + } + + get hasError() { + return this.error !== '' + } + + get isEmpty() { + return this.hasLoaded && !this.hasContent + } + + // public api + // = + + refresh = bundleAsync(async () => { + this._xLoading() + try { + const res = + await this.rootStore.agent.app.bsky.unspecced.getPopularFeedGenerators( + {}, + ) + this._replaceAll(res) + this._xIdle() + } catch (e: any) { + this._xIdle(e) + } + }) + + clear() { + this.isLoading = false + this.isRefreshing = false + this.hasLoaded = false + this.error = '' + this.feeds = [] + } + + // state transitions + // = + + _xLoading() { + this.isLoading = true + this.isRefreshing = true + this.error = '' + } + + _xIdle(err?: any) { + this.isLoading = false + this.isRefreshing = false + this.hasLoaded = true + this.error = cleanError(err) + if (err) { + this.rootStore.log.error('Failed to fetch popular feeds', err) + } + } + + // helper functions + // = + + _replaceAll(res: AppBskyUnspeccedGetPopularFeedGenerators.Response) { + this.feeds = [] + for (const f of res.data.feeds) { + this.feeds.push(new CustomFeedModel(this.rootStore, f)) + } + } +} diff --git a/src/view/com/feeds/SavedFeeds.tsx b/src/view/com/feeds/SavedFeeds.tsx index e92e741da..610562c9d 100644 --- a/src/view/com/feeds/SavedFeeds.tsx +++ b/src/view/com/feeds/SavedFeeds.tsx @@ -53,14 +53,28 @@ export const SavedFeeds = observer( const renderListFooterComponent = useCallback(() => { return ( <> - - - - Change Order - - + + + + + Discover new feeds + + + {!store.me.savedFeeds.isEmpty && ( + + + + Change Order + + + )} + ) - }, [pal]) + }, [pal, store.me.savedFeeds.isEmpty]) const renderItem = useCallback( ({item}) => , @@ -118,14 +132,16 @@ export const SavedFeeds = observer( ) const styles = StyleSheet.create({ + footerLinks: { + marginTop: 8, + borderBottomWidth: 1, + }, footerLink: { flexDirection: 'row', borderTopWidth: 1, - borderBottomWidth: 1, paddingHorizontal: 26, paddingVertical: 18, gap: 18, - marginTop: 8, }, empty: { paddingHorizontal: 18, @@ -134,7 +150,4 @@ const styles = StyleSheet.create({ marginHorizontal: 18, marginTop: 10, }, - feedItem: { - borderTopWidth: 1, - }, }) diff --git a/src/view/screens/CustomFeed.tsx b/src/view/screens/CustomFeed.tsx index 0690a17d8..49798d758 100644 --- a/src/view/screens/CustomFeed.tsx +++ b/src/view/screens/CustomFeed.tsx @@ -220,7 +220,7 @@ export const CustomFeedScreen = withAuthRequired( )} {isDesktopWeb && ( - +