Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add tab bar header navigation #5082

Merged
merged 33 commits into from
Mar 20, 2024
Merged
Show file tree
Hide file tree
Changes from 25 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
fafb4ac
chore: add title for discover screen
MuckT Mar 13, 2024
c2bd286
chore: add account circle button
MuckT Mar 13, 2024
11bd86b
chore: add title style to header
MuckT Mar 13, 2024
d2f0fc9
feat: use new header on wallet assets
MuckT Mar 13, 2024
932b4ec
Merge branch 'main' into tomm/act-1108
MuckT Mar 13, 2024
a544525
feat: create shared header for tab navigator screens
MuckT Mar 14, 2024
dddd0c4
chore: use shared tab header
MuckT Mar 14, 2024
fdba8da
fix: wrap with animated view for header scroll on android
MuckT Mar 14, 2024
2ffe340
feat: handle scrollable header in discover and home tabs
MuckT Mar 15, 2024
0cdac6b
test: adjust for multiple matching text elements
MuckT Mar 15, 2024
95a5c43
Merge branch 'main' into tomm/act-1108
MuckT Mar 15, 2024
2aeef88
fix: remove unnecessary wrapper causing test failures
MuckT Mar 15, 2024
26ffb10
test(tab-nav): check labels and navigation
MuckT Mar 15, 2024
926aa3a
feat: ripple for header items on android
MuckT Mar 15, 2024
585a26f
fix: right header items for wallet home
MuckT Mar 15, 2024
9551481
fix: move test id to touchable
MuckT Mar 15, 2024
8111b63
fix: maintain existing onScroll in new handleScroll
MuckT Mar 15, 2024
2597590
docs: nested animated view fix for android
MuckT Mar 18, 2024
e2846b7
fix: call on scroll
MuckT Mar 18, 2024
d5cc11c
style: use tab header in shared screen options
MuckT Mar 18, 2024
74a55b5
style: prefer named export
MuckT Mar 18, 2024
c4cf8fe
chore: add analytics for account circle tapped
MuckT Mar 18, 2024
51af4d9
fix: rename refresh control colors prop to proper case
MuckT Mar 18, 2024
54e3825
chore: conditionally adjust padding for new tab view
MuckT Mar 18, 2024
af9d1c8
docs: add missing description for account circle tapped
MuckT Mar 18, 2024
ce2177b
fix: call function on scroll in handle scroll
MuckT Mar 19, 2024
fc03d3e
fix: conditionally set scroll
MuckT Mar 19, 2024
f588a88
style: simplify titleStyle type
MuckT Mar 19, 2024
f70e4ca
Merge branch 'main' into tomm/act-1108
MuckT Mar 19, 2024
3a4ac76
fix: conditionally call scroll handler on tab nav availability
MuckT Mar 19, 2024
2133d98
docs: link cleanup todos to cleanup issue in linear
MuckT Mar 19, 2024
beb06fe
chore: change header title styles
MuckT Mar 19, 2024
f155a47
chore: deprecate TopBarIconButton
MuckT Mar 19, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion locales/base/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -2179,7 +2179,8 @@
"title": "Welcome"
},
"discover": {
"tabName": "Discover"
"tabName": "Discover",
"title": "Discover"
}
}
}
1 change: 1 addition & 0 deletions src/analytics/Events.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ export enum AppEvents {

export enum HomeEvents {
hamburger_tapped = 'hamburger_tapped',
account_circle_tapped = 'account_circle_tapped',
drawer_navigation = 'drawer_navigation',
drawer_address_copy = 'drawer_address_copy',
profile_address_copy = 'profile_address_copy',
Expand Down
1 change: 1 addition & 0 deletions src/analytics/Properties.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ interface AppEventsProperties {

interface HomeEventsProperties {
[HomeEvents.hamburger_tapped]: undefined
[HomeEvents.account_circle_tapped]: undefined
[HomeEvents.drawer_navigation]: {
navigateTo: string
}
Expand Down
1 change: 1 addition & 0 deletions src/analytics/docs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ export const eventDocs: Record<AnalyticsEventType, string> = {
[AppEvents.multichain_beta_contact_support]: `When the user taps the Contact Support button on the multichain beta screen`,
[AppEvents.handle_deeplink]: `When a deeplink that leads into the app is detected and handled`,
[HomeEvents.hamburger_tapped]: ``,
[HomeEvents.account_circle_tapped]: `When the account circle used in the tab navigation is tapped`,
[HomeEvents.drawer_navigation]: ``,
[HomeEvents.drawer_address_copy]: ``,
[HomeEvents.profile_address_copy]: `When a user copies their wallet address from the profile screen`,
Expand Down
45 changes: 45 additions & 0 deletions src/components/AccountCircleButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import React from 'react'
import { StyleProp, StyleSheet, View, ViewStyle } from 'react-native'
import { HomeEvents } from 'src/analytics/Events'
import ValoraAnalytics from 'src/analytics/ValoraAnalytics'
import Touchable from 'src/components/Touchable'
import AccountCircle from 'src/icons/AccountCircle'
import { navigate } from 'src/navigator/NavigationService'
import { Screens } from 'src/navigator/Screens'
import { Spacing } from 'src/styles/styles'

interface Props {
style?: StyleProp<ViewStyle>
size?: number
testID?: string
}

export default function AccountCircleButton({ style, size, testID }: Props) {
const onPress = () => {
ValoraAnalytics.track(HomeEvents.account_circle_tapped)
navigate(Screens.ProfileMenu)

Check warning on line 20 in src/components/AccountCircleButton.tsx

View check run for this annotation

Codecov / codecov/patch

src/components/AccountCircleButton.tsx#L19-L20

Added lines #L19 - L20 were not covered by tests
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we add an analytics event for this?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added in c4cf8fe!

}

return (
<View style={styles.container}>
<Touchable
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Touchable used here and in other added icons instead of TopBarButton.tsx to better handle ripple on Android.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is this something TopBarIconButton can support? if not, is there a purpose of keeping TopBarIconButton around?

testID={testID}
onPress={onPress}
style={[style, styles.button]}
borderRadius={Spacing.Thick24}
>
<AccountCircle size={size} />
</Touchable>
</View>
)
}

const styles = StyleSheet.create({
container: {
justifyContent: 'center',
alignItems: 'center',
},
button: {
padding: Spacing.Small12,
},
})
31 changes: 23 additions & 8 deletions src/components/QrScanButton.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import React from 'react'
import { StyleProp, ViewStyle } from 'react-native'
import { StyleProp, StyleSheet, View, ViewStyle } from 'react-native'
import { QrScreenEvents } from 'src/analytics/Events'
import ValoraAnalytics from 'src/analytics/ValoraAnalytics'
import Touchable from 'src/components/Touchable'
import ScanIcon from 'src/icons/ScanIcon'
import { navigate } from 'src/navigator/NavigationService'
import { Screens } from 'src/navigator/Screens'
import { TopBarIconButton } from 'src/navigator/TopBarButton'
import { Spacing } from 'src/styles/styles'

interface Props {
style?: StyleProp<ViewStyle>
Expand All @@ -20,11 +21,25 @@ export default function QrScanButton({ style, size, testID }: Props) {
}

return (
<TopBarIconButton
testID={testID}
icon={<ScanIcon size={size} />}
onPress={onPress}
style={style}
/>
<View style={styles.container}>
<Touchable
testID={testID}
onPress={onPress}
style={[style, styles.button]}
borderRadius={Spacing.Thick24}
>
<ScanIcon size={size} />
</Touchable>
</View>
)
}

const styles = StyleSheet.create({
container: {
justifyContent: 'center',
alignItems: 'center',
},
button: {
padding: Spacing.Small12,
},
})
75 changes: 58 additions & 17 deletions src/dappsExplorer/DAppsExplorerScreenSearchFilter.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,17 @@
import { NativeStackScreenProps } from '@react-navigation/native-stack'
import React, { useEffect, useMemo, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { RefreshControl, SectionList, SectionListProps, StyleSheet, Text, View } from 'react-native'
import {
LayoutChangeEvent,
RefreshControl,
SectionList,
SectionListProps,
StyleSheet,
Text,
View,
} from 'react-native'
import { ScrollView } from 'react-native-gesture-handler'
import Animated from 'react-native-reanimated'
import Animated, { useAnimatedScrollHandler, useSharedValue } from 'react-native-reanimated'
import { SafeAreaView, useSafeAreaInsets } from 'react-native-safe-area-context'
import { DappExplorerEvents } from 'src/analytics/Events'
import ValoraAnalytics from 'src/analytics/ValoraAnalytics'
Expand Down Expand Up @@ -31,10 +39,11 @@
import DrawerTopBar from 'src/navigator/DrawerTopBar'
import { styles as headerStyles } from 'src/navigator/Headers'
import { Screens } from 'src/navigator/Screens'
import useScrollAwareHeader from 'src/navigator/ScrollAwareHeader'
import { StackParamList } from 'src/navigator/types'
import { useDispatch, useSelector } from 'src/redux/hooks'
import colors from 'src/styles/colors'
import fontStyles from 'src/styles/fonts'
import { Colors } from 'src/styles/colors'
import fontStyles, { typeScale } from 'src/styles/fonts'
import { Spacing } from 'src/styles/styles'

const AnimatedSectionList =
Expand All @@ -52,7 +61,7 @@
Screens.DAppsExplorerScreen | Screens.TabDiscover
>

export function DAppsExplorerScreenSearchFilter({ route }: Props) {
export function DAppsExplorerScreenSearchFilter({ navigation, route }: Props) {
const { t } = useTranslation()

// temporary parameter while we build the tab navigator, should be cleaned up
Expand All @@ -62,11 +71,11 @@
const insets = useSafeAreaInsets()

const sectionListRef = useRef<SectionList>(null)
const scrollPosition = useRef(new Animated.Value(0)).current
const scrollPositionValue = useRef(new Animated.Value(0)).current
const horizontalScrollView = useRef<ScrollView>(null)
const dappRankingsBottomSheetRef = useRef<BottomSheetRefType>(null)

const onScroll = Animated.event([{ nativeEvent: { contentOffset: { y: scrollPosition } } }])
const onScroll = Animated.event([{ nativeEvent: { contentOffset: { y: scrollPositionValue } } }])
const dispatch = useDispatch()
const loading = useSelector(dappsListLoadingSelector)
const error = useSelector(dappsListErrorSelector)
Expand Down Expand Up @@ -136,6 +145,28 @@
})
}

// Scroll Aware Header
const scrollPosition = useSharedValue(0)
const [titleHeight, setTitleHeight] = useState(0)

const handleMeasureTitleHeight = (event: LayoutChangeEvent) => {
setTitleHeight(event.nativeEvent.layout.height)

Check warning on line 153 in src/dappsExplorer/DAppsExplorerScreenSearchFilter.tsx

View check run for this annotation

Codecov / codecov/patch

src/dappsExplorer/DAppsExplorerScreenSearchFilter.tsx#L153

Added line #L153 was not covered by tests
}

const handleScroll = useAnimatedScrollHandler((event) => {
onScroll
scrollPosition.value = event.contentOffset.y

Check warning on line 158 in src/dappsExplorer/DAppsExplorerScreenSearchFilter.tsx

View check run for this annotation

Codecov / codecov/patch

src/dappsExplorer/DAppsExplorerScreenSearchFilter.tsx#L157-L158

Added lines #L157 - L158 were not covered by tests
})

useScrollAwareHeader({
navigation,
title: isTabNavigator ? t('bottomTabsNavigator.discover.title') : '',
titleStyle: isTabNavigator ? { ...typeScale.labelSemiBoldMedium } : null,
scrollPosition,
startFadeInPosition: titleHeight - titleHeight * 0.33,
animationDistance: titleHeight * 0.33,
})

const sections: SectionData[] = useMemo(() => {
const dappsMatchingFilter = selectedFilter
? nonFavoriteDappsWithCategoryNames.filter((dapp) => selectedFilter.filterFn(dapp))
Expand Down Expand Up @@ -193,7 +224,7 @@
<DrawerTopBar
rightElement={<QrScanButton testID={'DAppsExplorerScreen/QRScanButton'} />}
middleElement={<Text style={headerStyles.headerTitle}>{t('dappsScreen.title')}</Text>}
scrollPosition={scrollPosition}
scrollPosition={scrollPositionValue}
/>
)}
<>
Expand All @@ -206,8 +237,8 @@
<AnimatedSectionList
refreshControl={
<RefreshControl
tintColor={colors.primary}
colors={[colors.primary]}
tintColor={Colors.primary}
colors={[Colors.primary]}
style={styles.refreshControl}
refreshing={loading}
onRefresh={() => dispatch(fetchDappsList())}
Expand All @@ -221,14 +252,19 @@
}
ListHeaderComponent={
<>
{isTabNavigator && (
<Text onLayout={handleMeasureTitleHeight} style={styles.title}>
{t('bottomTabsNavigator.discover.title')}
</Text>
)}
<DappFeaturedActions onPressShowDappRankings={handleShowDappRankings} />
<SearchInput
onChangeText={(text) => {
setSearchTerm(text)
}}
value={searchTerm}
multiline={false}
placeholderTextColor={colors.gray4}
placeholderTextColor={Colors.gray4}
underlineColorAndroid="transparent"
placeholder={t('dappsScreen.searchPlaceHolder') ?? undefined}
showClearButton={true}
Expand All @@ -237,8 +273,8 @@
<FilterChipsCarousel
chips={filterChips}
onSelectChip={handleToggleFilterChip}
primaryColor={colors.infoDark}
secondaryColor={colors.infoLight}
primaryColor={Colors.infoDark}
secondaryColor={Colors.infoLight}
style={styles.dappFilterView}
forwardedRef={horizontalScrollView}
/>
Expand All @@ -252,7 +288,7 @@
// Workaround iOS setting an incorrect automatic inset at the top
scrollIndicatorInsets={{ top: 0.01 }}
scrollEventThrottle={16}
onScroll={onScroll}
onScroll={isTabNavigator ? handleScroll : onScroll}
sections={sections}
renderItem={({ item: dapp, index, section }) => {
return (
Expand Down Expand Up @@ -322,19 +358,19 @@
flexGrow: 1,
},
refreshControl: {
backgroundColor: colors.white,
backgroundColor: Colors.white,
},
sectionList: {
flex: 1,
},
sectionTitle: {
...fontStyles.label,
color: colors.gray4,
color: Colors.gray4,
marginTop: Spacing.Large32,
},
disclaimer: {
...fontStyles.xsmall,
color: colors.gray4,
color: Colors.gray4,
textAlign: 'center',
marginTop: Spacing.Large32,
marginBottom: Spacing.Regular16,
Expand All @@ -343,6 +379,11 @@
flex: 1,
justifyContent: 'flex-end',
},
title: {
...typeScale.titleMedium,
color: Colors.black,
marginBottom: Spacing.Large32,
},
})

export default DAppsExplorerScreenSearchFilter
16 changes: 9 additions & 7 deletions src/escrow/EscrowedPaymentListScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,11 @@
import EscrowedPaymentListItem from 'src/escrow/EscrowedPaymentListItem'
import { getReclaimableEscrowPayments } from 'src/escrow/reducer'
import i18n from 'src/i18n'
import {
NotificationList,
titleWithBalanceNavigationOptions,
} from 'src/notifications/NotificationList'
import { HeaderTitleWithBalance, headerWithBackButton } from 'src/navigator/Headers'
import { NotificationList } from 'src/notifications/NotificationList'
import { useSelector } from 'src/redux/hooks'
import { Spacing } from 'src/styles/styles'
import { Currency } from 'src/utils/currencies'

export const listItemRenderer = (payment: EscrowedPayment, key: number | undefined = undefined) => {
return (
Expand All @@ -24,9 +23,12 @@
return <NotificationList items={sentEscrowedPayments} listItemRenderer={listItemRenderer} />
}

EscrowedPaymentListScreen.navigationOptions = titleWithBalanceNavigationOptions(
i18n.t('escrowedPaymentReminder')
)
EscrowedPaymentListScreen.navigationOptions = () => ({
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed due to a failing test case in 2aeef88.

...headerWithBackButton,
headerTitle: () => (

Check warning on line 28 in src/escrow/EscrowedPaymentListScreen.tsx

View check run for this annotation

Codecov / codecov/patch

src/escrow/EscrowedPaymentListScreen.tsx#L28

Added line #L28 was not covered by tests
<HeaderTitleWithBalance title={i18n.t('escrowedPaymentReminder')} token={Currency.Dollar} />
),
})

const styles = StyleSheet.create({
listItem: {
Expand Down
31 changes: 23 additions & 8 deletions src/home/NotificationBell.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import React from 'react'
import { StyleProp, ViewStyle } from 'react-native'
import { StyleProp, StyleSheet, View, ViewStyle } from 'react-native'
import { HomeEvents } from 'src/analytics/Events'
import ValoraAnalytics from 'src/analytics/ValoraAnalytics'
import Touchable from 'src/components/Touchable'
import { useNotifications } from 'src/home/NotificationCenter'
import NotificationBellIcon from 'src/icons/NotificationBellIcon'
import { navigate } from 'src/navigator/NavigationService'
import { Screens } from 'src/navigator/Screens'
import { TopBarIconButton } from 'src/navigator/TopBarButton'
import colors from 'src/styles/colors'
import { Spacing } from 'src/styles/styles'

interface Props {
style?: StyleProp<ViewStyle>
Expand All @@ -29,11 +30,25 @@ export default function NotificationBell({ style, size, testID }: Props) {
}

return (
<TopBarIconButton
testID={testID}
icon={<NotificationBellIcon size={size} notificationMark={notificationMark} />}
onPress={onPress}
style={style}
/>
<View style={styles.container}>
<Touchable
testID={testID}
onPress={onPress}
style={[style, styles.button]}
borderRadius={Spacing.Thick24}
>
<NotificationBellIcon size={size} notificationMark={notificationMark} />
</Touchable>
</View>
)
}

const styles = StyleSheet.create({
container: {
justifyContent: 'center',
alignItems: 'center',
},
button: {
padding: Spacing.Small12,
},
})
3 changes: 2 additions & 1 deletion src/home/WalletHome.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,8 @@ describe('WalletHome', () => {
jest.runOnlyPendingTimers()
})

expect(tree.getByText('bottomTabsNavigator.home.title')).toBeTruthy()
// Multiple elements use this text with the scroll aware header
expect(tree.queryAllByText('bottomTabsNavigator.home.title')).toBeTruthy()
expect(tree.queryByTestId('HomeActionsCarousel')).toBeTruthy()
expect(tree.queryByText('notificationCenterSpotlight.message')).toBeFalsy()
expect(tree.queryByTestId('HomeTokenBalance')).toBeFalsy()
Expand Down
Loading
Loading