From 51d1eb03880394a20237098f3adbfb10ae7eb9d3 Mon Sep 17 00:00:00 2001 From: Viktor Liu Date: Sat, 29 Oct 2016 11:21:50 +0200 Subject: [PATCH] Add more loading masks --- README.md | 2 -- src/scripts/GroupViewer.js | 11 +++++++++-- src/scripts/NavBar.js | 1 - src/scripts/NodeViewer.js | 12 ++++++++++-- src/scripts/Viewport.js | 35 ++++++++++++++++++++++++++++------- src/scripts/app.js | 12 +++++++++++- src/style/app.css | 1 + 7 files changed, 59 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index cff6387..9149ab8 100644 --- a/README.md +++ b/README.md @@ -472,8 +472,6 @@ For the format to use in `config.yml`/`db_location` or the auth backend see the - Log may have 'Key has been revoked' messages: happens when session keyring gets revoked once user (who (re)started the server) logs out. Please file a bug report in this case. -- Loading masks for long running ajax requests missing - - Tests ## SCREENSHOTS diff --git a/src/scripts/GroupViewer.js b/src/scripts/GroupViewer.js index eca72e0..c99010c 100644 --- a/src/scripts/GroupViewer.js +++ b/src/scripts/GroupViewer.js @@ -1,4 +1,5 @@ import React from 'react' +import Classnames from 'classnames' export default class GroupViewer extends React.Component { constructor(props) { @@ -16,7 +17,13 @@ export default class GroupViewer extends React.Component { } render() { - if (!this.props || !this.props.group) return null + var classes = Classnames({ + 'panel': true, + 'panel-default': true, + 'loading-mask': this.props.mask, + }) + + if (!this.props.group) return (
) var group = this.props.group @@ -38,7 +45,7 @@ export default class GroupViewer extends React.Component { } return ( -
+
{this.getIcon(group)} {group.title} diff --git a/src/scripts/NavBar.js b/src/scripts/NavBar.js index cf3d309..8187942 100644 --- a/src/scripts/NavBar.js +++ b/src/scripts/NavBar.js @@ -1,5 +1,4 @@ import React from 'react' -import Classnames from 'classnames' window.$ = window.jQuery = require('jquery') var Bootstrap = require('bootstrap') diff --git a/src/scripts/NodeViewer.js b/src/scripts/NodeViewer.js index 0f8226f..5245dbd 100644 --- a/src/scripts/NodeViewer.js +++ b/src/scripts/NodeViewer.js @@ -1,5 +1,6 @@ import React from 'react' import Clipboard from 'clipboard-js' +import Classnames from 'classnames' export default class NodeViewer extends React.Component { constructor() { @@ -8,6 +9,7 @@ export default class NodeViewer extends React.Component { } setHide(target, hide, name, data) { + if (!this.props.entry) return if (typeof data === 'undefined') data = null if (!name || name === 'password') @@ -130,7 +132,13 @@ export default class NodeViewer extends React.Component { } render() { - if (!this.props || !this.props.entry) return null + var classes = Classnames({ + 'panel': true, + 'panel-default': true, + 'loading-mask': this.props.mask, + }) + + if (!this.props.entry) return (
) var entry = this.props.entry @@ -222,7 +230,7 @@ export default class NodeViewer extends React.Component { icon = return ( -
+
{icon} {entry.title} diff --git a/src/scripts/Viewport.js b/src/scripts/Viewport.js index 0227a4a..434258b 100644 --- a/src/scripts/Viewport.js +++ b/src/scripts/Viewport.js @@ -41,9 +41,12 @@ export default class Viewport extends React.Component { if (cur == group) return + if (this.serverRequest) + this.serverRequest.abort() + this.setState({ - group: null, entry: null, + groupMask: true, }) this.serverRequest = KeePass4Web.ajax('get_group', { @@ -53,6 +56,7 @@ export default class Viewport extends React.Component { success: function (data) { this.setState({ group: data.data, + groupMask: false, }) this.scroll('group-viewer') @@ -65,18 +69,25 @@ export default class Viewport extends React.Component { // ignore already selected if (this.state.entry && this.state.entry.id && entry.id === this.state.entry.id) return - // remove entry first to rerender entry - // important for eye close/open buttons + if (this.serverRequest) + this.serverRequest.abort() + this.setState({ - entry: null + nodeMask: true, }) this.serverRequest = KeePass4Web.ajax('get_entry', { data: { id: entry.id, }, success: function (data) { + // remove entry first to rerender entry + // important for eye close/open buttons + this.setState({ + entry: null, + }) this.setState({ - entry: data.data + entry: data.data, + nodeMask: false, }) this.scroll('node-viewer') @@ -87,6 +98,15 @@ export default class Viewport extends React.Component { onSearch(refs, event) { event.preventDefault() + + if (this.serverRequest) + this.serverRequest.abort() + + this.setState({ + entry: null, + groupMask: true, + }) + this.serverRequest = KeePass4Web.ajax('search_entries', { data: { term: refs.term.value, @@ -97,14 +117,13 @@ export default class Viewport extends React.Component { this.setState({ cursor: null, group: data.data, - entry: null + groupMask: false, }) }.bind(this), error: KeePass4Web.error.bind(this), }) } - componentDidMount() { if (KeePass4Web.getCN()) { document.getElementById('logout').addEventListener('click', this.onLogout) @@ -145,12 +164,14 @@ export default class Viewport extends React.Component {
diff --git a/src/scripts/app.js b/src/scripts/app.js index 0a206d1..677ccbb 100644 --- a/src/scripts/app.js +++ b/src/scripts/app.js @@ -88,7 +88,7 @@ KeePass4Web.ajax = function(url, conf) { } conf.headers['X-CSRF-Token'] = KeePass4Web.getCSRFToken() - jQuery.ajax(conf) + return jQuery.ajax(conf) } KeePass4Web.logout = function(router) { @@ -146,6 +146,9 @@ KeePass4Web.getCSRFToken = function() { } KeePass4Web.error = function(r, s, e) { + // ignore aborted requests + if (e === 'abort') + return if (r.status == 401) { if (this.props && this.props.router) { // redirect first, to hide sensitive data @@ -162,6 +165,13 @@ KeePass4Web.error = function(r, s, e) { let error = e if (r.responseJSON) error = r.responseJSON.message + // disable remaining loading masks + if (this.state) { + this.setState({ + groupMask: false, + nodeMask: false, + }) + } alert(e) } } diff --git a/src/style/app.css b/src/style/app.css index 0c0decc..665812e 100644 --- a/src/style/app.css +++ b/src/style/app.css @@ -177,4 +177,5 @@ body { left: calc(50% - 12px); animation: 2s linear 0s normal none infinite running spin; filter: drop-shadow(0 0 2 rgba(0, 0, 0, 0.33)); + z-index: 2000; }