diff --git a/packages/common/src/store/ui/modals/parentSlice.ts b/packages/common/src/store/ui/modals/parentSlice.ts
index c9edbd4b658..952be569027 100644
--- a/packages/common/src/store/ui/modals/parentSlice.ts
+++ b/packages/common/src/store/ui/modals/parentSlice.ts
@@ -26,6 +26,7 @@ export const initialState: BasicModalsState = {
TrendingCategory: { isOpen: false },
TrendingTimeRange: { isOpen: false },
TrendingFilter: { isOpen: false },
+ FeedFilter: { isOpen: false },
TrendingRewardsExplainer: { isOpen: false },
SocialProof: { isOpen: false },
EditTrack: { isOpen: false },
diff --git a/packages/common/src/store/ui/modals/types.ts b/packages/common/src/store/ui/modals/types.ts
index 7a5076fb250..307360dfe97 100644
--- a/packages/common/src/store/ui/modals/types.ts
+++ b/packages/common/src/store/ui/modals/types.ts
@@ -61,6 +61,7 @@ export type Modals =
| 'TrendingCategory'
| 'TrendingTimeRange'
| 'TrendingFilter'
+ | 'FeedFilter'
| 'TrendingRewardsExplainer'
| 'SocialProof'
| 'EditTrack'
diff --git a/packages/mobile/src/app/Drawers.tsx b/packages/mobile/src/app/Drawers.tsx
index 4e407ca93ff..6f7d0405362 100644
--- a/packages/mobile/src/app/Drawers.tsx
+++ b/packages/mobile/src/app/Drawers.tsx
@@ -52,6 +52,7 @@ import { ReplaceTrackProgressDrawer } from 'app/screens/edit-track-screen/compon
import { EarlyReleaseConfirmationDrawer } from 'app/screens/edit-track-screen/components/EarlyReleaseConfirmationDrawer'
import { PublishConfirmationDrawer } from 'app/screens/edit-track-screen/components/PublishConfirmationDrawer'
import { ConnectNewWalletDrawer } from 'app/screens/external-wallets/components/ConnectNewWalletDrawer'
+import { FeedFilterDrawer } from 'app/screens/feed-screen'
import { WelcomeDrawer } from 'app/screens/sign-on-screen/components/WelcomeDrawer'
import { PickWinnersDrawer } from 'app/screens/track-screen/PickWinnersDrawer'
import {
@@ -115,6 +116,7 @@ const commonDrawersMap: { [Modal in Modals]?: ComponentType } = {
DeactivateAccountConfirmation: DeactivateAccountConfirmationDrawer,
TrendingGenreSelection: TrendingFilterDrawer,
TrendingFilter: TrendingCombinedFilterDrawer,
+ FeedFilter: FeedFilterDrawer,
Overflow: OverflowMenuDrawer,
SignOutConfirmation: SignOutConfirmationDrawer,
AddToCollection: AddToCollectionDrawer,
diff --git a/packages/mobile/src/screens/feed-screen/FeedFilterButton.tsx b/packages/mobile/src/screens/feed-screen/FeedFilterButton.tsx
new file mode 100644
index 00000000000..f92bdc4b442
--- /dev/null
+++ b/packages/mobile/src/screens/feed-screen/FeedFilterButton.tsx
@@ -0,0 +1,37 @@
+import { FeedFilter } from '@audius/common/models'
+import { feedPageSelectors, modalsActions } from '@audius/common/store'
+import { useDispatch, useSelector } from 'react-redux'
+
+import { Flex, IconLeading, SelectablePill } from '@audius/harmony-native'
+
+import { FEED_FILTER_MODAL } from './FeedFilterDrawer'
+
+export const FeedFilterButton = () => {
+ const dispatch = useDispatch()
+ const feedFilter = useSelector(feedPageSelectors.getFeedFilter)
+
+ const hasActiveFilters = (feedFilter ?? FeedFilter.ALL) !== FeedFilter.ALL
+
+ const handleOpenFilter = () => {
+ dispatch(
+ modalsActions.setVisibility({
+ modal: FEED_FILTER_MODAL,
+ visible: true
+ })
+ )
+ }
+
+ return (
+
+
+
+ )
+}
diff --git a/packages/mobile/src/screens/feed-screen/FeedFilterDrawer.tsx b/packages/mobile/src/screens/feed-screen/FeedFilterDrawer.tsx
new file mode 100644
index 00000000000..6b7d7aad311
--- /dev/null
+++ b/packages/mobile/src/screens/feed-screen/FeedFilterDrawer.tsx
@@ -0,0 +1,132 @@
+import { useCallback } from 'react'
+
+import { FeedFilter } from '@audius/common/models'
+import { feedPageActions, feedPageSelectors } from '@audius/common/store'
+import { Pressable, View } from 'react-native'
+import { useDispatch, useSelector } from 'react-redux'
+
+import { Flex, Text } from '@audius/harmony-native'
+import { RadioButton, Text as CoreText } from 'app/components/core'
+import { AppDrawer, useDrawerState } from 'app/components/drawer'
+import { makeStyles } from 'app/styles'
+
+export const FEED_FILTER_MODAL = 'FeedFilter' as const
+
+const filterLabels: Record = {
+ [FeedFilter.ALL]: 'All Posts',
+ [FeedFilter.ORIGINAL]: 'Original Posts',
+ [FeedFilter.REPOST]: 'Reposts'
+}
+
+const filterOptions: FeedFilter[] = [
+ FeedFilter.ALL,
+ FeedFilter.ORIGINAL,
+ FeedFilter.REPOST
+]
+
+const messages = {
+ title: 'Filter Feed',
+ sectionTitle: 'Post Type',
+ selected: 'Selected'
+}
+
+const useStyles = makeStyles(({ palette, spacing }) => ({
+ grabBar: {
+ width: 36,
+ height: 4,
+ borderRadius: 2,
+ backgroundColor: palette.neutralLight6
+ },
+ grabBarContainer: {
+ paddingTop: spacing(2),
+ paddingBottom: spacing(1),
+ alignItems: 'center'
+ },
+ content: {
+ paddingHorizontal: spacing(4),
+ paddingTop: spacing(4),
+ paddingBottom: spacing(6)
+ },
+ sectionTitle: {
+ marginBottom: spacing(2)
+ },
+ optionRow: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ justifyContent: 'space-between',
+ paddingVertical: spacing(4),
+ borderBottomWidth: 1,
+ borderBottomColor: palette.neutralLight8
+ }
+}))
+
+export const FeedFilterDrawer = () => {
+ const styles = useStyles()
+ const dispatch = useDispatch()
+ const currentFilter =
+ useSelector(feedPageSelectors.getFeedFilter) ?? FeedFilter.ALL
+
+ useDrawerState(FEED_FILTER_MODAL)
+
+ const handleSelectFilter = useCallback(
+ (filter: FeedFilter) => {
+ dispatch(feedPageActions.setFeedFilter(filter))
+ },
+ [dispatch]
+ )
+
+ const drawerHeader = useCallback(
+ (_props: { onClose: () => void }) => (
+
+
+
+
+
+
+ {messages.title}
+
+
+
+ ),
+ [styles]
+ )
+
+ return (
+
+
+
+
+ {messages.sectionTitle}
+
+
+ {filterOptions.map((filter) => (
+ handleSelectFilter(filter)}
+ style={styles.optionRow}
+ >
+
+
+
+ {filterLabels[filter]}
+
+
+ {currentFilter === filter ? (
+
+ {messages.selected}
+
+ ) : null}
+
+ ))}
+
+
+ )
+}
diff --git a/packages/mobile/src/screens/feed-screen/FeedFilters.tsx b/packages/mobile/src/screens/feed-screen/FeedFilters.tsx
deleted file mode 100644
index b12b33f8c7b..00000000000
--- a/packages/mobile/src/screens/feed-screen/FeedFilters.tsx
+++ /dev/null
@@ -1,58 +0,0 @@
-import { useCallback } from 'react'
-
-import { FeedFilter } from '@audius/common/models'
-import { FilterButton, useTheme } from '@audius/harmony-native'
-import { View } from 'react-native'
-
-const messages = {
- allPosts: 'All Posts',
- originalPosts: 'Original Posts',
- reposts: 'Reposts'
-}
-
-const filterOptions = [
- { label: messages.allPosts, value: FeedFilter.ALL },
- { label: messages.originalPosts, value: FeedFilter.ORIGINAL },
- { label: messages.reposts, value: FeedFilter.REPOST }
-]
-
-type FeedFiltersProps = {
- currentFilter: FeedFilter
- onSelectFilter: (filter: FeedFilter) => void
-}
-
-export const FeedFilters = ({
- currentFilter,
- onSelectFilter
-}: FeedFiltersProps) => {
- const { spacing, color } = useTheme()
-
- const handleChange = useCallback(
- (value: string | undefined) => {
- if (!value) return
- onSelectFilter(value as FeedFilter)
- },
- [onSelectFilter]
- )
-
- return (
-
-
-
- )
-}
diff --git a/packages/mobile/src/screens/feed-screen/FeedScreen.tsx b/packages/mobile/src/screens/feed-screen/FeedScreen.tsx
index dd178d3b74d..26a22c0e014 100644
--- a/packages/mobile/src/screens/feed-screen/FeedScreen.tsx
+++ b/packages/mobile/src/screens/feed-screen/FeedScreen.tsx
@@ -10,7 +10,7 @@ import {
FOR_YOU_INITIAL_PAGE_SIZE,
FOR_YOU_LOAD_MORE_PAGE_SIZE
} from '@audius/common/api'
-import { Name, FeedTab, type FeedFilter } from '@audius/common/models'
+import { Name, FeedTab } from '@audius/common/models'
import { feedPageActions, feedPageSelectors } from '@audius/common/store'
import { useDispatch, useSelector } from 'react-redux'
@@ -21,11 +21,11 @@ import { SuggestedFollows } from 'app/components/suggested-follows'
import { MobileRootHeader } from 'app/screens/app-screen/MobileRootHeader'
import { make, track } from 'app/services/analytics'
-import { FeedFilters } from './FeedFilters'
+import { FeedFilterButton } from './FeedFilterButton'
import { FeedTabs } from './FeedTabs'
const { getFeedTab, getFeedFilter } = feedPageSelectors
-const { setFeedTab, setFeedFilter } = feedPageActions
+const { setFeedTab } = feedPageActions
const messages = {
header: 'Your Feed',
@@ -80,12 +80,17 @@ export const FeedScreen = () => {
[dispatch]
)
- const handleSelectFilter = useCallback(
- (filter: FeedFilter) => {
- dispatch(setFeedFilter(filter))
- track(make({ eventName: Name.FEED_CHANGE_VIEW, view: filter }))
- },
- [dispatch]
+ // Memoized so the header isn't a new function reference on every render —
+ // otherwise Screen's setOptions runs each parent re-render and React
+ // Navigation rebuilds the header, remounting AccountPictureHeader and
+ // re-firing the profile-picture image-fetch path.
+ const renderHeader = useCallback(
+ () => (
+
+ {isForYou ? null : }
+
+ ),
+ [isForYou]
)
const lineupProps = isForYou
@@ -115,20 +120,9 @@ export const FeedScreen = () => {
}
return (
- (
-
- )}
- >
+
- {isForYou ? null : (
-
- )}