From cc5eb77658ee27db546fc68121e87f4f5a1a0dff Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Mon, 25 Dec 2017 14:25:13 -0700 Subject: [PATCH 1/2] Remove presence management The feature is incredibly buggy and doesn't work as expected due to server behaviour and client interaction. One of the major problems is the constantly confused presence state - this is caused by the mobile apps conflicting on the state of the web app, causing it to consider the user offline or online (and rarely away) depending on how riot-android/ios is behaving at the time. This reverts two PRs: * https://github.com/matrix-org/matrix-react-sdk/pull/1620 * https://github.com/matrix-org/matrix-react-sdk/pull/1482 The changes to the context menu positioning were not reverted as they are useful outside of presence management. Signed-off-by: Travis Ralston --- src/MatrixClientPeg.js | 1 - src/Presence.js | 39 +--- .../views/avatars/MemberPresenceAvatar.js | 168 ------------------ src/components/views/rooms/MessageComposer.js | 4 +- src/i18n/strings/en_EN.json | 1 + 5 files changed, 7 insertions(+), 206 deletions(-) delete mode 100644 src/components/views/avatars/MemberPresenceAvatar.js diff --git a/src/MatrixClientPeg.js b/src/MatrixClientPeg.js index 14dfa91fa4..6e3a380396 100644 --- a/src/MatrixClientPeg.js +++ b/src/MatrixClientPeg.js @@ -98,7 +98,6 @@ class MatrixClientPeg { const opts = utils.deepCopy(this.opts); // the react sdk doesn't work without this, so don't allow opts.pendingEventOrdering = "detached"; - opts.disablePresence = true; // we do this manually try { const promise = this.matrixClient.store.startup(); diff --git a/src/Presence.js b/src/Presence.js index 2652c64c96..fab518e1cb 100644 --- a/src/Presence.js +++ b/src/Presence.js @@ -56,27 +56,13 @@ class Presence { return this.state; } - /** - * Get the current status message. - * @returns {String} the status message, may be null - */ - getStatusMessage() { - return this.statusMessage; - } - /** * 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) - * @param {String} statusMessage an optional status message for the presence - * @param {boolean} maintain true to have this status maintained by this tracker */ - setState(newState, statusMessage=null, maintain=false) { - if (this.maintain) { - // Don't update presence if we're maintaining a particular status - return; - } - if (newState === this.state && statusMessage === this.statusMessage) { + setState(newState) { + if (newState === this.state) { return; } if (PRESENCE_STATES.indexOf(newState) === -1) { @@ -86,37 +72,21 @@ class Presence { return; } const old_state = this.state; - const old_message = this.statusMessage; this.state = newState; - this.statusMessage = statusMessage; - this.maintain = maintain; if (MatrixClientPeg.get().isGuest()) { return; // don't try to set presence when a guest; it won't work. } - const updateContent = { - presence: this.state, - status_msg: this.statusMessage ? this.statusMessage : '', - }; - const self = this; - MatrixClientPeg.get().setPresence(updateContent).done(function() { + MatrixClientPeg.get().setPresence(this.state).done(function() { console.log("Presence: %s", newState); - - // We have to dispatch because the js-sdk is unreliable at telling us about our own presence - dis.dispatch({action: "self_presence_updated", statusInfo: updateContent}); }, function(err) { console.error("Failed to set presence: %s", err); self.state = old_state; - self.statusMessage = old_message; }); } - stopMaintainingStatus() { - this.maintain = false; - } - /** * Callback called when the user made no action on the page for UNAVAILABLE_TIME ms. * @private @@ -125,8 +95,7 @@ class Presence { this.setState("unavailable"); } - _onUserActivity(payload) { - if (payload.action === "sync_state" || payload.action === "self_presence_updated") return; + _onUserActivity() { this._resetTimer(); } diff --git a/src/components/views/avatars/MemberPresenceAvatar.js b/src/components/views/avatars/MemberPresenceAvatar.js deleted file mode 100644 index 49cfee2cff..0000000000 --- a/src/components/views/avatars/MemberPresenceAvatar.js +++ /dev/null @@ -1,168 +0,0 @@ -/* - Copyright 2017 Travis Ralston - - 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. - */ - -'use strict'; - -import React from "react"; -import * as sdk from "../../../index"; -import MatrixClientPeg from "../../../MatrixClientPeg"; -import AccessibleButton from '../elements/AccessibleButton'; -import Presence from "../../../Presence"; -import dispatcher from "../../../dispatcher"; -import * as ContextualMenu from "../../structures/ContextualMenu"; -import SettingsStore from "../../../settings/SettingsStore"; - -// This is an avatar with presence information and controls on it. -module.exports = React.createClass({ - displayName: 'MemberPresenceAvatar', - - propTypes: { - member: React.PropTypes.object.isRequired, - width: React.PropTypes.number, - height: React.PropTypes.number, - resizeMethod: React.PropTypes.string, - }, - - getDefaultProps: function() { - return { - width: 40, - height: 40, - resizeMethod: 'crop', - }; - }, - - getInitialState: function() { - let presenceState = null; - let presenceMessage = null; - - // RoomMembers do not necessarily have a user. - if (this.props.member.user) { - presenceState = this.props.member.user.presence; - presenceMessage = this.props.member.user.presenceStatusMsg; - } - - return { - status: presenceState, - message: presenceMessage, - }; - }, - - componentWillMount: function() { - MatrixClientPeg.get().on("User.presence", this.onUserPresence); - this.dispatcherRef = dispatcher.register(this.onAction); - }, - - componentWillUnmount: function() { - if (MatrixClientPeg.get()) { - MatrixClientPeg.get().removeListener("User.presence", this.onUserPresence); - } - dispatcher.unregister(this.dispatcherRef); - }, - - onAction: function(payload) { - if (payload.action !== "self_presence_updated") return; - if (this.props.member.userId !== MatrixClientPeg.get().getUserId()) return; - this.setState({ - status: payload.statusInfo.presence, - message: payload.statusInfo.status_msg, - }); - }, - - onUserPresence: function(event, user) { - if (user.userId !== MatrixClientPeg.get().getUserId()) return; - this.setState({ - status: user.presence, - message: user.presenceStatusMsg, - }); - }, - - onStatusChange: function(newStatus) { - Presence.stopMaintainingStatus(); - if (newStatus === "online") { - Presence.setState(newStatus); - } else Presence.setState(newStatus, null, true); - }, - - onClick: function(e) { - const PresenceContextMenu = sdk.getComponent('context_menus.PresenceContextMenu'); - const elementRect = e.target.getBoundingClientRect(); - - // The window X and Y offsets are to adjust position when zoomed in to page - const x = (elementRect.left + window.pageXOffset) - (elementRect.width / 2) + 3; - const chevronOffset = 12; - let y = elementRect.top + (elementRect.height / 2) + window.pageYOffset; - y = y - (chevronOffset + 4); // where 4 is 1/4 the height of the chevron - - ContextualMenu.createMenu(PresenceContextMenu, { - chevronOffset: chevronOffset, - chevronFace: 'bottom', - left: x, - top: y, - menuWidth: 125, - currentStatus: this.state.status, - onChange: this.onStatusChange, - }); - - e.stopPropagation(); - - // XXX NB the following assumes that user is non-null, which is not valid - // const presenceState = this.props.member.user.presence; - // const presenceLastActiveAgo = this.props.member.user.lastActiveAgo; - // const presenceLastTs = this.props.member.user.lastPresenceTs; - // const presenceCurrentlyActive = this.props.member.user.currentlyActive; - // const presenceMessage = this.props.member.user.presenceStatusMsg; - }, - - render: function() { - const MemberAvatar = sdk.getComponent("avatars.MemberAvatar"); - - let onClickFn = null; - if (this.props.member.userId === MatrixClientPeg.get().getUserId()) { - onClickFn = this.onClick; - } - - const avatarNode = ( - - ); - let statusNode = ( - - ); - - // LABS: Disable presence management functions for now - // Also disable the presence information if there's no status information - if (!SettingsStore.isFeatureEnabled("feature_presence_management") || !this.state.status) { - statusNode = null; - onClickFn = null; - } - - let avatar = ( -
- { avatarNode } - { statusNode } -
- ); - if (onClickFn) { - avatar = ( - - { avatarNode } - { statusNode } - - ); - } - return avatar; - }, -}); diff --git a/src/components/views/rooms/MessageComposer.js b/src/components/views/rooms/MessageComposer.js index 2841f30423..665f6a8382 100644 --- a/src/components/views/rooms/MessageComposer.js +++ b/src/components/views/rooms/MessageComposer.js @@ -238,7 +238,7 @@ export default class MessageComposer extends React.Component { render() { const me = this.props.room.getMember(MatrixClientPeg.get().credentials.userId); const uploadInputStyle = {display: 'none'}; - const MemberPresenceAvatar = sdk.getComponent('avatars.MemberPresenceAvatar'); + const MemberAvatar = sdk.getComponent('avatars.MemberAvatar'); const TintableSvg = sdk.getComponent("elements.TintableSvg"); const MessageComposerInput = sdk.getComponent("rooms.MessageComposerInput"); @@ -246,7 +246,7 @@ export default class MessageComposer extends React.Component { controls.push(
- +
, ); diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index f28322398c..9da79e93fe 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -157,6 +157,7 @@ "%(widgetName)s widget modified by %(senderName)s": "%(widgetName)s widget modified by %(senderName)s", "%(widgetName)s widget added by %(senderName)s": "%(widgetName)s widget added by %(senderName)s", "%(widgetName)s widget removed by %(senderName)s": "%(widgetName)s widget removed by %(senderName)s", + "Message Pinning": "Message Pinning", "%(displayName)s is typing": "%(displayName)s is typing", "%(names)s and %(count)s others are typing|other": "%(names)s and %(count)s others are typing", "%(names)s and %(count)s others are typing|one": "%(names)s and one other is typing", From 99d3045b4f3e2128968104452f872212d98a8b8f Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Mon, 25 Dec 2017 14:33:46 -0700 Subject: [PATCH 2/2] Also remove the setting Signed-off-by: Travis Ralston --- src/settings/Settings.js | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/settings/Settings.js b/src/settings/Settings.js index 07de17ccfd..917d17854a 100644 --- a/src/settings/Settings.js +++ b/src/settings/Settings.js @@ -82,12 +82,6 @@ export const SETTINGS = { supportedLevels: LEVELS_FEATURE, default: false, }, - "feature_presence_management": { - isFeature: true, - displayName: _td("Presence Management"), - supportedLevels: LEVELS_FEATURE, - default: false, - }, "feature_tag_panel": { isFeature: true, displayName: _td("Tag Panel"),