From b6c9c00450c28c223342a177bf3d2e1240b3c86c Mon Sep 17 00:00:00 2001 From: hidden4003 Date: Wed, 25 Oct 2017 13:31:34 +0300 Subject: [PATCH] Auth check issue (#114) * Logout on auth fail, issue #110, #80 Fix notifications stuck on top of each other, move to center * Bump version --- package.json | 2 +- src/components/AlertContainer.css | 4 +-- src/components/Buttons/AutoRefreshSwitch.jsx | 19 +++++++++--- src/components/Layout/Header.jsx | 16 ++++------ src/components/Notification.css | 4 +-- src/components/Notification.jsx | 2 +- src/components/UserDropdown/UserDropdown.jsx | 32 ++++++++++++-------- src/core/api.js | 13 ++++++-- src/core/events.js | 1 + src/core/reducers.js | 2 +- src/core/sagas/index.js | 9 +++++- 11 files changed, 67 insertions(+), 37 deletions(-) diff --git a/package.json b/package.json index 72cd55f2d..901f2c918 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "jmmserver-webui", - "version": "0.3.3-dev5", + "version": "0.3.3-dev6", "private": true, "engines": { "node": ">=6", diff --git a/src/components/AlertContainer.css b/src/components/AlertContainer.css index 6ea901d8b..4ec68363b 100644 --- a/src/components/AlertContainer.css +++ b/src/components/AlertContainer.css @@ -1,7 +1,7 @@ .alertContainer { z-index: 100; position: fixed; - top: 50px; + top: 20px; width: 50%; - left: 25%; + left: 35%; } diff --git a/src/components/Buttons/AutoRefreshSwitch.jsx b/src/components/Buttons/AutoRefreshSwitch.jsx index 0271003c4..005622c7d 100644 --- a/src/components/Buttons/AutoRefreshSwitch.jsx +++ b/src/components/Buttons/AutoRefreshSwitch.jsx @@ -1,12 +1,13 @@ import PropTypes from 'prop-types'; import React from 'react'; +import { connect } from 'react-redux'; import cx from 'classnames'; import store from '../../core/store'; import { setAutoupdate } from '../../core/legacy-actions'; class AutoRefreshSwitch extends React.Component { static propTypes = { - enabled: PropTypes.bool.isRequired, + autoUpdate: PropTypes.bool.isRequired, }; constructor(props) { @@ -15,14 +16,14 @@ class AutoRefreshSwitch extends React.Component { } handleClick() { - const newState = !this.props.enabled; + const newState = !this.props.autoUpdate; store.dispatch(setAutoupdate(newState)); } render() { - const { enabled } = this.props; + const { autoUpdate } = this.props; return ( -
  • +
  • @@ -31,4 +32,12 @@ class AutoRefreshSwitch extends React.Component { } } -export default AutoRefreshSwitch; +function mapStateToProps(state) { + const { autoUpdate } = state; + + return { + autoUpdate, + }; +} + +export default connect(mapStateToProps)(AutoRefreshSwitch); diff --git a/src/components/Layout/Header.jsx b/src/components/Layout/Header.jsx index 30a846ba1..a60a140cb 100644 --- a/src/components/Layout/Header.jsx +++ b/src/components/Layout/Header.jsx @@ -16,16 +16,14 @@ class Header extends React.Component { countHasher: PropTypes.number, countGeneral: PropTypes.number, countImages: PropTypes.number, - autoUpdate: PropTypes.bool, sidebarToggle: PropTypes.bool, updateAvailable: PropTypes.bool, webuiVersionUpdate: PropTypes.object, - username: PropTypes.string, }; render() { - const { countHasher, countGeneral, countImages, autoUpdate, sidebarToggle, updateAvailable, - webuiVersionUpdate, username } = this.props; + const { countHasher, countGeneral, countImages, sidebarToggle, updateAvailable, + webuiVersionUpdate } = this.props; return (
    @@ -41,30 +39,28 @@ class Header extends React.Component {
      - +
    - +
    ); } } function mapStateToProps(state) { - const { queueStatus, autoUpdate, sidebarToggle, updateAvailable, - webuiVersionUpdate, apiSession, settings } = state; + const { queueStatus, sidebarToggle, updateAvailable, + webuiVersionUpdate, settings } = state; const items = queueStatus.items || {}; return { countHasher: items.hash ? items.hash.count : null, countGeneral: items.general ? items.general.count : null, countImages: items.image ? items.image.count : null, - autoUpdate, sidebarToggle, updateAvailable: updateAvailable.status, updateChannel: settings.other.updateChannel, webuiVersionUpdate, - username: apiSession.username, }; } diff --git a/src/components/Notification.css b/src/components/Notification.css index 44634d077..2e74ca2fc 100644 --- a/src/components/Notification.css +++ b/src/components/Notification.css @@ -1,5 +1,4 @@ .notify { - position: fixed; min-width: 350px; max-width: 450px; background-color: #fff; @@ -10,6 +9,8 @@ border: 1px solid #dedede; box-shadow: rgba(0, 0, 0, 0.0980392) 0 2px 4px; z-index: 999; + position: relative; + margin-bottom: 10px; } .notify .notify-icon { @@ -34,7 +35,6 @@ } .notify .notify-text { - float: left; padding: 10px 15px; margin-left: 50px; } diff --git a/src/components/Notification.jsx b/src/components/Notification.jsx index d8286b836..589798ab9 100644 --- a/src/components/Notification.jsx +++ b/src/components/Notification.jsx @@ -12,7 +12,7 @@ class Notification extends React.Component { render() { const { text, type } = this.props; return ( -
    +
    diff --git a/src/components/UserDropdown/UserDropdown.jsx b/src/components/UserDropdown/UserDropdown.jsx index 9d14c5117..5c118c5fb 100644 --- a/src/components/UserDropdown/UserDropdown.jsx +++ b/src/components/UserDropdown/UserDropdown.jsx @@ -2,24 +2,18 @@ import PropTypes from 'prop-types'; import React from 'react'; import cx from 'classnames'; import { Dropdown, MenuItem } from 'react-bootstrap'; -import store from '../../core/store'; -import history from '../../core/history'; -import { Logout } from '../../core/actions'; +import { connect } from 'react-redux'; import s from './UserDropdown.css'; +import Events from '../../core/events'; class UserDropdown extends React.Component { static propTypes = { user: PropTypes.string, + logout: PropTypes.func, }; - // TODO: Move this to saga - static handleLogout() { - store.dispatch(Logout()); - history.push({ pathname: '/' }); - } - render() { - const { user } = this.props; + const { user, logout } = this.props; return (
    @@ -28,7 +22,7 @@ class UserDropdown extends React.Component { Profile Change password - Logout + Logout
    @@ -36,4 +30,18 @@ class UserDropdown extends React.Component { } } -export default UserDropdown; +function mapStateToProps(state) { + const { apiSession } = state; + + return { + username: apiSession.username, + }; +} + +function mapDispatchToProps(dispatch) { + return { + logout: () => { dispatch(Events.LOGOUT); }, + }; +} + +export default connect(mapStateToProps, mapDispatchToProps)(UserDropdown); diff --git a/src/core/api.js b/src/core/api.js index 2589ba3b8..4644702a2 100644 --- a/src/core/api.js +++ b/src/core/api.js @@ -1,5 +1,7 @@ import 'isomorphic-fetch'; import store from './store'; +import Events from './events'; +import { setAutoupdate } from './legacy-actions'; function apiCallPost(apiAction, apiParams, apiKey) { return fetch(`/api${apiAction}`, { @@ -29,6 +31,11 @@ function apiCall(apiAction, apiParams, type = 'GET') { return fetch.then((response) => { if (response.status !== 200) { + if (response.status === 401) { + // FIXME: make a better fix + store.dispatch({ type: Events.LOGOUT }); + setAutoupdate(false); + } return Promise.reject(`Network error: ${apiAction} ${response.status}: ${response.statusText}`); } return Promise.resolve(response); @@ -73,8 +80,10 @@ function jsonApiCall(apiAction, apiParams, type) { function jsonApiResponse(apiAction, apiParams, type) { return jsonApiCall(apiAction, apiParams, type) .then((json) => { - if (json.code && json.code !== 200) { - return { error: true, code: json.code, message: json.message || 'No error message given.' }; + if (json.code) { + if (json.code !== 200) { + return { error: true, code: json.code, message: json.message || 'No error message given.' }; + } } return { data: json }; }) diff --git a/src/core/events.js b/src/core/events.js index 726bfd8a0..ea90b46b0 100644 --- a/src/core/events.js +++ b/src/core/events.js @@ -32,4 +32,5 @@ export default { FIRSTRUN_TEST_ANIDB: 'EVENT_FIRSTRUN_TEST_ANIDB', FIRSTRUN_GET_USER: 'EVENT_FIRSTRUN_GET_USER', FIRSTRUN_SET_USER: 'EVENT_FIRSTRUN_SET_USER', + LOGOUT: 'EVENT_LOGOUT', }; diff --git a/src/core/reducers.js b/src/core/reducers.js index 9bd78a96f..095822ee7 100644 --- a/src/core/reducers.js +++ b/src/core/reducers.js @@ -67,7 +67,7 @@ const fetching = handleAction(SET_FETCHING, (state, action) => { }, {}); const rootReducer = combineReducers({ - routerReducer, + router: routerReducer, globalAlert, apiSession, autoUpdate, diff --git a/src/core/sagas/index.js b/src/core/sagas/index.js index e3c0550ed..ac09cd18f 100644 --- a/src/core/sagas/index.js +++ b/src/core/sagas/index.js @@ -2,6 +2,7 @@ import { delay } from 'redux-saga'; import { put, takeEvery, call, select } from 'redux-saga/effects'; import { without } from 'lodash/array'; import { forEach } from 'lodash'; +import { push } from 'react-router-redux'; import * as Ajv from 'ajv'; import Api from '../api'; import Events from '../events'; @@ -9,7 +10,7 @@ import Dashboard from './dashboard'; import { QUEUE_GLOBAL_ALERT, SHOW_GLOBAL_ALERT, - GLOBAL_ALERT, SET_FETCHING, + GLOBAL_ALERT, SET_FETCHING, Logout, } from '../actions'; import { GET_DELTA } from '../actions/logs/Delta'; import { SET_CONTENTS, APPEND_CONTENTS } from '../actions/logs/Contents'; @@ -438,6 +439,11 @@ function* firstrunGetDatabaseSqlserverinstance() { } } +function* logout() { + yield put(Logout()); + yield put(push({ pathname: '/' })); +} + export default function* rootSaga() { yield [ takeEvery(QUEUE_GLOBAL_ALERT, queueGlobalAlert), @@ -470,5 +476,6 @@ export default function* rootSaga() { takeEvery(Events.FIRSTRUN_TEST_ANIDB, firstrunTestAnidb), takeEvery(Events.FIRSTRUN_GET_USER, firstrunGetDefaultuser), takeEvery(Events.FIRSTRUN_SET_USER, firstrunSetDefaultuser), + takeEvery(Events.LOGOUT, logout), ]; }