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 section about directory/file naming to the Readme #625

Merged
merged 7 commits into from
Oct 15, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
46 changes: 37 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,15 +100,6 @@ Our React Native Android app now uses the `Hermes` JS engine which requires your
1. The application uses [React-Router](https://reactrouter.com/native/guides/quick-start) for navigating between parts of the app.
1. [Higher Order Components](https://reactjs.org/docs/higher-order-components.html) are used to connect React components to persistent storage via Ion.

## Platform-Specific File Extensions
In most cases, the code written for this repo should be platform-independent. In such cases, each module should have a single file, `index.js`, which defines the module's exports. There are, however, some cases in which a feature is intrinsically tied to the underlying platform. In such cases, the following file extensions can be used to export platform-specific code from a module:
- Mobile => `index.native.js`
- iOS/Android => `index.ios.js`/`index.android.js`
- Web => `index.website.js`
- Desktop => `index.desktop.js`

Note that `index.js` should be the default. i.e: If you have mobile-specific implementation in `index.native.js`, then the desktop/web implementation can be contained in a shared `index.js`. Furthermore, `index.native.js` should not be included in the same module as `index.ios.js` or `index.android.js`, nor should `index.js` be included in the same module as `index.website.js` or `index.desktop.js`.

## Structure of the app
These are the main pieces of the application.

Expand Down Expand Up @@ -138,6 +129,43 @@ This layer is solely responsible for:
- Reflecting exactly the data that is in persistent storage by using `withIon()` to bind to Ion data.
- Taking user input and passing it to an action

### Directory structure

Almost all the code is located in the `src` folder, inside it there's some organization, we chose to name directories that are
created to house a collection of items in plural form and using camelCase (eg: pages, libs, etc), the main ones we have for now are:

- components: React native components that are re-used in several places.
- libs: Library classes/functions, these are not React native components (ie: they are not UI)
- pages: These are components that define pages in the app. The component that defines the page itself should be named
`<pageName>Page` if there are components used only inside one page, they should live in its own directory named after the `<pageName>`.
- styles: These files define styles used among components/pages

### File naming/structure

Files should be named after the component/function/constants they export, respecting the casing used for it. ie:

- If you export a constant named `CONST` it's file/directory should be named the `CONST`.
- If you export a component named `Text` the file/directory should be named `Text`
- If you export a function named `guid` the file/directory should be named `guid`.
- For files that are utilities that export several functions/classes use the UpperCamelCase version ie: `DateUtils`.
marcaaron marked this conversation as resolved.
Show resolved Hide resolved
- HOCs should be named in camelCase like withIon.
- All React components should be PascalCase (a.k.a. UpperCamelCase 🐫).

## Platform-Specific File Extensions
In most cases, the code written for this repo should be platform-independent. In such cases, each module should have a single file, `index.js`, which defines the module's exports. There are, however, some cases in which a feature is intrinsically tied to the underlying platform. In such cases, the following file extensions can be used to export platform-specific code from a module:
- Mobile => `index.native.js`
- iOS/Android => `index.ios.js`/`index.android.js`
- Web => `index.website.js`
- Desktop => `index.desktop.js`

Note that `index.js` should be the default. i.e: If you have mobile-specific implementation in `index.native.js`, then the desktop/web implementation can be contained in a shared `index.js`. Furthermore, `index.native.js` should not be included in the same module as `index.ios.js` or `index.android.js`, nor should `index.js` be included in the same module as `index.website.js` or `index.desktop.js`.

### API building

When adding new API commands (and preferrably when starting using a new one that was not yet used in this codebase) always
prefer to return the created/updated data in the command itself, instead of saving and reloading. ie: if we call `CreateTransaction`,
we should prefer making `CreateTransaction` return the data it just created instead of calling `CreateTransaction` then `Get` rvl=transactionList
Copy link
Contributor

Choose a reason for hiding this comment

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

prefer to return the created/updated data in the command itself

I'm confused by this statement. Are we talking about how to design the API command itself in PHP/Auth? e.g. if I add CreateTransaction to this codebase then I should update the API service to return some data instead of fetching data again after it is created in this app?

If that's correct, I don't really have anything against this, but it might require a better example and explanation as to why we should do this.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, that's exactly what I meant, I will add some details to this to make it clearer why

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Actually, I re-read it and I am unsure how to make it clearer, can you suggest an edit?

Copy link
Contributor

Choose a reason for hiding this comment

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

eh it's cool I think this will just intuitively make more sense once we have more API commands conforming to this rule. at the moment, I couldn't think of anything that even did this so thought it was strange to mention here.


# Deploying
## Continuous deployment / GitHub workflows
Every PR merged into `master` will kick off the **Create a new version** GitHub workflow defined in `.github/workflows/version.yml`.
Expand Down
14 changes: 7 additions & 7 deletions src/Expensify.js
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
import React, {Component} from 'react';
import {View} from 'react-native';
import PropTypes from 'prop-types';
import {recordCurrentlyViewedReportID, recordCurrentRoute} from './lib/actions/App';
import SignInPage from './page/SignInPage';
import HomePage from './page/home/HomePage';
import Ion from './lib/Ion';
import * as ActiveClientManager from './lib/ActiveClientManager';
import {recordCurrentlyViewedReportID, recordCurrentRoute} from './libs/actions/App';
import SignInPage from './pages/SignInPage';
import HomePage from './pages/home/HomePage';
import Ion from './libs/Ion';
import * as ActiveClientManager from './libs/ActiveClientManager';
import IONKEYS from './IONKEYS';
import withIon from './components/withIon';
import styles from './style/StyleSheet';
import styles from './styles/StyleSheet';

import {
Route,
Router,
Redirect,
Switch
} from './lib/Router';
} from './libs/Router';
import ROUTES from './ROUTES';

// Initialize the store when the app loads for the first time
Expand Down
2 changes: 1 addition & 1 deletion src/components/InlineCodeBlock/index.android.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import {View} from 'react-native';
import {webViewStyles} from '../../style/StyleSheet';
import {webViewStyles} from '../../styles/StyleSheet';

const propTypes = {
children: PropTypes.node.isRequired,
Expand Down
2 changes: 1 addition & 1 deletion src/components/InlineCodeBlock/index.ios.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import {View} from 'react-native';
import styles, {webViewStyles} from '../../style/StyleSheet';
import styles, {webViewStyles} from '../../styles/StyleSheet';

const propTypes = {
children: PropTypes.node.isRequired,
Expand Down
2 changes: 1 addition & 1 deletion src/components/InlineCodeBlock/index.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from 'react';
import PropTypes from 'prop-types';
import {Text} from 'react-native';
import {webViewStyles} from '../../style/StyleSheet';
import {webViewStyles} from '../../styles/StyleSheet';

const propTypes = {
children: PropTypes.node.isRequired,
Expand Down
2 changes: 1 addition & 1 deletion src/components/InvertedFlatList/BaseInvertedFlatList.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import _ from 'underscore';
import React, {forwardRef, Component} from 'react';
import PropTypes from 'prop-types';
import {FlatList, View} from 'react-native';
import {lastItem} from '../../lib/CollectionUtils';
import {lastItem} from '../../libs/CollectionUtils';

const propTypes = {
// Same as FlatList can be any array of anything
Expand Down
2 changes: 1 addition & 1 deletion src/components/PressableLink/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React from 'react';
import {Link} from '../../lib/Router';
import {Link} from '../../libs/Router';

/**
* On web, no changes to Link are required to make links pressable
Expand Down
2 changes: 1 addition & 1 deletion src/components/PressableLink/index.native.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from 'react';
import PropTypes from 'prop-types';
import {Link} from '../../lib/Router';
import {Link} from '../../libs/Router';

/**
* On the native layers, we need to convert the onClick prop to onPress for react-router-native
Expand Down
4 changes: 2 additions & 2 deletions src/components/Text.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ import React from 'react';
import PropTypes from 'prop-types';
import _ from 'underscore';
import {Text as RNText} from 'react-native';
import fontFamily from '../style/fontFamily';
import {colors} from '../style/StyleSheet';
import fontFamily from '../styles/fontFamily';
import {colors} from '../styles/StyleSheet';

const propTypes = {
// The color of the text
Expand Down
2 changes: 1 addition & 1 deletion src/components/withIon.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
*/
import React from 'react';
import _ from 'underscore';
import Ion from '../lib/Ion';
import Ion from '../libs/Ion';

/**
* Returns the display name of a component
Expand Down
4 changes: 2 additions & 2 deletions src/lib/API.js → src/libs/API.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import CONFIG from '../CONFIG';
import * as Pusher from './Pusher/pusher';
import ROUTES from '../ROUTES';
import Str from './Str';
import Guid from './Guid';
import guid from './guid';
import redirectToSignIn from './actions/SignInRedirect';

// Queue for network requests so we don't lose actions done by the user while offline
Expand Down Expand Up @@ -364,7 +364,7 @@ function authenticate(parameters) {
// After the user authenticates, create a new login for the user so that we can reauthenticate when the
// authtoken expires
.then(response => (
createLogin(Str.generateDeviceLoginID(), Guid())
createLogin(Str.generateDeviceLoginID(), guid())
.then(() => setSuccessfulSignInData(response, parameters.exitTo))
))
.catch((error) => {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import _ from 'underscore';
import Guid from '../Guid';
import guid from '../guid';
import Ion from '../Ion';
import IONKEYS from '../../IONKEYS';

const clientID = Guid();
const clientID = guid();
const maxClients = 20;

let activeClients;
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
4 changes: 2 additions & 2 deletions src/lib/Str.js → src/libs/Str.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import _ from 'underscore';
import {AllHtmlEntities} from 'html-entities';
import Guid from './Guid';
import guid from './guid';


const Str = {
Expand Down Expand Up @@ -59,7 +59,7 @@ const Str = {
* @returns {string}
*/
generateDeviceLoginID() {
return `react-native-chat-${Guid()}`;
return `react-native-chat-${guid()}`;
},

/**
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
8 changes: 4 additions & 4 deletions src/page/SignInPage.js → src/pages/SignInPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,12 @@ import {
import PropTypes from 'prop-types';
import _ from 'underscore';
import CONFIG from '../CONFIG';
import compose from '../lib/compose';
import {withRouter} from '../lib/Router';
import {signIn} from '../lib/actions/Session';
import compose from '../libs/compose';
import {withRouter} from '../libs/Router';
import {signIn} from '../libs/actions/Session';
import IONKEYS from '../IONKEYS';
import withIon from '../components/withIon';
import styles, {colors} from '../style/StyleSheet';
import styles, {colors} from '../styles/StyleSheet';
import logo from '../../assets/images/expensify-logo_reversed.png';

const propTypes = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@ import React from 'react';
import {View, Image, TouchableOpacity} from 'react-native';
import PropTypes from 'prop-types';
import Text from '../../components/Text';
import styles from '../../style/StyleSheet';
import styles from '../../styles/StyleSheet';
import IONKEYS from '../../IONKEYS';
import withIon from '../../components/withIon';
import {withRouter} from '../../lib/Router';
import {withRouter} from '../../libs/Router';
import LHNToggle from '../../../assets/images/icon-menu-toggle.png';
import compose from '../../lib/compose';
import compose from '../../libs/compose';

const propTypes = {
// Toggles the hamburger menu open and closed
Expand Down
14 changes: 7 additions & 7 deletions src/page/home/HomePage.js → src/pages/home/HomePage.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,17 @@ import {
Easing
} from 'react-native';
import {SafeAreaInsetsContext, SafeAreaProvider} from 'react-native-safe-area-context';
import {Route} from '../../lib/Router';
import styles, {getSafeAreaPadding} from '../../style/StyleSheet';
import {Route} from '../../libs/Router';
import styles, {getSafeAreaPadding} from '../../styles/StyleSheet';
import Header from './HeaderView';
import Sidebar from './sidebar/SidebarView';
import Main from './MainView';
import {subscribeToReportCommentEvents, fetchAll as fetchAllReports} from '../../lib/actions/Report';
import {fetch as fetchPersonalDetails} from '../../lib/actions/PersonalDetails';
import * as Pusher from '../../lib/Pusher/pusher';
import UnreadIndicatorUpdater from '../../lib/UnreadIndicatorUpdater';
import {subscribeToReportCommentEvents, fetchAll as fetchAllReports} from '../../libs/actions/Report';
import {fetch as fetchPersonalDetails} from '../../libs/actions/PersonalDetails';
import * as Pusher from '../../libs/Pusher/pusher';
import UnreadIndicatorUpdater from '../../libs/UnreadIndicatorUpdater';
import ROUTES from '../../ROUTES';
import NetworkConnection from '../../lib/NetworkConnection';
import NetworkConnection from '../../libs/NetworkConnection';

const windowSize = Dimensions.get('window');
const widthBreakPoint = 1000;
Expand Down
6 changes: 3 additions & 3 deletions src/page/home/MainView.js → src/pages/home/MainView.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ import _ from 'underscore';
import ReportView from './report/ReportView';
import withIon from '../../components/withIon';
import IONKEYS from '../../IONKEYS';
import styles from '../../style/StyleSheet';
import {withRouter} from '../../lib/Router';
import compose from '../../lib/compose';
import styles from '../../styles/StyleSheet';
import {withRouter} from '../../libs/Router';
import compose from '../../libs/compose';

const propTypes = {
// This comes from withRouter
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@ import React from 'react';
import PropTypes from 'prop-types';
import {View, Image, TouchableOpacity} from 'react-native';
import _ from 'underscore';
import styles, {colors} from '../../../style/StyleSheet';
import styles, {colors} from '../../../styles/StyleSheet';
import TextInputFocusable from '../../../components/TextInputFocusable';
import sendIcon from '../../../../assets/images/icon-send.png';
import IONKEYS from '../../../IONKEYS';
import paperClipIcon from '../../../../assets/images/icon-paper-clip.png';
import ImagePicker from '../../../lib/ImagePicker';
import ImagePicker from '../../../libs/ImagePicker';
import withIon from '../../../components/withIon';
import {addAction, saveReportComment} from '../../../lib/actions/Report';
import {addAction, saveReportComment} from '../../../libs/actions/Report';

const propTypes = {
// A method to call when the form is submitted
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import React from 'react';
import PropTypes from 'prop-types';
import {Text} from 'react-native';
import DateUtils from '../../../lib/DateUtils';
import styles from '../../../style/StyleSheet';
import DateUtils from '../../../libs/DateUtils';
import styles from '../../../styles/StyleSheet';

const propTypes = {
// UTC timestamp for when the action was created
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@ import {
Linking, ActivityIndicator, View, Dimensions
} from 'react-native';
import PropTypes from 'prop-types';
import Str from '../../../lib/Str';
import Str from '../../../libs/Str';
import ReportActionFragmentPropTypes from './ReportActionFragmentPropTypes';
import styles, {webViewStyles, colors} from '../../../style/StyleSheet';
import styles, {webViewStyles, colors} from '../../../styles/StyleSheet';
import Text from '../../../components/Text';
import AnchorForCommentsOnly from '../../../components/AnchorForCommentsOnly';
import {getAuthToken} from '../../../lib/API';
import {getAuthToken} from '../../../libs/API';
import InlineCodeBlock from '../../../components/InlineCodeBlock';

const propTypes = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {View} from 'react-native';
import PropTypes from 'prop-types';
import ReportActionPropTypes from './ReportActionPropTypes';
import ReportActionItemMessage from './ReportActionItemMessage';
import styles from '../../../style/StyleSheet';
import styles from '../../../styles/StyleSheet';

const propTypes = {
// All the data of the action
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React from 'react';
import {View} from 'react-native';
import PropTypes from 'prop-types';
import _ from 'underscore';
import styles from '../../../style/StyleSheet';
import styles from '../../../styles/StyleSheet';
import ReportActionItemFragment from './ReportActionItemFragment';
import ReportActionPropTypes from './ReportActionPropTypes';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import _ from 'underscore';
import ReportActionPropTypes from './ReportActionPropTypes';
import ReportActionItemMessage from './ReportActionItemMessage';
import ReportActionItemFragment from './ReportActionItemFragment';
import styles from '../../../style/StyleSheet';
import styles from '../../../styles/StyleSheet';
import CONST from '../../../CONST';
import ReportActionItemDate from './ReportActionItemDate';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@ import _ from 'underscore';
import lodashGet from 'lodash.get';
import Text from '../../../components/Text';
import withIon from '../../../components/withIon';
import {fetchActions, updateLastReadActionID} from '../../../lib/actions/Report';
import {fetchActions, updateLastReadActionID} from '../../../libs/actions/Report';
import IONKEYS from '../../../IONKEYS';
import ReportActionItem from './ReportActionItem';
import styles from '../../../style/StyleSheet';
import styles from '../../../styles/StyleSheet';
import ReportActionPropTypes from './ReportActionPropTypes';
import InvertedFlatList from '../../../components/InvertedFlatList';
import {lastItem} from '../../../lib/CollectionUtils';
import {lastItem} from '../../../libs/CollectionUtils';

const propTypes = {
// The ID of the report actions will be created for
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ import {View} from 'react-native';
import PropTypes from 'prop-types';
import ReportActionView from './ReportActionsView';
import ReportActionCompose from './ReportActionCompose';
import {addAction} from '../../../lib/actions/Report';
import {addAction} from '../../../libs/actions/Report';
import KeyboardSpacer from '../../../components/KeyboardSpacer';
import styles from '../../../style/StyleSheet';
import styles from '../../../styles/StyleSheet';

const propTypes = {
// The ID of the report actions will be created for
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from 'react';

import styles from '../../../../style/StyleSheet';
import openURLInNewTab from '../../../../lib/openURLInNewTab';
import styles from '../../../../styles/StyleSheet';
import openURLInNewTab from '../../../../libs/openURLInNewTab';
import Text from '../../../../components/Text';

const AppLinks = () => (
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from 'react';
import styles from '../../../../style/StyleSheet';
import openURLInNewTab from '../../../../lib/openURLInNewTab';
import styles from '../../../../styles/StyleSheet';
import openURLInNewTab from '../../../../libs/openURLInNewTab';
import Text from '../../../../components/Text';

const AppLinks = () => (
Expand Down
Loading