From 6c2abad18ae503380c107daf7417d6651138b997 Mon Sep 17 00:00:00 2001 From: bochaco Date: Wed, 5 Jul 2017 19:43:50 +0900 Subject: [PATCH 1/3] feat/Display a loading spinner when the app is processing some task --- email_app/app/components/home.js | 38 +++++++++++---------- email_app/app/containers/home_container.js | 3 +- email_app/app/reducers/initialiser.js | 39 ++++++++++++++++++++-- email_app/package.json | 1 + 4 files changed, 60 insertions(+), 21 deletions(-) diff --git a/email_app/app/components/home.js b/email_app/app/components/home.js index d73c43b..4121ad8 100755 --- a/email_app/app/components/home.js +++ b/email_app/app/components/home.js @@ -2,12 +2,14 @@ import React, { Component } from 'react'; import PropTypes from 'prop-types'; import { Link, IndexLink } from 'react-router'; import Modal from 'react-modal'; +const Loading = require('react-loading-animation'); import className from 'classnames'; import pkg from '../../package.json'; import { CONSTANTS } from '../constants'; const modalStyles = { content : { + border : '1px solid #ccc', top : '50%', left : '50%', right : 'auto', @@ -20,39 +22,41 @@ const modalStyles = { export default class Home extends Component { constructor() { super(); - this.state = { - reconnecting: false - }; this.reconnect = this.reconnect.bind(this); } reconnect() { - this.setState({reconnecting: true}); - return this.props.reconnectApplication() - .catch((err) => 'not reconnected') - .then(() => this.setState({reconnecting: false})) + return this.props.reconnectApplication(); } render() { const { router } = this.context; - const { coreData, inboxSize, savedSize, network_status } = this.props; + const { coreData, inboxSize, savedSize, network_status, processing } = this.props; - const isNetworkDisconnected = (network_status !== CONSTANTS.NET_STATUS_CONNECTED); + const isModalOpen = processing.state || (network_status !== CONSTANTS.NET_STATUS_CONNECTED); + let processingModalStyle = { ...modalStyles, content: { ...modalStyles.content, border: '0px' } }; return (
-
-
The application hast lost network connection.

-
Make sure the network link is up before trying to reconnect.

- -
+ {processing.state ? ( +
+
+
{processing.msg}
+
+ ) : ( +
+
The application hast lost network connection.

+
Make sure the network link is up before trying to reconnect.

+ +
+ )}
diff --git a/email_app/app/containers/home_container.js b/email_app/app/containers/home_container.js index 6c38f0c..affbc6e 100644 --- a/email_app/app/containers/home_container.js +++ b/email_app/app/containers/home_container.js @@ -7,7 +7,8 @@ const mapStateToProps = state => { coreData: state.initializer.coreData, inboxSize: state.initializer.inboxSize, savedSize: state.initializer.savedSize, - network_status: state.initializer.network_status + network_status: state.initializer.network_status, + processing: state.initializer.processing }; }; diff --git a/email_app/app/reducers/initialiser.js b/email_app/app/reducers/initialiser.js index 243a43a..606803c 100644 --- a/email_app/app/reducers/initialiser.js +++ b/email_app/app/reducers/initialiser.js @@ -4,6 +4,10 @@ import { MESSAGES, APP_STATUS, CONSTANTS, SAFE_APP_ERROR_CODES } from '../consta const initialState = { app_status: null, network_status: null, + processing: { + state: false, + msg: null + }, app: null, tasks: [], accounts: [], @@ -44,8 +48,15 @@ const initializer = (state = initialState, action) => { case ACTION_TYPES.NET_STATUS_CHANGED: return { ...state, network_status: action.payload }; break; + case `${ACTION_TYPES.RECONNECT_APP}_LOADING`: + return { ...state, processing: { state: true, msg: 'Reconnecting...' } }; + break; + case `${ACTION_TYPES.RECONNECT_APP}_ERROR`: case `${ACTION_TYPES.RECONNECT_APP}_SUCCESS`: - return { ...state, network_status: CONSTANTS.NET_STATUS_CONNECTED }; + return { ...state, + network_status: CONSTANTS.NET_STATUS_CONNECTED, + processing: { state: false, msg: null } + }; break; case `${ACTION_TYPES.GET_CONFIG}_LOADING`: return { ...state, app_status: APP_STATUS.READING_CONFIG }; @@ -57,19 +68,41 @@ const initializer = (state = initialState, action) => { app_status: APP_STATUS.READY }; break; + case `${ACTION_TYPES.CREATE_ACCOUNT}_LOADING`: + return { ...state, processing: { state: true, msg: 'Creating account...' } }; + break; + case `${ACTION_TYPES.CREATE_ACCOUNT}_ERROR`: + return { ...state, processing: { state: false, msg: null } }; + break; case `${ACTION_TYPES.STORE_NEW_ACCOUNT}_SUCCESS`: return { ...state, accounts: action.payload, - coreData: { ...state.coreData, id: action.payload.id } + coreData: { ...state.coreData, id: action.payload.id }, + processing: { state: false, msg: null } }; break; + case `${ACTION_TYPES.STORE_NEW_ACCOUNT}_ERROR`: + return { ...state, processing: { state: false, msg: null } }; + break; case `${ACTION_TYPES.REFRESH_EMAIL}_LOADING`: return { ...state, coreData: { ...state.coreData, inbox: [], saved: [] }, inboxSize: 0, - savedSize: 0 + savedSize: 0, + processing: { state: true, msg: 'Reading emails...' } }; break; + case `${ACTION_TYPES.REFRESH_EMAIL}_SUCCESS`: + case `${ACTION_TYPES.REFRESH_EMAIL}_ERROR`: + return { ...state, processing: { state: false, msg: null } }; + break; + case `${ACTION_TYPES.MAIL_PROCESSING}_LOADING`: + return { ...state, processing: { state: true, msg: null } }; + break; + case `${ACTION_TYPES.MAIL_PROCESSING}_SUCCESS`: + case `${ACTION_TYPES.MAIL_PROCESSING}_ERROR`: + return { ...state, processing: { state: false, msg: null } }; + break; case ACTION_TYPES.PUSH_TO_INBOX: { let inbox = Object.assign({}, state.coreData.inbox, action.payload); return { ...state, diff --git a/email_app/package.json b/email_app/package.json index 01d67f4..e57b0c2 100644 --- a/email_app/package.json +++ b/email_app/package.json @@ -114,6 +114,7 @@ "react": "^15.4.2", "react-dom": "^15.4.2", "react-hot-loader": "^3.0.0-beta.6", + "react-loading-animation": "^1.3.0", "react-modal": "^2.1.0", "react-redux": "^5.0.3", "react-router": "^3.0.2", From 67ded01936ae3dd88487ea6b6db892f7e73b6854 Mon Sep 17 00:00:00 2001 From: bochaco Date: Thu, 6 Jul 2017 14:55:51 +0900 Subject: [PATCH 2/3] refactor/Refactoring some code which handles the redux actions of different pages --- email_app/app/actions/actionTypes.js | 1 - .../app/actions/create_account_actions.js | 4 +- email_app/app/actions/mail_actions.js | 13 +- email_app/app/components/create_account.js | 3 +- email_app/app/components/home.js | 5 +- email_app/app/components/initializer.js | 1 + email_app/app/components/mail_list.js | 160 ++++++++---------- .../app/containers/compose_mail_container.js | 6 +- .../containers/create_account_container.js | 2 +- email_app/app/containers/home_container.js | 4 +- .../app/containers/mail_inbox_container.js | 4 +- email_app/app/less/authenticate.less | 2 + email_app/app/less/base.less | 2 + email_app/app/less/buttons.less | 4 +- email_app/app/less/compose_mail.less | 2 + email_app/app/less/form.less | 2 + email_app/app/less/home.less | 2 + email_app/app/less/list.less | 2 + email_app/app/less/variables.less | 16 ++ email_app/app/less/view_mail.less | 2 + email_app/app/reducers/create_account.js | 14 +- email_app/app/reducers/initialiser.js | 10 +- email_app/app/reducers/mail.js | 7 - 23 files changed, 134 insertions(+), 134 deletions(-) diff --git a/email_app/app/actions/actionTypes.js b/email_app/app/actions/actionTypes.js index 2915ad6..a156af1 100644 --- a/email_app/app/actions/actionTypes.js +++ b/email_app/app/actions/actionTypes.js @@ -15,7 +15,6 @@ const ACTION_TYPES = { // Mail Inbox PUSH_MAIL: 'PUSH_MAIL', MAIL_PROCESSING: 'MAIL_PROCESSING', - CLEAR_MAIL_PROCESSING: 'CLEAR_MAIL_PROCESSING', SET_ACTIVE_MAIL: 'SET_ACTIVE_MAIL', CANCEL_COMPOSE: 'CANCEL_COMPOSE', diff --git a/email_app/app/actions/create_account_actions.js b/email_app/app/actions/create_account_actions.js index cb920ea..37f0c35 100644 --- a/email_app/app/actions/create_account_actions.js +++ b/email_app/app/actions/create_account_actions.js @@ -12,6 +12,6 @@ export const createAccount = (emailId) => { }; export const createAccountError = (error) => ({ - type: ACTION_TYPES.CREATE_ACCOUNT_ERROR, - error + type: ACTION_TYPES.CREATE_ACCOUNT, + payload: Promise.reject(error) }); diff --git a/email_app/app/actions/mail_actions.js b/email_app/app/actions/mail_actions.js index 13fec7f..ae164b0 100644 --- a/email_app/app/actions/mail_actions.js +++ b/email_app/app/actions/mail_actions.js @@ -6,9 +6,8 @@ export const sendEmail = (email, to) => { let app = getState().initializer.app; return dispatch({ type: ACTION_TYPES.MAIL_PROCESSING, + msg: 'Sending email...', payload: storeEmail(app, email, to) - .then(() => dispatch(clearMailProcessing)) - .then(() => Promise.resolve()) }); }; }; @@ -18,9 +17,8 @@ export const saveEmail = (account, key) => { let app = getState().initializer.app; return dispatch({ type: ACTION_TYPES.MAIL_PROCESSING, + msg: 'Saving email...', payload: archiveEmail(app, account, key) - .then(() => dispatch(clearMailProcessing)) - .then(() => Promise.resolve()) }); }; }; @@ -30,17 +28,12 @@ export const deleteEmail = (container, key) => { let app = getState().initializer.app; return dispatch({ type: ACTION_TYPES.MAIL_PROCESSING, + msg: 'Deleting email...', payload: removeEmail(app, container, key) - .then(() => dispatch(clearMailProcessing)) - .then(() => Promise.resolve()) }); }; }; -export const clearMailProcessing = _ => ({ - type: ACTION_TYPES.CLEAR_MAIL_PROCESSING -}); - export const cancelCompose = _ => ({ type: ACTION_TYPES.CANCEL_COMPOSE }); diff --git a/email_app/app/components/create_account.js b/email_app/app/components/create_account.js index ff6d113..b64e571 100644 --- a/email_app/app/components/create_account.js +++ b/email_app/app/components/create_account.js @@ -41,7 +41,6 @@ export default class CreateAccount extends Component { render() { const { processing, error } = this.props; - return (
@@ -54,7 +53,7 @@ export default class CreateAccount extends Component {
Email Id must be less than {CONSTANTS.EMAIL_ID_MAX_LENGTH} characters. (This is just a restriction in this tutorial)
- +

{error.message}

diff --git a/email_app/app/components/home.js b/email_app/app/components/home.js index 4121ad8..eb5c7a1 100755 --- a/email_app/app/components/home.js +++ b/email_app/app/components/home.js @@ -27,7 +27,10 @@ export default class Home extends Component { } reconnect() { - return this.props.reconnectApplication(); + const { reconnectApplication, accounts, refreshEmail } = this.props; + return reconnectApplication() + .then(() => refreshEmail(accounts), + (err) => 'failed reconnecting'); } render() { diff --git a/email_app/app/components/initializer.js b/email_app/app/components/initializer.js index 53c98be..a1f2e3c 100644 --- a/email_app/app/components/initializer.js +++ b/email_app/app/components/initializer.js @@ -28,6 +28,7 @@ export default class Initializer extends Component { refreshConfig() { const { setInitializerTask, refreshConfig } = this.props; setInitializerTask(MESSAGES.INITIALIZE.CHECK_CONFIGURATION); + return refreshConfig() .then((_) => { if (Object.keys(this.props.accounts).length > 0) { diff --git a/email_app/app/components/mail_list.js b/email_app/app/components/mail_list.js index c680375..8a92c55 100644 --- a/email_app/app/components/mail_list.js +++ b/email_app/app/components/mail_list.js @@ -9,27 +9,12 @@ export default class MailList extends Component { super(); this.listColors = {}; this.activeType = null; - this.goBack = this.goBack.bind(this); this.refreshEmail = this.refreshEmail.bind(this); this.handleDeleteFromInbox = this.handleDeleteFromInbox.bind(this); this.handleDeleteSaved = this.handleDeleteSaved.bind(this); this.handleSave = this.handleSave.bind(this); } - goBack() { - const { router } = this.context; - - switch (this.activeType) { - case CONSTANTS.HOME_TABS.INBOX: { - return this.props.refreshEmail(); - } - case CONSTANTS.HOME_TABS.SAVED: { - router.push('/home'); - return router.push('/saved'); - } - } - } - refreshEmail(account) { this.props.refreshEmail(account) .catch((error) => { @@ -76,87 +61,82 @@ export default class MailList extends Component { render() { const self = this; - const { processing, coreData, error, inboxSize, inbox, savedSize, saved } = this.props; + const { coreData, error, inboxSize, inbox, savedSize, saved } = this.props; let container = null; - if (processing) { - container =
  • Loading...
  • - } else if (Object.keys(error).length > 0) { - container =
  • Error in fetching emails!
  • - } else { - if (inbox) { - this.activeType = CONSTANTS.HOME_TABS.INBOX; - container = ( -
    - { - inboxSize === 0 ?
  • Inbox empty
  • : Object.keys(coreData.inbox).map((key) => { - let mail = coreData.inbox[key]; - if (!self.listColors.hasOwnProperty(mail.from)) { - self.listColors[mail.from] = `bg-color-${Object.keys(self.listColors).length % 10}` - } - return ( -
  • -
    - {mail.from[0]} -
    -
    -

    {mail.from}

    -

    {dateformat(new Date(mail.time), CONSTANTS.DATE_FORMAT)}

    -

    {mail.subject}

    -

    {mail.body}

    + if (inbox) { + this.activeType = CONSTANTS.HOME_TABS.INBOX; + container = ( +
    + { + inboxSize === 0 ?
  • Inbox empty
  • : Object.keys(coreData.inbox).map((key) => { + let mail = coreData.inbox[key]; + if (!self.listColors.hasOwnProperty(mail.from)) { + self.listColors[mail.from] = `bg-color-${Object.keys(self.listColors).length % 10}` + } + return ( +
  • +
    + {mail.from[0]} +
    +
    +

    {mail.from}

    +

    {dateformat(new Date(mail.time), CONSTANTS.DATE_FORMAT)}

    +

    {mail.subject}

    +

    {mail.body}

    +
    +
    +
    +
    -
    -
    - -
    -
    - -
    +
    +
    -
  • - ) - }) - } -
    - ); - } - if (saved) { - this.activeType = CONSTANTS.HOME_TABS.SAVED; - container = ( -
    - { - savedSize === 0 ?
  • Saved empty
  • : Object.keys(coreData.saved).map((key) => { - let mail = coreData.saved[key]; - if (!mail) { - return; - } - if (!self.listColors.hasOwnProperty(mail.from)) { - self.listColors[mail.from] = `bg-color-${Object.keys(self.listColors).length % 10}` - } - return ( -
  • -
    - {mail.from[0]} -
    -
    -

    {mail.from}

    -

    {dateformat(new Date(mail.time), CONSTANTS.DATE_FORMAT)}

    -

    {mail.subject}

    -

    {mail.body}

    -
    -
    -
    - -
    +
    +
  • + ) + }) + } +
    + ); + } + if (saved) { + this.activeType = CONSTANTS.HOME_TABS.SAVED; + container = ( +
    + { + savedSize === 0 ?
  • Saved empty
  • : Object.keys(coreData.saved).map((key) => { + let mail = coreData.saved[key]; + if (!mail) { + return; + } + if (!self.listColors.hasOwnProperty(mail.from)) { + self.listColors[mail.from] = `bg-color-${Object.keys(self.listColors).length % 10}` + } + return ( +
  • +
    + {mail.from[0]} +
    +
    +

    {mail.from}

    +

    {dateformat(new Date(mail.time), CONSTANTS.DATE_FORMAT)}

    +

    {mail.subject}

    +

    {mail.body}

    +
    +
    +
    +
    -
  • - ) - }) - } -
    - ); - } +
    + + ) + }) + } +
    + ); } + return (