Skip to content

Commit

Permalink
Merge branch 'main' into update-staging-from-main
Browse files Browse the repository at this point in the history
  • Loading branch information
OSBotify committed Jun 15, 2021
2 parents d71ae41 + 08f909c commit d982352
Show file tree
Hide file tree
Showing 70 changed files with 1,250 additions and 484 deletions.
6 changes: 3 additions & 3 deletions .github/ISSUE_TEMPLATE/Standard.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,15 @@ labels: AutoAssignerTriage
If you haven’t already, check out our [contributing guidelines](https://github.com/Expensify/ReactNativeChat/blob/main/CONTRIBUTING.md) for onboarding and email contributors@expensify.com to request to join our Slack channel!
___

## Action Performed:
Break down in numbered steps

## Expected Result:
Describe what you think should've happened

## Actual Result:
Describe what actually happened

## Action Performed:
Break down in numbered steps

## Workaround:
Can the user still use Expensify without this being fixed? Have you informed them of the workaround?

Expand Down
27 changes: 27 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
* [Debugging](#debugging)
* [Structure of the app](#structure-of-the-app)
* [Philosophy](#Philosophy)
* [Internationalization](#Internationalization)
* [Deploying](#deploying)

#### Additional Reading
Expand Down Expand Up @@ -236,6 +237,32 @@ This application is built with the following principles.

----

# Internationalization
This application is built with Internationalization (I18n) / Localization (L10n) support, so it's important to always
localize the following types of data when presented to the user (even accessibility texts that are not rendered):

- Texts: See [translate method](https://github.com/Expensify/Expensify.cash/blob/655ba416d552d5c88e57977a6e0165fb7eb7ab58/src/libs/translate.js#L15)
- Date/time: see [DateUtils](https://github.com/Expensify/Expensify.cash/blob/f579946fbfbdc62acc5bd281dc75cabb803d9af0/src/libs/DateUtils.js)
- Numbers and amounts: see [numberFormat](https://github.com/Expensify/Expensify.cash/tree/965f92fc2a5a2a0d01e6114bf5aa8755b9d9fd1a/src/libs/numberFormat)
- Phones: see [LocalPhoneNumber](https://github.com/Expensify/Expensify.cash/blob/bdfbafe18ee2d60f766c697744f23fad64b62cad/src/libs/LocalePhoneNumber.js#L51-L52)

In most cases, you will be needing to localize data used in a component, if that's the case, there's a HOC [withLocalize](https://github.com/Expensify/Expensify.cash/blob/37465dbd07da1feab8347835d82ed3d2302cde4c/src/components/withLocalize.js).
It will abstract most of the logic you need (mostly subscribe to the [PREFERRED_LOCALE](https://github.com/Expensify/Expensify.cash/blob/6cf1a56df670a11bf61aa67eeb64c1f87161dea1/src/ONYXKEYS.js#L88) Onyx key)
and is the preferred way of localizing things inside components.

Some pointers:

- All translations are stored in language files in [src/languages](https://github.com/Expensify/Expensify.cash/tree/b114bc86ff38e3feca764e75b3f5bf4f60fcd6fe/src/languages).
- We try to group translations by their pages/components
- A common rule of thumb is to move a common word/phrase to be shared when it's in 3 places
- Always prefer longer and more complex strings in the translation files. For example
if you need to generate the text `User has sent $20.00 to you on Oct 25th at 10:05am`, add just one
key to the translation file and use the arrow function version, like so:
`nameOfTheKey: ({amount, dateTime}) => "User has sent " + amount + " to you on " + dateTime,`.
This is because the order of the phrases might vary from one language to another.

----

# Deploying
## QA and deploy cycles
We utilize a CI/CD deployment system built using [GitHub Actions](https://github.com/features/actions) to ensure that new code is automatically deployed to our users as fast as possible. As part of this process, all code is first deployed to our staging environments, where it undergoes quality assurance (QA) testing before it is deployed to production. Typically, pull requests are deployed to staging immediately after they are merged.
Expand Down
4 changes: 2 additions & 2 deletions android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -148,8 +148,8 @@ android {
minSdkVersion rootProject.ext.minSdkVersion
targetSdkVersion rootProject.ext.targetSdkVersion
multiDexEnabled rootProject.ext.multiDexEnabled
versionCode 1001006804
versionName "1.0.68-4"
versionCode 1001006805
versionName "1.0.68-5"
}
splits {
abi {
Expand Down
1 change: 1 addition & 0 deletions assets/images/workspace-default-avatar.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: 1 addition & 1 deletion ios/ExpensifyCash/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@
</dict>
</array>
<key>CFBundleVersion</key>
<string>1.0.68.4</string>
<string>1.0.68.5</string>
<key>ITSAppUsesNonExemptEncryption</key>
<false/>
<key>LSApplicationQueriesSchemes</key>
Expand Down
2 changes: 1 addition & 1 deletion ios/ExpensifyCashTests/Info.plist
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,6 @@
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1.0.68.4</string>
<string>1.0.68.5</string>
</dict>
</plist>
2 changes: 1 addition & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "expensify.cash",
"version": "1.0.68-4",
"version": "1.0.68-5",
"author": "Expensify, Inc.",
"homepage": "https://expensify.cash",
"description": "Expensify.cash is the next generation of Expensify: a reimagination of payments based atop a foundation of chat.",
Expand Down
5 changes: 4 additions & 1 deletion src/App.js
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
import React from 'react';
import {SafeAreaProvider} from 'react-native-safe-area-context';
import CustomStatusBar from './components/CustomStatusBar';
import ErrorBoundary from './components/ErrorBoundary';
import Expensify from './Expensify';

const App = () => (
<SafeAreaProvider>
<CustomStatusBar />
<Expensify />
<ErrorBoundary errorMessage="E.cash crash caught by error boundary">
<Expensify />
</ErrorBoundary>
</SafeAreaProvider>
);

Expand Down
2 changes: 2 additions & 0 deletions src/CONST.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ const CONST = {
IOU: 'IOU',
PAY_WITH_EXPENSIFY: 'payWithExpensify',
FREE_PLAN: 'freePlan',
DEFAULT_ROOMS: 'defaultRooms',
},
BUTTON_STATES: {
DEFAULT: 'default',
Expand Down Expand Up @@ -80,6 +81,7 @@ const CONST = {
MODAL_TYPE: {
CONFIRM: 'confirm',
CENTERED: 'centered',
CENTERED_UNSWIPEABLE: 'centered_unswipeable',
BOTTOM_DOCKED: 'bottom_docked',
POPOVER: 'popover',
RIGHT_DOCKED: 'right_docked',
Expand Down
12 changes: 11 additions & 1 deletion src/Expensify.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import lodashGet from 'lodash/get';
import PropTypes from 'prop-types';
import React, {PureComponent} from 'react';
import {View, StatusBar, AppState} from 'react-native';
import {View, AppState} from 'react-native';
import Onyx, {withOnyx} from 'react-native-onyx';

import BootSplash from './libs/BootSplash';
import StatusBar from './libs/StatusBar';
import listenToStorageEvents from './libs/listenToStorageEvents';
import * as ActiveClientManager from './libs/ActiveClientManager';
import ONYXKEYS from './ONYXKEYS';
Expand All @@ -18,6 +19,8 @@ import UpdateAppModal from './components/UpdateAppModal';
import Visibility from './libs/Visibility';
import GrowlNotification from './components/GrowlNotification';
import {growlRef} from './libs/Growl';
import Navigation from './libs/Navigation/Navigation';
import ROUTES from './ROUTES';

// Initialize the store when the app loads for the first time
Onyx.init({
Expand Down Expand Up @@ -54,6 +57,9 @@ const propTypes = {

/** Currently logged in user accountID */
accountID: PropTypes.number,

/** Should app immediately redirect to new workspace route once authenticated */
redirectToWorkspaceNewAfterSignIn: PropTypes.bool,
}),

/** Whether a new update is available and ready to install. */
Expand All @@ -67,6 +73,7 @@ const defaultProps = {
session: {
authToken: null,
accountID: null,
redirectToWorkspaceNewAfterSignIn: false,
},
updateAvailable: false,
initialReportDataLoaded: false,
Expand Down Expand Up @@ -113,6 +120,9 @@ class Expensify extends PureComponent {
const previousAuthToken = lodashGet(prevProps, 'session.authToken', null);
if (this.getAuthToken() && !previousAuthToken) {
BootSplash.show({fade: true});
if (lodashGet(this.props, 'session.redirectToWorkspaceNewAfterSignIn', false)) {
Navigation.navigate(ROUTES.WORKSPACE_NEW);
}
}

if (this.getAuthToken() && this.props.initialReportDataLoaded) {
Expand Down
3 changes: 3 additions & 0 deletions src/ONYXKEYS.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ export default {
// What the active route is for our navigator. Global route that determines what views to display.
CURRENT_URL: 'currentURL',

// Stores current date
CURRENT_DATE: 'currentDate',

// Currently viewed reportID
CURRENTLY_VIEWED_REPORTID: 'currentlyViewedReportID',

Expand Down
1 change: 1 addition & 0 deletions src/ROUTES.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ export default {
VALIDATE_LOGIN: 'v',
VALIDATE_LOGIN_WITH_VALIDATE_CODE: 'v/:accountID/:validateCode',
ENABLE_PAYMENTS: 'enable-payments',
WORKSPACE_NEW: 'workspace/new',

/**
* @param {String} route
Expand Down
10 changes: 9 additions & 1 deletion src/components/AttachmentModal.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React, {PureComponent} from 'react';
import PropTypes from 'prop-types';
import {View} from 'react-native';
import Str from 'expensify-common/lib/str';
import {withOnyx} from 'react-native-onyx';
import CONST from '../CONST';
import Modal from './Modal';
Expand Down Expand Up @@ -97,10 +98,17 @@ class AttachmentModal extends PureComponent {
const attachmentViewStyles = this.props.isSmallScreenWidth
? [styles.imageModalImageCenterContainer]
: [styles.imageModalImageCenterContainer, styles.p5];

// If our attachment is a PDF, make the Modal unswipeable
const modalType = (this.state.sourceURL
&& (Str.isPDF(this.state.sourceURL) || (this.state.file
&& Str.isPDF(this.state.file.name || this.props.translate('attachmentView.unknownFilename')))))
? CONST.MODAL.MODAL_TYPE.CENTERED_UNSWIPEABLE
: CONST.MODAL.MODAL_TYPE.CENTERED;
return (
<>
<Modal
type={CONST.MODAL.MODAL_TYPE.CENTERED}
type={modalType}
onSubmit={this.submitAndClose}
onClose={() => this.setState({isModalOpen: false})}
isVisible={this.state.isModalOpen}
Expand Down
3 changes: 2 additions & 1 deletion src/components/Button.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import OpacityView from './OpacityView';

const propTypes = {
/** The text for the button label */
text: PropTypes.string.isRequired,
text: PropTypes.string,

/** Indicates whether the button should be disabled and in the loading state */
isLoading: PropTypes.bool,
Expand Down Expand Up @@ -44,6 +44,7 @@ const propTypes = {
};

const defaultProps = {
text: '',
isLoading: false,
isDisabled: false,
onPress: () => {},
Expand Down
52 changes: 52 additions & 0 deletions src/components/ErrorBoundary/BaseErrorBoundary.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import React from 'react';
import PropTypes from 'prop-types';

const propTypes = {
/* A message posted to `logError` (along with error data) when this component intercepts an error */
errorMessage: PropTypes.string.isRequired,

/* A function to perform the actual logging since different platforms support different tools */
logError: PropTypes.func,

/* Actual content wrapped by this error boundary */
children: PropTypes.node.isRequired,
};

const defaultProps = {
logError: () => {},
};

/**
* This component captures an error in the child component tree and logs it to the server
* It can be used to wrap the entire app as well as to wrap specific parts for more granularity
* @see {@link https://reactjs.org/docs/error-boundaries.html#where-to-place-error-boundaries}
*/
class BaseErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = {hasError: false};
}

static getDerivedStateFromError() {
// Update state so the next render will show the fallback UI.
return {hasError: true};
}

componentDidCatch(error, errorInfo) {
this.props.logError(this.props.errorMessage, error, errorInfo);
}

render() {
if (this.state.hasError) {
// For the moment we've decided not to render any fallback UI
return null;
}

return this.props.children;
}
}

BaseErrorBoundary.propTypes = propTypes;
BaseErrorBoundary.defaultProps = defaultProps;

export default BaseErrorBoundary;
9 changes: 9 additions & 0 deletions src/components/ErrorBoundary/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import BaseErrorBoundary from './BaseErrorBoundary';
import Log from '../../libs/Log';

BaseErrorBoundary.defaultProps.logError = (errorMessage, error, errorInfo) => {
// Log the error to the server
Log.alert(errorMessage, 0, {error: error.message, errorInfo}, false);
};

export default BaseErrorBoundary;
16 changes: 16 additions & 0 deletions src/components/ErrorBoundary/index.native.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import crashlytics from '@react-native-firebase/crashlytics';

import BaseErrorBoundary from './BaseErrorBoundary';
import Log from '../../libs/Log';

BaseErrorBoundary.defaultProps.logError = (errorMessage, error, errorInfo) => {
// Log the error to the server
Log.alert(errorMessage, 0, {error: error.message, errorInfo}, false);

/* On native we also log the error to crashlytics
* Since the error was handled we need to manually tell crashlytics about it */
crashlytics().log(`errorInfo: ${JSON.stringify(errorInfo)}`);
crashlytics().recordError(error);
};

export default BaseErrorBoundary;
27 changes: 27 additions & 0 deletions src/components/FixedFooter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import React from 'react';
import {View} from 'react-native';
import PropTypes from 'prop-types';
import styles from '../styles/styles';

const propTypes = {
/** Children to wrap in FixedFooter. */
children: PropTypes.node.isRequired,

/** Styles to be assigned to Container */
style: PropTypes.arrayOf(PropTypes.object),
};

const defaultProps = {
style: [],
};

const FixedFooter = props => (
<View style={[styles.ph5, styles.pb5, ...props.style]}>
{props.children}
</View>
);

FixedFooter.propTypes = propTypes;
FixedFooter.defaultProps = defaultProps;
FixedFooter.displayName = 'FixedFooter';
export default FixedFooter;
Loading

0 comments on commit d982352

Please sign in to comment.