From df75024a21050e5f72be9fddad9f3bfbaaf5e6d9 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 13 Aug 2020 15:16:31 +0100 Subject: [PATCH 01/13] Switch out the globe icon and colour it depending on theme --- res/css/views/rooms/_RoomTileIcon.scss | 2 +- res/img/globe.svg | 5 +---- res/themes/legacy-dark/css/_legacy-dark.scss | 1 + res/themes/legacy-light/css/_legacy-light.scss | 1 + 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/res/css/views/rooms/_RoomTileIcon.scss b/res/css/views/rooms/_RoomTileIcon.scss index 2f3afdd446..a133e84133 100644 --- a/res/css/views/rooms/_RoomTileIcon.scss +++ b/res/css/views/rooms/_RoomTileIcon.scss @@ -31,7 +31,7 @@ limitations under the License. mask-position: center; mask-size: contain; mask-repeat: no-repeat; - background: $primary-fg-color; + background: $secondary-fg-color; mask-image: url('$(res)/img/globe.svg'); } diff --git a/res/img/globe.svg b/res/img/globe.svg index cc22bc6e66..635fa91cce 100644 --- a/res/img/globe.svg +++ b/res/img/globe.svg @@ -1,6 +1,3 @@ - - - - + diff --git a/res/themes/legacy-dark/css/_legacy-dark.scss b/res/themes/legacy-dark/css/_legacy-dark.scss index 4268fad030..287723ec9c 100644 --- a/res/themes/legacy-dark/css/_legacy-dark.scss +++ b/res/themes/legacy-dark/css/_legacy-dark.scss @@ -15,6 +15,7 @@ $room-highlight-color: #343a46; // typical text (dark-on-white in light skin) $primary-fg-color: $text-primary-color; +$secondary-fg-color: $primary-fg-color; $primary-bg-color: $bg-color; $muted-fg-color: $header-panel-text-primary-color; diff --git a/res/themes/legacy-light/css/_legacy-light.scss b/res/themes/legacy-light/css/_legacy-light.scss index 5ebb4ccc02..e5ae7f866e 100644 --- a/res/themes/legacy-light/css/_legacy-light.scss +++ b/res/themes/legacy-light/css/_legacy-light.scss @@ -23,6 +23,7 @@ $header-panel-bg-color: #f3f8fd; // typical text (dark-on-white in light skin) $primary-fg-color: #2e2f32; +$secondary-fg-color: $primary-fg-color; $primary-bg-color: #ffffff; $muted-fg-color: #61708b; // Commonly used in headings and relevant alt text From 4abbcd8c0693f15469f8036866463ce77dcdcc56 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 13 Aug 2020 15:24:21 +0100 Subject: [PATCH 02/13] Fix styling for selected community marker --- res/css/structures/_TagPanel.scss | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/res/css/structures/_TagPanel.scss b/res/css/structures/_TagPanel.scss index 78e8326772..b2d05ad7e6 100644 --- a/res/css/structures/_TagPanel.scss +++ b/res/css/structures/_TagPanel.scss @@ -108,13 +108,12 @@ limitations under the License. .mx_TagPanel .mx_TagTile.mx_TagTile_selected::before { content: ''; - height: calc(100% + 16px); + height: 100%; background-color: $accent-color; - width: 5px; + width: 4px; position: absolute; - left: -15px; + left: -12px; border-radius: 0 3px 3px 0; - top: -8px; // (16px from height / 2) } .mx_TagPanel .mx_TagTile.mx_AccessibleButton:focus { From 3f2c1500e54d83916625bbe4d1b7e28b692be721 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 13 Aug 2020 16:29:25 +0100 Subject: [PATCH 03/13] Fix /op slash command --- src/SlashCommands.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/SlashCommands.tsx b/src/SlashCommands.tsx index 2063ad3149..50a49ccf1c 100644 --- a/src/SlashCommands.tsx +++ b/src/SlashCommands.tsx @@ -733,7 +733,7 @@ export const Commands = [ const cli = MatrixClientPeg.get(); const room = cli.getRoom(roomId); if (!room) return reject(_t("Command failed")); - const member = room.getMember(args); + const member = room.getMember(userId); if (!member || getEffectiveMembership(member.membership) === EffectiveMembership.Leave) { return reject(_t("Could not find user in room")); } From 50b9da75976296a6b498f40c5abea9bfc55ffac6 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 13 Aug 2020 16:40:18 +0100 Subject: [PATCH 04/13] Put message previews for Emoji behind Labs --- src/settings/Settings.ts | 12 ++++++++++++ src/stores/room-list/previews/IPreview.ts | 2 +- .../room-list/previews/ReactionEventPreview.ts | 7 +++++++ 3 files changed, 20 insertions(+), 1 deletion(-) diff --git a/src/settings/Settings.ts b/src/settings/Settings.ts index 714d80f983..3d18c14e16 100644 --- a/src/settings/Settings.ts +++ b/src/settings/Settings.ts @@ -158,6 +158,18 @@ export const SETTINGS: {[setting: string]: ISetting} = { supportedLevels: LEVELS_FEATURE, default: false, }, + "feature_roomlist_preview_reactions_dms": { + isFeature: true, + displayName: _td("Show message previews for reactions in DMs"), + supportedLevels: LEVELS_FEATURE, + default: false, + }, + "feature_roomlist_preview_reactions_all": { + isFeature: true, + displayName: _td("Show message previews for reactions in all rooms"), + supportedLevels: LEVELS_FEATURE, + default: false, + }, "advancedRoomListLogging": { // TODO: Remove flag before launch: https://github.com/vector-im/element-web/issues/14231 displayName: _td("Enable advanced debugging for the room list"), diff --git a/src/stores/room-list/previews/IPreview.ts b/src/stores/room-list/previews/IPreview.ts index 9beb92bfbf..fe69637543 100644 --- a/src/stores/room-list/previews/IPreview.ts +++ b/src/stores/room-list/previews/IPreview.ts @@ -27,5 +27,5 @@ export interface IPreview { * @param tagId Optional. The tag where the room the event was sent in resides. * @returns The preview. */ - getTextFor(event: MatrixEvent, tagId?: TagID): string; + getTextFor(event: MatrixEvent, tagId?: TagID): string | null; } diff --git a/src/stores/room-list/previews/ReactionEventPreview.ts b/src/stores/room-list/previews/ReactionEventPreview.ts index 07fac107ca..c8f2be9a6e 100644 --- a/src/stores/room-list/previews/ReactionEventPreview.ts +++ b/src/stores/room-list/previews/ReactionEventPreview.ts @@ -19,9 +19,16 @@ import { TagID } from "../models"; import { MatrixEvent } from "matrix-js-sdk/src/models/event"; import { getSenderName, isSelf, shouldPrefixMessagesIn } from "./utils"; import { _t } from "../../../languageHandler"; +import SettingsStore from "../../../settings/SettingsStore"; +import DMRoomMap from "../../../utils/DMRoomMap"; export class ReactionEventPreview implements IPreview { public getTextFor(event: MatrixEvent, tagId?: TagID): string { + const showDms = SettingsStore.isFeatureEnabled("feature_roomlist_preview_reactions_dms"); + const showAll = SettingsStore.isFeatureEnabled("feature_roomlist_preview_reactions_all"); + + if (!showAll && (!showDms || DMRoomMap.shared().getUserIdForRoomId(event.getRoomId()))) return null; + const relation = event.getRelation(); if (!relation) return null; // invalid reaction (probably redacted) From e7bd656a198355f8322fdfe6a2a67dffafac94d9 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 13 Aug 2020 16:41:25 +0100 Subject: [PATCH 05/13] i18n --- 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 b636b5470e..0c6b6ae056 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -448,6 +448,8 @@ "Multiple integration managers": "Multiple integration managers", "Try out new ways to ignore people (experimental)": "Try out new ways to ignore people (experimental)", "Support adding custom themes": "Support adding custom themes", + "Show message previews for reactions in DMs": "Show message previews for reactions in DMs", + "Show message previews for reactions in all rooms": "Show message previews for reactions in all rooms", "Enable advanced debugging for the room list": "Enable advanced debugging for the room list", "Show info about bridges in room settings": "Show info about bridges in room settings", "Font size": "Font size", From 3ece2dd21d6a4298baedec6ac4d2de2de8f899bc Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Thu, 13 Aug 2020 19:24:11 +0100 Subject: [PATCH 06/13] Fix action bar safe area regression The action bar was recently moved, but the safe area was not, which left a gap between the event and the action bar, making it quite easy to trigger hover on a different event instead of reaching the action bar. Fixes https://github.com/vector-im/element-web/issues/14953 Regressed by https://github.com/matrix-org/matrix-react-sdk/pull/5056 --- res/css/views/messages/_MessageActionBar.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/css/views/messages/_MessageActionBar.scss b/res/css/views/messages/_MessageActionBar.scss index d2ff551668..1254b496b5 100644 --- a/res/css/views/messages/_MessageActionBar.scss +++ b/res/css/views/messages/_MessageActionBar.scss @@ -41,7 +41,7 @@ limitations under the License. width: calc(10px + 48px + 100% + 8px); // safe area + action bar height: calc(20px + 100%); - top: -20px; + top: -12px; left: -58px; z-index: -1; cursor: initial; From 4ec602b960f41648990204751a4a903a12097aaa Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 14 Aug 2020 10:20:59 +0100 Subject: [PATCH 07/13] Make cutout in the decorated room avatar transparent rather than fixed --- res/css/_components.scss | 1 - .../views/avatars/_DecoratedRoomAvatar.scss | 48 ++++- res/css/views/rooms/_RoomTileIcon.scss | 69 ------- .../roomlist/decorated-avatar-mask.svg | 3 + .../views/avatars/DecoratedRoomAvatar.tsx | 143 ++++++++++++++- src/components/views/rooms/RoomTileIcon.tsx | 168 ------------------ 6 files changed, 187 insertions(+), 245 deletions(-) delete mode 100644 res/css/views/rooms/_RoomTileIcon.scss create mode 100644 res/img/element-icons/roomlist/decorated-avatar-mask.svg delete mode 100644 src/components/views/rooms/RoomTileIcon.tsx diff --git a/res/css/_components.scss b/res/css/_components.scss index 7dd8a2034d..a2d0e1ceb5 100644 --- a/res/css/_components.scss +++ b/res/css/_components.scss @@ -184,7 +184,6 @@ @import "./views/rooms/_RoomRecoveryReminder.scss"; @import "./views/rooms/_RoomSublist.scss"; @import "./views/rooms/_RoomTile.scss"; -@import "./views/rooms/_RoomTileIcon.scss"; @import "./views/rooms/_RoomUpgradeWarningBar.scss"; @import "./views/rooms/_SearchBar.scss"; @import "./views/rooms/_SendMessageComposer.scss"; diff --git a/res/css/views/avatars/_DecoratedRoomAvatar.scss b/res/css/views/avatars/_DecoratedRoomAvatar.scss index 48d72131b5..2e4caf8e91 100644 --- a/res/css/views/avatars/_DecoratedRoomAvatar.scss +++ b/res/css/views/avatars/_DecoratedRoomAvatar.scss @@ -18,10 +18,52 @@ limitations under the License. .mx_DecoratedRoomAvatar, .mx_TemporaryTile { position: relative; - .mx_RoomTileIcon { + &.mx_DecoratedRoomAvatar_cutout .mx_BaseAvatar { + mask-image: url('$(res)/img/element-icons/roomlist/decorated-avatar-mask.svg'); + mask-position: center; + mask-size: contain; + mask-repeat: no-repeat; + } + + .mx_DecoratedRoomAvatar_icon { position: absolute; - bottom: 0; - right: 0; + bottom: -2px; + right: -2px; + margin: 4px; + width: 8px; + height: 8px; + border-radius: 50%; + } + + .mx_DecoratedRoomAvatar_icon::before { + content: ''; + width: 8px; + height: 8px; + position: absolute; + border-radius: 8px; + } + + .mx_DecoratedRoomAvatar_icon_globe { + background-color: #fff; + } + .mx_DecoratedRoomAvatar_icon_globe::before { + mask-position: center; + mask-size: contain; + mask-repeat: no-repeat; + background: $secondary-fg-color; + mask-image: url('$(res)/img/globe.svg'); + } + + .mx_DecoratedRoomAvatar_icon_offline::before { + background-color: $presence-offline; + } + + .mx_DecoratedRoomAvatar_icon_online::before { + background-color: $presence-online; + } + + .mx_DecoratedRoomAvatar_icon_away::before { + background-color: $presence-away; } .mx_NotificationBadge, .mx_RoomTile_badgeContainer { diff --git a/res/css/views/rooms/_RoomTileIcon.scss b/res/css/views/rooms/_RoomTileIcon.scss deleted file mode 100644 index a133e84133..0000000000 --- a/res/css/views/rooms/_RoomTileIcon.scss +++ /dev/null @@ -1,69 +0,0 @@ -/* -Copyright 2020 The Matrix.org Foundation C.I.C. - -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_RoomTileIcon { - width: 12px; - height: 12px; - border-radius: 12px; - background-color: $roomlist-bg-color; // to match the room list itself -} - -.mx_RoomTileIcon_globe::before { - content: ''; - width: 8px; - height: 8px; - top: 2px; - left: 2px; - position: absolute; - mask-position: center; - mask-size: contain; - mask-repeat: no-repeat; - background: $secondary-fg-color; - mask-image: url('$(res)/img/globe.svg'); -} - -.mx_RoomTileIcon_offline::before { - content: ''; - width: 8px; - height: 8px; - top: 2px; - left: 2px; - position: absolute; - border-radius: 8px; - background-color: $presence-offline; -} - -.mx_RoomTileIcon_online::before { - content: ''; - width: 8px; - height: 8px; - top: 2px; - left: 2px; - position: absolute; - border-radius: 8px; - background-color: $presence-online; -} - -.mx_RoomTileIcon_away::before { - content: ''; - width: 8px; - height: 8px; - top: 2px; - left: 2px; - position: absolute; - border-radius: 8px; - background-color: $presence-away; -} diff --git a/res/img/element-icons/roomlist/decorated-avatar-mask.svg b/res/img/element-icons/roomlist/decorated-avatar-mask.svg new file mode 100644 index 0000000000..fb09c16bba --- /dev/null +++ b/res/img/element-icons/roomlist/decorated-avatar-mask.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/components/views/avatars/DecoratedRoomAvatar.tsx b/src/components/views/avatars/DecoratedRoomAvatar.tsx index daf28400f2..e6dadf676c 100644 --- a/src/components/views/avatars/DecoratedRoomAvatar.tsx +++ b/src/components/views/avatars/DecoratedRoomAvatar.tsx @@ -14,15 +14,22 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React from 'react'; +import React from "react"; +import classNames from "classnames"; import { Room } from "matrix-js-sdk/src/models/room"; +import { User } from "matrix-js-sdk/src/models/user"; +import { MatrixEvent } from "matrix-js-sdk/src/models/event"; import { TagID } from '../../../stores/room-list/models'; import RoomAvatar from "./RoomAvatar"; -import RoomTileIcon from "../rooms/RoomTileIcon"; import NotificationBadge from '../rooms/NotificationBadge'; import { RoomNotificationStateStore } from "../../../stores/notifications/RoomNotificationStateStore"; import { NotificationState } from "../../../stores/notifications/NotificationState"; +import {isPresenceEnabled} from "../../../utils/presence"; +import {MatrixClientPeg} from "../../../MatrixClientPeg"; +import {_t} from "../../../languageHandler"; +import TextWithTooltip from "../elements/TextWithTooltip"; +import DMRoomMap from "../../../utils/DMRoomMap"; interface IProps { room: Room; @@ -36,18 +43,134 @@ interface IProps { interface IState { notificationState?: NotificationState; + icon: Icon; +} + +enum Icon { + // Note: the names here are used in CSS class names + None = "NONE", // ... except this one + Globe = "GLOBE", + PresenceOnline = "ONLINE", + PresenceAway = "AWAY", + PresenceOffline = "OFFLINE", +} + +function tooltipText(variant: Icon) { + switch (variant) { + case Icon.Globe: + return _t("This room is public"); + case Icon.PresenceOnline: + return _t("Online"); + case Icon.PresenceAway: + return _t("Away"); + case Icon.PresenceOffline: + return _t("Offline"); + } } export default class DecoratedRoomAvatar extends React.PureComponent { + private _dmUser: User; + private isUnmounted = false; + private isWatchingTimeline = false; constructor(props: IProps) { super(props); this.state = { notificationState: RoomNotificationStateStore.instance.getRoomState(this.props.room), + icon: this.calculateIcon(), }; } + public componentWillUnmount() { + this.isUnmounted = true; + if (this.isWatchingTimeline) this.props.room.off('Room.timeline', this.onRoomTimeline); + this.dmUser = null; // clear listeners, if any + } + + private get isPublicRoom(): boolean { + const joinRules = this.props.room.currentState.getStateEvents("m.room.join_rules", ""); + const joinRule = joinRules && joinRules.getContent().join_rule; + return joinRule === 'public'; + } + + private get dmUser(): User { + return this._dmUser; + } + + private set dmUser(val: User) { + const oldUser = this._dmUser; + this._dmUser = val; + if (oldUser && oldUser !== this._dmUser) { + oldUser.off('User.currentlyActive', this.onPresenceUpdate); + oldUser.off('User.presence', this.onPresenceUpdate); + } + if (this._dmUser && oldUser !== this._dmUser) { + this._dmUser.on('User.currentlyActive', this.onPresenceUpdate); + this._dmUser.on('User.presence', this.onPresenceUpdate); + } + } + + private onRoomTimeline = (ev: MatrixEvent, room: Room) => { + if (this.isUnmounted) return; + + // apparently these can happen? + if (!room) return; + if (this.props.room.roomId !== room.roomId) return; + + if (ev.getType() === 'm.room.join_rules' || ev.getType() === 'm.room.member') { + this.setState({icon: this.calculateIcon()}); + } + }; + + private onPresenceUpdate = () => { + if (this.isUnmounted) return; + + let newIcon = this.getPresenceIcon(); + if (newIcon !== this.state.icon) this.setState({icon: newIcon}); + }; + + private getPresenceIcon(): Icon { + if (!this.dmUser) return Icon.None; + + let icon = Icon.None; + + const isOnline = this.dmUser.currentlyActive || this.dmUser.presence === 'online'; + if (isOnline) { + icon = Icon.PresenceOnline; + } else if (this.dmUser.presence === 'offline') { + icon = Icon.PresenceOffline; + } else if (this.dmUser.presence === 'unavailable') { + icon = Icon.PresenceAway; + } + + return icon; + } + + private calculateIcon(): Icon { + let icon = Icon.None; + + // We look at the DMRoomMap and not the tag here so that we don't exclude DMs in Favourites + const otherUserId = DMRoomMap.shared().getUserIdForRoomId(this.props.room.roomId); + if (otherUserId && this.props.room.getJoinedMemberCount() === 2) { + // Track presence, if available + if (isPresenceEnabled()) { + if (otherUserId) { + this.dmUser = MatrixClientPeg.get().getUser(otherUserId); + icon = this.getPresenceIcon(); + } + } + } else { + // Track publicity + icon = this.isPublicRoom ? Icon.Globe : Icon.None; + if (!this.isWatchingTimeline) { + this.props.room.on('Room.timeline', this.onRoomTimeline); + this.isWatchingTimeline = true; + } + } + return icon; + } + public render(): React.ReactNode { let badge: React.ReactNode; if (this.props.displayBadge) { @@ -58,7 +181,19 @@ export default class DecoratedRoomAvatar extends React.PureComponent; } - return
+ let icon; + if (this.state.icon !== Icon.None) { + icon = ; + } + + const classes = classNames("mx_DecoratedRoomAvatar", { + mx_DecoratedRoomAvatar_cutout: icon, + }); + + return
- + {icon} {badge}
; } diff --git a/src/components/views/rooms/RoomTileIcon.tsx b/src/components/views/rooms/RoomTileIcon.tsx deleted file mode 100644 index 94833ac818..0000000000 --- a/src/components/views/rooms/RoomTileIcon.tsx +++ /dev/null @@ -1,168 +0,0 @@ -/* -Copyright 2020 The Matrix.org Foundation C.I.C. - -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 { Room } from "matrix-js-sdk/src/models/room"; -import { DefaultTagID, TagID } from "../../../stores/room-list/models"; -import { User } from "matrix-js-sdk/src/models/user"; -import { MatrixEvent } from "matrix-js-sdk/src/models/event"; -import DMRoomMap from "../../../utils/DMRoomMap"; -import { MatrixClientPeg } from "../../../MatrixClientPeg"; -import { isPresenceEnabled } from "../../../utils/presence"; -import { _t } from "../../../languageHandler"; -import TextWithTooltip from "../elements/TextWithTooltip"; - -enum Icon { - // Note: the names here are used in CSS class names - None = "NONE", // ... except this one - Globe = "GLOBE", - PresenceOnline = "ONLINE", - PresenceAway = "AWAY", - PresenceOffline = "OFFLINE", -} - -function tooltipText(variant: Icon) { - switch (variant) { - case Icon.Globe: - return _t("This room is public"); - case Icon.PresenceOnline: - return _t("Online"); - case Icon.PresenceAway: - return _t("Away"); - case Icon.PresenceOffline: - return _t("Offline"); - } -} - -interface IProps { - room: Room; -} - -interface IState { - icon: Icon; -} - -export default class RoomTileIcon extends React.Component { - private _dmUser: User; - private isUnmounted = false; - private isWatchingTimeline = false; - - constructor(props: IProps) { - super(props); - - this.state = { - icon: this.calculateIcon(), - }; - } - - private get isPublicRoom(): boolean { - const joinRules = this.props.room.currentState.getStateEvents("m.room.join_rules", ""); - const joinRule = joinRules && joinRules.getContent().join_rule; - return joinRule === 'public'; - } - - private get dmUser(): User { - return this._dmUser; - } - - private set dmUser(val: User) { - const oldUser = this._dmUser; - this._dmUser = val; - if (oldUser && oldUser !== this._dmUser) { - oldUser.off('User.currentlyActive', this.onPresenceUpdate); - oldUser.off('User.presence', this.onPresenceUpdate); - } - if (this._dmUser && oldUser !== this._dmUser) { - this._dmUser.on('User.currentlyActive', this.onPresenceUpdate); - this._dmUser.on('User.presence', this.onPresenceUpdate); - } - } - - public componentWillUnmount() { - this.isUnmounted = true; - if (this.isWatchingTimeline) this.props.room.off('Room.timeline', this.onRoomTimeline); - this.dmUser = null; // clear listeners, if any - } - - private onRoomTimeline = (ev: MatrixEvent, room: Room) => { - if (this.isUnmounted) return; - - // apparently these can happen? - if (!room) return; - if (this.props.room.roomId !== room.roomId) return; - - if (ev.getType() === 'm.room.join_rules' || ev.getType() === 'm.room.member') { - this.setState({icon: this.calculateIcon()}); - } - }; - - private onPresenceUpdate = () => { - if (this.isUnmounted) return; - - let newIcon = this.getPresenceIcon(); - if (newIcon !== this.state.icon) this.setState({icon: newIcon}); - }; - - private getPresenceIcon(): Icon { - if (!this.dmUser) return Icon.None; - - let icon = Icon.None; - - const isOnline = this.dmUser.currentlyActive || this.dmUser.presence === 'online'; - if (isOnline) { - icon = Icon.PresenceOnline; - } else if (this.dmUser.presence === 'offline') { - icon = Icon.PresenceOffline; - } else if (this.dmUser.presence === 'unavailable') { - icon = Icon.PresenceAway; - } - - return icon; - } - - private calculateIcon(): Icon { - let icon = Icon.None; - - // We look at the DMRoomMap and not the tag here so that we don't exclude DMs in Favourites - const otherUserId = DMRoomMap.shared().getUserIdForRoomId(this.props.room.roomId); - if (otherUserId && this.props.room.getJoinedMemberCount() === 2) { - // Track presence, if available - if (isPresenceEnabled()) { - if (otherUserId) { - this.dmUser = MatrixClientPeg.get().getUser(otherUserId); - icon = this.getPresenceIcon(); - } - } - } else { - // Track publicity - icon = this.isPublicRoom ? Icon.Globe : Icon.None; - if (!this.isWatchingTimeline) { - this.props.room.on('Room.timeline', this.onRoomTimeline); - this.isWatchingTimeline = true; - } - } - return icon; - } - - public render(): React.ReactElement { - if (this.state.icon === Icon.None) return null; - - return ; - } -} From d861536487d44fb2dc5594e1bc27cc6b0fe821c9 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 14 Aug 2020 10:30:54 +0100 Subject: [PATCH 08/13] Make globe transparent --- res/css/views/avatars/_DecoratedRoomAvatar.scss | 3 --- 1 file changed, 3 deletions(-) diff --git a/res/css/views/avatars/_DecoratedRoomAvatar.scss b/res/css/views/avatars/_DecoratedRoomAvatar.scss index 2e4caf8e91..e0afd9de66 100644 --- a/res/css/views/avatars/_DecoratedRoomAvatar.scss +++ b/res/css/views/avatars/_DecoratedRoomAvatar.scss @@ -43,9 +43,6 @@ limitations under the License. border-radius: 8px; } - .mx_DecoratedRoomAvatar_icon_globe { - background-color: #fff; - } .mx_DecoratedRoomAvatar_icon_globe::before { mask-position: center; mask-size: contain; From 228d1af6dca3dce8cdce8ee7bc482595291eec32 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 14 Aug 2020 11:01:03 +0100 Subject: [PATCH 09/13] i18n --- src/i18n/strings/en_EN.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index b636b5470e..0f8891b5fe 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1186,8 +1186,6 @@ "%(count)s unread messages.|other": "%(count)s unread messages.", "%(count)s unread messages.|one": "1 unread message.", "Unread messages.": "Unread messages.", - "This room is public": "This room is public", - "Away": "Away", "Add a topic": "Add a topic", "Upgrading this room will shut down the current instance of the room and create an upgraded room with the same name.": "Upgrading this room will shut down the current instance of the room and create an upgraded room with the same name.", "This room has already been upgraded.": "This room has already been upgraded.", @@ -1893,6 +1891,8 @@ "Take picture": "Take picture", "Remove for everyone": "Remove for everyone", "Remove for me": "Remove for me", + "This room is public": "This room is public", + "Away": "Away", "User Status": "User Status", "powered by Matrix": "powered by Matrix", "This homeserver would like to make sure you are not a robot.": "This homeserver would like to make sure you are not a robot.", From f982c7b334c5854538283f1f48bc60aefc7181e8 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 14 Aug 2020 13:22:52 +0100 Subject: [PATCH 10/13] Message Action Bar, subscribe to localEventIdReplaced events --- src/components/views/messages/MessageActionBar.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/components/views/messages/MessageActionBar.js b/src/components/views/messages/MessageActionBar.js index 513f21b8b7..fa722a1e06 100644 --- a/src/components/views/messages/MessageActionBar.js +++ b/src/components/views/messages/MessageActionBar.js @@ -116,11 +116,13 @@ export default class MessageActionBar extends React.PureComponent { componentDidMount() { this.props.mxEvent.on("Event.decrypted", this.onDecrypted); this.props.mxEvent.on("Event.beforeRedaction", this.onBeforeRedaction); + this.props.mxEvent.on("Event.localEventIdReplaced", this.onEcho); } componentWillUnmount() { this.props.mxEvent.removeListener("Event.decrypted", this.onDecrypted); this.props.mxEvent.removeListener("Event.beforeRedaction", this.onBeforeRedaction); + this.props.mxEvent.removeListener("Event.localEventIdReplaced", this.onEcho); } onDecrypted = () => { @@ -134,6 +136,11 @@ export default class MessageActionBar extends React.PureComponent { this.forceUpdate(); }; + onEcho = () => { + // When an event is sent and echoed the possible actions change. + this.forceUpdate(); + }; + onFocusChange = (focused) => { if (!this.props.onFocusChange) { return; From de9816574f0e3ef5a3bc7193264a962e7fc6ea25 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 14 Aug 2020 13:30:27 +0100 Subject: [PATCH 11/13] Make event subscriptions conditional when needed --- src/components/views/messages/MessageActionBar.js | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/components/views/messages/MessageActionBar.js b/src/components/views/messages/MessageActionBar.js index fa722a1e06..043fe1a985 100644 --- a/src/components/views/messages/MessageActionBar.js +++ b/src/components/views/messages/MessageActionBar.js @@ -114,15 +114,19 @@ export default class MessageActionBar extends React.PureComponent { static contextType = RoomContext; componentDidMount() { - this.props.mxEvent.on("Event.decrypted", this.onDecrypted); + if (this.props.mxEvent.isBeingDecrypted()) { + this.props.mxEvent.once("Event.decrypted", this.onDecrypted); + } this.props.mxEvent.on("Event.beforeRedaction", this.onBeforeRedaction); - this.props.mxEvent.on("Event.localEventIdReplaced", this.onEcho); + if (this.props.mxEvent.getId()[0] !== "!") { + this.props.mxEvent.once("Event.localEventIdReplaced", this.onEcho); + } } componentWillUnmount() { - this.props.mxEvent.removeListener("Event.decrypted", this.onDecrypted); - this.props.mxEvent.removeListener("Event.beforeRedaction", this.onBeforeRedaction); - this.props.mxEvent.removeListener("Event.localEventIdReplaced", this.onEcho); + this.props.mxEvent.off("Event.decrypted", this.onDecrypted); + this.props.mxEvent.off("Event.beforeRedaction", this.onBeforeRedaction); + this.props.mxEvent.off("Event.localEventIdReplaced", this.onEcho); } onDecrypted = () => { From 591847b27426264d8052e07f484036c1bd51ea8b Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 14 Aug 2020 13:37:11 +0100 Subject: [PATCH 12/13] Watch for Event.status instead as we can do some things in SENT --- src/components/views/messages/MessageActionBar.js | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/components/views/messages/MessageActionBar.js b/src/components/views/messages/MessageActionBar.js index 043fe1a985..4a6b3c39fb 100644 --- a/src/components/views/messages/MessageActionBar.js +++ b/src/components/views/messages/MessageActionBar.js @@ -18,6 +18,7 @@ limitations under the License. import React, {useEffect} from 'react'; import PropTypes from 'prop-types'; +import { EventStatus } from 'matrix-js-sdk'; import { _t } from '../../../languageHandler'; import * as sdk from '../../../index'; @@ -114,19 +115,19 @@ export default class MessageActionBar extends React.PureComponent { static contextType = RoomContext; componentDidMount() { + if (this.props.mxEvent.status && this.props.mxEvent !== EventStatus.SENT) { + this.props.mxEvent.on("Event.status", this.onSent); + } if (this.props.mxEvent.isBeingDecrypted()) { this.props.mxEvent.once("Event.decrypted", this.onDecrypted); } this.props.mxEvent.on("Event.beforeRedaction", this.onBeforeRedaction); - if (this.props.mxEvent.getId()[0] !== "!") { - this.props.mxEvent.once("Event.localEventIdReplaced", this.onEcho); - } } componentWillUnmount() { + this.props.mxEvent.off("Event.status", this.onSent); this.props.mxEvent.off("Event.decrypted", this.onDecrypted); this.props.mxEvent.off("Event.beforeRedaction", this.onBeforeRedaction); - this.props.mxEvent.off("Event.localEventIdReplaced", this.onEcho); } onDecrypted = () => { @@ -140,7 +141,7 @@ export default class MessageActionBar extends React.PureComponent { this.forceUpdate(); }; - onEcho = () => { + onSent = () => { // When an event is sent and echoed the possible actions change. this.forceUpdate(); }; From 655cc955708d04f8c26600448c6b04b2709d4c13 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 14 Aug 2020 13:37:28 +0100 Subject: [PATCH 13/13] fix typo --- src/components/views/messages/MessageActionBar.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/messages/MessageActionBar.js b/src/components/views/messages/MessageActionBar.js index 4a6b3c39fb..c94f296eac 100644 --- a/src/components/views/messages/MessageActionBar.js +++ b/src/components/views/messages/MessageActionBar.js @@ -115,7 +115,7 @@ export default class MessageActionBar extends React.PureComponent { static contextType = RoomContext; componentDidMount() { - if (this.props.mxEvent.status && this.props.mxEvent !== EventStatus.SENT) { + if (this.props.mxEvent.status && this.props.mxEvent.status !== EventStatus.SENT) { this.props.mxEvent.on("Event.status", this.onSent); } if (this.props.mxEvent.isBeingDecrypted()) {