From cb5ad8d6f900073925244f6d59c07396d94c2c80 Mon Sep 17 00:00:00 2001 From: Will Hunt Date: Sun, 13 Jan 2019 14:47:08 +0000 Subject: [PATCH 01/17] Limit the length of topics and names in the RoomDirectory --- src/components/structures/RoomDirectory.js | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/components/structures/RoomDirectory.js b/src/components/structures/RoomDirectory.js index 4c7d004015..762185146c 100644 --- a/src/components/structures/RoomDirectory.js +++ b/src/components/structures/RoomDirectory.js @@ -34,6 +34,9 @@ import { _t } from '../../languageHandler'; import {instanceForInstanceId, protocolNameForInstanceId} from '../../utils/DirectoryUtils'; +const MAX_NAME_LENGTH = 80; +const MAX_TOPIC_LENGTH = 160; + linkifyMatrix(linkify); module.exports = React.createClass({ @@ -390,7 +393,6 @@ module.exports = React.createClass({ const self = this; let guestRead; let guestJoin; let perms; for (let i = 0; i < rooms.length; i++) { - const name = rooms[i].name || get_display_alias_for_room(rooms[i]) || _t('Unnamed room'); guestRead = null; guestJoin = null; @@ -410,7 +412,15 @@ module.exports = React.createClass({ perms =
{guestRead}{guestJoin}
; } + let name = rooms[i].name || get_display_alias_for_room(rooms[i]) || _t('Unnamed room'); + if (name.length > MAX_NAME_LENGTH) { + name = `${name.substring(0, MAX_NAME_LENGTH)}...`; + } + let topic = rooms[i].topic || ''; + if (topic.length > MAX_TOPIC_LENGTH) { + topic = `${topic.substring(0, MAX_TOPIC_LENGTH)}...`; + } topic = linkifyString(sanitizeHtml(topic)); rows.push( From ef78036a7220c9b7e29253ade82332c9a8762fa3 Mon Sep 17 00:00:00 2001 From: Matthew Hodgson Date: Tue, 15 Jan 2019 14:40:58 +0000 Subject: [PATCH 02/17] fix vector-im/riot-web#8105 --- src/components/views/dialogs/ChangelogDialog.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/dialogs/ChangelogDialog.js b/src/components/views/dialogs/ChangelogDialog.js index 3c9414fd88..965029c069 100644 --- a/src/components/views/dialogs/ChangelogDialog.js +++ b/src/components/views/dialogs/ChangelogDialog.js @@ -36,7 +36,7 @@ export default class ChangelogDialog extends React.Component { for (let i=0; i { if (response.statusCode < 200 || response.statusCode >= 300) { this.setState({ [REPOS[i]]: response.statusText }); From 6f62158f9490aba065b01c2fd5f45ffa9acdb480 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 15 Jan 2019 09:52:06 -0700 Subject: [PATCH 03/17] Fix setting label for unknown invites Fixes https://github.com/vector-im/riot-web/issues/8126 --- src/components/structures/UserSettings.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/structures/UserSettings.js b/src/components/structures/UserSettings.js index 6ba7bcc4dc..37f60c47bf 100644 --- a/src/components/structures/UserSettings.js +++ b/src/components/structures/UserSettings.js @@ -86,7 +86,7 @@ const SIMPLE_SETTINGS = [ { id: "pinMentionedRooms" }, { id: "pinUnreadRooms" }, { id: "showDeveloperTools" }, - { id: "alwaysRetryInvites" }, + { id: "alwaysInviteUnknownUsers" }, ]; // These settings must be defined in SettingsStore From e08c70b82089299bbf736f92dafe6a8ec5daf773 Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Fri, 11 Jan 2019 14:59:14 -0600 Subject: [PATCH 04/17] Prune unsupported setting RoomSubList.showEmpty The code reading this setting was previously removed in #2229. --- src/components/structures/UserSettings.js | 1 - src/i18n/strings/en_EN.json | 1 - src/settings/Settings.js | 5 ----- 3 files changed, 7 deletions(-) diff --git a/src/components/structures/UserSettings.js b/src/components/structures/UserSettings.js index 15b9181d21..dc469c0ca3 100644 --- a/src/components/structures/UserSettings.js +++ b/src/components/structures/UserSettings.js @@ -82,7 +82,6 @@ const SIMPLE_SETTINGS = [ { id: "VideoView.flipVideoHorizontally" }, { id: "TagPanel.disableTagPanel" }, { id: "enableWidgetScreenshots" }, - { id: "RoomSubList.showEmpty" }, { id: "pinMentionedRooms" }, { id: "pinUnreadRooms" }, { id: "showDeveloperTools" }, diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 0086ed0378..a7b75c136b 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -296,7 +296,6 @@ "Pin rooms I'm mentioned in to the top of the room list": "Pin rooms I'm mentioned in to the top of the room list", "Pin unread rooms to the top of the room list": "Pin unread rooms to the top of the room list", "Enable widget screenshots on supported widgets": "Enable widget screenshots on supported widgets", - "Show empty room list headings": "Show empty room list headings", "Always invite users which may not exist": "Always invite users which may not exist", "Show developer tools": "Show developer tools", "Collecting app version information": "Collecting app version information", diff --git a/src/settings/Settings.js b/src/settings/Settings.js index a04301e31e..f64e999a32 100644 --- a/src/settings/Settings.js +++ b/src/settings/Settings.js @@ -324,11 +324,6 @@ export const SETTINGS = { supportedLevels: ['room-device'], default: false, }, - "RoomSubList.showEmpty": { - supportedLevels: LEVELS_ACCOUNT_SETTINGS, - displayName: _td('Show empty room list headings'), - default: true, - }, "alwaysInviteUnknownUsers": { supportedLevels: LEVELS_ACCOUNT_SETTINGS, displayName: _td('Always invite users which may not exist'), From c76b273fae2d200c24d718cdc34237a45802b266 Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Fri, 11 Jan 2019 18:24:06 -0600 Subject: [PATCH 05/17] Update RoomTiles on custom status change --- src/components/views/rooms/RoomTile.js | 8 ++++- src/settings/Settings.js | 2 ++ .../controllers/CustomStatusController.js | 29 +++++++++++++++++++ 3 files changed, 38 insertions(+), 1 deletion(-) create mode 100644 src/settings/controllers/CustomStatusController.js diff --git a/src/components/views/rooms/RoomTile.js b/src/components/views/rooms/RoomTile.js index 95073b7be8..8778340601 100644 --- a/src/components/views/rooms/RoomTile.js +++ b/src/components/views/rooms/RoomTile.js @@ -112,7 +112,13 @@ module.exports = React.createClass({ this.setState({ notificationCount: this.props.room.getUnreadNotificationCount(), }); - break; + break; + // RoomTiles are one of the few components that may show custom status and + // also remain on screen while in Settings toggling the feature. This ensures + // you can clearly see the status hide and show when toggling the feature. + case 'feature_custom_status_changed': + this.forceUpdate(); + break; } }, diff --git a/src/settings/Settings.js b/src/settings/Settings.js index f64e999a32..836e906b6e 100644 --- a/src/settings/Settings.js +++ b/src/settings/Settings.js @@ -22,6 +22,7 @@ import { NotificationsEnabledController, } from "./controllers/NotificationControllers"; import LazyLoadingController from "./controllers/LazyLoadingController"; +import CustomStatusController from "./controllers/CustomStatusController"; // These are just a bunch of helper arrays to avoid copy/pasting a bunch of times const LEVELS_ROOM_SETTINGS = ['device', 'room-device', 'room-account', 'account', 'config']; @@ -88,6 +89,7 @@ export const SETTINGS = { displayName: _td("Custom user status messages"), supportedLevels: LEVELS_FEATURE, default: false, + controller: new CustomStatusController(), }, "feature_lazyloading": { isFeature: true, diff --git a/src/settings/controllers/CustomStatusController.js b/src/settings/controllers/CustomStatusController.js new file mode 100644 index 0000000000..183947972b --- /dev/null +++ b/src/settings/controllers/CustomStatusController.js @@ -0,0 +1,29 @@ +/* +Copyright 2019 New Vector 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. +*/ + +import SettingController from "./SettingController"; +import dis from "../../dispatcher"; + +export default class CustomStatusController extends SettingController { + onChange(level, roomId, newValue) { + // Dispatch setting change so that some components that are still visible when the + // Settings page is open (such as RoomTiles) can reflect the change. + dis.dispatch({ + action: "feature_custom_status_changed", + value: newValue, + }); + } +} From 7d6b996952e2982f461fcc6e974beab51c484e27 Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Fri, 11 Jan 2019 20:00:12 -0600 Subject: [PATCH 06/17] Tweak composer button padding so it won't affect status --- res/css/views/rooms/_MessageComposer.scss | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/res/css/views/rooms/_MessageComposer.scss b/res/css/views/rooms/_MessageComposer.scss index 39640575ba..c1acf56232 100644 --- a/res/css/views/rooms/_MessageComposer.scss +++ b/res/css/views/rooms/_MessageComposer.scss @@ -65,10 +65,6 @@ limitations under the License. display: block; } -.mx_MessageComposer .mx_AccessibleButton { - padding: 0 12px 0 0; -} - .mx_MessageComposer_composecontrols { width: 100%; } @@ -185,7 +181,7 @@ limitations under the License. /*display: table-cell;*/ /*vertical-align: middle;*/ /*padding-left: 10px;*/ - padding-right: 5px; + padding-right: 12px; cursor: pointer; padding-top: 4px; } From eae1e4c9aa5395a01a2970153d3bfb10b49950a4 Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Mon, 14 Jan 2019 10:26:11 -0600 Subject: [PATCH 07/17] Tweak custom status avatar ring Adjusts the appearance of the avatar ring to match the latest comps. In addition, we now always render the surrounding button element, which simplifies styling since the same size is now occupied both with and without the feature. This improves alignment between text in the composer and text in the message history (https://github.com/vector-im/riot-web/issues/8111). --- .../avatars/_MemberStatusMessageAvatar.scss | 16 +++++++++++---- res/css/views/rooms/_MessageComposer.scss | 2 +- .../avatars/MemberStatusMessageAvatar.js | 20 +++++++++++-------- 3 files changed, 25 insertions(+), 13 deletions(-) diff --git a/res/css/views/avatars/_MemberStatusMessageAvatar.scss b/res/css/views/avatars/_MemberStatusMessageAvatar.scss index 29cae9df34..03e7fdd188 100644 --- a/res/css/views/avatars/_MemberStatusMessageAvatar.scss +++ b/res/css/views/avatars/_MemberStatusMessageAvatar.scss @@ -14,8 +14,16 @@ See the License for the specific language governing permissions and limitations under the License. */ -.mx_MemberStatusMessageAvatar_hasStatus { - border: 2px solid $accent-color; - border-radius: 40px; - padding-right: 0 !important; /* Override AccessibleButton styling */ +.mx_MemberStatusMessageAvatar .mx_BaseAvatar { + padding: 1.5px; + border: 1.2px solid transparent; + border-radius: 14px; +} + +.mx_MemberStatusMessageAvatar .mx_BaseAvatar_initial { + left: 1.5px; +} + +.mx_MemberStatusMessageAvatar_hasStatus .mx_BaseAvatar { + border-color: $accent-color; } diff --git a/res/css/views/rooms/_MessageComposer.scss b/res/css/views/rooms/_MessageComposer.scss index c1acf56232..4a052482ad 100644 --- a/res/css/views/rooms/_MessageComposer.scss +++ b/res/css/views/rooms/_MessageComposer.scss @@ -58,7 +58,7 @@ limitations under the License. } .mx_MessageComposer .mx_MessageComposer_avatar { - padding: 0 28px; + padding: 0 27px; } .mx_MessageComposer .mx_MessageComposer_avatar .mx_BaseAvatar { diff --git a/src/components/views/avatars/MemberStatusMessageAvatar.js b/src/components/views/avatars/MemberStatusMessageAvatar.js index aebd1741b7..5118a25cdb 100644 --- a/src/components/views/avatars/MemberStatusMessageAvatar.js +++ b/src/components/views/avatars/MemberStatusMessageAvatar.js @@ -96,21 +96,25 @@ export default class MemberStatusMessageAvatar extends React.Component { }; render() { - if (!SettingsStore.isFeatureEnabled("feature_custom_status")) { - return ; - } + const customStatusFeatureEnabled = + SettingsStore.isFeatureEnabled("feature_custom_status"); - const hasStatus = this.props.member.user ? !!this.props.member.user._unstable_statusMessage : false; + let onClick = null; + let hasStatus = false; + + if (customStatusFeatureEnabled) { + onClick = this._onClick; + if (this.props.member.user) { + hasStatus = !!this.props.member.user._unstable_statusMessage; + } + } const classes = classNames({ "mx_MemberStatusMessageAvatar": true, "mx_MemberStatusMessageAvatar_hasStatus": hasStatus, }); - return + return Date: Mon, 14 Jan 2019 11:43:56 -0600 Subject: [PATCH 08/17] Update context menu styling to match custom status comps --- res/css/structures/_ContextualMenu.scss | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/res/css/structures/_ContextualMenu.scss b/res/css/structures/_ContextualMenu.scss index fa69c6fb90..a01cd896a8 100644 --- a/res/css/structures/_ContextualMenu.scss +++ b/res/css/structures/_ContextualMenu.scss @@ -30,8 +30,8 @@ limitations under the License. } .mx_ContextualMenu { - border-radius: 2px; - box-shadow: 0 0 5px 0 rgba(0, 0, 0, 0.21); + border-radius: 4px; + box-shadow: 4px 4px 12px 0 rgba(118, 131, 156, 0.6);; background-color: $menu-bg-color; color: $primary-fg-color; position: absolute; From fc3055f54149ed3bb63b02721d1b57258a06a5d8 Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Mon, 14 Jan 2019 12:47:50 -0600 Subject: [PATCH 09/17] Tweak custom status menu size and placement --- .../views/avatars/MemberStatusMessageAvatar.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/components/views/avatars/MemberStatusMessageAvatar.js b/src/components/views/avatars/MemberStatusMessageAvatar.js index 5118a25cdb..a11e887efe 100644 --- a/src/components/views/avatars/MemberStatusMessageAvatar.js +++ b/src/components/views/avatars/MemberStatusMessageAvatar.js @@ -79,18 +79,18 @@ export default class MemberStatusMessageAvatar extends React.Component { 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 + const x = (elementRect.left + window.pageXOffset); + const chevronWidth = 16; // See .mx_ContextualMenu_chevron_bottom + const chevronOffset = (elementRect.width - chevronWidth) / 2; + const chevronMargin = 1; // Add some spacing away from target + const y = elementRect.top + window.pageYOffset - chevronMargin; ContextualMenu.createMenu(StatusMessageContextMenu, { chevronOffset: chevronOffset, chevronFace: 'bottom', left: x, top: y, - menuWidth: 190, + menuWidth: 226, user: this.props.member.user, }); }; From 5b88b64950fc6033347a7003dbf2681868adad36 Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Mon, 14 Jan 2019 16:41:14 -0600 Subject: [PATCH 10/17] Rework custom status context menu This updates the custom status context menu to match the latest comps. A single button is used for setting / clearing, depending on what is appropriate. The state logic is also changed to depend on events and storage from js-sdk for the committed status message. This makes it easy to distinguish the value being edited from what's currently committed. --- .../_StatusMessageContextMenu.scss | 55 ++++++----- res/img/icons-checkmark.svg | 17 ---- res/themes/dharma/css/_dharma.scss | 4 +- res/themes/light/css/_base.scss | 2 + .../context_menus/StatusMessageContextMenu.js | 98 +++++++++++++------ src/i18n/strings/en_EN.json | 4 +- 6 files changed, 103 insertions(+), 77 deletions(-) delete mode 100644 res/img/icons-checkmark.svg diff --git a/res/css/views/context_menus/_StatusMessageContextMenu.scss b/res/css/views/context_menus/_StatusMessageContextMenu.scss index 873ad99495..8b25f3a122 100644 --- a/res/css/views/context_menus/_StatusMessageContextMenu.scss +++ b/res/css/views/context_menus/_StatusMessageContextMenu.scss @@ -14,42 +14,43 @@ See the License for the specific language governing permissions and limitations under the License. */ -.mx_StatusMessageContextMenu_message { - display: inline-block; - border-radius: 3px 0 0 3px; +.mx_StatusMessageContextMenu { + padding: 10px; +} + +.mx_StatusMessageContextMenu_form { + display: flex; + flex-direction: column; +} + +input.mx_StatusMessageContextMenu_message { + border-radius: 4px; border: 1px solid $input-border-color; - font-size: 13px; - padding: 7px 7px 7px 9px; - width: 135px; - background-color: $primary-bg-color !important; + padding: 6.5px 11px; + background-color: $primary-bg-color; + font-weight: normal; + margin: 0 0 10px; } -.mx_StatusMessageContextMenu_submit { - display: inline-block; +.mx_StatusMessageContextMenu_message::placeholder { + color: $memberstatus-placeholder-color; } -.mx_StatusMessageContextMenu_submitFaded { - opacity: 0.5; +.mx_StatusMessageContextMenu_submit, +.mx_StatusMessageContextMenu_clear { + @mixin mx_DialogButton; + align-self: start; + font-size: 12px; + padding: 6px 1em; + border: 1px solid transparent; } -.mx_StatusMessageContextMenu_submit img { - vertical-align: middle; - margin-left: 8px; -} - -.mx_StatusMessageContextMenu hr { - border: 0.5px solid $menu-border-color; -} - -.mx_StatusMessageContextMenu_clearIcon { - margin: 5px 15px 5px 5px; - vertical-align: middle; +.mx_StatusMessageContextMenu_submit[disabled] { + opacity: 0.49; } .mx_StatusMessageContextMenu_clear { - padding: 2px; -} - -.mx_StatusMessageContextMenu_hasStatus .mx_StatusMessageContextMenu_clear { color: $warning-color; + background-color: transparent; + border: 1px solid $warning-color; } diff --git a/res/img/icons-checkmark.svg b/res/img/icons-checkmark.svg deleted file mode 100644 index 3c5392003d..0000000000 --- a/res/img/icons-checkmark.svg +++ /dev/null @@ -1,17 +0,0 @@ - - - - Tick - Created with Sketch. - - - - - - - - - - - - diff --git a/res/themes/dharma/css/_dharma.scss b/res/themes/dharma/css/_dharma.scss index 732cabf494..73dc0a71e4 100644 --- a/res/themes/dharma/css/_dharma.scss +++ b/res/themes/dharma/css/_dharma.scss @@ -68,7 +68,7 @@ $event-selected-color: #f7f7f7; $primary-hairline-color: #e5e5e5; // used for the border of input text fields -$input-border-color: #f0f0f0; +$input-border-color: #e7e7e7; $input-darker-bg-color: rgba(193, 201, 214, 0.29); $input-darker-fg-color: #9fa9ba; $input-lighter-bg-color: #f2f5f8; @@ -192,6 +192,8 @@ $progressbar-color: #000; $room-warning-bg-color: #fff8e3; +$memberstatus-placeholder-color: $roomtile-name-color; + /*** form elements ***/ // .mx_textinput is a container for a text input diff --git a/res/themes/light/css/_base.scss b/res/themes/light/css/_base.scss index 10a8fcd1e5..cf539bd1f2 100644 --- a/res/themes/light/css/_base.scss +++ b/res/themes/light/css/_base.scss @@ -188,6 +188,8 @@ $progressbar-color: #000; $room-warning-bg-color: #fff8e3; +$memberstatus-placeholder-color: $roomtile-name-color; + // ***** Mixins! ***** @define-mixin mx_DialogButton { diff --git a/src/components/views/context_menus/StatusMessageContextMenu.js b/src/components/views/context_menus/StatusMessageContextMenu.js index d062fc2a3e..9916f5d347 100644 --- a/src/components/views/context_menus/StatusMessageContextMenu.js +++ b/src/components/views/context_menus/StatusMessageContextMenu.js @@ -19,7 +19,6 @@ import PropTypes from 'prop-types'; import { _t } from '../../../languageHandler'; import MatrixClientPeg from '../../../MatrixClientPeg'; import AccessibleButton from '../elements/AccessibleButton'; -import classNames from 'classnames'; export default class StatusMessageContextMenu extends React.Component { static propTypes = { @@ -31,13 +30,42 @@ export default class StatusMessageContextMenu extends React.Component { super(props, context); this.state = { - message: props.user ? props.user._unstable_statusMessage : "", + message: this.comittedStatusMessage, }; } + componentWillMount() { + const { user } = this.props; + if (!user) { + return; + } + user.on("User._unstable_statusMessage", this._onStatusMessageCommitted); + } + + componentWillUmount() { + const { user } = this.props; + if (!user) { + return; + } + user.removeListener( + "User._unstable_statusMessage", + this._onStatusMessageCommitted, + ); + } + + get comittedStatusMessage() { + return this.props.user ? this.props.user._unstable_statusMessage : ""; + } + + _onStatusMessageCommitted = () => { + // The `User` object has observed a status message change. + this.setState({ + message: this.comittedStatusMessage, + }); + }; + _onClearClick = async (e) => { await MatrixClientPeg.get()._unstable_setStatusMessage(""); - this.setState({message: ""}); }; _onSubmit = (e) => { @@ -46,41 +74,49 @@ export default class StatusMessageContextMenu extends React.Component { }; _onStatusChange = (e) => { - this.setState({message: e.target.value}); + // The input field's value was changed. + this.setState({ + message: e.target.value, + }); }; render() { - const formSubmitClasses = classNames({ - "mx_StatusMessageContextMenu_submit": true, - "mx_StatusMessageContextMenu_submitFaded": !this.state.message, // no message == faded - }); + let actionButton; + if (this.comittedStatusMessage) { + if (this.state.message === this.comittedStatusMessage) { + actionButton = + {_t("Clear status")} + ; + } else { + actionButton = + {_t("Update status")} + ; + } + } else { + actionButton = + {_t("Set status")} + ; + } - const form =
- - - - + const form = + + {actionButton}
; - const clearIcon = this.state.message ? "img/cancel-red.svg" : "img/cancel.svg"; - const clearButton = - {_t('Clear - {_t("Clear status")} - ; - - const menuClasses = classNames({ - "mx_StatusMessageContextMenu": true, - "mx_StatusMessageContextMenu_hasStatus": this.state.message, - }); - - return
+ return
{ form } -
- { clearButton }
; } } diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index a7b75c136b..ef0ff1ebf7 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1087,8 +1087,10 @@ "Forget": "Forget", "Low Priority": "Low Priority", "Direct Chat": "Direct Chat", - "Set a new status...": "Set a new status...", "Clear status": "Clear status", + "Update status": "Update status", + "Set status": "Set status", + "Set a new status...": "Set a new status...", "View as Grid": "View as Grid", "View Community": "View Community", "Sorry, your browser is not able to run Riot.": "Sorry, your browser is not able to run Riot.", From 233288475ecc8d56b91cdb880d4ccb58c38839cc Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Mon, 14 Jan 2019 16:59:15 -0600 Subject: [PATCH 11/17] Use status events for avatar ring Similar to changes in `StatusMessageContextMenu`, this changes to using the new status message event emitted in js-sdk to simplify state handling for the avatar ring when a status is present. --- .../avatars/MemberStatusMessageAvatar.js | 60 +++++++++++-------- 1 file changed, 35 insertions(+), 25 deletions(-) diff --git a/src/components/views/avatars/MemberStatusMessageAvatar.js b/src/components/views/avatars/MemberStatusMessageAvatar.js index a11e887efe..a98fcaa537 100644 --- a/src/components/views/avatars/MemberStatusMessageAvatar.js +++ b/src/components/views/avatars/MemberStatusMessageAvatar.js @@ -40,38 +40,50 @@ export default class MemberStatusMessageAvatar extends React.Component { constructor(props, context) { super(props, context); + + this.state = { + hasStatus: this.hasStatus, + }; } componentWillMount() { if (this.props.member.userId !== MatrixClientPeg.get().getUserId()) { throw new Error("Cannot use MemberStatusMessageAvatar on anyone but the logged in user"); } - } - - componentDidMount() { - MatrixClientPeg.get().on("RoomState.events", this._onRoomStateEvents); - - if (this.props.member.user) { - this.setState({message: this.props.member.user._unstable_statusMessage}); - } else { - this.setState({message: ""}); + if (!SettingsStore.isFeatureEnabled("feature_custom_status")) { + return; } - } - - componentWillUnmount() { - if (MatrixClientPeg.get()) { - MatrixClientPeg.get().removeListener("RoomState.events", this._onRoomStateEvents); + const { user } = this.props.member; + if (!user) { + return; } + user.on("User._unstable_statusMessage", this._onStatusMessageCommitted); } - _onRoomStateEvents = (ev, state) => { - if (ev.getStateKey() !== MatrixClientPeg.get().getUserId()) return; - if (ev.getType() !== "im.vector.user_status") return; - // TODO: We should be relying on `this.props.member.user._unstable_statusMessage` - // We don't currently because the js-sdk doesn't emit a specific event for this - // change, and we don't want to race it. This should be improved when we rip out - // the im.vector.user_status stuff and replace it with a complete solution. - this.setState({message: ev.getContent()["status"]}); + componentWillUmount() { + const { user } = this.props.member; + if (!user) { + return; + } + user.removeListener( + "User._unstable_statusMessage", + this._onStatusMessageCommitted, + ); + } + + get hasStatus() { + const { user } = this.props.member; + if (!user) { + return false; + } + return !!user._unstable_statusMessage; + } + + _onStatusMessageCommitted = () => { + // The `User` object has observed a status message change. + this.setState({ + hasStatus: this.hasStatus, + }); }; _onClick = (e) => { @@ -104,9 +116,7 @@ export default class MemberStatusMessageAvatar extends React.Component { if (customStatusFeatureEnabled) { onClick = this._onClick; - if (this.props.member.user) { - hasStatus = !!this.props.member.user._unstable_statusMessage; - } + hasStatus = this.state.hasStatus; } const classes = classNames({ From 443198c180c95d487764da3d6cf03eaa16f27d41 Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Mon, 14 Jan 2019 17:27:35 -0600 Subject: [PATCH 12/17] Fix lints in MemberTile.js --- .eslintignore.errorfiles | 1 - src/components/views/rooms/MemberTile.js | 9 ++++----- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/.eslintignore.errorfiles b/.eslintignore.errorfiles index 2b57d4e9e2..30cc55377c 100644 --- a/.eslintignore.errorfiles +++ b/.eslintignore.errorfiles @@ -48,7 +48,6 @@ src/components/views/rooms/LinkPreviewWidget.js src/components/views/rooms/MemberDeviceInfo.js src/components/views/rooms/MemberInfo.js src/components/views/rooms/MemberList.js -src/components/views/rooms/MemberTile.js src/components/views/rooms/MessageComposer.js src/components/views/rooms/PinnedEventTile.js src/components/views/rooms/RoomList.js diff --git a/src/components/views/rooms/MemberTile.js b/src/components/views/rooms/MemberTile.js index ba951792d0..4189e406b5 100644 --- a/src/components/views/rooms/MemberTile.js +++ b/src/components/views/rooms/MemberTile.js @@ -21,10 +21,8 @@ import SettingsStore from "../../../settings/SettingsStore"; const React = require('react'); import PropTypes from 'prop-types'; -const MatrixClientPeg = require('../../../MatrixClientPeg'); const sdk = require('../../../index'); const dis = require('../../../dispatcher'); -const Modal = require("../../../Modal"); import { _t } from '../../../languageHandler'; module.exports = React.createClass({ @@ -74,17 +72,18 @@ module.exports = React.createClass({ }, getPowerLabel: function() { - return _t("%(userName)s (power %(powerLevelNumber)s)", {userName: this.props.member.userId, powerLevelNumber: this.props.member.powerLevel}); + return _t("%(userName)s (power %(powerLevelNumber)s)", { + userName: this.props.member.userId, + powerLevelNumber: this.props.member.powerLevel, + }); }, render: function() { const MemberAvatar = sdk.getComponent('avatars.MemberAvatar'); - const BaseAvatar = sdk.getComponent('avatars.BaseAvatar'); const EntityTile = sdk.getComponent('rooms.EntityTile'); const member = this.props.member; const name = this._getDisplayName(); - const active = -1; const presenceState = member.user ? member.user.presence : null; let statusMessage = null; From 77cee8e67ea4b3937746d6f863680d4821c45fef Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Mon, 14 Jan 2019 17:51:42 -0600 Subject: [PATCH 13/17] Update MemberTile on status change --- src/components/views/rooms/MemberTile.js | 43 ++++++++++++++++++++++-- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/src/components/views/rooms/MemberTile.js b/src/components/views/rooms/MemberTile.js index 4189e406b5..926734a767 100644 --- a/src/components/views/rooms/MemberTile.js +++ b/src/components/views/rooms/MemberTile.js @@ -40,7 +40,46 @@ module.exports = React.createClass({ }, getInitialState: function() { - return {}; + return { + statusMessage: this.getStatusMessage(), + }; + }, + + componentDidMount() { + if (!SettingsStore.isFeatureEnabled("feature_custom_status")) { + return; + } + const { user } = this.props.member; + if (!user) { + return; + } + user.on("User._unstable_statusMessage", this._onStatusMessageCommitted); + }, + + componentWillUmount() { + const { user } = this.props.member; + if (!user) { + return; + } + user.removeListener( + "User._unstable_statusMessage", + this._onStatusMessageCommitted, + ); + }, + + getStatusMessage() { + const { user } = this.props.member; + if (!user) { + return ""; + } + return user._unstable_statusMessage; + }, + + _onStatusMessageCommitted() { + // The `User` object has observed a status message change. + this.setState({ + statusMessage: this.getStatusMessage(), + }); }, shouldComponentUpdate: function(nextProps, nextState) { @@ -88,7 +127,7 @@ module.exports = React.createClass({ let statusMessage = null; if (member.user && SettingsStore.isFeatureEnabled("feature_custom_status")) { - statusMessage = member.user._unstable_statusMessage; + statusMessage = this.state.statusMessage; } const av = ( From d8de607edcf06112ec82983e5f46538e1b3762f2 Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Tue, 15 Jan 2019 09:20:43 -0600 Subject: [PATCH 14/17] Update RoomTile on status change --- src/components/views/rooms/RoomTile.js | 65 ++++++++++++++++++++++---- 1 file changed, 57 insertions(+), 8 deletions(-) diff --git a/src/components/views/rooms/RoomTile.js b/src/components/views/rooms/RoomTile.js index 8778340601..1f9c0c1523 100644 --- a/src/components/views/rooms/RoomTile.js +++ b/src/components/views/rooms/RoomTile.js @@ -62,6 +62,7 @@ module.exports = React.createClass({ notifState: RoomNotifs.getRoomNotifsState(this.props.room.roomId), notificationCount: this.props.room.getUnreadNotificationCount(), selected: this.props.room.roomId === ActiveRoomObserver.getActiveRoomId(), + statusMessage: this._getStatusMessage(), }); }, @@ -79,6 +80,33 @@ module.exports = React.createClass({ return Boolean(dmRooms); }, + _shouldShowStatusMessage() { + if (!SettingsStore.isFeatureEnabled("feature_custom_status")) { + return false; + } + const isInvite = this.props.room.getMyMembership() === "invite"; + const isJoined = this.props.room.getMyMembership() === "join"; + const looksLikeDm = this.props.room.getInvitedAndJoinedMemberCount() === 2; + return !isInvite && isJoined && looksLikeDm; + }, + + _getStatusMessageUser() { + const selfId = MatrixClientPeg.get().getUserId(); + const otherMember = this.props.room.currentState.getMembersExcept([selfId])[0]; + if (!otherMember) { + return null; + } + return otherMember.user; + }, + + _getStatusMessage() { + const statusUser = this._getStatusMessageUser(); + if (!statusUser) { + return ""; + } + return statusUser._unstable_statusMessage; + }, + onRoomTimeline: function(ev, room) { if (room !== this.props.room) return; this.setState({ @@ -134,6 +162,16 @@ module.exports = React.createClass({ MatrixClientPeg.get().on("Room.name", this.onRoomName); ActiveRoomObserver.addListener(this.props.room.roomId, this._onActiveRoomChange); this.dispatcherRef = dis.register(this.onAction); + + if (this._shouldShowStatusMessage()) { + const statusUser = this._getStatusMessageUser(); + if (statusUser) { + statusUser.on( + "User._unstable_statusMessage", + this._onStatusMessageCommitted, + ); + } + } }, componentWillUnmount: function() { @@ -145,6 +183,16 @@ module.exports = React.createClass({ } ActiveRoomObserver.removeListener(this.props.room.roomId, this._onActiveRoomChange); dis.unregister(this.dispatcherRef); + + if (this._shouldShowStatusMessage()) { + const statusUser = this._getStatusMessageUser(); + if (statusUser) { + statusUser.removeListener( + "User._unstable_statusMessage", + this._onStatusMessageCommitted, + ); + } + } }, componentWillReceiveProps: function(props) { @@ -172,6 +220,13 @@ module.exports = React.createClass({ return false; }, + _onStatusMessageCommitted() { + // The status message `User` object has observed a message change. + this.setState({ + statusMessage: this._getStatusMessage(), + }); + }, + onClick: function(ev) { if (this.props.onClick) { this.props.onClick(this.props.room.roomId, ev); @@ -257,15 +312,9 @@ module.exports = React.createClass({ const mentionBadges = this.props.highlight && this._shouldShowMentionBadge(); const badges = notifBadges || mentionBadges; - const isJoined = this.props.room.getMyMembership() === "join"; - const looksLikeDm = this.props.room.getInvitedAndJoinedMemberCount() === 2; let subtext = null; - if (!isInvite && isJoined && looksLikeDm && SettingsStore.isFeatureEnabled("feature_custom_status")) { - const selfId = MatrixClientPeg.get().getUserId(); - const otherMember = this.props.room.currentState.getMembersExcept([selfId])[0]; - if (otherMember && otherMember.user && otherMember.user._unstable_statusMessage) { - subtext = otherMember.user._unstable_statusMessage; - } + if (this._shouldShowStatusMessage()) { + subtext = this.state.statusMessage; } const classes = classNames({ From 632bbde59813d4394ad31ba6d883faad08b013f3 Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Tue, 15 Jan 2019 09:27:12 -0600 Subject: [PATCH 15/17] Remove usused setting value from CustomStatusController --- src/settings/controllers/CustomStatusController.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/settings/controllers/CustomStatusController.js b/src/settings/controllers/CustomStatusController.js index 183947972b..0fc6619d92 100644 --- a/src/settings/controllers/CustomStatusController.js +++ b/src/settings/controllers/CustomStatusController.js @@ -23,7 +23,6 @@ export default class CustomStatusController extends SettingController { // Settings page is open (such as RoomTiles) can reflect the change. dis.dispatch({ action: "feature_custom_status_changed", - value: newValue, }); } } From 4b13774585bc7952986d7d4db0103a7c3bbe0dcb Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Tue, 15 Jan 2019 10:20:27 -0600 Subject: [PATCH 16/17] Revert to avatar only when custom status disabled This returns to the previous behavior of avatar only without a button when the custom status feature is disabled so that you don't get pointer cursor for something that does nothing when clicked. The avatar ring spacing is kept consistent with and without the feature enabled by using a different class in CSS. --- .../avatars/_MemberStatusMessageAvatar.scss | 4 +-- .../avatars/MemberStatusMessageAvatar.js | 27 +++++++++---------- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/res/css/views/avatars/_MemberStatusMessageAvatar.scss b/res/css/views/avatars/_MemberStatusMessageAvatar.scss index 03e7fdd188..7ac4036eef 100644 --- a/res/css/views/avatars/_MemberStatusMessageAvatar.scss +++ b/res/css/views/avatars/_MemberStatusMessageAvatar.scss @@ -14,13 +14,13 @@ See the License for the specific language governing permissions and limitations under the License. */ -.mx_MemberStatusMessageAvatar .mx_BaseAvatar { +.mx_MessageComposer_avatar .mx_BaseAvatar { padding: 1.5px; border: 1.2px solid transparent; border-radius: 14px; } -.mx_MemberStatusMessageAvatar .mx_BaseAvatar_initial { +.mx_MessageComposer_avatar .mx_BaseAvatar_initial { left: 1.5px; } diff --git a/src/components/views/avatars/MemberStatusMessageAvatar.js b/src/components/views/avatars/MemberStatusMessageAvatar.js index a98fcaa537..0258c4b0c8 100644 --- a/src/components/views/avatars/MemberStatusMessageAvatar.js +++ b/src/components/views/avatars/MemberStatusMessageAvatar.js @@ -108,27 +108,26 @@ export default class MemberStatusMessageAvatar extends React.Component { }; render() { - const customStatusFeatureEnabled = - SettingsStore.isFeatureEnabled("feature_custom_status"); + const avatar = ; - let onClick = null; - let hasStatus = false; - - if (customStatusFeatureEnabled) { - onClick = this._onClick; - hasStatus = this.state.hasStatus; + if (!SettingsStore.isFeatureEnabled("feature_custom_status")) { + return avatar; } const classes = classNames({ "mx_MemberStatusMessageAvatar": true, - "mx_MemberStatusMessageAvatar_hasStatus": hasStatus, + "mx_MemberStatusMessageAvatar_hasStatus": this.state.hasStatus, }); - return - + return + {avatar} ; } } From 807d3aba6e9669d263fc90277b90d7cc947ad35e Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Tue, 15 Jan 2019 16:02:08 -0600 Subject: [PATCH 17/17] Use integer padding / border for avatar It seems fractional spacing results in different behavior across browsers, including unbalanced spacing, making the avatar appear uncentered. Here we avoid this by using integers that seem to closely match the comps. Fixes https://github.com/vector-im/riot-web/issues/8134. --- res/css/views/avatars/_MemberStatusMessageAvatar.scss | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/res/css/views/avatars/_MemberStatusMessageAvatar.scss b/res/css/views/avatars/_MemberStatusMessageAvatar.scss index 7ac4036eef..c101a5d8a8 100644 --- a/res/css/views/avatars/_MemberStatusMessageAvatar.scss +++ b/res/css/views/avatars/_MemberStatusMessageAvatar.scss @@ -15,13 +15,13 @@ limitations under the License. */ .mx_MessageComposer_avatar .mx_BaseAvatar { - padding: 1.5px; - border: 1.2px solid transparent; - border-radius: 14px; + padding: 2px; + border: 1px solid transparent; + border-radius: 15px; } .mx_MessageComposer_avatar .mx_BaseAvatar_initial { - left: 1.5px; + left: 2px; } .mx_MemberStatusMessageAvatar_hasStatus .mx_BaseAvatar {