From 9d17ed74074e3d94ca2b6d957941968b765b79fa Mon Sep 17 00:00:00 2001
From: Travis Ralston <travpc@gmail.com>
Date: Sat, 14 Oct 2017 14:33:55 -0600
Subject: [PATCH 1/6] CSS for presence member avatar

Signed-off-by: Travis Ralston <travpc@gmail.com>
---
 src/skins/vector/css/_components.scss         |  1 +
 .../views/avatars/_MemberPresenceAvatar.scss  | 43 +++++++++++++++++++
 src/skins/vector/css/themes/_base.scss        |  5 +++
 3 files changed, 49 insertions(+)
 create mode 100644 src/skins/vector/css/matrix-react-sdk/views/avatars/_MemberPresenceAvatar.scss

diff --git a/src/skins/vector/css/_components.scss b/src/skins/vector/css/_components.scss
index 4948ac85..809c6384 100644
--- a/src/skins/vector/css/_components.scss
+++ b/src/skins/vector/css/_components.scss
@@ -15,6 +15,7 @@
 @import "./matrix-react-sdk/structures/_UserSettings.scss";
 @import "./matrix-react-sdk/structures/login/_Login.scss";
 @import "./matrix-react-sdk/views/avatars/_BaseAvatar.scss";
+@import "./matrix-react-sdk/views/avatars/_MemberPresenceAvatar.scss";
 @import "./matrix-react-sdk/views/dialogs/_BugReportDialog.scss";
 @import "./matrix-react-sdk/views/dialogs/_ChatCreateOrReuseChatDialog.scss";
 @import "./matrix-react-sdk/views/dialogs/_ChatInviteDialog.scss";
diff --git a/src/skins/vector/css/matrix-react-sdk/views/avatars/_MemberPresenceAvatar.scss b/src/skins/vector/css/matrix-react-sdk/views/avatars/_MemberPresenceAvatar.scss
new file mode 100644
index 00000000..a15f8eaa
--- /dev/null
+++ b/src/skins/vector/css/matrix-react-sdk/views/avatars/_MemberPresenceAvatar.scss
@@ -0,0 +1,43 @@
+/*
+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.
+*/
+
+.mx_MemberPresenceAvatar {
+    display: inline-block;
+    position: relative;
+}
+
+.mx_MemberPresenceAvatar_status {
+    display: block;
+    width: 10px;
+    height: 10px;
+    border-radius: 10px;
+
+    position: absolute;
+    bottom: -2px;
+    right: -3px;
+}
+
+.mx_MemberPresenceAvatar_status_online {
+    background-color: $presence-online;
+}
+
+.mx_MemberPresenceAvatar_status_unavailable {
+    background-color: $presence-unavailable;
+}
+
+.mx_MemberPresenceAvatar_status_offline {
+    background-color: $presence-offline;
+}
diff --git a/src/skins/vector/css/themes/_base.scss b/src/skins/vector/css/themes/_base.scss
index a13c517b..44c5440a 100644
--- a/src/skins/vector/css/themes/_base.scss
+++ b/src/skins/vector/css/themes/_base.scss
@@ -114,6 +114,11 @@ $e2e-verified-color: #76cfa5; // N.B. *NOT* the same as $accent-color
 $e2e-unverified-color: #e8bf37;
 $e2e-warning-color: #ba6363;
 
+// presence
+$presence-online: #60de00;
+$presence-unavailable: #deb800;
+$presence-offline: #b7b7b7;
+
 /*** ImageView ***/
 $lightbox-bg-color: #454545;
 $lightbox-fg-color: #ffffff;

From 1ca041c59ce953ce0625885807c4b9fd1372b64c Mon Sep 17 00:00:00 2001
From: Travis Ralston <travpc@gmail.com>
Date: Sat, 14 Oct 2017 21:45:29 -0600
Subject: [PATCH 2/6] CSS for more positioning options on context menus

Signed-off-by: Travis Ralston <travpc@gmail.com>
---
 .../structures/_ContextualMenu.scss           | 54 +++++++++++++++++++
 1 file changed, 54 insertions(+)

diff --git a/src/skins/vector/css/matrix-react-sdk/structures/_ContextualMenu.scss b/src/skins/vector/css/matrix-react-sdk/structures/_ContextualMenu.scss
index d3e73a9a..a0191b92 100644
--- a/src/skins/vector/css/matrix-react-sdk/structures/_ContextualMenu.scss
+++ b/src/skins/vector/css/matrix-react-sdk/structures/_ContextualMenu.scss
@@ -94,6 +94,60 @@ limitations under the License.
     left: 1px;
 }
 
+.mx_ContextualMenu.mx_ContextualMenu_top {
+    top: 8px;
+}
+
+.mx_ContextualMenu_chevron_top {
+    position: absolute;
+    left: 0px;
+    top: -8px;
+    width: 0;
+    height: 0;
+    border-left: 8px solid transparent;
+    border-bottom: 8px solid $menu-border-color;
+    border-right: 8px solid transparent;
+}
+
+.mx_ContextualMenu_chevron_top:after{
+    content:'';
+    width: 0;
+    height: 0;
+    border-left: 7px solid transparent;
+    border-bottom: 7px solid $menu-bg-color;
+    border-right: 7px solid transparent;
+    position:absolute;
+    left: -7px;
+    top: 1px;
+}
+
+.mx_ContextualMenu.mx_ContextualMenu_bottom {
+    bottom: 8px;
+}
+
+.mx_ContextualMenu_chevron_bottom {
+    position: absolute;
+    left: 0px;
+    bottom: -8px;
+    width: 0;
+    height: 0;
+    border-left: 8px solid transparent;
+    border-top: 8px solid $menu-border-color;
+    border-right: 8px solid transparent;
+}
+
+.mx_ContextualMenu_chevron_bottom:after{
+    content:'';
+    width: 0;
+    height: 0;
+    border-left: 7px solid transparent;
+    border-top: 7px solid $menu-bg-color;
+    border-right: 7px solid transparent;
+    position:absolute;
+    left: -7px;
+    bottom: 1px;
+}
+
 .mx_ContextualMenu_field {
     padding: 3px 6px 3px 6px;
     cursor: pointer;

From deb50b2d437f6158844e864e335674b6e0118f0e Mon Sep 17 00:00:00 2001
From: Travis Ralston <travpc@gmail.com>
Date: Sat, 14 Oct 2017 23:15:26 -0600
Subject: [PATCH 3/6] Add presence context menu and styling

Signed-off-by: Travis Ralston <travpc@gmail.com>
---
 .../context_menus/PresenceContextMenu.js      | 100 ++++++++++++++++++
 src/i18n/strings/en_EN.json                   |   2 +
 src/skins/vector/css/_components.scss         |   1 +
 .../_PresenceContextMenuOption.scss           |  42 ++++++++
 4 files changed, 145 insertions(+)
 create mode 100644 src/components/views/context_menus/PresenceContextMenu.js
 create mode 100644 src/skins/vector/css/vector-web/views/context_menus/_PresenceContextMenuOption.scss

diff --git a/src/components/views/context_menus/PresenceContextMenu.js b/src/components/views/context_menus/PresenceContextMenu.js
new file mode 100644
index 00000000..46078726
--- /dev/null
+++ b/src/components/views/context_menus/PresenceContextMenu.js
@@ -0,0 +1,100 @@
+/*
+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.
+*/
+
+import React from 'react';
+import { _t, _td } from 'matrix-react-sdk/lib/languageHandler';
+import sdk from 'matrix-react-sdk';
+
+const STATUS_LABELS = {
+    "online": _td("Online"),
+    "unavailable": _td("Away"),
+    "offline": _td("Appear Offline"),
+};
+
+const PresenceContextMenuOption = React.createClass({
+    displayName: 'PresenceContextMenuOption',
+
+    propTypes: {
+        forStatus: React.PropTypes.string.isRequired,
+        isCurrent: React.PropTypes.bool,
+        onChange: React.PropTypes.func.isRequired,
+    },
+
+    onClick: function() {
+        this.props.onChange(this.props.forStatus);
+    },
+
+    render: function() {
+        const AccessibleButton = sdk.getComponent("elements.AccessibleButton");
+
+        const indicatorClasses = "mx_PresenceContextMenuOption_indicator "
+            + "mx_PresenceContextMenuOption_indicator_" + this.props.forStatus;
+
+        let classNames = "mx_PresenceContextMenuOption";
+        if (this.props.isCurrent) classNames += " mx_PresenceContextMenuOption_current";
+
+        return (
+            <AccessibleButton className={classNames} element="div" onClick={this.onClick}>
+                <div className={indicatorClasses}></div>
+                { _t(STATUS_LABELS[this.props.forStatus]) }
+            </AccessibleButton>
+        );
+    },
+});
+
+module.exports = React.createClass({
+    displayName: 'PresenceContextMenu',
+
+    propTypes: {
+        // "online", "unavailable", or "offline"
+        currentStatus: React.PropTypes.string.isRequired,
+
+        // Called when the user wants to change their status.
+        // Args: (newStatus:string)
+        onChange: React.PropTypes.func.isRequired,
+
+        // callback called when the menu is dismissed
+        onFinished: React.PropTypes.func,
+    },
+
+    getInitialState() {
+        return {
+            currentStatus: this.props.currentStatus,
+        };
+    },
+
+    onChange: function(newStatus) {
+        this.props.onChange(newStatus);
+        this.setState({currentStatus: newStatus});
+    },
+
+    render: function() {
+        const statusElements = [];
+        for (let status of Object.keys(STATUS_LABELS)) {
+            statusElements.push((
+                <PresenceContextMenuOption forStatus={status} key={status}
+                                           onChange={this.onChange}
+                                           isCurrent={status === this.state.currentStatus} />
+            ));
+        }
+
+        return (
+            <div>
+                { statusElements }
+            </div>
+        );
+    },
+});
diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json
index 16d47d10..fba305d8 100644
--- a/src/i18n/strings/en_EN.json
+++ b/src/i18n/strings/en_EN.json
@@ -72,6 +72,8 @@
   "Loading bug report module": "Loading bug report module",
   "Low Priority": "Low Priority",
   "Members": "Members",
+  "Appear Offline": "Appear Offline",
+  "Away": "Away",
   "Mentions only": "Mentions only",
   "Messages containing my display name": "Messages containing my display name",
   "Messages containing <span>keywords</span>": "Messages containing <span>keywords</span>",
diff --git a/src/skins/vector/css/_components.scss b/src/skins/vector/css/_components.scss
index 76ac3945..748b33f8 100644
--- a/src/skins/vector/css/_components.scss
+++ b/src/skins/vector/css/_components.scss
@@ -80,6 +80,7 @@
 @import "./vector-web/structures/_RoomSubList.scss";
 @import "./vector-web/structures/_ViewSource.scss";
 @import "./vector-web/views/context_menus/_MessageContextMenu.scss";
+@import "vector-web/views/context_menus/_PresenceContextMenuOption.scss";
 @import "./vector-web/views/context_menus/_RoomTileContextMenu.scss";
 @import "./vector-web/views/dialogs/_ChangelogDialog.scss";
 @import "./vector-web/views/dialogs/_DevtoolsDialog.scss";
diff --git a/src/skins/vector/css/vector-web/views/context_menus/_PresenceContextMenuOption.scss b/src/skins/vector/css/vector-web/views/context_menus/_PresenceContextMenuOption.scss
new file mode 100644
index 00000000..bfe81125
--- /dev/null
+++ b/src/skins/vector/css/vector-web/views/context_menus/_PresenceContextMenuOption.scss
@@ -0,0 +1,42 @@
+/*
+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.
+*/
+.mx_PresenceContextMenuOption_indicator {
+    width: 10px;
+    height: 10px;
+    border-radius: 10px;
+    display: inline-block;
+    margin-right: 5px;
+}
+
+.mx_PresenceContextMenuOption_indicator.mx_PresenceContextMenuOption_indicator_online {
+    background-color: $presence-online;
+}
+
+.mx_PresenceContextMenuOption_indicator.mx_PresenceContextMenuOption_indicator_unavailable {
+    background-color: $presence-unavailable;
+}
+
+.mx_PresenceContextMenuOption_indicator.mx_PresenceContextMenuOption_indicator_offline {
+    background-color: $presence-offline;
+}
+
+.mx_PresenceContextMenuOption {
+    padding: 2px;
+}
+
+.mx_PresenceContextMenuOption.mx_PresenceContextMenuOption_current {
+    font-weight: 700;
+}

From ef5fc7347ab7d206e25020829564dc97764e8590 Mon Sep 17 00:00:00 2001
From: Travis Ralston <travpc@gmail.com>
Date: Sun, 15 Oct 2017 20:20:43 -0600
Subject: [PATCH 4/6] Don't update presence status if the user is clicks twice

Signed-off-by: Travis Ralston <travpc@gmail.com>
---
 src/components/views/context_menus/PresenceContextMenu.js | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/components/views/context_menus/PresenceContextMenu.js b/src/components/views/context_menus/PresenceContextMenu.js
index 46078726..3a3dc2fa 100644
--- a/src/components/views/context_menus/PresenceContextMenu.js
+++ b/src/components/views/context_menus/PresenceContextMenu.js
@@ -34,6 +34,7 @@ const PresenceContextMenuOption = React.createClass({
     },
 
     onClick: function() {
+        if (this.isCurrent) return;
         this.props.onChange(this.props.forStatus);
     },
 

From 53185fc23dbd4dfb6876d5096166ac3dde52bb8c Mon Sep 17 00:00:00 2001
From: Travis Ralston <travpc@gmail.com>
Date: Wed, 25 Oct 2017 11:27:16 -0600
Subject: [PATCH 5/6] Remove i18n for making the merge easier

Signed-off-by: Travis Ralston <travpc@gmail.com>
---
 src/i18n/strings/en_EN.json | 2 --
 1 file changed, 2 deletions(-)

diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json
index fba305d8..16d47d10 100644
--- a/src/i18n/strings/en_EN.json
+++ b/src/i18n/strings/en_EN.json
@@ -72,8 +72,6 @@
   "Loading bug report module": "Loading bug report module",
   "Low Priority": "Low Priority",
   "Members": "Members",
-  "Appear Offline": "Appear Offline",
-  "Away": "Away",
   "Mentions only": "Mentions only",
   "Messages containing my display name": "Messages containing my display name",
   "Messages containing <span>keywords</span>": "Messages containing <span>keywords</span>",

From 6c796cd31174ac392657dfc755212134081b738a Mon Sep 17 00:00:00 2001
From: Travis Ralston <travpc@gmail.com>
Date: Wed, 25 Oct 2017 11:28:57 -0600
Subject: [PATCH 6/6] Re-add i18n post-merge

Signed-off-by: Travis Ralston <travpc@gmail.com>
---
 src/i18n/strings/en_EN.json | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json
index 543ee7bb..3299a1cc 100644
--- a/src/i18n/strings/en_EN.json
+++ b/src/i18n/strings/en_EN.json
@@ -68,6 +68,8 @@
     "What's New": "What's New",
     "Update": "Update",
     "What's new?": "What's new?",
+    "Appear Offline": "Appear Offline",
+    "Away": "Away",
     "A new version of Riot is available.": "A new version of Riot is available.",
     "To return to your account in future you need to <u>set a password</u>": "To return to your account in future you need to <u>set a password</u>",
     "Set Password": "Set Password",