Skip to content

Commit

Permalink
Merge pull request #101 from wednesday-solutions/dev
Browse files Browse the repository at this point in the history
Recoil, PostHog, Sentry Integrations
  • Loading branch information
Anurag-Wednesday authored Sep 11, 2024
2 parents 3769d60 + ca5affe commit 2d7a300
Show file tree
Hide file tree
Showing 89 changed files with 3,387 additions and 3,341 deletions.
4 changes: 4 additions & 0 deletions .env
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
JSON_PLACEHOLDER_API="https://jsonplaceholder.typicode.com"
SIMPSONS_API="https://thesimpsonsquoteapi.glitch.me/"
SENTRY_DSN= "YOU_SENTRY_DSN"
POSTHOG_KEY= 'YOUR_POSTHOG_PROJECT_KEY'
12 changes: 11 additions & 1 deletion .eslintignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,14 @@ android/**
ios/**
__tests__/**
**/tests/***
.eslintrc.js
web-build/**
.eslintrc.js
e2e/**/*.*
metrics/*
jest.setup.js
babel.config.js
reports
report.json
growthbook.js
**/tests/*.test.js
webpack.config.js
21 changes: 9 additions & 12 deletions .eslintrc.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
const fs = require('fs');
const path = require('path');


const prettierOptions = JSON.parse(
fs.readFileSync(path.resolve(__dirname, '.prettierrc'), 'utf8'),
fs.readFileSync(path.resolve(__dirname, '.prettierrc'), 'utf8')
);
module.exports = {
root: true,
parser: 'babel-eslint',
extends: [
'airbnb',
'prettier',
'prettier',
'prettier/react',
'plugin:prettier/recommended',
'plugin:sonarjs/recommended',
Expand All @@ -22,7 +21,6 @@ module.exports = {
'immutable',
'sonarjs',
'prettier',
'redux-saga',
'react-native',
'react',
'react-hooks',
Expand Down Expand Up @@ -54,13 +52,13 @@ module.exports = {
'import/no-unresolved': 0,
'import/prefer-default-export': 0,
'react/jsx-props-no-spreading': 0,
'camelcase': ['error', { 'properties': 'always', ignoreImports: false}],
camelcase: ['error', { properties: 'always', ignoreImports: false }],
indent: [
2,
2,
{
SwitchCase: 1,
},
SwitchCase: 1
}
],
'jsx-a11y/aria-props': 2,
'jsx-a11y/heading-has-content': 0,
Expand Down Expand Up @@ -96,8 +94,6 @@ module.exports = {
'react/require-extension': 0,
'react/self-closing-comp': 0,
'react/sort-comp': 0,
'redux-saga/no-yield-in-race': 2,
'redux-saga/yield-effects': 2,
'require-yield': 0,
'react/no-array-index-key': 0,
'react/jsx-curly-newline': 0,
Expand All @@ -120,7 +116,7 @@ module.exports = {
}
],
'no-shadow': 'error',
complexity: ['error', 10],
complexity: ['error', 4],
'no-empty': 'error',
'import/order': [
'error',
Expand All @@ -146,7 +142,8 @@ module.exports = {
}
],
'fp/no-nil': 0,
'fp/no-unused-expression': 0
'fp/no-unused-expression': 0,
'fp/no-throw': 0
},
settings: {
'import/resolver': {
Expand All @@ -171,4 +168,4 @@ module.exports = {
}
}
}
};
};
6 changes: 3 additions & 3 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
name: react-native-template
on:
push:
branches: [ master ]
branches: [master]
pull_request:
branches: [ master ]
branches: [master, dev]
jobs:
install-and-test:
runs-on: ubuntu-latest
Expand All @@ -13,7 +13,7 @@ jobs:
run: yarn

- name: Lint
run: npm run lint
run: yarn lint

- name: Test and generate coverage report
uses: artiomtr/jest-coverage-report-action@v2.2.9
Expand Down
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -62,4 +62,5 @@ buck-out/
web-build/
dist/
reports
coverage
coverage
.env.local
6 changes: 6 additions & 0 deletions App.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@

import { registerRootComponent } from 'expo';
import App from '@app/app';
import { SENTRY_DSN } from '@env';
import * as Sentry from '@sentry/react-native';

Sentry.init({
dsn: SENTRY_DSN
});

if (!window.Intl) {
new Promise(resolve => {
Expand Down
96 changes: 50 additions & 46 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,10 @@
</p>

<p>
An enterprise React Native template application showcasing - Testing strategies, Global state management, middleware support, a network layer, component library integration, localization, navigation configuration, and Continuous integration.
An enterprise React Native template application showcasing - Testing strategies, Global state management, middleware support, a network layer, component library integration, localization, navigation configuration, Continuous integration, analytics, feature flagging, and error tracking.
</p>

___

---

<p>
<h4>
Expand All @@ -31,9 +30,10 @@ An enterprise React Native template application showcasing - Testing strategies,
</a>
</div>

___
---

<span>We’re always looking for people who value their work, so come and join us. <a href="https://www.wednesday.is/hiring">We are hiring!</a></span>

<span>We’re always looking for people who value their work, so come and join us. <a href="https://www.wednesday.is/hiring">We are hiring!</a></span>
</div>

## Architecture
Expand All @@ -42,51 +42,58 @@ The driving goal of the architecture of the template is separation of concerns.

- **Presentational components are separated from scenes** (aka "screens").

Presentational components are small components that are concerned with *how things look*. Scenes usually define whole application screens and are concerned with *how things work*: they include presentational components and wire everything together.
If you are interested you can [read more about it here](https://medium.com/@dan_abramov/smart-and-dumb-components-7ca2f9a7c7d0).
Presentational components are small components that are concerned with _how things look_. Scenes usually define whole application screens and are concerned with _how things work_: they include presentational components and wire everything together.

If you are interested you can [read more about it here](https://medium.com/@dan_abramov/smart-and-dumb-components-7ca2f9a7c7d0).

### Atomic Design for react native architecture
Atomic design further solidifies the idea of seperating screens into components and scenes (containers). The design primarily focuses on reusablity of code, which brings us to the differentiation of components into atoms, molecules and organisms. Analogous to the Atomic design of chemicals, components are seperated by their composition. The components require increasing context as their complexity increases, since each component is tested, this promotes a more granular test coverage.

- **Atoms**
Atoms are the smallest components that can be reused. Button, Text, and Icons are good example of Atoms. Atoms can be used without context and cannot be further divided.

- **Molecules**
Molecules are built from one or more atoms that are slightly complex presentational components.
Atomic design further solidifies the idea of separating screens into components and scenes (containers). The design primarily focuses on reusability of code, which brings us to the differentiation of components into atoms, molecules, and organisms. Analogous to the Atomic design of chemicals, components are separated by their composition. The components require increasing context as their complexity increases, since each component is tested, this promotes a more granular test coverage.

- **Atoms**
Atoms are the smallest components that can be reused. Button, Text, and Icons are good examples of Atoms. Atoms can be used without context and cannot be further divided.

- **Molecules**
Molecules are built from one or more atoms that are slightly complex presentational components.

- **Organisms**
Organisms contain multiple molecules, atoms, and perform a specific purpose. In the example screen, an organism is used that displays the fetched character and quote.

- **State is managed using [Recoil](https://recoiljs.org/)**.

Recoil provides a set of utilities to manage global state in React Native. It allows for atoms (the smallest units of state) and selectors (to transform or combine state) to handle the app's state efficiently. This eliminates the need for Redux, actions, and reducers, simplifying the process for managing state across components.

- **Organisms**
Organisms contain multiple molecules, atoms and perform a specific purpose. In the example screen, an organism is used that displays the fetched character and quote.
Atoms are the core units of state, and selectors are derived state values computed from one or more atoms. Recoil's state management is highly reactive and more efficient for handling state at a granular level.

- **State is managed using global [Redux](https://redux.js.org/) stores**.
If you are interested you can [read more about it here](https://recoiljs.org/docs/introduction/getting-started).

When applications grow, sharing state and its changes can become very hard. Questions like "How can I access this data?" or "When did this change?" are common, just like passing data around components just to be able to use it in nested components.

With Redux, state is shared using global *stores*, and changes are predictable: *actions* are applied by *reducers* to the state. While the pattern can be a bit much for small projects, the clear separation of responsibilities and predictability helps with bigger applications.

If you are interested you can [read more about it here](https://redux.js.org/introduction/motivation).

- **Application side-effects (API calls, etc.) are separated from UI and state manipulation using [Redux Saga](https://redux-saga.js.org/)**.
- **Side Effects (API calls, etc.) are managed within components or with Recoil selectors**.

Using Redux Saga has two benefits: keeping application side-effects and related business logic out of UI components, as well as executing that logic in an asynchronous way without ending in callback hell.

Sagas are triggered by Redux actions and can also trigger Redux actions to alter state. By using JavaScript generators (`yield`), sagas are written in a synchronous-like manner while still executing asynchronously.
Recoil allows for managing side effects within the components themselves or through asynchronous selectors. This keeps your side effects closer to where they are needed.

## Analytics, Feature Flagging, and Error Tracking

- **[PostHog](https://posthog.com/)** is integrated to provide analytics and event tracking across the application. PostHog captures user interactions and events, which helps in analyzing user behavior and improving the app based on data-driven insights.

- **[GrowthBook](https://www.growthbook.io/)** is used for feature flagging. With GrowthBook, you can easily manage the rollout of features to users, enabling A/B testing, and controlling which features are visible to which segments of your user base without redeploying the app.

- **[Sentry](https://sentry.io/)** is used for error tracking and reporting. Sentry captures errors and exceptions from the app in real time, providing detailed insights into the errors that occur, enabling faster bug fixing and better app stability.

## Content

The React Native Template contains:

- a [React Native](https://facebook.github.io/react-native/) (v**0.60.6**) application (in "[ejected](https://github.com/react-community/create-react-native-app/blob/master/EJECTING.md)" mode to allow using dependencies that rely on native code)
- a [React Native](https://facebook.github.io/react-native/) (v**0.73.6**) application (in "[ejected](https://github.com/react-community/create-react-native-app/blob/master/EJECTING.md)" mode to allow using dependencies that rely on native code)
- a [clear directory layout](#directory-layout) to provide a base architecture for your application
- [Redux](https://redux.js.org/) (v4.0.1) to help manage state
- [Redux Persist](https://github.com/rt2zz/redux-persist) (v5.10.0) to persist the Redux state
- [Redux Sagas](https://redux-saga.js.org) (v1.0.2) to separate side-effects and logic from state and UI logic
- [React Navigation](https://reactnavigation.org/) (v3.11.2) with a [`NavigationService`](app/services/navigationService.js) to handle routing and navigation in the app, with a splash screen setup by default
- [reduxsauce](https://github.com/infinitered/reduxsauce) (v1.0.1) to facilitate using Redux
- [apisauce](https://github.com/infinitered/apisauce/) to make API calls (v0.19.0)
- [Recoil](https://recoiljs.org/) to manage global state
- [React Navigation](https://reactnavigation.org/) (v5.3.15) with a [`NavigationService`](app/services/navigationService.js) to handle routing and navigation in the app, with a splash screen setup by default
- [axios](https://github.com/axios/axios/) to make API calls (v0.27.2)
- [PostHog](https://posthog.com/) for analytics
- [GrowthBook](https://www.growthbook.io/) for feature flagging
- [Sentry](https://sentry.io/) for error tracking
- [prettier](https://prettier.io/) and [eslint](https://eslint.org/) preconfigured for React Native

The template includes an example (displaying fake user data) from UI components to the saga. The example is easy to remove so that it doesn't get in the way.
The template includes an example (displaying fake user data) from UI components to state management using Recoil. The example is easy to remove so that it doesn't get in the way.

## Directory layout

Expand All @@ -97,12 +104,10 @@ The template includes an example (displaying fake user data) from UI components
- [`app/scenes`](app/components/scenes): scenes are screens that can be navigated to
- [`app/config`](app/config): configuration of the application
- [`app/assets`](app/assets): assets (image, audio files, ...) used by the application
- [`app/navigators`](app/navigators): react navigation navigators
- [`app/navigators`](app/navigators): react navigation navigators
- [`app/services`](app/services): application services, e.g. API clients
- [`app/utils`](app/utils): Util methods and constants
- [`app/themes`](app/themes): base styles for the application
- [`app/rootSaga`](app/rootSaga): all the individual sagas need to be added here.
- [`app/rootReducer`](app/rootReducer): all the individual reducers need to be added here.

For more information on each directory, click the link and read the directory's README.

Expand All @@ -112,9 +117,8 @@ Node 8 or greater is required. Development for iOS requires a Mac and Xcode 9 or

You also need to install the dependencies required by React Native:

- for [Android development](https://facebook.github.io/react-native/docs/getting-started.html#installing-dependencies-3)
- for [iOS development](https://facebook.github.io/react-native/docs/getting-started.html#installing-dependencies)

- for [Android development](https://reactnative.dev/docs/set-up-your-environment?platform=android)
- for [iOS development](https://reactnative.dev/docs/set-up-your-environment?platform=ios)

## Using the template

Expand All @@ -126,16 +130,16 @@ To create a new project using the template:
- rename the React Native project to your own project name: `yarn run rename -- <YourProjectName>` (the default name is `ReactNativeApplication`)
- remove the LICENSE file and the "License" section from the README if your project is not open source


### Running expo project

### Android
- `yarn run android`

- `yarn run android`

### iOS

- `yarn run ios`


## Useful documentation

### Deployment
Expand All @@ -145,5 +149,5 @@ To create a new project using the template:

### Package dependencies

- You may want to use [CocoaPods](https://cocoapods.org/) to manage your dependencies (iOS only)
- You may want to use [CocoaPods](https://cocoapods.org/) to manage your dependencies (iOS only)
- [Using CocoaPods to manage your package dependencies](docs/setup%20cocoapods.md)
3 changes: 2 additions & 1 deletion android/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@ def enableProguardInReleaseBuilds = (findProperty('android.enableProguardInRelea
*/
def jscFlavor = 'org.webkit:android-jsc:+'

apply from: new File(["node", "--print", "require.resolve('@sentry/react-native/package.json')"].execute().text.trim(), "../sentry.gradle")
android {
ndkVersion rootProject.ext.ndkVersion

Expand Down Expand Up @@ -173,4 +174,4 @@ dependencies {
}

apply from: new File(["node", "--print", "require.resolve('@react-native-community/cli-platform-android/package.json', { paths: [require.resolve('react-native/package.json')] })"].execute(null, rootDir).text.trim(), "../native_modules.gradle");
applyNativeModulesAppBuildGradle(project)
applyNativeModulesAppBuildGradle(project)
7 changes: 7 additions & 0 deletions android/sentry.properties
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@

auth.token=YOUR_SENTRY_AUTH_TOKEN

defaults.org=YOUR_ORG_NAME
defaults.project=YOUR_PROJECT_NAME

defaults.url=https://sentry.io/
14 changes: 12 additions & 2 deletions app.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"expo": {
"name": "react-native-template-ws",
"slug": "rnt-ws",
"slug": "react-native-template-cd",
"version": "1.0.0",
"orientation": "portrait",
"icon": "./assets/icon.png",
Expand All @@ -22,6 +22,16 @@
},
"ios": {
"bundleIdentifier": "com.wednesdaysolutions.rntws"
}
},
"plugins": [
[
"@sentry/react-native/expo",
{
"url": "https://sentry.io/",
"project": "react-native",
"organization": "wednesday-solutions-5p"
}
]
]
}
}
23 changes: 10 additions & 13 deletions app/app.js
Original file line number Diff line number Diff line change
@@ -1,22 +1,19 @@
import React from 'react';
import { Provider } from 'react-redux';
import { PersistGate } from 'redux-persist/lib/integration/react';
import { RecoilRoot } from 'recoil';
import { I18nextProvider } from 'react-i18next';
import 'react-native-gesture-handler';
import LanguageProvider from '@atoms/LanguageProvider';
import RootScreen from '@scenes/RootScreen';
import createStore from '@app/rootReducer';
import { translationMessages } from './i18n';
import 'react-native-gesture-handler';

const { store, persistor } = createStore();
import i18n from '@app/i18n';

const App = () => (
<Provider store={store}>
<LanguageProvider messages={translationMessages}>
<PersistGate loading={null} persistor={persistor}>
<RecoilRoot>
<I18nextProvider i18n={i18n}>
<LanguageProvider>
<RootScreen />
</PersistGate>
</LanguageProvider>
</Provider>
</LanguageProvider>
</I18nextProvider>
</RecoilRoot>
);

export default App;
Binary file added app/assets/images/wednesday-logo-new.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added app/assets/images/wednesday-logo-old.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified app/assets/images/wednesday-logo.png
100755 → 100644
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 2d7a300

Please sign in to comment.