diff --git a/package-lock.json b/package-lock.json index 9ef52a6..005de9d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -5295,6 +5295,11 @@ "sort-keys": "^1.0.0" } }, + "normalizr": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/normalizr/-/normalizr-3.4.1.tgz", + "integrity": "sha512-gei+tJucERU8vYN6TFQL2k5YMLX2Yh7nlylKMJC65+Uu/LS3xQCDJc8cies72aHouycKYyVgcnyLRbaJsigXKw==" + }, "npm-run-all": { "version": "4.1.5", "resolved": "https://registry.npmjs.org/npm-run-all/-/npm-run-all-4.1.5.tgz", diff --git a/package.json b/package.json index ba01fc4..85bd795 100644 --- a/package.json +++ b/package.json @@ -44,6 +44,7 @@ "@fortawesome/free-solid-svg-icons": "^5.10.2", "@fortawesome/react-fontawesome": "^0.1.4", "lodash": "^4.17.15", + "normalizr": "^3.4.1", "react": "^16.9.0", "react-dom": "^16.9.0", "react-redux": "^7.1.1", diff --git a/src/actions/directory.ts b/src/actions/directory.ts new file mode 100644 index 0000000..07ed7a4 --- /dev/null +++ b/src/actions/directory.ts @@ -0,0 +1,73 @@ +import { Action, AnyAction } from 'redux' +import { ThunkAction, ThunkDispatch } from 'redux-thunk' +import { normalize } from 'normalizr' + +import { getGroups } from '../api/groups' +import { setEntities } from '../actions/entities' +import { startRequest, finishRequest } from '../actions/requests' +import { group } from '../store/schemas' +import { AppState } from '../types' + +const FETCH_ID = 'groups' + +export interface SetGroupsAction extends Action { + type: 'DIRECTORY_SET_GROUPS' + payload: string[] +} + +export interface AppendGroupsAction extends Action { + type: 'DIRECTORY_APPEND_GROUPS', + payload: string[] +} + +export interface SetContinuationAction extends Action { + type: 'DIRECTORY_SET_CONTINUATION' + payload: string +} + +export type DirectoryActions = SetGroupsAction | AppendGroupsAction | SetContinuationAction + +const setGroups = (groups: string[]): SetGroupsAction => ({ + type: 'DIRECTORY_SET_GROUPS', + payload: groups, +}) + +const appendGroups = (groups: string[]): AppendGroupsAction => ({ + type: 'DIRECTORY_APPEND_GROUPS', + payload: groups, +}) + +const setContinuation = (continuation: string): SetContinuationAction => ({ + type: 'DIRECTORY_SET_CONTINUATION', + payload: continuation, +}) + +const fetchGroups = (sort?: string, continuation?: string): ThunkAction, AppState, void, AnyAction> => { + return async (dispatch: ThunkDispatch) => { + dispatch(startRequest(FETCH_ID)) + + try { + const response = await getGroups(sort, continuation) + const groups = normalize(response.groups, group) + + dispatch(setEntities(groups.entities)) + dispatch(setGroups(groups.result)) + + if (response.continuation) { + dispatch(setContinuation(response.continuation)) + } + + dispatch(finishRequest(FETCH_ID, true)) + } catch (err) { + console.error(err) + dispatch(finishRequest(FETCH_ID, false)) + } + } +} + +export { + setGroups, + appendGroups, + setContinuation, + fetchGroups, +} diff --git a/src/actions/entities.ts b/src/actions/entities.ts index be7bfa2..9b771a1 100644 --- a/src/actions/entities.ts +++ b/src/actions/entities.ts @@ -1,5 +1,5 @@ import { Action } from 'redux' -import { Entity } from '../types/entities' +import { Entity, EntityStore } from '../types' export interface SetEntityAction extends Action { type: 'ENTITIES_SET_ENTITY' @@ -12,8 +12,7 @@ export interface SetEntityAction extends Action { export interface SetEntitiesAction extends Action { type: 'ENTITIES_SET_ENTITIES' payload: { - type: string - entities: Entity[] + entities: EntityStore } } @@ -27,10 +26,9 @@ const setEntity = (type: string, entity: Entity): SetEntityAction => ({ } }) -const setEntities = (type: string, entities: Entity[]): SetEntitiesAction => ({ +const setEntities = (entities: EntityStore): SetEntitiesAction => ({ type: 'ENTITIES_SET_ENTITIES', payload: { - type, entities, } }) diff --git a/src/actions/groups.ts b/src/actions/groups.ts deleted file mode 100644 index b44481d..0000000 --- a/src/actions/groups.ts +++ /dev/null @@ -1,46 +0,0 @@ -import { ThunkAction, ThunkDispatch } from 'redux-thunk' -import { Action, AnyAction } from 'redux' - -import { getGroups } from '../api/groups' -import { startRequest, finishRequest } from '../actions/requests' - -import { AppState } from '../types' -import { Entity } from '../types/entities' - -const FETCH_ID = 'groups' - -export interface FetchGroupsAction extends Action { - type: 'GROUPS_FETCH' - payload: { - groups: Entity[] - continuation?: string - } -} - -const setGroups = (groups: Entity[], continuation?: string): FetchGroupsAction => ({ - type: 'GROUPS_FETCH', - payload: { - groups, - continuation, - }, -}) - -const fetchGroups = (sort?: string, continuation?: string): ThunkAction, AppState, void, AnyAction> => { - return async (dispatch: ThunkDispatch) => { - dispatch(startRequest(FETCH_ID)) - - try { - const response = await getGroups(sort, continuation) - - dispatch(setGroups(response.groups, response.continuation)) - dispatch(finishRequest(FETCH_ID, true)) - } catch (err) { - console.error(err) - dispatch(finishRequest(FETCH_ID, false)) - } - } -} - -export { - fetchGroups, -} diff --git a/src/api/groups.ts b/src/api/groups.ts index 761dc49..6bff0a5 100644 --- a/src/api/groups.ts +++ b/src/api/groups.ts @@ -1,5 +1,5 @@ import { fetch } from './fetch' -import { Entity } from '../types/entities' +import { Entity } from '../types' interface GroupsResponse { groups: Entity[] @@ -13,7 +13,7 @@ export async function getGroups(sort: string = 'members', continuation?: string) } const querystring = Object.entries(params).filter(([name, value]) => value !== undefined).map(([name, value]) => `${name}=${value}`).join('&') - + return await fetch({ path: `/api/groups?${querystring}` }) diff --git a/src/components/app/app.tsx b/src/components/app/app.tsx index 195b605..34fdee9 100644 --- a/src/components/app/app.tsx +++ b/src/components/app/app.tsx @@ -1,8 +1,12 @@ import React, { FC } from 'react' import { HashRouter as Router, Route, Link } from 'react-router-dom' +import Spinner from '../spinner' import UserInfo from '../user-info' + import Home from '../pages/home' +import Register from '../pages/register' +import Directory from '../pages/directory' import './app.scss' @@ -30,6 +34,8 @@ const App: FC = ({ menuCollapsed, fetching }) => {

+ {fetching && } +