From 4ee80cc98750170ccd41fa8b3e20b3f17d0ca88b Mon Sep 17 00:00:00 2001 From: Dwayne Harris Date: Mon, 9 Sep 2019 22:53:30 -0400 Subject: [PATCH] WIP --- package-lock.json | 12 +++- package.json | 4 +- src/actions/authentication.ts | 9 +-- src/actions/directory.ts | 43 +++++++----- src/actions/entities.ts | 9 +-- src/actions/forms.ts | 18 ++--- src/actions/index.ts | 0 src/actions/menu.ts | 6 +- src/actions/notifications.ts | 70 +++++++++++++------ src/actions/requests.ts | 9 +-- src/api/groups.ts | 6 ++ src/components/app/app.tsx | 7 +- .../notification-container/index.ts | 6 +- .../notification-container.scss | 11 ++- .../notification-container.tsx | 1 + src/components/notification/index.tsx | 9 ++- src/components/pages/directory/index.ts | 2 +- src/components/pages/register/register.tsx | 15 +++- src/components/user-info/user-info.tsx | 2 +- src/reducers/notifications.ts | 23 +++++- src/store/index.ts | 2 +- src/store/schemas.ts | 9 +-- src/types/store.ts | 1 - 23 files changed, 168 insertions(+), 106 deletions(-) delete mode 100644 src/actions/index.ts diff --git a/package-lock.json b/package-lock.json index 005de9d..cff3018 100644 --- a/package-lock.json +++ b/package-lock.json @@ -347,6 +347,15 @@ } } }, + "@types/uuid": { + "version": "3.4.5", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-3.4.5.tgz", + "integrity": "sha512-MNL15wC3EKyw1VLF+RoVO4hJJdk9t/Hlv3rt1OL65Qvuadm4BYo6g9ZJQqoq7X8NBFSsQXgAujWciovh2lpVjA==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, "@types/webpack": { "version": "4.39.1", "resolved": "https://registry.npmjs.org/@types/webpack/-/webpack-4.39.1.tgz", @@ -8120,8 +8129,7 @@ "uuid": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.3.tgz", - "integrity": "sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ==", - "dev": true + "integrity": "sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ==" }, "v8-compile-cache": { "version": "2.0.3", diff --git a/package.json b/package.json index 85bd795..7119ea0 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ "@types/react-redux": "^7.1.2", "@types/react-router-dom": "^4.3.5", "@types/redux-logger": "^3.0.7", + "@types/uuid": "^3.4.5", "@types/webpack": "^4.39.1", "@types/webpack-dev-server": "^3.1.7", "bulma": "^0.7.5", @@ -52,6 +53,7 @@ "redux": "^4.0.4", "redux-logger": "^3.0.6", "redux-thunk": "^2.3.0", - "reselect": "^4.0.0" + "reselect": "^4.0.0", + "uuid": "^3.3.3" } } diff --git a/src/actions/authentication.ts b/src/actions/authentication.ts index 5f12a3d..970702d 100644 --- a/src/actions/authentication.ts +++ b/src/actions/authentication.ts @@ -12,17 +12,12 @@ export interface SetUserAction extends Action { export type AuthenticationActions = SetAuthenticatedAction | SetUserAction -const setAuthenticated = (authenticated: boolean): SetAuthenticatedAction => ({ +export const setAuthenticated = (authenticated: boolean): SetAuthenticatedAction => ({ type: 'AUTHENTICATION_SET_AUTHENTICATED', payload: authenticated, }) -const setUser = (userId: string): SetUserAction => ({ +export const setUser = (userId: string): SetUserAction => ({ type: 'AUTHENTICATION_SET_USER', payload: userId, }) - -export { - setAuthenticated, - setUser, -} diff --git a/src/actions/directory.ts b/src/actions/directory.ts index 5c8d31e..3a6636f 100644 --- a/src/actions/directory.ts +++ b/src/actions/directory.ts @@ -2,13 +2,14 @@ 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 { getGroup, getGroups } from '../api/groups' +import { setEntity, setEntities } from '../actions/entities' import { startRequest, finishRequest } from '../actions/requests' import { groupSchema } from '../store/schemas' import { AppState } from '../types' -const FETCH_ID = 'groups' +const FETCH_GROUP_ID = 'FETCH_GROUP' +const FETCH_GROUPS_ID = 'FETCH_GROUPS' export interface SetGroupsAction extends Action { type: 'DIRECTORY_SET_GROUPS' @@ -27,24 +28,39 @@ export interface SetContinuationAction extends Action { export type DirectoryActions = SetGroupsAction | AppendGroupsAction | SetContinuationAction -const setGroups = (groups: string[]): SetGroupsAction => ({ +export const setGroups = (groups: string[]): SetGroupsAction => ({ type: 'DIRECTORY_SET_GROUPS', payload: groups, }) -const appendGroups = (groups: string[]): AppendGroupsAction => ({ +export const appendGroups = (groups: string[]): AppendGroupsAction => ({ type: 'DIRECTORY_APPEND_GROUPS', payload: groups, }) -const setContinuation = (continuation: string): SetContinuationAction => ({ +export const setContinuation = (continuation: string): SetContinuationAction => ({ type: 'DIRECTORY_SET_CONTINUATION', payload: continuation, }) -const fetchGroups = (sort?: string, continuation?: string): ThunkAction, AppState, void, AnyAction> => { +export const fetchGroup = (id: string) => { return async (dispatch: ThunkDispatch) => { - dispatch(startRequest(FETCH_ID)) + dispatch(startRequest(FETCH_GROUP_ID)) + + try { + const group = await getGroup(id) + dispatch(setEntity('group', group)) + dispatch(finishRequest(FETCH_GROUP_ID, true)) + } catch (err) { + console.error(err) + dispatch(finishRequest(FETCH_GROUP_ID, false)) + } + } +} + +export const fetchGroups = (sort?: string, continuation?: string): ThunkAction, AppState, void, AnyAction> => { + return async (dispatch: ThunkDispatch) => { + dispatch(startRequest(FETCH_GROUPS_ID)) try { const response = await getGroups(sort, continuation) @@ -57,17 +73,10 @@ const fetchGroups = (sort?: string, continuation?: string): ThunkAction ({ +export const setEntity = (type: string, entity: Entity): SetEntityAction => ({ type: 'ENTITIES_SET_ENTITY', payload: { type, @@ -26,14 +26,9 @@ const setEntity = (type: string, entity: Entity): SetEntityAction => ({ } }) -const setEntities = (entities: EntityStore): SetEntitiesAction => ({ +export const setEntities = (entities: EntityStore): SetEntitiesAction => ({ type: 'ENTITIES_SET_ENTITIES', payload: { entities, } }) - -export { - setEntity, - setEntities, -} diff --git a/src/actions/forms.ts b/src/actions/forms.ts index ce5f1d1..075874d 100644 --- a/src/actions/forms.ts +++ b/src/actions/forms.ts @@ -37,16 +37,16 @@ export interface SetFieldNotificationAction extends Action { export type FormsActions = InitFormAction | InitFieldAction | SetFieldValueAction | SetFormNotificationAction | SetFieldNotificationAction -const initForm = (): InitFormAction => ({ +export const initForm = (): InitFormAction => ({ type: 'FORMS_INIT', }) -const initField = (name: string): InitFieldAction => ({ +export const initField = (name: string): InitFieldAction => ({ type: 'FORMS_INIT_FIELD', payload: name, }) -const setFieldValue = (name: string, value: any): SetFieldValueAction => ({ +export const setFieldValue = (name: string, value: any): SetFieldValueAction => ({ type: 'FORMS_SET_FIELD_VALUE', payload: { name, @@ -54,7 +54,7 @@ const setFieldValue = (name: string, value: any): SetFieldValueAction => ({ }, }) -const setFormNotification = (type: NotificationType, message: string): SetFormNotificationAction => ({ +export const setFormNotification = (type: NotificationType, message: string): SetFormNotificationAction => ({ type: 'FORMS_SET_FORM_NOTIFICATION', payload: { type, @@ -62,7 +62,7 @@ const setFormNotification = (type: NotificationType, message: string): SetFormNo } }) -const setFieldNotification = (name: string, type: NotificationType, message: string): SetFieldNotificationAction => ({ +export const setFieldNotification = (name: string, type: NotificationType, message: string): SetFieldNotificationAction => ({ type: 'FORMS_SET_FIELD_NOTIFICATION', payload: { name, @@ -70,11 +70,3 @@ const setFieldNotification = (name: string, type: NotificationType, message: str message, } }) - -export { - initForm, - initField, - setFieldValue, - setFormNotification, - setFieldNotification, -} diff --git a/src/actions/index.ts b/src/actions/index.ts deleted file mode 100644 index e69de29..0000000 diff --git a/src/actions/menu.ts b/src/actions/menu.ts index 0eb727a..e629e5b 100644 --- a/src/actions/menu.ts +++ b/src/actions/menu.ts @@ -7,11 +7,7 @@ export interface SetCollapsedAction extends Action { export type MenuActions = SetCollapsedAction -const setCollapsed = (collapsed: boolean): SetCollapsedAction => ({ +export const setCollapsed = (collapsed: boolean): SetCollapsedAction => ({ type: 'MENU_SET_COLLAPSED', payload: collapsed }) - -export { - setCollapsed, -} diff --git a/src/actions/notifications.ts b/src/actions/notifications.ts index 27e3acb..367e99d 100644 --- a/src/actions/notifications.ts +++ b/src/actions/notifications.ts @@ -1,36 +1,66 @@ -import { Action } from 'redux' +import { Action, AnyAction } from 'redux' +import { ThunkAction, ThunkDispatch } from 'redux-thunk' +import { v1 } from 'uuid' -export interface SetAutoAction extends Action { - type: 'NOTIFICATIONS_SET_AUTO' +import { AppState, NotificationType } from '../types' + +export interface AddNotificationAction extends Action { + type: 'NOTIFICATIONS_ADD_NOTIFICATION' payload: { id: string + type: NotificationType + content: string } } -export interface DismissAction extends Action { - type: 'NOTIFICATIONS_DISMISS' - payload: { - id: string - } +export interface RemoveNotificationAction extends Action { + type: 'NOTIFICATIONS_REMOVE_NOTIFICATION' + payload: string } -export type NotificationActions = SetAutoAction | DismissAction +export interface ClearNotificationAction extends Action { + type: 'NOTIFICATIONS_CLEAR_NOTIFICATION' + payload: string +} -const setAuto = (id: string): SetAutoAction => ({ - type: 'NOTIFICATIONS_SET_AUTO', +export interface SetNotificationAutoAction extends Action { + type: 'NOTIFICATIONS_SET_AUTO' + payload: string +} + +export type NotificationActions = AddNotificationAction | RemoveNotificationAction | ClearNotificationAction | SetNotificationAutoAction + +export const addNotification = (id: string, type: NotificationType, content: string): AddNotificationAction => ({ + type: 'NOTIFICATIONS_ADD_NOTIFICATION', payload: { id, - }, + type, + content, + } }) -const dismiss = (id: string): DismissAction => ({ - type: 'NOTIFICATIONS_DISMISS', - payload: { - id, - }, +export const removeNotification = (id: string): RemoveNotificationAction => ({ + type: 'NOTIFICATIONS_REMOVE_NOTIFICATION', + payload: id, +}) + +export const clearNotification = (id: string): ClearNotificationAction => ({ + type: 'NOTIFICATIONS_CLEAR_NOTIFICATION', + payload: id, }) -export { - setAuto, - dismiss, +export const setNotificationAuto = (id: string): SetNotificationAutoAction => ({ + type: 'NOTIFICATIONS_SET_AUTO', + payload: id, +}) + +export const showNotification = (type: NotificationType, content: string, expiration: number = 5000): ThunkAction, AppState, void, AnyAction> => { + return async (dispatch: ThunkDispatch) => { + const id = v1() + dispatch(addNotification(id, type, content)) + + setTimeout(() => { + dispatch(clearNotification(id)) + }, expiration) + } } diff --git a/src/actions/requests.ts b/src/actions/requests.ts index 9d0450c..4a1e7f4 100644 --- a/src/actions/requests.ts +++ b/src/actions/requests.ts @@ -17,22 +17,17 @@ export interface FinishRequestAction extends Action { export type RequestsActions = StartRequestAction | FinishRequestAction -const startRequest = (id: string): StartRequestAction => ({ +export const startRequest = (id: string): StartRequestAction => ({ type: 'REQUESTS_START_REQUEST', payload: { id, }, }) -const finishRequest = (id: string, succeeded: boolean): FinishRequestAction => ({ +export const finishRequest = (id: string, succeeded: boolean): FinishRequestAction => ({ type: 'REQUESTS_FINISH_REQUEST', payload: { id, succeeded, }, }) - -export { - startRequest, - finishRequest, -} diff --git a/src/api/groups.ts b/src/api/groups.ts index 6bff0a5..462823e 100644 --- a/src/api/groups.ts +++ b/src/api/groups.ts @@ -6,6 +6,12 @@ interface GroupsResponse { continuation?: string } +export async function getGroup(id: string) { + return await fetch({ + path: `/api/group/${id}` + }) +} + export async function getGroups(sort: string = 'members', continuation?: string) { const params = { sort, diff --git a/src/components/app/app.tsx b/src/components/app/app.tsx index 34fdee9..7dd5450 100644 --- a/src/components/app/app.tsx +++ b/src/components/app/app.tsx @@ -1,6 +1,7 @@ import React, { FC } from 'react' import { HashRouter as Router, Route, Link } from 'react-router-dom' +import NotificationContainer from '../notification-container' import Spinner from '../spinner' import UserInfo from '../user-info' @@ -52,9 +53,11 @@ const App: FC = ({ menuCollapsed, fetching }) => {
- - + +
+ + ) diff --git a/src/components/notification-container/index.ts b/src/components/notification-container/index.ts index 7f95ec1..b85a49f 100644 --- a/src/components/notification-container/index.ts +++ b/src/components/notification-container/index.ts @@ -1,7 +1,7 @@ import { Dispatch } from 'redux' import { connect } from 'react-redux' -import { setAuto, dismiss } from '../../actions/notifications' +import { setNotificationAuto, removeNotification } from '../../actions/notifications' import { getNotifications } from '../../selectors' import { AppState } from '../../types' @@ -13,10 +13,10 @@ const mapStateToProps = (state: AppState) => ({ const mapDispatchToProps = (dispatch: Dispatch) => ({ setAuto: (id: string) => { - dispatch(setAuto(id)) + dispatch(setNotificationAuto(id)) }, dismiss: (id: string) => { - dispatch(dismiss(id)) + dispatch(removeNotification(id)) } }) diff --git a/src/components/notification-container/notification-container.scss b/src/components/notification-container/notification-container.scss index ec953b6..ee85b56 100644 --- a/src/components/notification-container/notification-container.scss +++ b/src/components/notification-container/notification-container.scss @@ -1,6 +1,11 @@ +@import "../../../node_modules/bulma/sass/utilities/_all.sass"; +@import "../../../node_modules/bulma/sass/base/_all.sass"; +@import "../../../node_modules/bulma/sass/elements/button.sass"; +@import "../../../node_modules/bulma/sass/elements/notification.sass"; + div#notification-container { - bottom: 0; + bottom: 10px; position: absolute; - right: 0; - width: 25%; + right: 10px; + width: 40%; } diff --git a/src/components/notification-container/notification-container.tsx b/src/components/notification-container/notification-container.tsx index d9bbdbb..e8386c2 100644 --- a/src/components/notification-container/notification-container.tsx +++ b/src/components/notification-container/notification-container.tsx @@ -17,6 +17,7 @@ const NotificationContainer: FC = ({ notifications, setAuto, dismiss }) = {notifications.map(notification => { return ( = ({ id, type, auto, setAuto, dismiss, children }) getClassName(type), ].join(' ') + const handleDismiss: MouseEventHandler = e => { + e.stopPropagation() + dismiss(id) + } + return (
setAuto(id)}> - {!auto && } + {!auto && } {children}
) diff --git a/src/components/pages/directory/index.ts b/src/components/pages/directory/index.ts index cea692c..dd467d2 100644 --- a/src/components/pages/directory/index.ts +++ b/src/components/pages/directory/index.ts @@ -15,7 +15,7 @@ const mapStateToProps = (state: AppState) => ({ const mapDispatchToProps = (dispatch: ThunkDispatch) => ({ fetchGroups: () => { dispatch(fetchGroups()) - } + }, }) export default connect( diff --git a/src/components/pages/register/register.tsx b/src/components/pages/register/register.tsx index bd89510..6694c81 100644 --- a/src/components/pages/register/register.tsx +++ b/src/components/pages/register/register.tsx @@ -1,12 +1,21 @@ -import React, { FC } from 'react' +import React, { FC, useEffect } from 'react' +import { RouteComponentProps } from 'react-router' import { Entity } from '../../../types' -interface Props { +interface Params { + group: string +} + +type Props = RouteComponentProps & { group?: Entity } -const Register: FC = ({ group }) => { +const Register: FC = ({ group, match }) => { + useEffect(() => { + + }) + return (

Create a new Account

diff --git a/src/components/user-info/user-info.tsx b/src/components/user-info/user-info.tsx index b7b7ce8..e532ba6 100644 --- a/src/components/user-info/user-info.tsx +++ b/src/components/user-info/user-info.tsx @@ -32,7 +32,7 @@ const UserInfo: FC = ({ authenticated, user }) => {

Log In
- Sign Up + Sign Up

diff --git a/src/reducers/notifications.ts b/src/reducers/notifications.ts index 26af1d5..137316b 100644 --- a/src/reducers/notifications.ts +++ b/src/reducers/notifications.ts @@ -7,9 +7,28 @@ const initialState: NotificationsState = [] const reducer: Reducer = (state = initialState, action) => { switch (action.type) { + case 'NOTIFICATIONS_ADD_NOTIFICATION': + const { id, type, content } = action.payload + + return [ + ...state, + { + id, + type, + content, + auto: true, + }, + ] + case 'NOTIFICATIONS_REMOVE_NOTIFICATION': + return state.filter(notification => notification.id !== action.payload) + case 'NOTIFICATIONS_CLEAR_NOTIFICATION': + return state.filter(notification => { + if (notification.id === action.payload) return !notification.auto + return true + }) case 'NOTIFICATIONS_SET_AUTO': return state.map(notification => { - if (notification.id === action.payload.id) { + if (notification.id === action.payload) { return { ...notification, auto: false, @@ -18,8 +37,6 @@ const reducer: Reducer = (state = initi return notification }) - case 'NOTIFICATIONS_DISMISS': - return state.filter(notification => notification.id !== action.payload.id) default: return state } diff --git a/src/store/index.ts b/src/store/index.ts index 98760b8..172c2fb 100644 --- a/src/store/index.ts +++ b/src/store/index.ts @@ -22,7 +22,7 @@ const store = createStore( notifications, requests, }), - applyMiddleware(logger, thunk) + applyMiddleware(thunk, logger) ) export default store diff --git a/src/store/schemas.ts b/src/store/schemas.ts index a7b8110..ec54e6c 100644 --- a/src/store/schemas.ts +++ b/src/store/schemas.ts @@ -1,12 +1,7 @@ import { schema } from 'normalizr' -const groupSchema = new schema.Entity('groups') +export const groupSchema = new schema.Entity('groups') -const userSchema = new schema.Entity('users', { +export const userSchema = new schema.Entity('users', { groupSchema, }) - -export { - groupSchema, - userSchema, -} diff --git a/src/types/store.ts b/src/types/store.ts index 5b1156e..5bd9ded 100644 --- a/src/types/store.ts +++ b/src/types/store.ts @@ -25,7 +25,6 @@ export interface Notification { type: NotificationType content: string auto: boolean - expiration: number } export interface AuthenticationState {