diff --git a/src/Navigation.tsx b/src/Navigation.tsx index 26dc9f7a..d4c992eb 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 5a4126f6..29fadd70 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 b510f66f..2cdaa28b 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 88e9c066..3dee5e2b 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 44cec3af..7adc1cb1 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 e475624c..51de89bd 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 00000000..1d4343b2 --- /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%', + }, +})