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

Add Download Banner to mWeb #25418

Merged
merged 27 commits into from
Aug 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
d20ba5e
Add Mobile Banner Component, Styled (not functional)
grgia Aug 17, 2023
ecd7d39
Clean up and add functionality
grgia Aug 17, 2023
0c24cfa
Move out of folder
grgia Aug 17, 2023
aefdaeb
fix asset path
grgia Aug 17, 2023
8d14fa3
Add copy to en/es
grgia Aug 17, 2023
fde38d5
modify spanish translation from slack feedback
grgia Aug 17, 2023
e61e3f0
Fix lint
grgia Aug 17, 2023
81f1536
Design comments- remove period, fix line height in second line
grgia Aug 17, 2023
b19d81d
Merge branch 'main' into georgia-DownloadAppBanner
grgia Aug 17, 2023
b415c22
Fix Capitalization of 'Download the App' to 'Download the app'
grgia Aug 18, 2023
ae8c7e7
Remember if user has dismissed the banner
grgia Aug 18, 2023
ec791de
prettier
grgia Aug 18, 2023
bd78429
Merge branch 'main' into georgia-DownloadAppBanner
mountiny Aug 18, 2023
586c22a
Move MobileBanner down in tree
grgia Aug 19, 2023
1fffcb8
Merge branch 'georgia-DownloadAppBanner' of github.com:Expensify/App …
grgia Aug 19, 2023
d92db8e
Merge branch 'main' into georgia-DownloadAppBanner
grgia Aug 21, 2023
769bc7b
Merge branch 'main' into georgia-DownloadAppBanner
grgia Aug 22, 2023
f485cae
Initial Bottom Banner Styling
grgia Aug 22, 2023
9bcb429
center styling
grgia Aug 22, 2023
955e5ad
Rename to DownloadAppModal, clean up
grgia Aug 22, 2023
163f58c
prettier
grgia Aug 22, 2023
04ce142
Merge branch 'main' into georgia-DownloadAppBanner
grgia Aug 22, 2023
bc88a5e
rename action to DownloadAppModal
grgia Aug 22, 2023
f1633a6
remove unused import for lint
grgia Aug 22, 2023
849639b
Merge branch 'main' into georgia-DownloadAppBanner
grgia Aug 23, 2023
66b56e9
add shouldStackButtons prop
grgia Aug 23, 2023
2daf94a
add back SHOW_DOWNLOAD_APP_BANNER key
grgia Aug 23, 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
18 changes: 18 additions & 0 deletions assets/images/expensify-app-icon.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 2 additions & 0 deletions src/Expensify.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import AppleAuthWrapper from './components/SignInButtons/AppleAuthWrapper';
import EmojiPicker from './components/EmojiPicker/EmojiPicker';
import * as EmojiPickerAction from './libs/actions/EmojiPickerAction';
import * as DemoActions from './libs/actions/DemoActions';
import DownloadAppModal from './components/DownloadAppModal';
import DeeplinkWrapper from './components/DeeplinkWrapper';

// This lib needs to be imported, but it has nothing to export since all it contains is an Onyx connection
Expand Down Expand Up @@ -198,6 +199,7 @@ function Expensify(props) {
<KeyboardShortcutsModal />
<GrowlNotification ref={Growl.growlRef} />
<PopoverReportActionContextMenu ref={ReportActionContextMenu.contextMenuRef} />
<DownloadAppModal />
<EmojiPicker ref={EmojiPickerAction.emojiPickerRef} />
{/* We include the modal for showing a new update at the top level so the option is always present. */}
{props.updateAvailable ? <UpdateAppModal /> : null}
Expand Down
4 changes: 4 additions & 0 deletions src/ONYXKEYS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,9 @@ const ONYXKEYS = {
SESSION: 'session',
BETAS: 'betas',

/** Denotes if the Download App Banner has been dismissed */
SHOW_DOWNLOAD_APP_BANNER: 'showDownloadAppBanner',

/** NVP keys
* Contains the user's payPalMe data */
PAYPAL: 'paypal',
Expand Down Expand Up @@ -286,6 +289,7 @@ type OnyxValues = {
[ONYXKEYS.ACTIVE_CLIENTS]: string[];
[ONYXKEYS.DEVICE_ID]: string;
[ONYXKEYS.IS_SIDEBAR_LOADED]: boolean;
[ONYXKEYS.SHOW_DOWNLOAD_APP_BANNER]: boolean;
[ONYXKEYS.PERSISTED_REQUESTS]: OnyxTypes.Request[];
[ONYXKEYS.QUEUED_ONYX_UPDATES]: OnyxTypes.QueuedOnyxUpdates;
[ONYXKEYS.CURRENT_DATE]: string;
Expand Down
114 changes: 94 additions & 20 deletions src/components/ConfirmContent.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import Button from './Button';
import useLocalize from '../hooks/useLocalize';
import useNetwork from '../hooks/useNetwork';
import Text from './Text';
import variables from '../styles/variables';
import Icon from './Icon';

const propTypes = {
/** Title of the modal */
Expand Down Expand Up @@ -40,9 +42,30 @@ const propTypes = {
/** Whether we should show the cancel button */
shouldShowCancelButton: PropTypes.bool,

/** Icon to display above the title */
iconSource: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),

/** Whether to center the icon / text content */
shouldCenterContent: PropTypes.bool,

/** Whether to stack the buttons */
shouldStackButtons: PropTypes.bool,

/** Styles for title */
// eslint-disable-next-line react/forbid-prop-types
titleStyles: PropTypes.arrayOf(PropTypes.object),

/** Styles for prompt */
// eslint-disable-next-line react/forbid-prop-types
promptStyles: PropTypes.arrayOf(PropTypes.object),

/** Styles for view */
// eslint-disable-next-line react/forbid-prop-types
contentStyles: PropTypes.arrayOf(PropTypes.object),

/** Styles for icon */
// eslint-disable-next-line react/forbid-prop-types
iconAdditionalStyles: PropTypes.arrayOf(PropTypes.object),
};

const defaultProps = {
Expand All @@ -55,36 +78,87 @@ const defaultProps = {
shouldDisableConfirmButtonWhenOffline: false,
shouldShowCancelButton: true,
contentStyles: [],
iconSource: null,
shouldCenterContent: false,
shouldStackButtons: true,
titleStyles: [],
promptStyles: [],
iconAdditionalStyles: [],
};

function ConfirmContent(props) {
const {translate} = useLocalize();
const {isOffline} = useNetwork();

const isCentered = props.shouldCenterContent;

return (
<View style={[styles.m5, ...props.contentStyles]}>
<View style={[styles.flexRow, styles.mb4]}>
<Header title={props.title} />
<View style={isCentered ? [styles.alignItemsCenter, styles.mb6] : []}>
{!_.isEmpty(props.iconSource) ||
(_.isFunction(props.iconSource) && (
<View style={[styles.flexRow, styles.mb3]}>
<Icon
src={props.iconSource}
width={variables.downloadAppModalAppIconSize}
height={variables.downloadAppModalAppIconSize}
additionalStyles={[...props.iconAdditionalStyles]}
/>
</View>
))}

<View style={[styles.flexRow, isCentered ? {} : styles.mb4]}>
<Header
title={props.title}
textStyles={[...props.titleStyles]}
/>
</View>

{_.isString(props.prompt) ? <Text style={[...props.promptStyles, isCentered ? styles.textAlignCenter : {}]}>{props.prompt}</Text> : props.prompt}
</View>

{_.isString(props.prompt) ? <Text>{props.prompt}</Text> : props.prompt}

<Button
success={props.success}
danger={props.danger}
style={[styles.mt4]}
onPress={props.onConfirm}
pressOnEnter
text={props.confirmText || translate('common.yes')}
isDisabled={isOffline && props.shouldDisableConfirmButtonWhenOffline}
/>
{props.shouldShowCancelButton && (
<Button
style={[styles.mt3, styles.noSelect]}
onPress={props.onCancel}
text={props.cancelText || translate('common.no')}
shouldUseDefaultHover
/>
{props.shouldStackButtons ? (
Copy link
Contributor Author

Choose a reason for hiding this comment

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

This block is just swapping the buttons since props.shouldStackButtons is default behavior

Copy link
Contributor

Choose a reason for hiding this comment

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

Ah actually I was also wondering if it's worth just repositioning the buttons with flex order instead of duplicating the buttons. Maybe it's more annoying than I think though!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good shout! Since this PR is urgent, I think we should ignore non-blocking optimizations for now though

Copy link
Contributor

Choose a reason for hiding this comment

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

Gotcha!

Copy link
Contributor

Choose a reason for hiding this comment

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

Could we make an issue to track this change otherwise we never gonna update it 😂

<>
<Button
success={props.success}
danger={props.danger}
style={[styles.mt4]}
onPress={props.onConfirm}
pressOnEnter
text={props.confirmText || translate('common.yes')}
isDisabled={isOffline && props.shouldDisableConfirmButtonWhenOffline}
/>
{props.shouldShowCancelButton && (
<Button
style={[styles.mt3, styles.noSelect]}
onPress={props.onCancel}
text={props.cancelText || translate('common.no')}
shouldUseDefaultHover
/>
)}
</>
) : (
<View style={[styles.flexRow, styles.gap4]}>
{props.shouldShowCancelButton && (
<Button
style={[styles.noSelect, styles.flex1]}
onPress={props.onCancel}
text={props.cancelText || translate('common.no')}
shouldUseDefaultHover
medium
/>
)}
<Button
success={props.success}
danger={props.danger}
style={[styles.flex1]}
onPress={props.onConfirm}
pressOnEnter
text={props.confirmText || translate('common.yes')}
isDisabled={isOffline && props.shouldDisableConfirmButtonWhenOffline}
medium
/>
</View>
)}
</View>
);
Expand Down
33 changes: 33 additions & 0 deletions src/components/ConfirmModal.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,27 @@ const propTypes = {
/** Should we announce the Modal visibility changes? */
shouldSetModalVisibility: PropTypes.bool,

/** Icon to display above the title */
iconSource: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),

/** Styles for title */
// eslint-disable-next-line react/forbid-prop-types
titleStyles: PropTypes.arrayOf(PropTypes.object),

/** Styles for prompt */
// eslint-disable-next-line react/forbid-prop-types
promptStyles: PropTypes.arrayOf(PropTypes.object),

/** Styles for icon */
// eslint-disable-next-line react/forbid-prop-types
iconAdditionalStyles: PropTypes.arrayOf(PropTypes.object),

/** Whether to center the icon / text content */
shouldCenterContent: PropTypes.bool,

/** Whether to stack the buttons */
shouldStackButtons: PropTypes.bool,

...windowDimensionsPropTypes,
};

Expand All @@ -59,7 +80,13 @@ const defaultProps = {
shouldShowCancelButton: true,
shouldSetModalVisibility: true,
title: '',
iconSource: null,
onModalHide: () => {},
titleStyles: [],
iconAdditionalStyles: [],
promptStyles: [],
shouldCenterContent: false,
shouldStackButtons: true,
};

function ConfirmModal(props) {
Expand All @@ -85,6 +112,12 @@ function ConfirmModal(props) {
danger={props.danger}
shouldDisableConfirmButtonWhenOffline={props.shouldDisableConfirmButtonWhenOffline}
shouldShowCancelButton={props.shouldShowCancelButton}
shouldCenterContent={props.shouldCenterContent}
iconSource={props.iconSource}
iconAdditionalStyles={props.iconAdditionalStyles}
titleStyles={props.titleStyles}
promptStyles={props.promptStyles}
shouldStackButtons={props.shouldStackButtons}
/>
</Modal>
);
Expand Down
74 changes: 74 additions & 0 deletions src/components/DownloadAppModal.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import React, {useState} from 'react';
import PropTypes from 'prop-types';
import {withOnyx} from 'react-native-onyx';
import ONYXKEYS from '../ONYXKEYS';
import styles from '../styles/styles';
import CONST from '../CONST';
import AppIcon from '../../assets/images/expensify-app-icon.svg';
Copy link
Contributor

Choose a reason for hiding this comment

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

I think it's a bit tidier to add the app icon to Expensicons, that way it can also be reused easily.

Copy link
Contributor

Choose a reason for hiding this comment

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

Could we also make an issue for this? @grgia

import useLocalize from '../hooks/useLocalize';
import * as Link from '../libs/actions/Link';
import * as Browser from '../libs/Browser';
import getOperatingSystem from '../libs/getOperatingSystem';
import setShowDownloadAppModal from '../libs/actions/DownloadAppModal';
import ConfirmModal from './ConfirmModal';

const propTypes = {
/** ONYX PROP to hide banner for a user that has dismissed it */
// eslint-disable-next-line react/forbid-prop-types
showDownloadAppBanner: PropTypes.bool,
};

const defaultProps = {
showDownloadAppBanner: true,
};

function DownloadAppModal({showDownloadAppBanner}) {
const [shouldShowBanner, setshouldShowBanner] = useState(Browser.isMobile() && showDownloadAppBanner);

const {translate} = useLocalize();

const handleCloseBanner = () => {
setShowDownloadAppModal(false);
setshouldShowBanner(false);
};

let link = '';

if (getOperatingSystem() === CONST.OS.IOS) {
link = CONST.APP_DOWNLOAD_LINKS.IOS;
} else if (getOperatingSystem() === CONST.OS.ANDROID) {
link = CONST.APP_DOWNLOAD_LINKS.ANDROID;
}

const handleOpenAppStore = () => {
Link.openExternalLink(link, true);
};

return (
<ConfirmModal
title={translate('DownloadAppModal.downloadTheApp')}
isVisible={shouldShowBanner}
onConfirm={handleOpenAppStore}
onCancel={handleCloseBanner}
prompt={translate('DownloadAppModal.keepTheConversationGoing')}
confirmText={translate('common.download')}
cancelText={translate('DownloadAppModal.noThanks')}
shouldCenterContent
iconSource={AppIcon}
promptStyles={[styles.textNormal, styles.lh20]}
titleStyles={[styles.textHeadline]}
iconAdditionalStyles={[styles.appIconBorderRadius]}
shouldStackButtons={false}
/>
);
}

DownloadAppModal.displayName = 'DownloadAppModal';
DownloadAppModal.propTypes = propTypes;
DownloadAppModal.defaultProps = defaultProps;

export default withOnyx({
showDownloadAppBanner: {
key: ONYXKEYS.SHOW_DOWNLOAD_APP_BANNER,
},
})(DownloadAppModal);
5 changes: 5 additions & 0 deletions src/languages/en.js
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,11 @@ export default {
newFaceEnterMagicCode: ({login}) => `It's always great to see a new face around here! Please enter the magic code sent to ${login}. It should arrive within a minute or two.`,
welcomeEnterMagicCode: ({login}) => `Please enter the magic code sent to ${login}. It should arrive within a minute or two.`,
},
DownloadAppModal: {
Copy link
Contributor

Choose a reason for hiding this comment

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

I dont think other keys are capitalized

Suggested change
DownloadAppModal: {
downloadAppModal: {

downloadTheApp: 'Download the app',
keepTheConversationGoing: 'Keep the conversation going in New Expensify, download the app for an enhanced experience.',
noThanks: 'No thanks',
},
login: {
hero: {
header: 'Split bills, request payments, and chat with friends.',
Expand Down
5 changes: 5 additions & 0 deletions src/languages/es.js
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,11 @@ export default {
newFaceEnterMagicCode: ({login}) => `¡Siempre es genial ver una cara nueva por aquí! Por favor ingresa el código mágico enviado a ${login}. Debería llegar en un par de minutos.`,
welcomeEnterMagicCode: ({login}) => `Por favor, introduce el código mágico enviado a ${login}. Debería llegar en un par de minutos.`,
},
DownloadAppModal: {
downloadTheApp: 'Descarga la aplicación',
keepTheConversationGoing: 'Mantén la conversación en New Expensify, descarga la aplicación para una experiencia mejorada.',
noThanks: 'No, gracias',
},
login: {
hero: {
header: 'Divida las facturas, solicite pagos y chatee con sus amigos.',
Expand Down
11 changes: 11 additions & 0 deletions src/libs/actions/DownloadAppModal.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import Onyx from 'react-native-onyx';
import ONYXKEYS from '../../ONYXKEYS';

/**
* @param {Boolean} shouldShowBanner
*/
function setShowDownloadAppModal(shouldShowBanner) {
Onyx.set(ONYXKEYS.SHOW_DOWNLOAD_APP_BANNER, shouldShowBanner);
}

export default setShowDownloadAppModal;
5 changes: 5 additions & 0 deletions src/styles/styles.js
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,11 @@ const styles = {
color: themeColors.textSupporting,
},

appIconBorderRadius: {
overflow: 'hidden',
borderRadius: 12,
},

unitCol: {
margin: 0,
padding: 0,
Expand Down
Loading