custom feed screen
parent
61ea37ff81
commit
5010861160
|
@ -53,6 +53,7 @@ import {MutedAccounts} from 'view/screens/MutedAccounts'
|
||||||
import {BlockedAccounts} from 'view/screens/BlockedAccounts'
|
import {BlockedAccounts} from 'view/screens/BlockedAccounts'
|
||||||
import {getRoutingInstrumentation} from 'lib/sentry'
|
import {getRoutingInstrumentation} from 'lib/sentry'
|
||||||
import {SavedFeeds} from './view/screens/SavedFeeds'
|
import {SavedFeeds} from './view/screens/SavedFeeds'
|
||||||
|
import {CustomFeed} from './view/screens/CustomFeed'
|
||||||
|
|
||||||
const navigationRef = createNavigationContainerRef<AllNavigatorParams>()
|
const navigationRef = createNavigationContainerRef<AllNavigatorParams>()
|
||||||
|
|
||||||
|
@ -93,6 +94,7 @@ function commonScreens(Stack: typeof HomeTab) {
|
||||||
<Stack.Screen name="CopyrightPolicy" component={CopyrightPolicyScreen} />
|
<Stack.Screen name="CopyrightPolicy" component={CopyrightPolicyScreen} />
|
||||||
<Stack.Screen name="AppPasswords" component={AppPasswords} />
|
<Stack.Screen name="AppPasswords" component={AppPasswords} />
|
||||||
<Stack.Screen name="SavedFeeds" component={SavedFeeds} />
|
<Stack.Screen name="SavedFeeds" component={SavedFeeds} />
|
||||||
|
<Stack.Screen name="CustomFeed" component={CustomFeed} />
|
||||||
<Stack.Screen name="MutedAccounts" component={MutedAccounts} />
|
<Stack.Screen name="MutedAccounts" component={MutedAccounts} />
|
||||||
<Stack.Screen name="BlockedAccounts" component={BlockedAccounts} />
|
<Stack.Screen name="BlockedAccounts" component={BlockedAccounts} />
|
||||||
</>
|
</>
|
||||||
|
|
|
@ -21,6 +21,7 @@ export type CommonNavigatorParams = {
|
||||||
CopyrightPolicy: undefined
|
CopyrightPolicy: undefined
|
||||||
AppPasswords: undefined
|
AppPasswords: undefined
|
||||||
SavedFeeds: undefined
|
SavedFeeds: undefined
|
||||||
|
CustomFeed: {name: string; rkey: string}
|
||||||
MutedAccounts: undefined
|
MutedAccounts: undefined
|
||||||
BlockedAccounts: undefined
|
BlockedAccounts: undefined
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,7 @@ export const router = new Router({
|
||||||
Log: '/sys/log',
|
Log: '/sys/log',
|
||||||
AppPasswords: '/settings/app-passwords',
|
AppPasswords: '/settings/app-passwords',
|
||||||
SavedFeeds: '/settings/saved-feeds',
|
SavedFeeds: '/settings/saved-feeds',
|
||||||
|
CustomFeed: '/profile/:name/feed/:rkey',
|
||||||
MutedAccounts: '/settings/muted-accounts',
|
MutedAccounts: '/settings/muted-accounts',
|
||||||
BlockedAccounts: '/settings/blocked-accounts',
|
BlockedAccounts: '/settings/blocked-accounts',
|
||||||
Support: '/support',
|
Support: '/support',
|
||||||
|
|
|
@ -29,6 +29,10 @@ export class AlgoItemModel {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get getUri() {
|
||||||
|
return this.data.uri
|
||||||
|
}
|
||||||
|
|
||||||
// public apis
|
// public apis
|
||||||
// =
|
// =
|
||||||
async save() {
|
async save() {
|
||||||
|
@ -52,4 +56,20 @@ export class AlgoItemModel {
|
||||||
this.rootStore.log.error('Failed to unsanve feed', e)
|
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
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ import {
|
||||||
AppBskyFeedDefs,
|
AppBskyFeedDefs,
|
||||||
AppBskyFeedPost,
|
AppBskyFeedPost,
|
||||||
AppBskyFeedGetAuthorFeed as GetAuthorFeed,
|
AppBskyFeedGetAuthorFeed as GetAuthorFeed,
|
||||||
|
AppBskyFeedGetFeed as GetCustomFeed,
|
||||||
RichText,
|
RichText,
|
||||||
jsonToLex,
|
jsonToLex,
|
||||||
} from '@atproto/api'
|
} from '@atproto/api'
|
||||||
|
@ -305,8 +306,11 @@ export class PostsFeedModel {
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
public rootStore: RootStoreModel,
|
public rootStore: RootStoreModel,
|
||||||
public feedType: 'home' | 'author' | 'suggested' | 'goodstuff',
|
public feedType: 'home' | 'author' | 'suggested' | 'goodstuff' | 'custom',
|
||||||
params: GetTimeline.QueryParams | GetAuthorFeed.QueryParams,
|
params:
|
||||||
|
| GetTimeline.QueryParams
|
||||||
|
| GetAuthorFeed.QueryParams
|
||||||
|
| GetCustomFeed.QueryParams,
|
||||||
) {
|
) {
|
||||||
makeAutoObservable(
|
makeAutoObservable(
|
||||||
this,
|
this,
|
||||||
|
@ -595,13 +599,15 @@ export class PostsFeedModel {
|
||||||
// helper functions
|
// 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
|
this.pollCursor = res.data.feed[0]?.post.uri
|
||||||
return this._appendAll(res, true)
|
return this._appendAll(res, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
async _appendAll(
|
async _appendAll(
|
||||||
res: GetTimeline.Response | GetAuthorFeed.Response,
|
res: GetTimeline.Response | GetAuthorFeed.Response | GetCustomFeed.Response,
|
||||||
replace = false,
|
replace = false,
|
||||||
) {
|
) {
|
||||||
this.loadMoreCursor = res.data.cursor
|
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) {
|
for (const item of res.data.feed) {
|
||||||
const existingSlice = this.slices.find(slice =>
|
const existingSlice = this.slices.find(slice =>
|
||||||
slice.containsUri(item.post.uri),
|
slice.containsUri(item.post.uri),
|
||||||
|
@ -657,8 +665,13 @@ export class PostsFeedModel {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected async _getFeed(
|
protected async _getFeed(
|
||||||
params: GetTimeline.QueryParams | GetAuthorFeed.QueryParams = {},
|
params:
|
||||||
): Promise<GetTimeline.Response | GetAuthorFeed.Response> {
|
| GetTimeline.QueryParams
|
||||||
|
| GetAuthorFeed.QueryParams
|
||||||
|
| GetCustomFeed.QueryParams,
|
||||||
|
): Promise<
|
||||||
|
GetTimeline.Response | GetAuthorFeed.Response | GetCustomFeed.Response
|
||||||
|
> {
|
||||||
params = Object.assign({}, this.params, params)
|
params = Object.assign({}, this.params, params)
|
||||||
if (this.feedType === 'suggested') {
|
if (this.feedType === 'suggested') {
|
||||||
const responses = await getMultipleAuthorsPosts(
|
const responses = await getMultipleAuthorsPosts(
|
||||||
|
@ -680,6 +693,10 @@ export class PostsFeedModel {
|
||||||
}
|
}
|
||||||
} else if (this.feedType === 'home') {
|
} else if (this.feedType === 'home') {
|
||||||
return this.rootStore.agent.getTimeline(params as GetTimeline.QueryParams)
|
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') {
|
} else if (this.feedType === 'goodstuff') {
|
||||||
const res = await getGoodStuff(
|
const res = await getGoodStuff(
|
||||||
this.rootStore.session.currentSession?.accessJwt || '',
|
this.rootStore.session.currentSession?.accessJwt || '',
|
||||||
|
|
|
@ -1,5 +1,11 @@
|
||||||
import React from 'react'
|
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 {Text} from '../util/text/Text'
|
||||||
import {usePalette} from 'lib/hooks/usePalette'
|
import {usePalette} from 'lib/hooks/usePalette'
|
||||||
import {s} from 'lib/styles'
|
import {s} from 'lib/styles'
|
||||||
|
@ -7,13 +13,25 @@ import {UserAvatar} from '../util/UserAvatar'
|
||||||
import {Button} from '../util/forms/Button'
|
import {Button} from '../util/forms/Button'
|
||||||
import {observer} from 'mobx-react-lite'
|
import {observer} from 'mobx-react-lite'
|
||||||
import {AlgoItemModel} from 'state/models/feeds/algo/algo-item'
|
import {AlgoItemModel} from 'state/models/feeds/algo/algo-item'
|
||||||
|
import {useNavigation} from '@react-navigation/native'
|
||||||
|
import {NavigationProp} from 'lib/routes/types'
|
||||||
|
|
||||||
const AlgoItem = observer(
|
const AlgoItem = observer(
|
||||||
({item, style}: {item: AlgoItemModel; style?: StyleProp<ViewStyle>}) => {
|
({item, style}: {item: AlgoItemModel; style?: StyleProp<ViewStyle>}) => {
|
||||||
const pal = usePalette('default')
|
const pal = usePalette('default')
|
||||||
|
const navigation = useNavigation<NavigationProp>()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<View style={[styles.container, style]} key={item.data.uri}>
|
<TouchableOpacity
|
||||||
|
accessibilityRole="button"
|
||||||
|
style={[styles.container, style]}
|
||||||
|
onPress={() => {
|
||||||
|
navigation.navigate('CustomFeed', {
|
||||||
|
name: item.data.creator.did,
|
||||||
|
rkey: item.data.uri,
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
key={item.data.uri}>
|
||||||
<View style={[styles.headerContainer]}>
|
<View style={[styles.headerContainer]}>
|
||||||
<View style={[s.mr10]}>
|
<View style={[s.mr10]}>
|
||||||
<UserAvatar size={36} avatar={item.data.avatar} />
|
<UserAvatar size={36} avatar={item.data.avatar} />
|
||||||
|
@ -54,7 +72,7 @@ const AlgoItem = observer(
|
||||||
/>
|
/>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</TouchableOpacity>
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
|
@ -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<CommonNavigatorParams, 'CustomFeed'>
|
||||||
|
export const CustomFeed = withAuthRequired(
|
||||||
|
observer(({route}: Props) => {
|
||||||
|
const rootStore = useStores()
|
||||||
|
const scrollElRef = useRef<FlatList>(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 (
|
||||||
|
<View style={[styles.container]}>
|
||||||
|
<ViewHeader title={'Custom Feed'} showOnDesktop />
|
||||||
|
|
||||||
|
<Feed
|
||||||
|
scrollElRef={scrollElRef}
|
||||||
|
testID={'test-feed'}
|
||||||
|
key="default"
|
||||||
|
feed={algoFeed}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
flex: 1,
|
||||||
|
height: '100%',
|
||||||
|
},
|
||||||
|
})
|
Loading…
Reference in New Issue