diff --git a/frontend/src/actions/message.actions.js b/frontend/src/actions/message.actions.js new file mode 100644 index 0000000..0a87dcc --- /dev/null +++ b/frontend/src/actions/message.actions.js @@ -0,0 +1,11 @@ +export const ADD_MESSAGE = 'ADD_MESSAGE' + +export function addMessage(type, text) { + return { type: ADD_MESSAGE, text, messageType: type }; +} + +export const REMOVE_OLDEST_MESSAGE = 'REMOVE_OLDEST_MESSAGE' + +export function removeOldestMessage() { + return { type: REMOVE_OLDEST_MESSAGE } +} \ No newline at end of file diff --git a/frontend/src/components/message-list.js b/frontend/src/components/message-list.js new file mode 100644 index 0000000..23cd435 --- /dev/null +++ b/frontend/src/components/message-list.js @@ -0,0 +1,23 @@ +export function MessageList({ messages }) { + if (!Array.isArray(messages)) return null; + + return ( +
+
+
+ { + messages.map((m, i) => { + return ( +
+
+ {m.text} +
+
+ ) + }) + } +
+
+
+ ); +} \ No newline at end of file diff --git a/frontend/src/pages/login.js b/frontend/src/pages/login.js index 99c0b4e..45044bb 100644 --- a/frontend/src/pages/login.js +++ b/frontend/src/pages/login.js @@ -1,5 +1,5 @@ import React, { useState, useEffect } from 'react' -import Page from './page'; +import { ConnectedPage as Page } from './page'; import { login } from '../actions/auth.actions'; import { connect } from 'react-redux'; diff --git a/frontend/src/pages/page.js b/frontend/src/pages/page.js index fb32593..186c8a1 100644 --- a/frontend/src/pages/page.js +++ b/frontend/src/pages/page.js @@ -1,13 +1,33 @@ import React, { useEffect } from 'react' +import { MessageList } from '../components/message-list'; +import { connect } from 'react-redux'; +import { removeOldestMessage } from '../actions/message.actions'; -export default function Page({ title, children }) { +export default function Page({ title, messages, dispatch, children }) { useEffect(() => { document.title = title ? `${title } - PleaseWait` : 'PleaseWait'; }); + useEffect(() => { + if (!Array.isArray(messages) || messages.length === 0) return; + + let timeoutId = setTimeout(() => { + dispatch(removeOldestMessage()); + }, 5000); + + return () => clearTimeout(timeoutId); + }, [messages]) + return (
+ { children }
); -} \ No newline at end of file +} + +function mapStateToProps(state) { + return { messages: state.messages.sortedByTimestamp }; +} + +export const ConnectedPage = connect(mapStateToProps)(Page); \ No newline at end of file diff --git a/frontend/src/reducers/messages.reducers.js b/frontend/src/reducers/messages.reducers.js new file mode 100644 index 0000000..59a87db --- /dev/null +++ b/frontend/src/reducers/messages.reducers.js @@ -0,0 +1,32 @@ +import { ADD_MESSAGE, REMOVE_OLDEST_MESSAGE } from "../actions/message.actions"; + +const initialState = { + sortedByTimestamp: [] +}; + +export function messagesReducer(state = initialState, action) { + switch(action.type) { + case ADD_MESSAGE: + return handleAddMessage(state, action); + case REMOVE_OLDEST_MESSAGE: + return handleRemoveOldestMessage(state, action); + }; + return state; +} + +function handleAddMessage(state, action) { + return { + ...state, + sortedByTimestamp: [ + { ts: Date.now(), text: action.text, type: action.messageType }, + ...state.sortedByTimestamp + ] + } +}; + +function handleRemoveOldestMessage(state, action) { + return { + ...state, + sortedByTimestamp: state.sortedByTimestamp.slice(0, -1) + } +}; \ No newline at end of file diff --git a/frontend/src/reducers/session.reducers.js b/frontend/src/reducers/session.reducers.js index eeb15a8..2d245a7 100644 --- a/frontend/src/reducers/session.reducers.js +++ b/frontend/src/reducers/session.reducers.js @@ -15,7 +15,7 @@ export function sessionReducer(state = initialState, action) { function handleLoginSuccess(state, action) { return { - ...state.user, + ...state, isLoggedIn: true, user: { username: action.username, diff --git a/frontend/src/sagas/auth.sagas.js b/frontend/src/sagas/auth.sagas.js index f7d6435..f42bea7 100644 --- a/frontend/src/sagas/auth.sagas.js +++ b/frontend/src/sagas/auth.sagas.js @@ -1,5 +1,6 @@ import { call, put } from 'redux-saga/effects'; import { loginFailure, loginSuccess } from '../actions/auth.actions'; +import { addMessage } from '../actions/message.actions'; export function* loginSaga(action) { let result; @@ -7,10 +8,13 @@ export function* loginSaga(action) { result = yield call(doLogin, action.username, action.password); } catch(err) { yield put(loginFailure(action.username, err)); + yield put(addMessage('danger', "Une erreur inconnue bloque le fonctionnement normal de l'application. Veuillez réessayer plus tard.")); } if ('error' in result) { yield put(loginFailure(action.username, result.error)); + const message = result.error.message ? result.error.message : result.error.toString(); + yield put(addMessage('danger', message)); return } diff --git a/frontend/src/store/store.js b/frontend/src/store/store.js index 71b924b..dc8cbd6 100644 --- a/frontend/src/store/store.js +++ b/frontend/src/store/store.js @@ -2,11 +2,13 @@ import { createStore, applyMiddleware, combineReducers, compose } from 'redux' import createSagaMiddleware from 'redux-saga' import rootSaga from '../sagas/root' import { sessionReducer } from '../reducers/session.reducers'; +import { messagesReducer } from '../reducers/messages.reducers'; const sagaMiddleware = createSagaMiddleware() const rootReducer = combineReducers({ session: sessionReducer, + messages: messagesReducer, }); const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;