Improve pinned feed management

zio/stable
Paul Frazee 2023-05-17 23:32:19 -05:00
parent bec94ed82c
commit 129fc42e95
3 changed files with 79 additions and 71 deletions

View File

@ -81,7 +81,7 @@ export class SavedFeedsModel {
togglePinnedFeed(feed: CustomFeedModel) { togglePinnedFeed(feed: CustomFeedModel) {
if (!this.isPinned(feed)) { if (!this.isPinned(feed)) {
this.pinned.push(feed) this.pinned = [...this.pinned, feed]
} else { } else {
this.removePinnedFeed(feed.data.uri) this.removePinnedFeed(feed.data.uri)
} }

View File

@ -1,16 +1,17 @@
import React from 'react' import React, {forwardRef} from 'react'
import {Animated, View} from 'react-native' import {Animated, View} from 'react-native'
import PagerView, {PagerViewOnPageSelectedEvent} from 'react-native-pager-view' import PagerView, {PagerViewOnPageSelectedEvent} from 'react-native-pager-view'
import {useAnimatedValue} from 'lib/hooks/useAnimatedValue'
import {s} from 'lib/styles' import {s} from 'lib/styles'
export type PageSelectedEvent = PagerViewOnPageSelectedEvent export type PageSelectedEvent = PagerViewOnPageSelectedEvent
const AnimatedPagerView = Animated.createAnimatedComponent(PagerView) const AnimatedPagerView = Animated.createAnimatedComponent(PagerView)
export interface PagerRef {
setPage: (index: number) => void
}
export interface RenderTabBarFnProps { export interface RenderTabBarFnProps {
selectedPage: number selectedPage: number
position: Animated.Value
offset: Animated.Value
onSelect?: (index: number) => void onSelect?: (index: number) => void
} }
export type RenderTabBarFn = (props: RenderTabBarFnProps) => JSX.Element export type RenderTabBarFn = (props: RenderTabBarFnProps) => JSX.Element
@ -22,68 +23,60 @@ interface Props {
onPageSelected?: (index: number) => void onPageSelected?: (index: number) => void
testID?: string testID?: string
} }
export const Pager = ({ export const Pager = forwardRef<PagerRef, React.PropsWithChildren<Props>>(
children, (
tabBarPosition = 'top', {
initialPage = 0, children,
renderTabBar, tabBarPosition = 'top',
onPageSelected, initialPage = 0,
testID, renderTabBar,
}: React.PropsWithChildren<Props>) => { onPageSelected,
const [selectedPage, setSelectedPage] = React.useState(0) testID,
const position = useAnimatedValue(0) }: React.PropsWithChildren<Props>,
const offset = useAnimatedValue(0) ref,
const pagerView = React.useRef<PagerView>() ) => {
const [selectedPage, setSelectedPage] = React.useState(0)
const pagerView = React.useRef<PagerView>()
const onPageSelectedInner = React.useCallback( React.useImperativeHandle(ref, () => ({
(e: PageSelectedEvent) => { setPage: (index: number) => pagerView.current?.setPage(index),
setSelectedPage(e.nativeEvent.position) }))
onPageSelected?.(e.nativeEvent.position)
},
[setSelectedPage, onPageSelected],
)
const onTabBarSelect = React.useCallback( const onPageSelectedInner = React.useCallback(
(index: number) => { (e: PageSelectedEvent) => {
pagerView.current?.setPage(index) setSelectedPage(e.nativeEvent.position)
}, onPageSelected?.(e.nativeEvent.position)
[pagerView], },
) [setSelectedPage, onPageSelected],
)
return ( const onTabBarSelect = React.useCallback(
<View testID={testID}> (index: number) => {
{tabBarPosition === 'top' && pagerView.current?.setPage(index)
renderTabBar({ },
selectedPage, [pagerView],
position, )
offset,
onSelect: onTabBarSelect, return (
})} <View testID={testID}>
<AnimatedPagerView {tabBarPosition === 'top' &&
ref={pagerView} renderTabBar({
style={s.h100pct} selectedPage,
initialPage={initialPage} onSelect: onTabBarSelect,
onPageSelected={onPageSelectedInner} })}
onPageScroll={Animated.event( <AnimatedPagerView
[ ref={pagerView}
{ style={s.h100pct}
nativeEvent: { initialPage={initialPage}
position: position, onPageSelected={onPageSelectedInner}>
offset: offset, {children}
}, </AnimatedPagerView>
}, {tabBarPosition === 'bottom' &&
], renderTabBar({
{useNativeDriver: true}, selectedPage,
)}> onSelect: onTabBarSelect,
{children} })}
</AnimatedPagerView> </View>
{tabBarPosition === 'bottom' && )
renderTabBar({ },
selectedPage, )
position,
offset,
onSelect: onTabBarSelect,
})}
</View>
)
}

View File

@ -1,6 +1,7 @@
import React from 'react' import React from 'react'
import {FlatList, View} from 'react-native' import {FlatList, View} from 'react-native'
import {useFocusEffect, useIsFocused} from '@react-navigation/native' import {useFocusEffect, useIsFocused} from '@react-navigation/native'
import {AppBskyFeedGetFeed as GetCustomFeed} from '@atproto/api'
import {observer} from 'mobx-react-lite' import {observer} from 'mobx-react-lite'
import useAppState from 'react-native-appstate-hook' import useAppState from 'react-native-appstate-hook'
import {NativeStackScreenProps, HomeTabNavigatorParams} from 'lib/routes/types' import {NativeStackScreenProps, HomeTabNavigatorParams} from 'lib/routes/types'
@ -12,7 +13,7 @@ import {FollowingEmptyState} from 'view/com/posts/FollowingEmptyState'
import {WhatsHotEmptyState} from 'view/com/posts/WhatsHotEmptyState' import {WhatsHotEmptyState} from 'view/com/posts/WhatsHotEmptyState'
import {LoadLatestBtn} from '../com/util/load-latest/LoadLatestBtn' import {LoadLatestBtn} from '../com/util/load-latest/LoadLatestBtn'
import {FeedsTabBar} from '../com/pager/FeedsTabBar' import {FeedsTabBar} from '../com/pager/FeedsTabBar'
import {Pager, RenderTabBarFnProps} from 'view/com/pager/Pager' import {Pager, PagerRef, RenderTabBarFnProps} from 'view/com/pager/Pager'
import {FAB} from '../com/util/fab/FAB' import {FAB} from '../com/util/fab/FAB'
import {SavedFeeds} from 'view/com/feeds/SavedFeeds' import {SavedFeeds} from 'view/com/feeds/SavedFeeds'
import {useStores} from 'state/index' import {useStores} from 'state/index'
@ -29,7 +30,9 @@ type Props = NativeStackScreenProps<HomeTabNavigatorParams, 'Home'>
export const HomeScreen = withAuthRequired( export const HomeScreen = withAuthRequired(
observer((_opts: Props) => { observer((_opts: Props) => {
const store = useStores() const store = useStores()
const pagerRef = React.useRef<PagerRef>(null)
const [selectedPage, setSelectedPage] = React.useState(0) const [selectedPage, setSelectedPage] = React.useState(0)
const [customFeeds, setCustomFeeds] = React.useState<PostsFeedModel[]>([])
const [initialLanguages] = React.useState( const [initialLanguages] = React.useState(
store.preferences.contentLanguages, store.preferences.contentLanguages,
) )
@ -40,6 +43,17 @@ export const HomeScreen = withAuthRequired(
return feed return feed
}, [store]) }, [store])
React.useEffect(() => {
const feeds = []
for (const feed of store.me.savedFeeds.pinned) {
const model = new PostsFeedModel(store, 'custom', {feed: feed.uri})
model.setup()
feeds.push(model)
}
pagerRef.current?.setPage(0)
setCustomFeeds(feeds)
}, [store, store.me.savedFeeds.pinned, setCustomFeeds])
React.useEffect(() => { React.useEffect(() => {
// refresh whats hot when lang preferences change // refresh whats hot when lang preferences change
if (initialLanguages !== store.preferences.contentLanguages) { if (initialLanguages !== store.preferences.contentLanguages) {
@ -94,6 +108,7 @@ export const HomeScreen = withAuthRequired(
const initialPage = store.me.followsCount === 0 ? 1 : 0 const initialPage = store.me.followsCount === 0 ? 1 : 0
return ( return (
<Pager <Pager
ref={pagerRef}
testID="homeScreen" testID="homeScreen"
onPageSelected={onPageSelected} onPageSelected={onPageSelected}
renderTabBar={renderTabBar} renderTabBar={renderTabBar}
@ -113,13 +128,13 @@ export const HomeScreen = withAuthRequired(
feed={algoFeed} feed={algoFeed}
renderEmptyState={renderWhatsHotEmptyState} renderEmptyState={renderWhatsHotEmptyState}
/> />
{store.me.savedFeeds.pinned.map((f, index) => { {customFeeds.map((f, index) => {
return ( return (
<FeedPage <FeedPage
key={String(3 + index)} key={(f.params as GetCustomFeed.QueryParams).feed}
testID="customFeedPage" testID="customFeedPage"
isPageFocused={selectedPage === 2 + index} isPageFocused={selectedPage === 2 + index}
feed={new PostsFeedModel(store, 'custom', {feed: f.uri})} feed={f}
renderEmptyState={renderFollowingEmptyState} renderEmptyState={renderFollowingEmptyState}
/> />
) )