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

Implement animated WorkspacesListPage and LoungeAccessPage #23061

Merged
merged 36 commits into from
Jul 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
c605739
Use useLocalize hook
roryabraham Jul 18, 2023
6c43c44
Rename useOnNetworkReconnect to useNetwork
roryabraham Jul 18, 2023
a11e107
Make useNetwork return isOffline
roryabraham Jul 18, 2023
e05cca7
Rename onNetworkReconnect to onReconnect
roryabraham Jul 18, 2023
e6dcfdb
Use useNetwork hook
roryabraham Jul 18, 2023
54159bb
Use usePermissions hook
roryabraham Jul 18, 2023
03082e4
Add WorkspacePlanet asset
roryabraham Jul 18, 2023
490df01
Configure animated StatusBar background color for WorkspacesPage
roryabraham Jul 18, 2023
38bc35f
Remove unnecessary prop from PreferencesPage
roryabraham Jul 18, 2023
f6bb764
Remove unused prop from WorkspacesListPage
roryabraham Jul 18, 2023
06fa463
Add optional footer to IllustratedHeaderPageLayout
roryabraham Jul 18, 2023
228c69f
Implement IllustratedHeaderPageLayout for WorkspacesListPage
roryabraham Jul 18, 2023
8768fc1
Fix lint
roryabraham Jul 18, 2023
561783b
Remove extraneous ScrollView
roryabraham Jul 18, 2023
a99fd97
Create FeatureList component
roryabraham Jul 18, 2023
8ba6c9a
Use useWindowDimensions in MenuItem
roryabraham Jul 18, 2023
57f1dbf
Use hooks instead of HOCs in LoungeAccessPage
roryabraham Jul 18, 2023
49c4b92
Use FeatureList in LoungeAccessPage
roryabraham Jul 18, 2023
039dca4
Add ExpensifyLounge animation
roryabraham Jul 18, 2023
c950672
Implement IllustratedHeaderPageLayout for LoungeAccessPage
roryabraham Jul 18, 2023
a998031
Use default icon colors if the background is the standard appBG
roryabraham Jul 18, 2023
2740338
Add headline and description to FeatureList component
roryabraham Jul 18, 2023
d7b6462
Give IllustratedHeaderPageLayout a default backgroundColor
roryabraham Jul 18, 2023
a2734bc
Implement FeatureList in WorkspacesListPage
roryabraham Jul 18, 2023
f5cf3dd
fix scaling and padding issues
roryabraham Jul 18, 2023
cb6eab5
Merge branch 'main' into Rory-WorkspacePage
roryabraham Jul 19, 2023
2488546
Remove unneeded illustrationStyle props + width workaround
roryabraham Jul 19, 2023
60a7de7
Merge branch 'main' into Rory-WorkspacePage
roryabraham Jul 20, 2023
1bdf744
Fix propTypes of FeatureList
roryabraham Jul 20, 2023
7ef33e8
Merge branch 'main' into Rory-WorkspacePage
roryabraham Jul 22, 2023
90cc036
Make useNetwork take object w/ onReconnect for clarity
roryabraham Jul 22, 2023
36a1171
Remove unnecessary fragment
roryabraham Jul 24, 2023
e094b45
Fix default argument in useNetwork
roryabraham Jul 24, 2023
c476fae
Merge branch 'main' into Rory-WorkspacePage
roryabraham Jul 25, 2023
f833175
Use textColorfulBackground instead of iconColorfulBackground for title
roryabraham Jul 25, 2023
b9581dc
Fix OfflineIndicator style
roryabraham Jul 25, 2023
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
1 change: 1 addition & 0 deletions assets/animations/ExpensifyLounge.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions assets/animations/WorkspacePlanet.json

Large diffs are not rendered by default.

3 changes: 0 additions & 3 deletions src/CONST.js
Original file line number Diff line number Diff line change
Expand Up @@ -2541,9 +2541,6 @@ const CONST = {
ADJUSTABLE: 'adjustable',
IMAGE: 'image',
},
SETTINGS_LOUNGE_ACCESS: {
HEADER_IMAGE_ASPECT_RATIO: 0.64,
},
TRANSLATION_KEYS: {
ATTACHMENT: 'common.attachment',
},
Expand Down
1 change: 1 addition & 0 deletions src/SCREENS.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,6 @@ export default {
TRANSITION_BETWEEN_APPS: 'TransitionBetweenApps',
SETTINGS: {
PREFERENCES: 'Settings_Preferences',
WORKSPACES: 'Settings_Workspaces',
},
};
4 changes: 2 additions & 2 deletions src/components/Avatar.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import * as Expensicons from './Icon/Expensicons';
import Image from './Image';
import styles from '../styles/styles';
import * as ReportUtils from '../libs/ReportUtils';
import useOnNetworkReconnect from '../hooks/useOnNetworkReconnect';
import useNetwork from '../hooks/useNetwork';

const propTypes = {
/** Source for the avatar. Can be a URL or an icon. */
Expand Down Expand Up @@ -64,7 +64,7 @@ const defaultProps = {
function Avatar(props) {
const [imageError, setImageError] = useState(false);

useOnNetworkReconnect(() => setImageError(false));
useNetwork({onReconnect: () => setImageError(false)});

if (!props.source) {
return null;
Expand Down
53 changes: 53 additions & 0 deletions src/components/FeatureList.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import _ from 'underscore';
import React from 'react';
import {View} from 'react-native';
import PropTypes from 'prop-types';
import menuItemPropTypes from './menuItemPropTypes';
import MenuItem from './MenuItem';
import styles from '../styles/styles';
import useLocalize from '../hooks/useLocalize';
import Text from './Text';

const propTypes = {
/** A list of menuItems representing the feature list. */
menuItems: PropTypes.arrayOf(PropTypes.shape({...menuItemPropTypes, translationKey: PropTypes.string})).isRequired,

/** A headline translation key to show above the feature list. */
headline: PropTypes.string.isRequired,

/** A description translation key to show below the headline and above the feature list. */
description: PropTypes.string.isRequired,
};

function FeatureList({menuItems, headline, description}) {
const {translate} = useLocalize();
return (
<>
<View style={[styles.w100, styles.ph5, styles.pb5]}>
<Text
style={[styles.textHeadline, styles.preWrap, styles.mb2]}
numberOfLines={2}
>
{translate(headline)}
</Text>
<Text style={styles.baseFontStyle}>{translate(description)}</Text>
</View>
{_.map(menuItems, ({translationKey, icon}) => (
<MenuItem
key={translationKey}
title={translate(translationKey)}
icon={icon}
iconWidth={60}
iconHeight={60}
iconStyles={[styles.mr3, styles.ml3]}
interactive={false}
/>
))}
</>
);
}

FeatureList.propTypes = propTypes;
FeatureList.displayName = 'FeatureList';

export default FeatureList;
25 changes: 19 additions & 6 deletions src/components/IllustratedHeaderPageLayout.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import _ from 'underscore';
import React from 'react';
import PropTypes from 'prop-types';
import {ScrollView, View} from 'react-native';
Expand All @@ -9,35 +10,45 @@ import styles from '../styles/styles';
import themeColors from '../styles/themes/default';
import * as StyleUtils from '../styles/StyleUtils';
import useWindowDimensions from '../hooks/useWindowDimensions';
import FixedFooter from './FixedFooter';

const propTypes = {
...headerWithBackButtonPropTypes,

/** Children to display in the lower half of the page (below the header section w/ an animation) */
children: PropTypes.node.isRequired,

/** The background color to apply in the upper half of the screen. */
backgroundColor: PropTypes.string.isRequired,

/** The illustration to display in the header. Can be either an SVG component or a JSON object representing a Lottie animation. */
illustration: PropTypes.oneOfType([PropTypes.func, PropTypes.object]).isRequired,

/** The background color to apply in the upper half of the screen. */
backgroundColor: PropTypes.string,

/** A fixed footer to display at the bottom of the page. */
footer: PropTypes.node,
};

const defaultProps = {
backgroundColor: themeColors.appBG,
footer: null,
};

function IllustratedHeaderPageLayout({backgroundColor, children, illustration, ...propsToPassToHeader}) {
function IllustratedHeaderPageLayout({backgroundColor, children, illustration, footer, ...propsToPassToHeader}) {
const {windowHeight} = useWindowDimensions();
return (
<ScreenWrapper
style={[StyleUtils.getBackgroundColorStyle(backgroundColor)]}
shouldEnablePickerAvoiding={false}
includeSafeAreaPaddingBottom={false}
offlineIndicatorStyle={[StyleUtils.getBackgroundColorStyle(themeColors.appBG)]}
>
{({safeAreaPaddingBottomStyle}) => (
<>
<HeaderWithBackButton
// eslint-disable-next-line react/jsx-props-no-spreading
{...propsToPassToHeader}
titleColor={themeColors.iconColorfulBackground}
iconFill={themeColors.iconColorfulBackground}
titleColor={backgroundColor === themeColors.appBG ? undefined : themeColors.textColorfulBackground}
iconFill={backgroundColor === themeColors.appBG ? undefined : themeColors.iconColorfulBackground}
/>
<View style={[styles.flex1, StyleUtils.getBackgroundColorStyle(themeColors.appBG)]}>
<ScrollView
Expand All @@ -55,6 +66,7 @@ function IllustratedHeaderPageLayout({backgroundColor, children, illustration, .
</View>
<View style={[styles.pt5]}>{children}</View>
</ScrollView>
{!_.isNull(footer) && <FixedFooter>{footer}</FixedFooter>}
</View>
</>
)}
Expand All @@ -63,6 +75,7 @@ function IllustratedHeaderPageLayout({backgroundColor, children, illustration, .
}

IllustratedHeaderPageLayout.propTypes = propTypes;
IllustratedHeaderPageLayout.defaultProps = defaultProps;
IllustratedHeaderPageLayout.displayName = 'IllustratedHeaderPageLayout';

export default IllustratedHeaderPageLayout;
4 changes: 3 additions & 1 deletion src/components/LottieAnimations.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import ExpensifyLounge from '../../assets/animations/ExpensifyLounge.json';
import Fireworks from '../../assets/animations/Fireworks.json';
import Hands from '../../assets/animations/Hands.json';
import PreferencesDJ from '../../assets/animations/PreferencesDJ.json';
import ReviewingBankInfo from '../../assets/animations/ReviewingBankInfo.json';
import WorkspacePlanet from '../../assets/animations/WorkspacePlanet.json';

export {Fireworks, Hands, PreferencesDJ, ReviewingBankInfo};
export {ExpensifyLounge, Fireworks, Hands, PreferencesDJ, ReviewingBankInfo, WorkspacePlanet};
4 changes: 2 additions & 2 deletions src/components/MagicCodeInput.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import TextInput from './TextInput';
import FormHelpMessage from './FormHelpMessage';
import {withNetwork} from './OnyxProvider';
import networkPropTypes from './networkPropTypes';
import useOnNetworkReconnect from '../hooks/useOnNetworkReconnect';
import useNetwork from '../hooks/useNetwork';
import * as Browser from '../libs/Browser';

const propTypes = {
Expand Down Expand Up @@ -142,7 +142,7 @@ function MagicCodeInput(props) {
props.onFulfill(props.value);
};

useOnNetworkReconnect(validateAndSubmit);
useNetwork({onReconnect: validateAndSubmit});

useEffect(() => {
validateAndSubmit();
Expand Down
31 changes: 10 additions & 21 deletions src/components/MenuItem.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,14 @@ import colors from '../styles/colors';
import MultipleAvatars from './MultipleAvatars';
import * as defaultWorkspaceAvatars from './Icon/WorkspaceDefaultAvatars';
import PressableWithSecondaryInteraction from './PressableWithSecondaryInteraction';
import withWindowDimensions, {windowDimensionsPropTypes} from './withWindowDimensions';
import * as DeviceCapabilities from '../libs/DeviceCapabilities';
import ControlSelection from '../libs/ControlSelection';
import variables from '../styles/variables';
import * as Session from '../libs/actions/Session';
import Hoverable from './Hoverable';
import useWindowDimensions from '../hooks/useWindowDimensions';

const propTypes = {
...menuItemPropTypes,
...windowDimensionsPropTypes,
};
const propTypes = menuItemPropTypes;

const defaultProps = {
badgeText: undefined,
Expand Down Expand Up @@ -76,7 +73,9 @@ const defaultProps = {
shouldGreyOutWhenDisabled: true,
};

function MenuItem(props) {
const MenuItem = React.forwardRef((props, ref) => {
const {isSmallScreenWidth} = useWindowDimensions();

const isDeleted = _.contains(props.style, styles.offlineFeedback.deleted);
const descriptionVerticalMargin = props.shouldShowDescriptionOnTop ? styles.mb1 : styles.mt1;
const titleTextStyle = StyleUtils.combineStyles(
Expand Down Expand Up @@ -118,7 +117,7 @@ function MenuItem(props) {

props.onPress(e);
}, props.isAnonymousAction)}
onPressIn={() => props.shouldBlockSelection && props.isSmallScreenWidth && DeviceCapabilities.canUseTouchScreen() && ControlSelection.block()}
onPressIn={() => props.shouldBlockSelection && isSmallScreenWidth && DeviceCapabilities.canUseTouchScreen() && ControlSelection.block()}
onPressOut={ControlSelection.unblock}
onSecondaryInteraction={props.onSecondaryInteraction}
style={({pressed}) => [
Expand All @@ -130,7 +129,7 @@ function MenuItem(props) {
props.shouldGreyOutWhenDisabled && props.disabled && styles.buttonOpacityDisabled,
]}
disabled={props.disabled}
ref={props.forwardedRef}
ref={ref}
accessibilityRole={CONST.ACCESSIBILITY_ROLE.MENUITEM}
accessibilityLabel={props.title}
>
Expand Down Expand Up @@ -301,20 +300,10 @@ function MenuItem(props) {
)}
</Hoverable>
);
}
});

MenuItem.propTypes = propTypes;
MenuItem.defaultProps = defaultProps;
MenuItem.displayName = 'MenuItem';

const MenuItemWithWindowDimensions = withWindowDimensions(
React.forwardRef((props, ref) => (
<MenuItem
// eslint-disable-next-line react/jsx-props-no-spreading
{...props}
forwardedRef={ref}
/>
)),
);
MenuItemWithWindowDimensions.defaultProps = defaultProps;

export default MenuItemWithWindowDimensions;
export default MenuItem;
4 changes: 1 addition & 3 deletions src/components/MenuItemWithTopDescription.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@ import React from 'react';
import menuItemPropTypes from './menuItemPropTypes';
import MenuItem from './MenuItem';

const propTypes = {
...menuItemPropTypes,
};
const propTypes = menuItemPropTypes;

function MenuItemWithTopDescription(props) {
return (
Expand Down
2 changes: 1 addition & 1 deletion src/components/ScreenWrapper/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ class ScreenWrapper extends React.Component {
})
: this.props.children
}
{this.props.isSmallScreenWidth && this.props.shouldShowOfflineIndicator && <OfflineIndicator />}
{this.props.isSmallScreenWidth && this.props.shouldShowOfflineIndicator && <OfflineIndicator style={this.props.offlineIndicatorStyle} />}
</PickerAvoidingView>
</KeyboardAvoidingView>
</View>
Expand Down
5 changes: 5 additions & 0 deletions src/components/ScreenWrapper/propTypes.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import PropTypes from 'prop-types';
import stylePropTypes from '../../styles/stylePropTypes';
import {windowDimensionsPropTypes} from '../withWindowDimensions';
import {environmentPropTypes} from '../withEnvironment';

Expand Down Expand Up @@ -41,6 +42,9 @@ const propTypes = {

/** Whether to show offline indicator */
shouldShowOfflineIndicator: PropTypes.bool,

/** Styles for the offline indicator */
offlineIndicatorStyle: stylePropTypes,
};

const defaultProps = {
Expand All @@ -54,6 +58,7 @@ const defaultProps = {
shouldEnableMaxHeight: false,
shouldEnablePickerAvoiding: true,
shouldShowOfflineIndicator: true,
offlineIndicatorStyle: [],
};

export {propTypes, defaultProps};
12 changes: 8 additions & 4 deletions src/hooks/useOnNetworkReconnect.js → src/hooks/useNetwork.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@ import {useRef, useContext, useEffect} from 'react';
import {NetworkContext} from '../components/OnyxProvider';

/**
* @param {Function} onNetworkReconnect
* @param {Object} [options]
* @param {Function} [options.onReconnect]
* @returns {Object}
*/
export default function useOnNetworkReconnect(onNetworkReconnect) {
const callback = useRef(onNetworkReconnect);
callback.current = onNetworkReconnect;
export default function useNetwork({onReconnect = () => {}} = {}) {
const callback = useRef(onReconnect);
callback.current = onReconnect;

const {isOffline} = useContext(NetworkContext);
const prevOfflineStatusRef = useRef(isOffline);
Expand All @@ -24,4 +26,6 @@ export default function useOnNetworkReconnect(onNetworkReconnect) {
// Used to store previous prop values to compare on next render
prevOfflineStatusRef.current = isOffline;
}, [isOffline]);

return {isOffline};
}
5 changes: 5 additions & 0 deletions src/languages/en.js
Original file line number Diff line number Diff line change
Expand Up @@ -1125,6 +1125,11 @@ export default {
emptyWorkspace: {
title: 'Create a new workspace',
subtitle: "Workspaces are where you'll chat with your team, reimburse expenses, issue cards, send invoices, pay bills, and more — all in one place.",
features: {
trackAndCollect: 'Track and collect receipts',
companyCards: 'Company credit cards',
reimbursements: 'Easy reimbursements',
},
},
new: {
newWorkspace: 'New workspace',
Expand Down
7 changes: 6 additions & 1 deletion src/languages/es.js
Original file line number Diff line number Diff line change
Expand Up @@ -1134,6 +1134,11 @@ export default {
emptyWorkspace: {
title: 'Crear un nuevo espacio de trabajo',
subtitle: 'En los espacios de trabajo es donde puedes chatear con tu equipo, reembolsar gastos, emitir tarjetas, enviar y pagar facturas y mas — todo en un mismo lugar',
features: {
trackAndCollect: 'Organiza recibos',
companyCards: 'Tarjetas de crédito corporativas',
reimbursements: 'Reembolsos fáciles',
},
},
new: {
newWorkspace: 'Nuevo espacio de trabajo',
Expand Down Expand Up @@ -1462,7 +1467,7 @@ export default {
expenseManagement: 'Gestión de Gastos',
spendManagement: 'Control de Gastos',
expenseReports: 'Informes de Gastos',
companyCreditCard: 'Tarjeta de Crédito de Empresa',
companyCreditCard: 'Tarjeta de Crédito Corporativa',
receiptScanningApp: 'Aplicación de Escaneado de Recibos',
billPay: 'Pago de Facturas',
invoicing: 'Facturación',
Expand Down
2 changes: 1 addition & 1 deletion src/libs/Navigation/AppNavigator/ModalStackNavigators.js
Original file line number Diff line number Diff line change
Expand Up @@ -316,7 +316,7 @@ const SettingsModalStackNavigator = createModalStackNavigator([
const SettingsWorkspacesPage = require('../../../pages/workspace/WorkspacesListPage').default;
return SettingsWorkspacesPage;
},
name: 'Settings_Workspaces',
name: SCREENS.SETTINGS.WORKSPACES,
},
{
getComponent: () => {
Expand Down
2 changes: 1 addition & 1 deletion src/libs/Navigation/linkingConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export default {
Settings_Root: {
path: ROUTES.SETTINGS,
},
Settings_Workspaces: {
[SCREENS.SETTINGS.WORKSPACES]: {
path: ROUTES.SETTINGS_WORKSPACES,
exact: true,
},
Expand Down
Loading