> This document has been <b>updated to</b> version <b>2.0.0</b>

### What is it
* this package is meant to build a `redux` `store` object with some default configs + payping redux actions & reducers

* plus it comes with [`redux-persist`](https://github.com/rt2zz/redux-persist) integration as an optional feature

* it uses [`redux-thunk`](https://github.com/reduxjs/redux-thunk) & `promise` as default middlewares
* * `promise` middleware: it allows redux to use promisified actions

* it also has [`redux-devtools`](https://github.com/reduxjs/redux-devtools) & [`redux-logger`](https://github.com/LogRocket/redux-logger) enabled when `NODE_ENV` is not equal to `"production"` `"test"`

<hr/>

### How to use
##### Initial work:

1. create a directory called `redux` in your app root dir and `actions`, `reducers` & `configs` subdirectories in it.

2. create `index.ts` file in `redux/configs` just like the following code:

```javascript
// root/redux/configs/index.ts

import { SdkConfigureStore } from "@payping/redux/store";

export const reduxConfig: SdkConfigureStore = {};
```

3. At the `index.tsx` file in your app root dir, create `store` in it by passing `reduxConfig` to the `configureStore()` method

```javascript
// root/index.tsx

import { configureStore } from "@payping/redux";
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import App from './App';
import { reduxConfig } from "./redux/configs";

export const { store } = configureStore(reduxConfig);

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root')
);
```

4. export all actions from package in `redux/actions/index.ts` file:

```javascript
// root/redux/actions/index.ts

export * from "@payping/redux/actions";
```

##### Assumptions:
By combining some of the following assumptions, you can build the `store` object:

1. I <b>don't want</b> to use `redux-persist` and I <b>have no</b> custom redux configs or additional actions & reducers.

2. I <b>want</b> to use `redux-persist`.

3. I <b>have some</b> custom redux configs.

4. I <b>have some</b> additional actions or reducers.

**Pick the instruction you need, from bellow:**
##### Instructions:


> whenever you needed to add some reducers to your redux, create a new interface in `redux/configs/index.ts`, called `AppStoreStates` which extends `SdkStoreStates`. then modify `configureStore(reduxConfig)` to `configureStore<AppStoreStates>(reduxConfig)`. and istead of `SdkStoreStates`, use `AppStoreStates` wherever you needed that.

> use `SdkThunkDispatch` type whenever you want to add type for a dispatcher
-  ***for assumption 1:***
	-  you don't need to do anything

<br/>

-  ***for assumption 2:***
1. create `persistConfig.ts` file in `redux/configs` just like the following code:

```javascript
// root/redux/configs/persistConfig.ts

import { SdkPersistConfig } from "@payping/redux/store/persistConfig";
// use localForage for web & desktop, and AsyncStorage for react-native
import localForage as storage from "localforage";
// import AsyncStorage as storage from '@react-native-community/async-storage';
import env from "../env";

const { storeVersion } = env;

const persistConfig: SdkPersistConfig = {
  storage,
  version: storeVersion,
};

export default { ...persistConfig };
```
> by default, it create persisted reducer with `stateReconciler: autoMergeLevel2` and `version: 2` options

> change `version` option, whenever you changed the reducers architecture

> you can add any other `redux-persist` related config (check `SdkPersistConfig` interface) you want to it.

2. import and add `persistConfig` to `redux/configs/index.ts` just like the following code:

```javascript
// root/redux/configs/index.ts

import { SdkConfigureStore } from "@payping/redux/store";
import persistConfig from "./persistConfig";

export const reduxConfig: SdkConfigureStore = {
  appPersistConfig: persistConfig
};
```

3. wrap your `ReactDom.render()` in a function called `onRehydrated` in order to render the app after persisted redux store, rehydrated from storage. it's for preventing any issue in redux related logics. then, pass the `onRehydrated` to the `configureStore()` method.

```javascript
// root/index.tsx

import { configureStore } from "@payping/redux";
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import App from './App';
import { PersistGate } from "redux-persist/integration/react";
import { reduxConfig } from "./redux/configs";

// eslint-disable-next-line @typescript-eslint/no-use-before-define
export const { store, persistor } = configureStore({ ...reduxConfig, onRehydrated });

const onRehydrated = (): void => {
  ReactDOM.render(
    <Provider store={store}>
      <PersistGate loading={null} persistor={persistor}>
        <App />
      </PersistGate>
    </Provider>,
    document.getElementById("root")
  );
};
```

<br/>

-  ***for assumption 3:***
	* add your redux configs in `redux/configs/index.ts`. e.g. adding `router` middleware:

```javascript
// root/redux/configs/index.ts

import { SdkStoreStates } from "@payping/redux/reducers";
import { SdkConfigureStore } from "@payping/redux/store";
import { RouterState, connectRouter, routerActions, routerMiddleware } from "connected-react-router";
import { LocationState, createHashHistory } from "history";
import { AnyAction, Reducer, StoreEnhancer } from "redux";

export interface AppStoreStates extends SdkStoreStates {
  router: RouterState<LocationState>;
}

export const history = createHashHistory();

const router = routerMiddleware(history);

const appReducers = {
  router: connectRouter(history) as Reducer<RouterState<LocationState>, AnyAction>,
};

const appMiddlewares = [router];
const composeActionCreators = { ...routerActions };

export const reduxConfig: SdkConfigureStore = {
  appMiddlewares,
  appReducers,
  composeActionCreators
};
```

<br/>

-  ***for assumption 4:***

1. define your actions and reducers like the samples at the end of this page in `redux/actions` or `redux/reducers` respectively.

2. export your actions in `redux/actions/index.ts` like below:

```javascript
// root/redux/actions/index.ts

export * from "@payping/redux/actions";
export { logout, refreshToken, toggleLoggingIn } from "./appAuth";
```

3. add your reducer in `appReducers` at `redux/configs/index.ts` and add it to `AppStoreStates` interface (implement interface if it doesn't exist).

```javascript
// root/redux/configs/index.ts

import { SdkStoreStates } from "@payping/redux/reducers";
import { SdkConfigureStore } from "@payping/redux/store";
import appAuth, { AppAuthState } from "../reducers/appAuth";

export interface AppStoreStates extends SdkStoreStates {
  appAuth: AppAuthState;
}

const appReducers = {
  appAuth
};

export const reduxConfig: SdkConfigureStore = {
  appReducers,
};
```

4. modify `configureStore(reduxConfig)` to `configureStore<AppStoreStates>(reduxConfig)`.
<hr/>

#### sample action
```javascript
// root/redux/actions/appAuth.ts

import { CLEAR_LIST, LOGGED_OUT, LOGGED_IN, SdkThunkActionResult, SdkThunkDispatch } from "@payping/redux/reducers";
import {
  LoggedInAction,
  TOGGLE_LOGGING_IN,
  ToggleLoggingInAction
} from "../reducers/appAuth";

function toggleLoggingIn(): ToggleLoggingInAction {
  return {
    type: TOGGLE_LOGGING_IN
  };
}

function loggedIn(token: string, expiresIn: number): LoggedInAction {
  return {
    payload: { expiresIn, token },
    type: LOGGED_IN
  };
}

function logoutDispatchs(): SdkThunkActionResult<Promise<void>> {
  return async (dispatch): Promise<void> => {
    dispatch({ type: LOGGED_OUT });
  };
}

export { toggleLoggingIn, loggedIn, logoutDispatchs };
```

#### sample reducer

```javascript
// root/redux/reducers/appAuth.ts

import { LoggedOutAction } from "@payping/redux/reducers";

export const TOGGLE_LOGGING_IN = "TOGGLE_LOGGING_IN";
export const LOGGED_OUT = "LOGGED_OUT";
export const LOGGED_IN = "LOGGED_IN";

export interface AppAuthState {
  isLoggingIn: boolean;
}

export interface ToggleLoggingInAction {
  type: typeof TOGGLE_LOGGING_IN;
}

export interface LoggedInAction {
  type: typeof LOGGED_IN;
  payload: { token: string; expiresIn: number };
}

export type AuthActionTypes = ToggleLoggingInAction | LoggedInAction | LoggedOutAction;

export const initialState: AppAuthState = {
  isLoggingIn: false
};

export default function appAuth(state = initialState, action: AuthActionTypes): AppAuthState {
  if (action.type === TOGGLE_LOGGING_IN) {
    return {
      ...state,
      isLoggingIn: !state.isLoggingIn
    };
  }

  if (action.type === LOGGED_IN) {
    const { token, expiresIn } = action.payload;
    return {
      ...state,
      expiresIn,
      token
    };
  }

  if (action.type === LOGGED_OUT) {
    return initialState;
  }

  return state;
}
```

### Checking changes before Publish:
1. Run script for **building** package on local (`customPrepublishOnly:app` or `customPrepublishOnly:admin`).
2. Output will be in a `dist|distAdmin` folder at the root of the project.
3. Go to the project that you want to install this package in and install it locally:
   ```shell
   yarn add ../redux/dist/
   or
   yarn add ../redux/distAdmin/
	```
   > **NOTE:** If your project has submodules you need to use the `-W` option in `yarn add` command and replace the version of the package in all package.json files with this route `../redux/dist/`.

### How to Publish:

#### Admin:
1. Create a release branch for this new version (its name should be the new version that you want to publish plus admin for example `1.0.4-admin`):

2. Edit `build_config/admin/package.json` version (pay attention to [Semver](https://semver.org))

3. Run script for publishing package on registry:
> NOTE: In your terminal you should be logged in at our registry at https://npmregistry.manatadbir.ir
> If you aren't, run:
> ```shell
>  npm logout
>  npm login --registry=https://npmregistry.manatadbir.ir
> ```
```shell
yarn customPublish:admin
```

4. Finish your release branch

#### Dashboard:
1. Create a release branch for this new version (its name should be the new version that you want to publish plus app for example `1.0.4-app`):

2. Edit `build_config/app/package.json` version (pay attention to [Semver](https://semver.org))

3. Run script for publishing package on registry:
> NOTE: In your terminal you should be logged in at npm registry at https://registry.npmjs.org.
> If you aren't, run:
> ```shell
>  npm logout
>  npm login
> ```

```shell
yarn customPublish:app
```

4. Finish your release branch
