From a850f19cd40486a6328ac93c49fa1be3fe7d1945 Mon Sep 17 00:00:00 2001 From: David Baker Date: Mon, 26 Oct 2015 13:54:54 +0000 Subject: [PATCH] Separate out the activity watcher from presence code so I can hook read receipts into it without tangling it into the presence code. --- src/Presence.js | 66 +++++++++++++++-------------- src/UserActivity.js | 57 +++++++++++++++++++++++++ src/controllers/pages/MatrixChat.js | 3 ++ 3 files changed, 95 insertions(+), 31 deletions(-) create mode 100644 src/UserActivity.js diff --git a/src/Presence.js b/src/Presence.js index d77058abd8..1f5617514a 100644 --- a/src/Presence.js +++ b/src/Presence.js @@ -15,58 +15,54 @@ limitations under the License. */ var MatrixClientPeg = require("./MatrixClientPeg"); +var dis = require("./dispatcher"); // Time in ms after that a user is considered as unavailable/away var UNAVAILABLE_TIME_MS = 3 * 60 * 1000; // 3 mins var PRESENCE_STATES = ["online", "offline", "unavailable"]; -// The current presence state -var state, timer; - -module.exports = { +class Presence { /** * Start listening the user activity to evaluate his presence state. * Any state change will be sent to the Home Server. */ - start: function() { - var self = this; + start() { this.running = true; - if (undefined === state) { - // The user is online if they move the mouse or press a key - document.onmousemove = function() { self._resetTimer(); }; - document.onkeypress = function() { self._resetTimer(); }; + if (undefined === this.state) { this._resetTimer(); + this.dispatcherRef = dis.register(this._onUserActivity.bind(this)); } - }, + } /** * Stop tracking user activity */ - stop: function() { + stop() { this.running = false; - if (timer) { - clearTimeout(timer); - timer = undefined; + if (this.timer) { + clearInterval(this.timer); + this.timer = undefined; + dis.unregister(this.dispatcherRef); } - state = undefined; - }, + this.state = undefined; + } /** * Get the current presence state. * @returns {string} the presence state (see PRESENCE enum) */ - getState: function() { - return state; - }, + getState() { + return this.state; + } /** * Set the presence state. * If the state has changed, the Home Server will be notified. * @param {string} newState the new presence state (see PRESENCE enum) */ - setState: function(newState) { - if (newState === state) { + setState(newState) { + if (newState === this.state) { return; } if (PRESENCE_STATES.indexOf(newState) === -1) { @@ -75,33 +71,41 @@ module.exports = { if (!this.running) { return; } - state = newState; - MatrixClientPeg.get().setPresence(state).done(function() { + var old_state = this.state; + this.state = newState; + MatrixClientPeg.get().setPresence(this.state).done(function() { console.log("Presence: %s", newState); }, function(err) { console.error("Failed to set presence: %s", err); + this.state = old_state; }); - }, + } /** * Callback called when the user made no action on the page for UNAVAILABLE_TIME ms. * @private */ - _onUnavailableTimerFire: function() { + _onUnavailableTimerFire() { this.setState("unavailable"); - }, + } + + _onUserActivity() { + this._resetTimer(); + } /** * Callback called when the user made an action on the page * @private */ - _resetTimer: function() { + _resetTimer() { var self = this; this.setState("online"); // Re-arm the timer - clearTimeout(timer); - timer = setTimeout(function() { + clearTimeout(this.timer); + this.timer = setTimeout(function() { self._onUnavailableTimerFire(); }, UNAVAILABLE_TIME_MS); } -}; +} + +module.exports = new Presence(); diff --git a/src/UserActivity.js b/src/UserActivity.js new file mode 100644 index 0000000000..46a46f0b0e --- /dev/null +++ b/src/UserActivity.js @@ -0,0 +1,57 @@ +/* +Copyright 2015 OpenMarket Ltd + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +var dis = require("./dispatcher"); + +var MIN_DISPATCH_INTERVAL = 1 * 1000; + +/** + * This class watches for user activity (moving the mouse or pressing a key) + * and dispatches the user_activity action at times when the user is interacting + * with the app (but at a much lower frequency than mouse move events) + */ +class UserActivity { + + /** + * Start listening to user activity + */ + start() { + document.onmousemove = this._onUserActivity.bind(this); + document.onkeypress = this._onUserActivity.bind(this); + this.lastActivityAt = (new Date).getTime(); + this.lastDispatchAt = 0; + } + + /** + * Stop tracking user activity + */ + stop() { + document.onmousemove = undefined; + document.onkeypress = undefined; + } + + _onUserActivity() { + this.lastActivityAt = (new Date).getTime(); + if (this.lastDispatchAt < this.lastActivityAt - MIN_DISPATCH_INTERVAL) { + this.lastDispatchAt = this.lastActivityAt; + dis.dispatch({ + action: 'user_activity' + }); + } + } +} + +module.exports = new UserActivity(); diff --git a/src/controllers/pages/MatrixChat.js b/src/controllers/pages/MatrixChat.js index 2a712b22f6..97fc3a1fe1 100644 --- a/src/controllers/pages/MatrixChat.js +++ b/src/controllers/pages/MatrixChat.js @@ -16,6 +16,7 @@ limitations under the License. var MatrixClientPeg = require("../../MatrixClientPeg"); var RoomListSorter = require("../../RoomListSorter"); +var UserActivity = require("../../UserActivity"); var Presence = require("../../Presence"); var dis = require("../../dispatcher"); @@ -92,6 +93,7 @@ module.exports = { window.localStorage.clear(); } Notifier.stop(); + UserActivity.stop(); Presence.stop(); MatrixClientPeg.get().stopClient(); MatrixClientPeg.get().removeAllListeners(); @@ -316,6 +318,7 @@ module.exports = { }); }); Notifier.start(); + UserActivity.start(); Presence.start(); cli.startClient(); },