first cut of improving UX for deleting devices.

This adds a 5 minute auth cache to speed up the process of deleting
old devices.  It has the following nastinesses (mainly due to being
written on a flight whilst juggling kids):
 * the auth cache is done as context attached to MatrixChat.
   one could argue that it should be per-client instead, but we don't
   yet have multiple clients.
 * the auth cache is only maintained currently in DevicesPanelEntry
   (i.e. set & invalidated).  One could argue that it might be better
   maintained in InteractiveAuth.js or a dedicated cache object
   abstraction, but given the only use I can think of is when managing
   devices, perhaps this is good enough for now.
This commit is contained in:
Matthew Hodgson 2017-02-21 00:19:49 +00:00
parent 7d07e7f958
commit 6af0b9618a
2 changed files with 24 additions and 5 deletions

View file

@ -68,6 +68,7 @@ module.exports = React.createClass({
childContextTypes: { childContextTypes: {
appConfig: React.PropTypes.object, appConfig: React.PropTypes.object,
authCache: React.PropTypes.object,
}, },
AuxPanel: { AuxPanel: {
@ -77,6 +78,10 @@ module.exports = React.createClass({
getChildContext: function() { getChildContext: function() {
return { return {
appConfig: this.props.config, appConfig: this.props.config,
authCache: {
auth: {},
lastUpdate: 0,
},
}; };
}, },

View file

@ -19,6 +19,11 @@ import React from 'react';
import sdk from '../../../index'; import sdk from '../../../index';
import MatrixClientPeg from '../../../MatrixClientPeg'; import MatrixClientPeg from '../../../MatrixClientPeg';
import Modal from '../../../Modal'; import Modal from '../../../Modal';
import DateUtils from '../../../DateUtils';
import utils from 'matrix-js-sdk/lib/utils';
const AUTH_CACHE_AGE = 5 * 60 * 1000; // 5 minutes
export default class DevicesPanelEntry extends React.Component { export default class DevicesPanelEntry extends React.Component {
constructor(props, context) { constructor(props, context) {
@ -30,7 +35,6 @@ export default class DevicesPanelEntry extends React.Component {
}; };
this._unmounted = false; this._unmounted = false;
this._onDeleteClick = this._onDeleteClick.bind(this); this._onDeleteClick = this._onDeleteClick.bind(this);
this._onDisplayNameChanged = this._onDisplayNameChanged.bind(this); this._onDisplayNameChanged = this._onDisplayNameChanged.bind(this);
this._makeDeleteRequest = this._makeDeleteRequest.bind(this); this._makeDeleteRequest = this._makeDeleteRequest.bind(this);
@ -53,8 +57,12 @@ export default class DevicesPanelEntry extends React.Component {
_onDeleteClick() { _onDeleteClick() {
this.setState({deleting: true}); this.setState({deleting: true});
// try without interactive auth to start off if (this.context.authCache.lastUpdate < Date.now() - AUTH_CACHE_AGE) {
this._makeDeleteRequest(null).catch((error) => { this.context.authCache.auth = null;
}
// try with auth cache (which is null, so no interactive auth, to start off)
this._makeDeleteRequest(this.context.authCache.auth).catch((error) => {
if (this._unmounted) { return; } if (this._unmounted) { return; }
if (error.httpStatus !== 401 || !error.data || !error.data.flows) { if (error.httpStatus !== 401 || !error.data || !error.data.flows) {
// doesn't look like an interactive-auth failure // doesn't look like an interactive-auth failure
@ -83,6 +91,9 @@ export default class DevicesPanelEntry extends React.Component {
} }
_makeDeleteRequest(auth) { _makeDeleteRequest(auth) {
this.context.authCache.auth = auth;
this.context.authCache.lastUpdate = Date.now();
const device = this.props.device; const device = this.props.device;
return MatrixClientPeg.get().deleteDevice(device.device_id, auth).then( return MatrixClientPeg.get().deleteDevice(device.device_id, auth).then(
() => { () => {
@ -110,8 +121,7 @@ export default class DevicesPanelEntry extends React.Component {
let lastSeen = ""; let lastSeen = "";
if (device.last_seen_ts) { if (device.last_seen_ts) {
// todo: format the timestamp as "5 minutes ago" or whatever. const lastSeenDate = DateUtils.formatDate(new Date(device.last_seen_ts));
const lastSeenDate = new Date(device.last_seen_ts);
lastSeen = device.last_seen_ip + " @ " + lastSeen = device.last_seen_ip + " @ " +
lastSeenDate.toLocaleString(); lastSeenDate.toLocaleString();
} }
@ -160,6 +170,10 @@ DevicesPanelEntry.propTypes = {
onDeleted: React.PropTypes.func, onDeleted: React.PropTypes.func,
}; };
DevicesPanelEntry.contextTypes = {
authCache: React.PropTypes.object,
};
DevicesPanelEntry.defaultProps = { DevicesPanelEntry.defaultProps = {
onDeleted: function() {}, onDeleted: function() {},
}; };