Improve pinned feed management
parent
bec94ed82c
commit
129fc42e95
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,19 +23,25 @@ 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,
|
children,
|
||||||
tabBarPosition = 'top',
|
tabBarPosition = 'top',
|
||||||
initialPage = 0,
|
initialPage = 0,
|
||||||
renderTabBar,
|
renderTabBar,
|
||||||
onPageSelected,
|
onPageSelected,
|
||||||
testID,
|
testID,
|
||||||
}: React.PropsWithChildren<Props>) => {
|
}: React.PropsWithChildren<Props>,
|
||||||
|
ref,
|
||||||
|
) => {
|
||||||
const [selectedPage, setSelectedPage] = React.useState(0)
|
const [selectedPage, setSelectedPage] = React.useState(0)
|
||||||
const position = useAnimatedValue(0)
|
|
||||||
const offset = useAnimatedValue(0)
|
|
||||||
const pagerView = React.useRef<PagerView>()
|
const pagerView = React.useRef<PagerView>()
|
||||||
|
|
||||||
|
React.useImperativeHandle(ref, () => ({
|
||||||
|
setPage: (index: number) => pagerView.current?.setPage(index),
|
||||||
|
}))
|
||||||
|
|
||||||
const onPageSelectedInner = React.useCallback(
|
const onPageSelectedInner = React.useCallback(
|
||||||
(e: PageSelectedEvent) => {
|
(e: PageSelectedEvent) => {
|
||||||
setSelectedPage(e.nativeEvent.position)
|
setSelectedPage(e.nativeEvent.position)
|
||||||
|
@ -55,35 +62,21 @@ export const Pager = ({
|
||||||
{tabBarPosition === 'top' &&
|
{tabBarPosition === 'top' &&
|
||||||
renderTabBar({
|
renderTabBar({
|
||||||
selectedPage,
|
selectedPage,
|
||||||
position,
|
|
||||||
offset,
|
|
||||||
onSelect: onTabBarSelect,
|
onSelect: onTabBarSelect,
|
||||||
})}
|
})}
|
||||||
<AnimatedPagerView
|
<AnimatedPagerView
|
||||||
ref={pagerView}
|
ref={pagerView}
|
||||||
style={s.h100pct}
|
style={s.h100pct}
|
||||||
initialPage={initialPage}
|
initialPage={initialPage}
|
||||||
onPageSelected={onPageSelectedInner}
|
onPageSelected={onPageSelectedInner}>
|
||||||
onPageScroll={Animated.event(
|
|
||||||
[
|
|
||||||
{
|
|
||||||
nativeEvent: {
|
|
||||||
position: position,
|
|
||||||
offset: offset,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
{useNativeDriver: true},
|
|
||||||
)}>
|
|
||||||
{children}
|
{children}
|
||||||
</AnimatedPagerView>
|
</AnimatedPagerView>
|
||||||
{tabBarPosition === 'bottom' &&
|
{tabBarPosition === 'bottom' &&
|
||||||
renderTabBar({
|
renderTabBar({
|
||||||
selectedPage,
|
selectedPage,
|
||||||
position,
|
|
||||||
offset,
|
|
||||||
onSelect: onTabBarSelect,
|
onSelect: onTabBarSelect,
|
||||||
})}
|
})}
|
||||||
</View>
|
</View>
|
||||||
)
|
)
|
||||||
}
|
},
|
||||||
|
)
|
||||||
|
|
|
@ -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}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
|
|
Loading…
Reference in New Issue