#22: refactor actions and reducer

This commit is contained in:
SleepWalker 2018-05-05 09:42:21 +03:00
parent 6e9542f592
commit bfff6894b5
5 changed files with 77 additions and 41 deletions

View File

@ -78,7 +78,7 @@
"no-self-compare": "error", "no-self-compare": "error",
"no-sequences": "error", "no-sequences": "error",
"no-throw-literal": "error", "no-throw-literal": "error",
"no-unused-expressions": ["warn", {"allowShortCircuit": true, "allowTernary": true}], "flowtype/no-unused-expressions": ["warn", {"allowShortCircuit": true, "allowTernary": true}],
"no-useless-call": "warn", "no-useless-call": "warn",
"no-useless-concat": "warn", "no-useless-concat": "warn",
"no-void": "error", "no-void": "error",

View File

@ -75,7 +75,7 @@
"css-loader": "^0.28.0", "css-loader": "^0.28.0",
"enzyme": "^2.2.0", "enzyme": "^2.2.0",
"eslint": "^4.0.0", "eslint": "^4.0.0",
"eslint-plugin-flowtype": "2.35.1", "eslint-plugin-flowtype": "^2.46.3",
"eslint-plugin-react": "^7.3.0", "eslint-plugin-react": "^7.3.0",
"exports-loader": "^0.6.3", "exports-loader": "^0.6.3",
"extract-text-webpack-plugin": "^1.0.0", "extract-text-webpack-plugin": "^1.0.0",

View File

@ -1,16 +1,19 @@
// @flow // @flow
import oauth from 'services/api/oauth';
import type { Dispatch } from 'redux'; import type { Dispatch } from 'redux';
import type { Apps } from './reducer';
import type { OauthAppResponse } from 'services/api/oauth'; import type { OauthAppResponse } from 'services/api/oauth';
import type { User } from 'components/user'; import type { User } from 'components/user';
import oauth from 'services/api/oauth';
export const SET_AVAILABLE = 'SET_AVAILABLE'; import type { Apps } from './reducer';
export function setAppsList(apps: Array<OauthAppResponse>) {
type SetAvailableAction = { type: 'apps:setAvailable', payload: Array<OauthAppResponse> };
type DeleteAppAction = { type: 'apps:deleteApp', payload: string };
type AddAppAction = { type: 'apps:addApp', payload: OauthAppResponse };
export type Action = SetAvailableAction | DeleteAppAction | AddAppAction;
export function setAppsList(apps: Array<OauthAppResponse>): SetAvailableAction {
return { return {
type: SET_AVAILABLE, type: 'apps:setAvailable',
payload: apps, payload: apps,
}; };
} }
@ -20,49 +23,55 @@ export function getApp(state: {apps: Apps}, clientId: string): ?OauthAppResponse
} }
export function fetchApp(clientId: string) { export function fetchApp(clientId: string) {
return async (dispatch: Dispatch<any>, getState: () => {apps: Apps}) => { return async (dispatch: Dispatch<any>): Promise<void> => {
const fetchedApp = await oauth.getApp(clientId); const app = await oauth.getApp(clientId);
const { available } = getState().apps;
let newApps: Array<OauthAppResponse>;
if (available.some((app) => app.clientId === fetchedApp.clientId)) {
newApps = available.map((app) => app.clientId === fetchedApp.clientId ? fetchedApp : app);
} else {
newApps = [...available, fetchedApp];
}
return dispatch(setAppsList(newApps)); dispatch(addApp(app));
};
}
function addApp(app: OauthAppResponse): AddAppAction {
return {
type: 'apps:addApp',
payload: app
}; };
} }
export function fetchAvailableApps() { export function fetchAvailableApps() {
return async (dispatch: Dispatch<any>, getState: () => {user: User}) => { return async (dispatch: Dispatch<any>, getState: () => {user: User}): Promise<void> => {
const { id } = getState().user; const { id } = getState().user;
if (!id) { if (!id) {
throw new Error('store.user.id is required for this action'); throw new Error('store.user.id is required for this action');
} }
const apps = await oauth.getAppsByUser(id); const apps = await oauth.getAppsByUser(id);
return dispatch(setAppsList(apps)); dispatch(setAppsList(apps));
}; };
} }
export function deleteApp(clientId: string) { export function deleteApp(clientId: string) {
return async (dispatch: Dispatch<any>, getState: () => {apps: Apps}) => { return async (dispatch: Dispatch<any>): Promise<void> => {
await oauth.delete(clientId); await oauth.delete(clientId);
const apps = getState().apps.available.filter((app) => app.clientId !== clientId);
return dispatch(setAppsList(apps)); dispatch(createDeleteAppAction(clientId));
};
}
function createDeleteAppAction(clientId: string): DeleteAppAction {
return {
type: 'apps:deleteApp',
payload: clientId
}; };
} }
export function resetApp(clientId: string, resetSecret: bool) { export function resetApp(clientId: string, resetSecret: bool) {
return async (dispatch: Dispatch<any>, getState: () => {apps: Apps}) => { return async (dispatch: Dispatch<any>): Promise<void> => {
const result = await oauth.reset(clientId, resetSecret); const { data: app } = await oauth.reset(clientId, resetSecret);
if (resetSecret) {
const apps = getState().apps.available.map((app) => app.clientId === clientId ? result.data : app);
return dispatch(setAppsList(apps)); if (resetSecret) {
dispatch(addApp(app));
} }
}; };
} }

View File

@ -1,6 +1,6 @@
// @flow // @flow
import { SET_AVAILABLE } from './actions';
import type { OauthAppResponse } from 'services/api/oauth'; import type { OauthAppResponse } from 'services/api/oauth';
import type { Action } from './actions';
export type Apps = { export type Apps = {
+available: Array<OauthAppResponse>, +available: Array<OauthAppResponse>,
@ -12,18 +12,41 @@ const defaults: Apps = {
export default function apps( export default function apps(
state: Apps = defaults, state: Apps = defaults,
{type, payload}: {type: string, payload: ?Object} action: Action
) { ) {
switch (type) { switch (action.type) {
case SET_AVAILABLE: case 'apps:setAvailable':
return { return {
...state, ...state,
available: payload, available: action.payload,
};
case 'apps:addApp': {
const { payload } = action;
const available: Array<OauthAppResponse> = [...state.available];
let index = available.findIndex((app) => app.clientId === payload.clientId);
if (index === -1) {
index = available.length;
}
available[index] = action.payload;
return {
...state,
available
};
}
case 'apps:deleteApp':
return {
...state,
available: state.available.filter((app) => app.clientId !== action.payload),
}; };
default: default:
return state || { (action.type: empty);
...defaults
}; return state;
} }
} }

View File

@ -2448,9 +2448,9 @@ escodegen@^1.6.1:
optionalDependencies: optionalDependencies:
source-map "~0.5.6" source-map "~0.5.6"
eslint-plugin-flowtype@2.35.1: eslint-plugin-flowtype@2.46.3:
version "2.35.1" version "2.46.3"
resolved "https://registry.yarnpkg.com/eslint-plugin-flowtype/-/eslint-plugin-flowtype-2.35.1.tgz#9ad98181b467a3645fbd2a8d430393cc17a4ea63" resolved "https://registry.yarnpkg.com/eslint-plugin-flowtype/-/eslint-plugin-flowtype-2.46.3.tgz#7e84131d87ef18b496b1810448593374860b4e8e"
dependencies: dependencies:
lodash "^4.15.0" lodash "^4.15.0"
@ -4145,10 +4145,14 @@ lodash@^3.10.0, lodash@^3.2.0, lodash@^3.8.0:
version "3.10.1" version "3.10.1"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-3.10.1.tgz#5bf45e8e49ba4189e17d482789dfd15bd140b7b6" resolved "https://registry.yarnpkg.com/lodash/-/lodash-3.10.1.tgz#5bf45e8e49ba4189e17d482789dfd15bd140b7b6"
lodash@^4.0.0, lodash@^4.14.0, lodash@^4.15.0, lodash@^4.17.2, lodash@^4.17.3, lodash@^4.17.4, lodash@^4.2.0, lodash@^4.2.1, lodash@^4.3.0, lodash@^4.5.0, lodash@^4.6.1, lodash@~4.17.4: lodash@^4.0.0, lodash@^4.14.0, lodash@^4.17.2, lodash@^4.17.3, lodash@^4.17.4, lodash@^4.2.0, lodash@^4.2.1, lodash@^4.3.0, lodash@^4.5.0, lodash@^4.6.1, lodash@~4.17.4:
version "4.17.4" version "4.17.4"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.4.tgz#78203a4d1c328ae1d86dca6460e369b57f4055ae"
lodash@^4.15.0:
version "4.17.10"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.10.tgz#1b7793cf7259ea38fb3661d4d38b3260af8ae4e7"
log4js@^0.6.31: log4js@^0.6.31:
version "0.6.38" version "0.6.38"
resolved "https://registry.yarnpkg.com/log4js/-/log4js-0.6.38.tgz#2c494116695d6fb25480943d3fc872e662a522fd" resolved "https://registry.yarnpkg.com/log4js/-/log4js-0.6.38.tgz#2c494116695d6fb25480943d3fc872e662a522fd"