Skip to content

Commit d988f15

Browse files
authored
Update mobile headers (#13844)
1 parent 5ddf517 commit d988f15

10 files changed

Lines changed: 237 additions & 106 deletions

File tree

packages/mobile/src/screens/app-screen/AccountPictureHeader.tsx

Lines changed: 1 addition & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,20 @@
11
import { useCurrentUserId } from '@audius/common/api'
2-
import {
3-
useAccountHasClaimableRewards,
4-
useRemoteVar
5-
} from '@audius/common/hooks'
6-
import { StringKeys } from '@audius/common/services'
7-
import { chatSelectors } from '@audius/common/store'
82
import { useDrawerProgress } from '@react-navigation/drawer'
93
import type { StyleProp, ViewStyle } from 'react-native'
10-
import { View } from 'react-native'
114
import { TouchableOpacity } from 'react-native-gesture-handler'
125
import Animated, {
136
interpolate,
147
useAnimatedStyle
158
} from 'react-native-reanimated'
169
import type { SharedValue } from 'react-native-reanimated'
17-
import { useSelector } from 'react-redux'
1810

1911
import { ProfilePicture } from 'app/components/core'
2012
import { makeStyles } from 'app/styles'
2113

22-
const { getHasUnreadMessages } = chatSelectors
23-
24-
const useStyles = makeStyles(({ spacing, palette }) => ({
14+
const useStyles = makeStyles(({ spacing }) => ({
2515
root: {
2616
height: spacing(8) + 2,
2717
width: spacing(8) + 2
28-
},
29-
notificationBubbleRoot: {
30-
height: spacing(4),
31-
width: spacing(4),
32-
borderColor: palette.white,
33-
borderWidth: 2,
34-
borderRadius: 10,
35-
position: 'absolute',
36-
top: 0,
37-
right: 0
38-
},
39-
notificationBubble: {
40-
flex: 1,
41-
backgroundColor: palette.secondary,
42-
overflow: 'hidden',
43-
borderRadius: 10
4418
}
4519
}))
4620

@@ -54,10 +28,6 @@ export const AccountPictureHeader = (props: AccountPictureHeaderProps) => {
5428
const drawerProgress = useDrawerProgress() as SharedValue<number>
5529
const styles = useStyles()
5630
const { data: accountId } = useCurrentUserId()
57-
const challengeRewardIds = useRemoteVar(StringKeys.CHALLENGE_REWARD_IDS)
58-
const hasClaimableRewards = useAccountHasClaimableRewards(challengeRewardIds)
59-
const hasUnreadMessages = useSelector(getHasUnreadMessages)
60-
const showNotificationBubble = hasClaimableRewards || hasUnreadMessages
6131

6232
const animatedStyle = useAnimatedStyle(() => ({
6333
opacity: interpolate(drawerProgress.value, [0, 1], [1, 0])
@@ -71,11 +41,6 @@ export const AccountPictureHeader = (props: AccountPictureHeaderProps) => {
7141
style={styles.root}
7242
borderWidth='thin'
7343
/>
74-
{showNotificationBubble ? (
75-
<View style={styles.notificationBubbleRoot}>
76-
<View style={styles.notificationBubble} />
77-
</View>
78-
) : null}
7944
</TouchableOpacity>
8045
</Animated.View>
8146
)
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
import type { ReactNode } from 'react'
2+
import { useCallback, useContext } from 'react'
3+
4+
import { View } from 'react-native'
5+
import { useSafeAreaInsets } from 'react-native-safe-area-context'
6+
7+
import { IconAudiusLogoHorizontal } from '@audius/harmony-native'
8+
import { GradientText } from 'app/components/core'
9+
import { useDrawer } from 'app/hooks/useDrawer'
10+
import { makeStyles } from 'app/styles'
11+
12+
import { AppDrawerContext } from '../app-drawer-screen'
13+
14+
import { AccountPictureHeader } from './AccountPictureHeader'
15+
16+
type MobileRootHeaderProps = {
17+
title: string
18+
children?: ReactNode
19+
showDivider?: boolean
20+
}
21+
22+
const useStyles = makeStyles(({ palette, spacing, typography }) => ({
23+
container: {
24+
backgroundColor: palette.white
25+
},
26+
logoContainer: {
27+
position: 'absolute',
28+
alignSelf: 'center',
29+
top: 22,
30+
width: 78,
31+
height: 16,
32+
opacity: 0.45
33+
},
34+
row: {
35+
flexDirection: 'row',
36+
alignItems: 'center',
37+
gap: spacing(4),
38+
paddingHorizontal: spacing(4),
39+
paddingVertical: spacing(3)
40+
},
41+
title: {
42+
fontSize: 24,
43+
lineHeight: 32,
44+
fontFamily: typography.fontByWeight.heavy
45+
},
46+
titleContainer: {
47+
flex: 1,
48+
minWidth: 0
49+
},
50+
divider: {
51+
height: 1,
52+
backgroundColor: palette.neutralLight8
53+
}
54+
}))
55+
56+
/**
57+
* Custom header for root (bottom-bar) tab screens.
58+
*
59+
* Layout (single row): [Avatar] [GradientText title] [right content]
60+
*
61+
* Audius logo is positioned in the status bar area (under dynamic island)
62+
* at low opacity so it appears in screenshots but is hidden behind the
63+
* system chrome during normal use.
64+
*/
65+
export const MobileRootHeader = (props: MobileRootHeaderProps) => {
66+
const { title, children, showDivider = true } = props
67+
const insets = useSafeAreaInsets()
68+
const styles = useStyles()
69+
const { drawerHelpers } = useContext(AppDrawerContext)
70+
const { isOpen: isNowPlayingDrawerOpen } = useDrawer('NowPlaying')
71+
72+
const handleOpenLeftNavDrawer = useCallback(() => {
73+
if (isNowPlayingDrawerOpen) return
74+
drawerHelpers?.openDrawer()
75+
}, [drawerHelpers, isNowPlayingDrawerOpen])
76+
77+
return (
78+
<View style={styles.container}>
79+
<View style={styles.logoContainer}>
80+
<IconAudiusLogoHorizontal height={16} width={78} color='subdued' />
81+
</View>
82+
<View style={[styles.row, { marginTop: insets.top }]}>
83+
<AccountPictureHeader onPress={handleOpenLeftNavDrawer} />
84+
<View style={styles.titleContainer}>
85+
<GradientText accessibilityRole='header' style={styles.title}>
86+
{title}
87+
</GradientText>
88+
</View>
89+
{children}
90+
</View>
91+
{showDivider ? <View style={styles.divider} /> : null}
92+
</View>
93+
)
94+
}

packages/mobile/src/screens/feed-screen/FeedFilterButton.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@ export const FeedFilterButton = () => {
2525
}, [dispatch])
2626

2727
return (
28-
<ScreenHeaderButton onPress={handlePress} label={messageMap[feedFilter]} />
28+
<ScreenHeaderButton
29+
onPress={handlePress}
30+
label={messageMap[feedFilter]}
31+
size='large'
32+
/>
2933
)
3034
}

packages/mobile/src/screens/feed-screen/FeedScreen.tsx

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,13 @@ import {
77
} from '@audius/common/store'
88
import { useDispatch } from 'react-redux'
99

10-
import { IconFeed } from '@audius/harmony-native'
11-
import { Screen, ScreenContent, ScreenHeader } from 'app/components/core'
10+
import { Screen, ScreenContent } from 'app/components/core'
1211
import { Lineup } from 'app/components/lineup'
1312
import { EndOfLineupNotice } from 'app/components/lineup/EndOfLineupNotice'
1413
import { OnlineOnly } from 'app/components/offline-placeholder/OnlineOnly'
1514
import { SuggestedFollows } from 'app/components/suggested-follows'
1615
import { useAppTabScreen } from 'app/hooks/useAppTabScreen'
16+
import { MobileRootHeader } from 'app/screens/app-screen/MobileRootHeader'
1717

1818
import { FeedFilterButton } from './FeedFilterButton'
1919
const { getDiscoverFeedLineup } = feedPageSelectors
@@ -39,12 +39,16 @@ export const FeedScreen = () => {
3939
)
4040

4141
return (
42-
<Screen url='Feed'>
43-
<ScreenHeader text={messages.header} icon={IconFeed}>
44-
<OnlineOnly>
45-
<FeedFilterButton />
46-
</OnlineOnly>
47-
</ScreenHeader>
42+
<Screen
43+
url='Feed'
44+
header={() => (
45+
<MobileRootHeader title={messages.header} showDivider={false}>
46+
<OnlineOnly>
47+
<FeedFilterButton />
48+
</OnlineOnly>
49+
</MobileRootHeader>
50+
)}
51+
>
4852
<ScreenContent>
4953
<Lineup
5054
pullToRefresh

packages/mobile/src/screens/library-screen/LibraryCategorySelectionMenu.tsx

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,12 @@ const { setSelectedCategory } = libraryPageActions
2121
const useStyles = makeStyles(({ spacing }) => ({
2222
container: {
2323
flexGrow: 1,
24-
flexDirection: 'row',
25-
marginTop: spacing(3)
24+
flexDirection: 'row'
2625
},
2726
scrollContainer: {
28-
columnGap: spacing(2)
27+
columnGap: spacing(2),
28+
paddingHorizontal: spacing(4),
29+
paddingBottom: spacing(2)
2930
}
3031
}))
3132

@@ -130,6 +131,7 @@ export const LibraryCategorySelectionMenu = () => {
130131
<View style={styles.container}>
131132
<ScrollView
132133
horizontal
134+
showsHorizontalScrollIndicator={false}
133135
accessibilityRole='radiogroup'
134136
contentContainerStyle={styles.scrollContainer}
135137
alwaysBounceHorizontal={false}
@@ -140,6 +142,7 @@ export const LibraryCategorySelectionMenu = () => {
140142
<SelectablePill
141143
key={value}
142144
type='radio'
145+
size='large'
143146
label={label}
144147
value={value}
145148
onChange={handleChange}

packages/mobile/src/screens/library-screen/LibraryScreen.tsx

Lines changed: 17 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,13 @@
11
import { LibraryPageTabs } from '@audius/common/store'
2+
import { View } from 'react-native'
23

3-
import {
4-
IconAlbum,
5-
IconLibrary,
6-
IconNote,
7-
IconPlaylists
8-
} from '@audius/harmony-native'
9-
import { Screen, ScreenContent, ScreenHeader } from 'app/components/core'
4+
import { IconAlbum, IconNote, IconPlaylists } from '@audius/harmony-native'
5+
import { Screen, ScreenContent } from 'app/components/core'
106
import { ScreenPrimaryContent } from 'app/components/core/Screen/ScreenPrimaryContent'
117
import { ScreenSecondaryContent } from 'app/components/core/Screen/ScreenSecondaryContent'
128
import { TopTabNavigator } from 'app/components/top-tab-bar'
139
import { useAppTabScreen } from 'app/hooks/useAppTabScreen'
10+
import { MobileRootHeader } from 'app/screens/app-screen/MobileRootHeader'
1411
import { makeStyles } from 'app/styles'
1512

1613
import { AlbumsTab } from './AlbumsTab'
@@ -41,29 +38,28 @@ const libraryScreens = [
4138
}
4239
]
4340

44-
const useHeaderStyles = makeStyles(({ spacing }) => ({
45-
root: {
46-
flexWrap: 'wrap',
47-
height: 88,
48-
paddingVertical: spacing(2)
41+
const useStyles = makeStyles(({ palette }) => ({
42+
subHeader: {
43+
backgroundColor: palette.white
4944
}
5045
}))
5146

5247
export const LibraryScreen = () => {
5348
useAppTabScreen()
54-
const headerStyles = useHeaderStyles()
49+
const styles = useStyles()
5550

5651
return (
57-
<Screen>
58-
<ScreenPrimaryContent>
59-
<ScreenHeader
60-
text={messages.header}
61-
icon={IconLibrary}
62-
styles={headerStyles}
63-
>
52+
<Screen
53+
header={() => (
54+
<MobileRootHeader title={messages.header} showDivider={false}>
6455
<LibraryDownloadSection />
56+
</MobileRootHeader>
57+
)}
58+
>
59+
<ScreenPrimaryContent>
60+
<View style={styles.subHeader}>
6561
<LibraryCategorySelectionMenu />
66-
</ScreenHeader>
62+
</View>
6763
</ScreenPrimaryContent>
6864
<ScreenContent isOfflineCapable>
6965
<ScreenSecondaryContent>

packages/mobile/src/screens/notifications-screen/NotificationsScreen.tsx

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@ import { useCallback } from 'react'
33
import { useMarkNotificationsAsViewed } from '@audius/common/api'
44
import { useFocusEffect } from '@react-navigation/native'
55

6-
import { IconNotificationOn } from '@audius/harmony-native'
7-
import { Screen, ScreenContent, ScreenHeader } from 'app/components/core'
6+
import { Screen, ScreenContent } from 'app/components/core'
87
import { ScreenPrimaryContent } from 'app/components/core/Screen/ScreenPrimaryContent'
98
import { ScreenSecondaryContent } from 'app/components/core/Screen/ScreenSecondaryContent'
109
import { useAppTabScreen } from 'app/hooks/useAppTabScreen'
10+
import { MobileRootHeader } from 'app/screens/app-screen/MobileRootHeader'
1111

1212
import { NotificationList } from './NotificationList'
1313

@@ -26,14 +26,8 @@ export const NotificationsScreen = () => {
2626
useFocusEffect(handleMarkAsViewed)
2727

2828
return (
29-
<Screen>
30-
<ScreenPrimaryContent>
31-
<ScreenHeader
32-
text={messages.header}
33-
icon={IconNotificationOn}
34-
iconProps={{ height: 28, width: 28 }}
35-
/>
36-
</ScreenPrimaryContent>
29+
<Screen header={() => <MobileRootHeader title={messages.header} />}>
30+
<ScreenPrimaryContent>{null}</ScreenPrimaryContent>
3731
<ScreenContent>
3832
<ScreenSecondaryContent>
3933
<NotificationList />
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import { TimeRange } from '@audius/common/models'
2+
import { modalsActions, trendingPageSelectors } from '@audius/common/store'
3+
import { ALL_GENRES } from '@audius/common/utils'
4+
import { useDispatch, useSelector } from 'react-redux'
5+
6+
import { Flex, IconLeading, SelectablePill } from '@audius/harmony-native'
7+
8+
import { TRENDING_FILTER_MODAL } from './TrendingCombinedFilterDrawer'
9+
10+
export const TrendingFilterButton = () => {
11+
const dispatch = useDispatch()
12+
const timeRange = useSelector(trendingPageSelectors.getTrendingTimeRange)
13+
const genre = useSelector(trendingPageSelectors.getTrendingGenre)
14+
15+
const hasActiveFilters =
16+
(timeRange ?? TimeRange.WEEK) !== TimeRange.WEEK ||
17+
(genre ?? ALL_GENRES) !== ALL_GENRES
18+
19+
const handleOpenFilter = () => {
20+
dispatch(
21+
modalsActions.setVisibility({
22+
modal: TRENDING_FILTER_MODAL,
23+
visible: true
24+
})
25+
)
26+
}
27+
28+
return (
29+
<Flex>
30+
<SelectablePill
31+
type='button'
32+
icon={IconLeading}
33+
size='large'
34+
isSelected={hasActiveFilters}
35+
isControlled
36+
onPress={handleOpenFilter}
37+
accessibilityLabel='Open filter'
38+
/>
39+
</Flex>
40+
)
41+
}

0 commit comments

Comments
 (0)