-
Notifications
You must be signed in to change notification settings - Fork 4k
[Home Page] Create ForYouSection Component (1/2) - Preparing the Home Page layout #80555
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,66 @@ | ||
| import type {ReactNode} from 'react'; | ||
| import React from 'react'; | ||
| import {View} from 'react-native'; | ||
| import useTheme from '@hooks/useTheme'; | ||
| import useThemeStyles from '@hooks/useThemeStyles'; | ||
| import variables from '@styles/variables'; | ||
| import type IconAsset from '@src/types/utils/IconAsset'; | ||
| import Icon from './Icon'; | ||
| import Text from './Text'; | ||
|
|
||
| type WidgetContainerProps = { | ||
| /** The icon to display along with the title */ | ||
| icon?: IconAsset; | ||
|
|
||
| /** The text to display in the title of the widget */ | ||
| title?: string; | ||
|
|
||
| /** Custom color for the title text */ | ||
| titleColor?: string; | ||
|
|
||
| /** The width of the icon. */ | ||
| iconWidth?: number; | ||
|
|
||
| /** The height of the icon. */ | ||
| iconHeight?: number; | ||
|
|
||
| /** The content to display inside the widget container */ | ||
| children: ReactNode; | ||
| }; | ||
|
|
||
| function WidgetContainer({ | ||
| children, | ||
| icon, | ||
| title, | ||
| titleColor, | ||
| iconWidth = variables.iconSizeNormal, | ||
| iconHeight = variables.iconSizeNormal, | ||
| }: WidgetContainerProps) { | ||
| const styles = useThemeStyles(); | ||
| const theme = useTheme(); | ||
|
|
||
| return ( | ||
| <View style={styles.widgetContainer}> | ||
| <View style={[styles.flexRow, styles.alignItemsStart, styles.mh8, styles.mt8, styles.mb5]}> | ||
| {!!icon && ( | ||
| <View style={[styles.flexGrow0, styles.flexShrink0]}> | ||
| <Icon | ||
| src={icon} | ||
| width={iconWidth} | ||
| height={iconHeight} | ||
| /> | ||
| </View> | ||
| )} | ||
| <View style={[styles.flexShrink1, styles.flexGrow1, styles.flexRow, styles.alignItemsCenter, styles.gap2]}> | ||
| {!!title && ( | ||
| <Text style={styles.getWidgetContainerTitleStyle(titleColor ?? theme.text)}>{title}</Text> | ||
| )} | ||
| </View> | ||
| </View> | ||
| {children} | ||
| </View> | ||
| ); | ||
| } | ||
|
|
||
| export type {WidgetContainerProps}; | ||
| export default WidgetContainer; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -988,6 +988,11 @@ const translations = { | |
| description: "We're fine-tuning a few more bits and pieces of New Expensify to accommodate your specific setup. In the meantime, head over to Expensify Classic.", | ||
| }, | ||
| }, | ||
| homePage: { | ||
| forYou: 'For you', | ||
| announcements: 'Announcements', | ||
| discover: 'Discover', | ||
| }, | ||
|
Comment on lines
+991
to
+995
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Only Useful? React with 👍 / 👎. |
||
| allSettingsScreen: { | ||
| subscription: 'Subscription', | ||
| domains: 'Domains', | ||
|
|
||
This file was deleted.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| import React from 'react'; | ||
| import {View} from 'react-native'; | ||
| import WidgetContainer from '@components/WidgetContainer'; | ||
| import useLocalize from '@hooks/useLocalize'; | ||
|
|
||
| /** | ||
| * This is an empty placeholder component for the Announcements section. | ||
| * The actual implementation will be added in upcoming PRs. | ||
| */ | ||
| function AnnouncementsSection() { | ||
| const {translate} = useLocalize(); | ||
|
|
||
| return ( | ||
| <WidgetContainer title={translate('homePage.announcements')}> | ||
| <View style={{height: 400}} /> | ||
| </WidgetContainer> | ||
| ); | ||
| } | ||
|
|
||
| export default AnnouncementsSection; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| import React from 'react'; | ||
| import {View} from 'react-native'; | ||
| import WidgetContainer from '@components/WidgetContainer'; | ||
| import useLocalize from '@hooks/useLocalize'; | ||
|
|
||
| /** | ||
| * This is an empty placeholder component for the Discover section. | ||
| * The actual implementation will be added in upcoming PRs. | ||
| */ | ||
| function DiscoverSection() { | ||
| const {translate} = useLocalize(); | ||
|
|
||
| return ( | ||
| <WidgetContainer title={translate('homePage.discover')}> | ||
| <View style={{height: 400}} /> | ||
| </WidgetContainer> | ||
| ); | ||
| } | ||
|
|
||
| export default DiscoverSection; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,98 @@ | ||
| import React from 'react'; | ||
| import {View} from 'react-native'; | ||
| import Button from '@components/Button'; | ||
| import WidgetContainer from '@components/WidgetContainer'; | ||
| import useLocalize from '@hooks/useLocalize'; | ||
| import useOnyx from '@hooks/useOnyx'; | ||
| import useThemeStyles from '@hooks/useThemeStyles'; | ||
| import Navigation from '@libs/Navigation/Navigation'; | ||
| import {buildQueryStringFromFilterFormValues} from '@libs/SearchQueryUtils'; | ||
| import CONST from '@src/CONST'; | ||
| import ONYXKEYS from '@src/ONYXKEYS'; | ||
| import ROUTES from '@src/ROUTES'; | ||
| import {accountIDSelector} from '@src/selectors/Session'; | ||
|
|
||
| /** | ||
| * This is a placeholder component for the For You section. | ||
| * The actual implementation will be added in upcoming PRs. | ||
| */ | ||
| function ForYouSection() { | ||
| const styles = useThemeStyles(); | ||
| const {translate} = useLocalize(); | ||
| const [accountID] = useOnyx(ONYXKEYS.SESSION, {canBeMissing: false, selector: accountIDSelector}); | ||
|
|
||
| const handleGoToSearch = () => { | ||
| Navigation.navigate( | ||
| ROUTES.SEARCH_ROOT.getRoute({ | ||
| query: buildQueryStringFromFilterFormValues({ | ||
| type: CONST.SEARCH.DATA_TYPES.EXPENSE_REPORT, | ||
| action: CONST.SEARCH.ACTION_FILTERS.SUBMIT, | ||
| from: [`${accountID}`], | ||
| }), | ||
| }), | ||
| ); | ||
| }; | ||
|
|
||
| const handleGoToApproveSearch = () => { | ||
| Navigation.navigate( | ||
| ROUTES.SEARCH_ROOT.getRoute({ | ||
| query: buildQueryStringFromFilterFormValues({ | ||
| type: CONST.SEARCH.DATA_TYPES.EXPENSE_REPORT, | ||
| action: CONST.SEARCH.ACTION_FILTERS.APPROVE, | ||
| to: [`${accountID}`], | ||
| }), | ||
| }), | ||
| ); | ||
| }; | ||
|
|
||
| const handleGoToPaySearch = () => { | ||
| Navigation.navigate( | ||
| ROUTES.SEARCH_ROOT.getRoute({ | ||
| query: buildQueryStringFromFilterFormValues({ | ||
| type: CONST.SEARCH.DATA_TYPES.EXPENSE_REPORT, | ||
| action: CONST.SEARCH.ACTION_FILTERS.PAY, | ||
| reimbursable: CONST.SEARCH.BOOLEAN.YES, | ||
| payer: accountID?.toString(), | ||
| }), | ||
| }), | ||
| ); | ||
| }; | ||
|
|
||
| const handleGoToExportSearch = () => { | ||
| Navigation.navigate( | ||
| ROUTES.SEARCH_ROOT.getRoute({ | ||
| query: buildQueryStringFromFilterFormValues({ | ||
| type: CONST.SEARCH.DATA_TYPES.EXPENSE_REPORT, | ||
| action: CONST.SEARCH.ACTION_FILTERS.EXPORT, | ||
| exporter: [`${accountID}`], | ||
| exportedOn: CONST.SEARCH.DATE_PRESETS.NEVER, | ||
| }), | ||
| }), | ||
| ); | ||
| }; | ||
|
|
||
| return ( | ||
| <WidgetContainer title={translate('homePage.forYou')}> | ||
| <View style={[styles.flexColumn, styles.gap3]}> | ||
| <Button | ||
| text="Go to submitted expense reports" | ||
| onPress={handleGoToSearch} | ||
|
Comment on lines
+77
to
+79
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
These button labels are hard-coded English strings, so users in non-English locales will always see English text even when the app is localized. This is user-facing and inconsistent with the rest of the Home page, which uses Useful? React with 👍 / 👎. |
||
| /> | ||
| <Button | ||
| text="Go to expense reports to approve" | ||
| onPress={handleGoToApproveSearch} | ||
| /> | ||
| <Button | ||
| text="Go to expense reports to pay" | ||
| onPress={handleGoToPaySearch} | ||
| /> | ||
| <Button | ||
| text="Go to expense reports to export" | ||
| onPress={handleGoToExportSearch} | ||
| /> | ||
| </View> | ||
| </WidgetContainer> | ||
| ); | ||
| } | ||
|
|
||
| export default ForYouSection; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,68 @@ | ||
| import React, {useEffect} from 'react'; | ||
| import {View} from 'react-native'; | ||
| import NavigationTabBar from '@components/Navigation/NavigationTabBar'; | ||
| import NAVIGATION_TABS from '@components/Navigation/NavigationTabBar/NAVIGATION_TABS'; | ||
| import ScreenWrapper from '@components/ScreenWrapper'; | ||
| import ScrollView from '@components/ScrollView'; | ||
| import useResponsiveLayout from '@hooks/useResponsiveLayout'; | ||
| import useThemeStyles from '@hooks/useThemeStyles'; | ||
| import {confirmReadyToOpenApp} from '@libs/actions/App'; | ||
| import usePreloadFullScreenNavigators from '@libs/Navigation/AppNavigator/usePreloadFullScreenNavigators'; | ||
| import TopBar from '@components/Navigation/TopBar'; | ||
| import useLocalize from '@hooks/useLocalize'; | ||
| import AnnouncementsSection from './AnnouncementsSection'; | ||
| import DiscoverSection from './DiscoverSection'; | ||
| import ForYouSection from './ForYouSection'; | ||
|
|
||
| function HomePage() { | ||
| const {shouldUseNarrowLayout} = useResponsiveLayout(); | ||
| const shouldDisplayLHB = !shouldUseNarrowLayout; | ||
| const styles = useThemeStyles(); | ||
| const {translate} = useLocalize(); | ||
|
|
||
| useEffect(() => { | ||
| confirmReadyToOpenApp(); | ||
| }, []); | ||
|
|
||
| // This hook preloads the screens of adjacent tabs to make changing tabs faster. | ||
| usePreloadFullScreenNavigators(); | ||
|
|
||
| return ( | ||
| <ScreenWrapper | ||
| shouldEnablePickerAvoiding={false} | ||
| shouldShowOfflineIndicatorInWideScreen | ||
| testID="HomePage" | ||
| enableEdgeToEdgeBottomSafeAreaPadding={false} | ||
| bottomContent={ | ||
| shouldUseNarrowLayout && ( | ||
| <NavigationTabBar | ||
| selectedTab={NAVIGATION_TABS.HOME} | ||
| shouldShowFloatingButtons | ||
| /> | ||
| ) | ||
| } | ||
| > | ||
| <TopBar | ||
| breadcrumbLabel={translate('common.home')} | ||
| shouldShowLoadingBar={false} | ||
| /> | ||
| <ScrollView | ||
| contentContainerStyle={styles.homePageContentContainer} | ||
| addBottomSafeAreaPadding | ||
| > | ||
| <View style={styles.homePageMainLayout(shouldUseNarrowLayout)}> | ||
| <View style={styles.homePageLeftColumn(shouldUseNarrowLayout)}> | ||
| <ForYouSection /> | ||
| <DiscoverSection /> | ||
| </View> | ||
| <View style={styles.homePageRightColumn(shouldUseNarrowLayout)}> | ||
| <AnnouncementsSection /> | ||
| </View> | ||
| </View> | ||
| </ScrollView> | ||
| {shouldDisplayLHB && <NavigationTabBar selectedTab={NAVIGATION_TABS.HOME} />} | ||
| </ScreenWrapper> | ||
| ); | ||
| } | ||
|
|
||
| export default HomePage; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just thinking
Maybe we should just pass the icon as a parameter?
Instead of three parameters