diff --git a/src/state/models/feed-view.ts b/src/state/models/feed-view.ts
index 42b753b2..81760132 100644
--- a/src/state/models/feed-view.ts
+++ b/src/state/models/feed-view.ts
@@ -257,7 +257,7 @@ export class FeedModel {
constructor(
public rootStore: RootStoreModel,
- public feedType: 'home' | 'author' | 'suggested',
+ public feedType: 'home' | 'author' | 'suggested' | 'goodstuff',
params: GetTimeline.QueryParams | GetAuthorFeed.QueryParams,
) {
makeAutoObservable(
@@ -634,6 +634,16 @@ export class FeedModel {
return this.rootStore.api.app.bsky.feed.getTimeline(
params as GetTimeline.QueryParams,
)
+ } else if (this.feedType === 'goodstuff') {
+ const res = await this.rootStore.api.app.bsky.feed.getAuthorFeed({
+ ...params,
+ author: 'jay.bsky.social',
+ } as GetAuthorFeed.QueryParams)
+ res.data.feed = mergePosts([res], {repostsOnly: true})
+ res.data.feed.forEach(item => {
+ delete item.reason
+ })
+ return res
} else {
return this.rootStore.api.app.bsky.feed.getAuthorFeed(
params as GetAuthorFeed.QueryParams,
diff --git a/src/view/com/profile/FollowButton.tsx b/src/view/com/profile/FollowButton.tsx
index f24c3d0c..7a194cee 100644
--- a/src/view/com/profile/FollowButton.tsx
+++ b/src/view/com/profile/FollowButton.tsx
@@ -42,7 +42,7 @@ const FollowButton = observer(
return (
diff --git a/src/view/com/util/Pager.tsx b/src/view/com/util/Pager.tsx
index 1a3ff642..9ce5006c 100644
--- a/src/view/com/util/Pager.tsx
+++ b/src/view/com/util/Pager.tsx
@@ -2,16 +2,25 @@ import React from 'react'
import {Animated, StyleSheet, View} from 'react-native'
import PagerView, {PagerViewOnPageSelectedEvent} from 'react-native-pager-view'
import {useAnimatedValue} from 'lib/hooks/useAnimatedValue'
-import {TabBar} from './TabBar'
+import {s} from 'lib/styles'
export type PageSelectedEvent = PagerViewOnPageSelectedEvent
const AnimatedPagerView = Animated.createAnimatedComponent(PagerView)
+export interface TabBarProps {
+ selectedPage: number
+ position: Animated.Value
+ offset: Animated.Value
+ onSelect?: (index: number) => void
+}
+
interface Props {
+ renderTabBar: (props: TabBarProps) => JSX.Element
onPageSelected?: (e: PageSelectedEvent) => void
}
export const Pager = ({
children,
+ renderTabBar,
onPageSelected,
}: React.PropsWithChildren) => {
const [selectedPage, setSelectedPage] = React.useState(0)
@@ -36,16 +45,10 @@ export const Pager = ({
return (
-
+ {renderTabBar({selectedPage, position, offset, onSelect: onTabBarSelect})}
)
}
-
-const styles = StyleSheet.create({
- tabBar: {
- flexDirection: 'row',
- },
-})
diff --git a/src/view/com/util/PostMeta.tsx b/src/view/com/util/PostMeta.tsx
index 0bb40210..3f9e6935 100644
--- a/src/view/com/util/PostMeta.tsx
+++ b/src/view/com/util/PostMeta.tsx
@@ -44,7 +44,7 @@ export const PostMeta = observer(function (opts: PostMetaOpts) {
// two-liner with follow button
return (
-
+
@@ -134,8 +135,13 @@ const styles = StyleSheet.create({
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
+ width: '100%',
paddingBottom: 2,
},
+ metaTwoLineLeft: {
+ flex: 1,
+ paddingRight: 40,
+ },
metaTwoLineTop: {
flexDirection: 'row',
alignItems: 'baseline',
diff --git a/src/view/com/util/TabBar.tsx b/src/view/com/util/TabBar.tsx
index 3a823e42..d9f48577 100644
--- a/src/view/com/util/TabBar.tsx
+++ b/src/view/com/util/TabBar.tsx
@@ -37,7 +37,7 @@ export function TabBar({
const panX = Animated.add(position, offset)
const underlineStyle = {
- backgroundColor: pal.colors.text,
+ backgroundColor: pal.colors.link,
left: panX.interpolate({
inputRange: items.map((_item, i) => i),
outputRange: itemLayouts.map(l => l.x),
@@ -79,11 +79,8 @@ export function TabBar({
onPressItem(i)}>
+ type="xl-medium"
+ style={selected ? pal.text : pal.textLight}>
{item}
@@ -100,20 +97,14 @@ const styles = StyleSheet.create({
paddingHorizontal: 14,
},
item: {
- paddingTop: 8,
- paddingBottom: 12,
- marginRight: 14,
- paddingHorizontal: 10,
- },
- label: {
- fontWeight: '600',
- },
- labelSelected: {
- fontWeight: '600',
+ paddingTop: 6,
+ paddingBottom: 14,
+ marginRight: 24,
},
underline: {
position: 'absolute',
- height: 4,
+ height: 3,
bottom: 0,
+ borderRadius: 4,
},
})
diff --git a/src/view/screens/Home.tsx b/src/view/screens/Home.tsx
index 6c708e2f..fa200e93 100644
--- a/src/view/screens/Home.tsx
+++ b/src/view/screens/Home.tsx
@@ -1,15 +1,23 @@
import React from 'react'
-import {FlatList, StyleSheet, View, useWindowDimensions} from 'react-native'
+import {
+ FlatList,
+ StyleSheet,
+ TouchableOpacity,
+ View,
+ useWindowDimensions,
+} from 'react-native'
import {useFocusEffect, useIsFocused} from '@react-navigation/native'
import {observer} from 'mobx-react-lite'
import useAppState from 'react-native-appstate-hook'
import {NativeStackScreenProps, HomeTabNavigatorParams} from 'lib/routes/types'
+import {FeedModel} from 'state/models/feed-view'
import {withAuthRequired} from 'view/com/auth/withAuthRequired'
-import {ViewHeader} from '../com/util/ViewHeader'
import {Feed} from '../com/posts/Feed'
import {LoadLatestBtn} from '../com/util/LoadLatestBtn'
import {WelcomeBanner} from '../com/util/WelcomeBanner'
import {UserAvatar} from 'view/com/util/UserAvatar'
+import {TabBar} from 'view/com/util/TabBar'
+import {Pager, PageSelectedEvent, TabBarProps} from 'view/com/util/Pager'
import {FAB} from '../com/util/FAB'
import {useStores} from 'state/index'
import {usePalette} from 'lib/hooks/usePalette'
@@ -18,157 +26,255 @@ import {useOnMainScroll} from 'lib/hooks/useOnMainScroll'
import {useAnalytics} from 'lib/analytics'
import {ComposeIcon2} from 'lib/icons'
-import {Pager, PageSelectedEvent} from 'view/com/util/Pager'
-import {Text} from 'view/com/util/text/Text'
-
-const HEADER_HEIGHT = 42
+const TAB_BAR_HEIGHT = 82
type Props = NativeStackScreenProps
export const HomeScreen = withAuthRequired((_opts: Props) => {
const store = useStores()
+ const pal = usePalette('default')
+ const [selectedPage, setSelectedPage] = React.useState(0)
+
+ useFocusEffect(
+ React.useCallback(() => {
+ store.shell.setIsDrawerSwipeDisabled(selectedPage > 0)
+ return () => {
+ store.shell.setIsDrawerSwipeDisabled(false)
+ }
+ }, [store, selectedPage]),
+ )
+
const onPageSelected = React.useCallback(
(e: PageSelectedEvent) => {
+ setSelectedPage(e.nativeEvent.position)
store.shell.setIsDrawerSwipeDisabled(e.nativeEvent.position > 0)
},
[store],
)
- useFocusEffect(
- React.useCallback(() => {
- return () => {
- store.shell.setIsDrawerSwipeDisabled(false)
- }
- }, [store]),
+ const onPressAvi = React.useCallback(() => {
+ store.shell.openDrawer()
+ }, [store])
+
+ const renderTabBar = React.useCallback(
+ (props: TabBarProps) => {
+ return (
+
+
+
+
+
+
+ )
+ },
+ [store.me.avatar, pal, onPressAvi],
)
return (
-
-
- First page
-
+
+
- Second page
-
-
- Third page
+
)
})
-function MyPage({children}) {
+
+const AlgoView = observer(() => {
+ const store = useStores()
+ const onMainScroll = useOnMainScroll(store)
+ const {screen, track} = useAnalytics()
+ const scrollElRef = React.useRef(null)
+ const {appState} = useAppState({
+ onForeground: () => doPoll(true),
+ })
+ const isFocused = useIsFocused()
+ const winDim = useWindowDimensions()
+ const containerStyle = React.useMemo(
+ () => ({height: winDim.height - TAB_BAR_HEIGHT}),
+ [winDim],
+ )
+ const algoFeed = React.useMemo(() => {
+ const feed = new FeedModel(store, 'goodstuff', {})
+ feed.setup()
+ return feed
+ }, [store])
+
+ const doPoll = React.useCallback(
+ (knownActive = false) => {
+ if ((!knownActive && appState !== 'active') || !isFocused) {
+ return
+ }
+ if (algoFeed.isLoading) {
+ return
+ }
+ store.log.debug('HomeScreen: Polling for new posts')
+ algoFeed.checkForLatest()
+ },
+ [appState, isFocused, store, algoFeed],
+ )
+
+ const scrollToTop = React.useCallback(() => {
+ scrollElRef.current?.scrollToOffset({offset: 0})
+ }, [scrollElRef])
+
+ useFocusEffect(
+ React.useCallback(() => {
+ const softResetSub = store.onScreenSoftReset(scrollToTop)
+ const feedCleanup = algoFeed.registerListeners()
+ const pollInterval = setInterval(doPoll, 15e3)
+
+ screen('Feed')
+ store.log.debug('HomeScreen: Updating feed')
+ if (algoFeed.hasContent) {
+ algoFeed.update()
+ }
+
+ return () => {
+ clearInterval(pollInterval)
+ softResetSub.remove()
+ feedCleanup()
+ }
+ }, [store, doPoll, scrollToTop, screen, algoFeed]),
+ )
+
+ const onPressCompose = React.useCallback(() => {
+ track('HomeScreen:PressCompose')
+ store.shell.openComposer({})
+ }, [store, track])
+
+ const onPressTryAgain = React.useCallback(() => {
+ algoFeed.refresh()
+ }, [algoFeed])
+
+ const onPressLoadLatest = React.useCallback(() => {
+ algoFeed.refresh()
+ scrollToTop()
+ }, [algoFeed, scrollToTop])
+
return (
-
- {children}
+
+ {store.shell.isOnboarding && }
+
+ {algoFeed.hasNewLatest && !algoFeed.isRefreshing && (
+
+ )}
+ }
+ />
)
-}
+})
+
+const FollowingView = observer(() => {
+ const store = useStores()
+ const onMainScroll = useOnMainScroll(store)
+ const {screen, track} = useAnalytics()
+ const scrollElRef = React.useRef(null)
+ const {appState} = useAppState({
+ onForeground: () => doPoll(true),
+ })
+ const isFocused = useIsFocused()
+ const winDim = useWindowDimensions()
+ const containerStyle = React.useMemo(
+ () => ({height: winDim.height - TAB_BAR_HEIGHT}),
+ [winDim],
+ )
+
+ const doPoll = React.useCallback(
+ (knownActive = false) => {
+ if ((!knownActive && appState !== 'active') || !isFocused) {
+ return
+ }
+ if (store.me.mainFeed.isLoading) {
+ return
+ }
+ store.log.debug('HomeScreen: Polling for new posts')
+ store.me.mainFeed.checkForLatest()
+ },
+ [appState, isFocused, store],
+ )
+
+ const scrollToTop = React.useCallback(() => {
+ scrollElRef.current?.scrollToOffset({offset: 0})
+ }, [scrollElRef])
+
+ useFocusEffect(
+ React.useCallback(() => {
+ const softResetSub = store.onScreenSoftReset(scrollToTop)
+ const feedCleanup = store.me.mainFeed.registerListeners()
+ const pollInterval = setInterval(doPoll, 15e3)
+
+ screen('Feed')
+ store.log.debug('HomeScreen: Updating feed')
+ if (store.me.mainFeed.hasContent) {
+ store.me.mainFeed.update()
+ }
+
+ return () => {
+ clearInterval(pollInterval)
+ softResetSub.remove()
+ feedCleanup()
+ }
+ }, [store, doPoll, scrollToTop, screen]),
+ )
+
+ const onPressCompose = React.useCallback(() => {
+ track('HomeScreen:PressCompose')
+ store.shell.openComposer({})
+ }, [store, track])
+
+ const onPressTryAgain = React.useCallback(() => {
+ store.me.mainFeed.refresh()
+ }, [store])
+
+ const onPressLoadLatest = React.useCallback(() => {
+ store.me.mainFeed.refresh()
+ scrollToTop()
+ }, [store, scrollToTop])
+
+ return (
+
+ {store.shell.isOnboarding && }
+
+ {store.me.mainFeed.hasNewLatest && !store.me.mainFeed.isRefreshing && (
+
+ )}
+ }
+ />
+
+ )
+})
const styles = StyleSheet.create({
tabBar: {
flexDirection: 'row',
+ alignItems: 'center',
+ paddingHorizontal: 18,
+ },
+ tabBarAvi: {
+ marginRight: 16,
},
})
-/*
-type Props = NativeStackScreenProps
-export const HomeScreen = withAuthRequired(
- observer(function Home(_opts: Props) {
- const store = useStores()
- const onMainScroll = useOnMainScroll(store)
- const {screen, track} = useAnalytics()
- const scrollElRef = React.useRef(null)
- const {appState} = useAppState({
- onForeground: () => doPoll(true),
- })
- const isFocused = useIsFocused()
-
- const doPoll = React.useCallback(
- (knownActive = false) => {
- if ((!knownActive && appState !== 'active') || !isFocused) {
- return
- }
- if (store.me.mainFeed.isLoading) {
- return
- }
- store.log.debug('HomeScreen: Polling for new posts')
- store.me.mainFeed.checkForLatest()
- },
- [appState, isFocused, store],
- )
-
- const scrollToTop = React.useCallback(() => {
- // NOTE: the feed is offset by the height of the collapsing header,
- // so we scroll to the negative of that height -prf
- scrollElRef.current?.scrollToOffset({offset: -HEADER_HEIGHT})
- }, [scrollElRef])
-
- useFocusEffect(
- React.useCallback(() => {
- const softResetSub = store.onScreenSoftReset(scrollToTop)
- const feedCleanup = store.me.mainFeed.registerListeners()
- const pollInterval = setInterval(doPoll, 15e3)
-
- screen('Feed')
- store.log.debug('HomeScreen: Updating feed')
- if (store.me.mainFeed.hasContent) {
- store.me.mainFeed.update()
- }
-
- return () => {
- clearInterval(pollInterval)
- softResetSub.remove()
- feedCleanup()
- }
- }, [store, doPoll, scrollToTop, screen]),
- )
-
- const onPressCompose = React.useCallback(() => {
- track('HomeScreen:PressCompose')
- store.shell.openComposer({})
- }, [store, track])
-
- const onPressTryAgain = React.useCallback(() => {
- store.me.mainFeed.refresh()
- }, [store])
-
- const onPressLoadLatest = React.useCallback(() => {
- store.me.mainFeed.refresh()
- scrollToTop()
- }, [store, scrollToTop])
-
- return (
-
- {store.shell.isOnboarding && }
-
- {!store.shell.isOnboarding && (
-
- )}
- {store.me.mainFeed.hasNewLatest && !store.me.mainFeed.isRefreshing && (
-
- )}
- }
- />
-
- )
- }),
-)
-*/