diff --git a/.eslintrc b/.eslintrc index d54d531..a273afc 100644 --- a/.eslintrc +++ b/.eslintrc @@ -78,7 +78,7 @@ "no-self-compare": "error", "no-sequences": "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-concat": "warn", "no-void": "error", diff --git a/package.json b/package.json index 3224ab7..07acc02 100644 --- a/package.json +++ b/package.json @@ -75,7 +75,7 @@ "css-loader": "^0.28.0", "enzyme": "^2.2.0", "eslint": "^4.0.0", - "eslint-plugin-flowtype": "2.35.1", + "eslint-plugin-flowtype": "^2.46.3", "eslint-plugin-react": "^7.3.0", "exports-loader": "^0.6.3", "extract-text-webpack-plugin": "^1.0.0", diff --git a/src/components/dev/apps/actions.js b/src/components/dev/apps/actions.js index 9f4cf51..666fbb0 100644 --- a/src/components/dev/apps/actions.js +++ b/src/components/dev/apps/actions.js @@ -1,16 +1,19 @@ // @flow - -import oauth from 'services/api/oauth'; - import type { Dispatch } from 'redux'; -import type { Apps } from './reducer'; import type { OauthAppResponse } from 'services/api/oauth'; import type { User } from 'components/user'; +import oauth from 'services/api/oauth'; -export const SET_AVAILABLE = 'SET_AVAILABLE'; -export function setAppsList(apps: Array) { +import type { Apps } from './reducer'; + +type SetAvailableAction = { type: 'apps:setAvailable', payload: Array }; +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): SetAvailableAction { return { - type: SET_AVAILABLE, + type: 'apps:setAvailable', payload: apps, }; } @@ -20,49 +23,55 @@ export function getApp(state: {apps: Apps}, clientId: string): ?OauthAppResponse } export function fetchApp(clientId: string) { - return async (dispatch: Dispatch, getState: () => {apps: Apps}) => { - const fetchedApp = await oauth.getApp(clientId); - const { available } = getState().apps; - let newApps: Array; - if (available.some((app) => app.clientId === fetchedApp.clientId)) { - newApps = available.map((app) => app.clientId === fetchedApp.clientId ? fetchedApp : app); - } else { - newApps = [...available, fetchedApp]; - } + return async (dispatch: Dispatch): Promise => { + const app = await oauth.getApp(clientId); - return dispatch(setAppsList(newApps)); + dispatch(addApp(app)); + }; +} + +function addApp(app: OauthAppResponse): AddAppAction { + return { + type: 'apps:addApp', + payload: app }; } export function fetchAvailableApps() { - return async (dispatch: Dispatch, getState: () => {user: User}) => { + return async (dispatch: Dispatch, getState: () => {user: User}): Promise => { const { id } = getState().user; + if (!id) { throw new Error('store.user.id is required for this action'); } const apps = await oauth.getAppsByUser(id); - return dispatch(setAppsList(apps)); + dispatch(setAppsList(apps)); }; } export function deleteApp(clientId: string) { - return async (dispatch: Dispatch, getState: () => {apps: Apps}) => { + return async (dispatch: Dispatch): Promise => { 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) { - return async (dispatch: Dispatch, getState: () => {apps: Apps}) => { - const result = await oauth.reset(clientId, resetSecret); - if (resetSecret) { - const apps = getState().apps.available.map((app) => app.clientId === clientId ? result.data : app); + return async (dispatch: Dispatch): Promise => { + const { data: app } = await oauth.reset(clientId, resetSecret); - return dispatch(setAppsList(apps)); + if (resetSecret) { + dispatch(addApp(app)); } }; } diff --git a/src/components/dev/apps/reducer.js b/src/components/dev/apps/reducer.js index 4b0a535..dfd23ec 100644 --- a/src/components/dev/apps/reducer.js +++ b/src/components/dev/apps/reducer.js @@ -1,6 +1,6 @@ // @flow -import { SET_AVAILABLE } from './actions'; import type { OauthAppResponse } from 'services/api/oauth'; +import type { Action } from './actions'; export type Apps = { +available: Array, @@ -12,18 +12,41 @@ const defaults: Apps = { export default function apps( state: Apps = defaults, - {type, payload}: {type: string, payload: ?Object} + action: Action ) { - switch (type) { - case SET_AVAILABLE: + switch (action.type) { + case 'apps:setAvailable': return { ...state, - available: payload, + available: action.payload, + }; + + case 'apps:addApp': { + const { payload } = action; + const available: Array = [...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: - return state || { - ...defaults - }; + (action.type: empty); + + return state; } } diff --git a/yarn.lock b/yarn.lock index 1e18582..474080b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2448,9 +2448,9 @@ escodegen@^1.6.1: optionalDependencies: source-map "~0.5.6" -eslint-plugin-flowtype@2.35.1: - version "2.35.1" - resolved "https://registry.yarnpkg.com/eslint-plugin-flowtype/-/eslint-plugin-flowtype-2.35.1.tgz#9ad98181b467a3645fbd2a8d430393cc17a4ea63" +eslint-plugin-flowtype@2.46.3: + version "2.46.3" + resolved "https://registry.yarnpkg.com/eslint-plugin-flowtype/-/eslint-plugin-flowtype-2.46.3.tgz#7e84131d87ef18b496b1810448593374860b4e8e" dependencies: lodash "^4.15.0" @@ -4145,10 +4145,14 @@ lodash@^3.10.0, lodash@^3.2.0, lodash@^3.8.0: version "3.10.1" 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" 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: version "0.6.38" resolved "https://registry.yarnpkg.com/log4js/-/log4js-0.6.38.tgz#2c494116695d6fb25480943d3fc872e662a522fd"