From 61a260cd405eacac4c52777bd048657a9fda3702 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Sun, 18 Apr 2021 15:40:56 +0200 Subject: [PATCH 01/52] Format mxid --- res/css/views/messages/_SenderProfile.scss | 12 ++++++ .../views/messages/SenderProfile.js | 43 ++++++++++++++----- 2 files changed, 45 insertions(+), 10 deletions(-) diff --git a/res/css/views/messages/_SenderProfile.scss b/res/css/views/messages/_SenderProfile.scss index 655cb39489..e3a38bad6d 100644 --- a/res/css/views/messages/_SenderProfile.scss +++ b/res/css/views/messages/_SenderProfile.scss @@ -15,6 +15,18 @@ limitations under the License. */ .mx_SenderProfile_name { + display: flex; + align-items: center; + gap: 5px; +} + +.mx_SenderProfile_displayName { font-weight: 600; } +.mx_SenderProfile_mxid { + font-weight: 600; + font-family: monospace; + font-size: 1rem; + color: gray; +} diff --git a/src/components/views/messages/SenderProfile.js b/src/components/views/messages/SenderProfile.js index bd10526799..264d4ed3de 100644 --- a/src/components/views/messages/SenderProfile.js +++ b/src/components/views/messages/SenderProfile.js @@ -89,7 +89,21 @@ export default class SenderProfile extends React.Component { render() { const {mxEvent} = this.props; const colorClass = getUserNameColorClass(mxEvent.getSender()); - const name = mxEvent.sender ? mxEvent.sender.name : mxEvent.getSender(); + + let disambiguate; + let displayName; + let mxid; + + const sender = mxEvent.sender; + if (sender) { + disambiguate = sender.disambiguate; + displayName = sender.rawDisplayName; + mxid = sender.userId; + } else { + disambiguate = false; + displayName = mxEvent.getSender(); + mxid = mxEvent.getSender(); + } const {msgtype} = mxEvent.getContent(); if (msgtype === 'm.emote') { @@ -108,20 +122,29 @@ export default class SenderProfile extends React.Component { />; } - const nameElem = name || ''; - - // Name + flair - const nameFlair = - - { nameElem } + const displayNameElement = ( + + { displayName || '' } - { flair } - ; + ); + + let mxidElement; + if (disambiguate) { + mxidElement = ( + + { `[${mxid || ""}]` } + + ); + } return (
- { nameFlair } +
+ { displayNameElement } + { mxidElement } + { flair } +
); From 39eb487f49718453ac152d97119f764527a67821 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Tue, 20 Apr 2021 11:09:03 +0200 Subject: [PATCH 02/52] TS conversion MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- .../{SenderProfile.js => SenderProfile.tsx} | 58 +++++++++---------- 1 file changed, 29 insertions(+), 29 deletions(-) rename src/components/views/messages/{SenderProfile.js => SenderProfile.tsx} (82%) diff --git a/src/components/views/messages/SenderProfile.js b/src/components/views/messages/SenderProfile.tsx similarity index 82% rename from src/components/views/messages/SenderProfile.js rename to src/components/views/messages/SenderProfile.tsx index 264d4ed3de..fa635eaedb 100644 --- a/src/components/views/messages/SenderProfile.js +++ b/src/components/views/messages/SenderProfile.tsx @@ -15,26 +15,37 @@ */ import React from 'react'; -import PropTypes from 'prop-types'; import Flair from '../elements/Flair.js'; import FlairStore from '../../../stores/FlairStore'; import {getUserNameColorClass} from '../../../utils/FormattingUtils'; import MatrixClientContext from "../../../contexts/MatrixClientContext"; import {replaceableComponent} from "../../../utils/replaceableComponent"; +import MatrixEvent from "matrix-js-sdk/src/models/event"; + +interface IProps { + mxEvent: MatrixEvent; + onClick(): void; + enableFlair: boolean; +} + +interface IState { + userGroups; + relatedGroups; +} @replaceableComponent("views.messages.SenderProfile") -export default class SenderProfile extends React.Component { - static propTypes = { - mxEvent: PropTypes.object.isRequired, // event whose sender we're showing - onClick: PropTypes.func, - }; - +export default class SenderProfile extends React.Component { static contextType = MatrixClientContext; + unmounted: boolean; - state = { - userGroups: null, - relatedGroups: [], - }; + constructor(props: IProps) { + super(props) + + this.state = { + userGroups: null, + relatedGroups: [], + }; + } componentDidMount() { this.unmounted = false; @@ -89,28 +100,17 @@ export default class SenderProfile extends React.Component { render() { const {mxEvent} = this.props; const colorClass = getUserNameColorClass(mxEvent.getSender()); - - let disambiguate; - let displayName; - let mxid; - - const sender = mxEvent.sender; - if (sender) { - disambiguate = sender.disambiguate; - displayName = sender.rawDisplayName; - mxid = sender.userId; - } else { - disambiguate = false; - displayName = mxEvent.getSender(); - mxid = mxEvent.getSender(); - } const {msgtype} = mxEvent.getContent(); + const disambiguate = mxEvent.sender?.disambiguate; + const displayName = mxEvent.sender?.rawDisplayName || mxEvent.getSender() || ""; + const mxid = mxEvent.sender?.userId || mxEvent.getSender() || ""; + if (msgtype === 'm.emote') { return ; // emote message must include the name so don't duplicate it } - let flair =
; + let flair; if (this.props.enableFlair) { const displayedGroups = this._getDisplayedGroups( this.state.userGroups, this.state.relatedGroups, @@ -124,7 +124,7 @@ export default class SenderProfile extends React.Component { const displayNameElement = ( - { displayName || '' } + { displayName } ); @@ -132,7 +132,7 @@ export default class SenderProfile extends React.Component { if (disambiguate) { mxidElement = ( - { `[${mxid || ""}]` } + { `[${mxid}]` } ); } From faca498523460e59215bbe819521fcda01069729 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Tue, 20 Apr 2021 11:12:16 +0200 Subject: [PATCH 03/52] Don't use gap MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/css/views/messages/_SenderProfile.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/css/views/messages/_SenderProfile.scss b/res/css/views/messages/_SenderProfile.scss index e3a38bad6d..d7763cda08 100644 --- a/res/css/views/messages/_SenderProfile.scss +++ b/res/css/views/messages/_SenderProfile.scss @@ -17,7 +17,6 @@ limitations under the License. .mx_SenderProfile_name { display: flex; align-items: center; - gap: 5px; } .mx_SenderProfile_displayName { @@ -29,4 +28,5 @@ limitations under the License. font-family: monospace; font-size: 1rem; color: gray; + margin-left: 5px; } From 4e1409dc2c7258816e51efb204a09ef608055117 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Tue, 20 Apr 2021 11:40:50 +0200 Subject: [PATCH 04/52] Add private Co-authored-by: Michael Telatynski <7t3chguy@googlemail.com> --- src/components/views/messages/SenderProfile.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/messages/SenderProfile.tsx b/src/components/views/messages/SenderProfile.tsx index fa635eaedb..f0dd77f746 100644 --- a/src/components/views/messages/SenderProfile.tsx +++ b/src/components/views/messages/SenderProfile.tsx @@ -36,7 +36,7 @@ interface IState { @replaceableComponent("views.messages.SenderProfile") export default class SenderProfile extends React.Component { static contextType = MatrixClientContext; - unmounted: boolean; + private unmounted: boolean; constructor(props: IProps) { super(props) From ffcd79f4a3215c1153e50ec599a8cd18b2d1e5a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Wed, 21 Apr 2021 17:34:03 +0200 Subject: [PATCH 05/52] Remove brackets MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/views/messages/SenderProfile.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/messages/SenderProfile.tsx b/src/components/views/messages/SenderProfile.tsx index f0dd77f746..b2bfe00168 100644 --- a/src/components/views/messages/SenderProfile.tsx +++ b/src/components/views/messages/SenderProfile.tsx @@ -132,7 +132,7 @@ export default class SenderProfile extends React.Component { if (disambiguate) { mxidElement = ( - { `[${mxid}]` } + { mxid } ); } From eee1294374c568ee3e9c956b5fb3acaf537918a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Wed, 21 Apr 2021 17:37:25 +0200 Subject: [PATCH 06/52] Make both have the same baseline MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/css/views/messages/_SenderProfile.scss | 5 ----- src/components/views/messages/SenderProfile.tsx | 8 +++----- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/res/css/views/messages/_SenderProfile.scss b/res/css/views/messages/_SenderProfile.scss index d7763cda08..14e8f6583f 100644 --- a/res/css/views/messages/_SenderProfile.scss +++ b/res/css/views/messages/_SenderProfile.scss @@ -14,11 +14,6 @@ See the License for the specific language governing permissions and limitations under the License. */ -.mx_SenderProfile_name { - display: flex; - align-items: center; -} - .mx_SenderProfile_displayName { font-weight: 600; } diff --git a/src/components/views/messages/SenderProfile.tsx b/src/components/views/messages/SenderProfile.tsx index b2bfe00168..70040e4ef7 100644 --- a/src/components/views/messages/SenderProfile.tsx +++ b/src/components/views/messages/SenderProfile.tsx @@ -140,11 +140,9 @@ export default class SenderProfile extends React.Component { return (
-
- { displayNameElement } - { mxidElement } - { flair } -
+ { displayNameElement } + { mxidElement } + { flair }
); From 9658a760c8bd96dd91498d064e7898c5a70fe19f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Wed, 21 Apr 2021 17:41:21 +0200 Subject: [PATCH 07/52] Use timestamp color MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/css/views/messages/_SenderProfile.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/css/views/messages/_SenderProfile.scss b/res/css/views/messages/_SenderProfile.scss index 14e8f6583f..378d9e6f1a 100644 --- a/res/css/views/messages/_SenderProfile.scss +++ b/res/css/views/messages/_SenderProfile.scss @@ -22,6 +22,6 @@ limitations under the License. font-weight: 600; font-family: monospace; font-size: 1rem; - color: gray; + color: $event-timestamp-color; margin-left: 5px; } From 55365e632b524015f3dca528e18d2c593861bf0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Thu, 22 Apr 2021 07:52:39 +0200 Subject: [PATCH 08/52] Use the correct selector in E2EE tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- test/end-to-end-tests/src/usecases/timeline.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/end-to-end-tests/src/usecases/timeline.js b/test/end-to-end-tests/src/usecases/timeline.js index 3889dce108..01dc618571 100644 --- a/test/end-to-end-tests/src/usecases/timeline.js +++ b/test/end-to-end-tests/src/usecases/timeline.js @@ -122,7 +122,7 @@ function getAllEventTiles(session) { } async function getMessageFromEventTile(eventTile) { - const senderElement = await eventTile.$(".mx_SenderProfile_name"); + const senderElement = await eventTile.$(".mx_SenderProfile_displayName"); const className = await (await eventTile.getProperty("className")).jsonValue(); const classNames = className.split(" "); const bodyElement = await eventTile.$(".mx_EventTile_body"); From fa534e4755df403ee23f82ecd420b94c8c184d79 Mon Sep 17 00:00:00 2001 From: Germain Souquet Date: Tue, 27 Apr 2021 14:47:56 +0100 Subject: [PATCH 09/52] Add room intro warning when e2ee is not enabled --- res/css/views/rooms/_NewRoomIntro.scss | 20 ++++++++++++++++++++ src/components/views/rooms/NewRoomIntro.tsx | 20 ++++++++++++++++++++ src/i18n/strings/en_EN.json | 1 + 3 files changed, 41 insertions(+) diff --git a/res/css/views/rooms/_NewRoomIntro.scss b/res/css/views/rooms/_NewRoomIntro.scss index 9c2a428cb3..e9d80c48c9 100644 --- a/res/css/views/rooms/_NewRoomIntro.scss +++ b/res/css/views/rooms/_NewRoomIntro.scss @@ -69,4 +69,24 @@ limitations under the License. font-size: $font-15px; color: $secondary-fg-color; } + + .mx_NewRoomIntro_message:not(:first-child) { + padding-top: 1em; + margin-top: 1em; + border-top: 1px solid currentColor; + } + + .mx_NewRoomIntro_message[role=alert]::before { + --size: 25px; + content: "!"; + float: left; + border-radius: 50%; + width: var(--size); + height: var(--size); + line-height: var(--size); + text-align: center; + background: $button-danger-bg-color; + color: #fff; + margin-right: calc(var(--size) / 2); + } } diff --git a/src/components/views/rooms/NewRoomIntro.tsx b/src/components/views/rooms/NewRoomIntro.tsx index 3f6054304d..9b003ade89 100644 --- a/src/components/views/rooms/NewRoomIntro.tsx +++ b/src/components/views/rooms/NewRoomIntro.tsx @@ -31,6 +31,14 @@ import dis from "../../../dispatcher/dispatcher"; import SpaceStore from "../../../stores/SpaceStore"; import {showSpaceInvite} from "../../../utils/space"; +import { privateShouldBeEncrypted } from "../../../createRoom"; + +function hasExpectedEncryptionSettings(room): boolean { + const isEncrypted: boolean = room._client.isRoomEncrypted(room.roomId); + const isPublic: boolean = room.getJoinRule() === "public"; + return isPublic || !privateShouldBeEncrypted() || isEncrypted; +} + const NewRoomIntro = () => { const cli = useContext(MatrixClientContext); const {room, roomId} = useContext(RoomContext); @@ -168,6 +176,18 @@ const NewRoomIntro = () => { return
{ body } + + { !hasExpectedEncryptionSettings(room) &&

+ {_t("Messages in this room are not end-to-end encrypted. " + + "Messages sent in an unencrypted room can be seen by the server and third parties. " + + "Learn more about encryption.", {}, + { + a: sub => {sub}, + })} + +

}
; }; diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index c6f7a8d25e..7ea9227938 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1490,6 +1490,7 @@ "Invite to just this room": "Invite to just this room", "Add a photo, so people can easily spot your room.": "Add a photo, so people can easily spot your room.", "This is the start of .": "This is the start of .", + "Messages in this room are not end-to-end encrypted. Messages sent in an unencrypted room can be seen by the server and third parties. Learn more about encryption.": "Messages in this room are not end-to-end encrypted. Messages sent in an unencrypted room can be seen by the server and third parties. Learn more about encryption.", "No pinned messages.": "No pinned messages.", "Loading...": "Loading...", "Pinned Messages": "Pinned Messages", From 48d3e41351bd869c232176d956d8254d17f5dc77 Mon Sep 17 00:00:00 2001 From: Robin Townsend Date: Sat, 5 Jun 2021 01:23:51 -0400 Subject: [PATCH 10/52] Cache frequently used settings values in RoomContext Signed-off-by: Robin Townsend --- src/components/structures/RoomView.tsx | 64 +++++++++++++++++++++----- src/contexts/RoomContext.ts | 6 ++- 2 files changed, 57 insertions(+), 13 deletions(-) diff --git a/src/components/structures/RoomView.tsx b/src/components/structures/RoomView.tsx index 5ffc2fd0da..fa67013ccb 100644 --- a/src/components/structures/RoomView.tsx +++ b/src/components/structures/RoomView.tsx @@ -155,7 +155,6 @@ export interface IState { canPeek: boolean; showApps: boolean; isPeeking: boolean; - showReadReceipts: boolean; showRightPanel: boolean; // error object, as from the matrix client/server API // If we failed to load information about the room, @@ -183,6 +182,11 @@ export interface IState { canReact: boolean; canReply: boolean; layout: Layout; + showReadReceipts: boolean; + showRedactions: boolean; + showJoinLeaves: boolean; + showAvatarChanges: boolean; + showDisplaynameChanges: boolean; matrixClientIsReady: boolean; showUrlPreview?: boolean; e2eStatus?: E2EStatus; @@ -200,8 +204,7 @@ export default class RoomView extends React.Component { private readonly dispatcherRef: string; private readonly roomStoreToken: EventSubscription; private readonly rightPanelStoreToken: EventSubscription; - private readonly showReadReceiptsWatchRef: string; - private readonly layoutWatcherRef: string; + private settingWatchers: string[]; private unmounted = false; private permalinkCreators: Record = {}; @@ -232,7 +235,6 @@ export default class RoomView extends React.Component { canPeek: false, showApps: false, isPeeking: false, - showReadReceipts: true, showRightPanel: RightPanelStore.getSharedInstance().isOpenForRoom, joining: false, atEndOfLiveTimeline: true, @@ -242,6 +244,11 @@ export default class RoomView extends React.Component { canReact: false, canReply: false, layout: SettingsStore.getValue("layout"), + showReadReceipts: true, + showRedactions: true, + showJoinLeaves: true, + showAvatarChanges: true, + showDisplaynameChanges: true, matrixClientIsReady: this.context && this.context.isInitialSyncComplete(), dragCounter: 0, }; @@ -268,9 +275,11 @@ export default class RoomView extends React.Component { WidgetEchoStore.on(UPDATE_EVENT, this.onWidgetEchoStoreUpdate); WidgetStore.instance.on(UPDATE_EVENT, this.onWidgetStoreUpdate); - this.showReadReceiptsWatchRef = SettingsStore.watchSetting("showReadReceipts", null, - this.onReadReceiptsChange); - this.layoutWatcherRef = SettingsStore.watchSetting("layout", null, this.onLayoutChange); + this.settingWatchers = [ + SettingsStore.watchSetting("layout", null, () => + this.setState({ layout: SettingsStore.getValue("layout") }), + ), + ]; } private onWidgetStoreUpdate = () => { @@ -327,9 +336,42 @@ export default class RoomView extends React.Component { // we should only peek once we have a ready client shouldPeek: this.state.matrixClientIsReady && RoomViewStore.shouldPeek(), showReadReceipts: SettingsStore.getValue("showReadReceipts", roomId), + showRedactions: SettingsStore.getValue("showRedactions", roomId), + showJoinLeaves: SettingsStore.getValue("showJoinLeaves", roomId), + showAvatarChanges: SettingsStore.getValue("showAvatarChanges", roomId), + showDisplaynameChanges: SettingsStore.getValue("showDisplaynameChanges", roomId), wasContextSwitch: RoomViewStore.getWasContextSwitch(), }; + // Add watchers for each of the settings we just looked up + this.settingWatchers = this.settingWatchers.concat([ + SettingsStore.watchSetting("showReadReceipts", null, () => + this.setState({ + showReadReceipts: SettingsStore.getValue("showReadReceipts", roomId), + }), + ), + SettingsStore.watchSetting("showRedactions", null, () => + this.setState({ + showRedactions: SettingsStore.getValue("showRedactions", roomId), + }), + ), + SettingsStore.watchSetting("showJoinLeaves", null, () => + this.setState({ + showJoinLeaves: SettingsStore.getValue("showJoinLeaves", roomId), + }), + ), + SettingsStore.watchSetting("showAvatarChanges", null, () => + this.setState({ + showAvatarChanges: SettingsStore.getValue("showAvatarChanges", roomId), + }), + ), + SettingsStore.watchSetting("showDisplaynameChanges", null, () => + this.setState({ + showDisplaynameChanges: SettingsStore.getValue("showDisplaynameChanges", roomId), + }), + ), + ]); + if (!initial && this.state.shouldPeek && !newState.shouldPeek) { // Stop peeking because we have joined this room now this.context.stopPeeking(); @@ -638,10 +680,6 @@ export default class RoomView extends React.Component { ); } - if (this.showReadReceiptsWatchRef) { - SettingsStore.unwatchSetting(this.showReadReceiptsWatchRef); - } - // cancel any pending calls to the rate_limited_funcs this.updateRoomMembers.cancelPendingCall(); @@ -649,7 +687,9 @@ export default class RoomView extends React.Component { // console.log("Tinter.tint from RoomView.unmount"); // Tinter.tint(); // reset colourscheme - SettingsStore.unwatchSetting(this.layoutWatcherRef); + for (const watcher of this.settingWatchers) { + SettingsStore.unwatchSetting(watcher); + } } private onUserScroll = () => { diff --git a/src/contexts/RoomContext.ts b/src/contexts/RoomContext.ts index e925f8624b..1efa1c03e7 100644 --- a/src/contexts/RoomContext.ts +++ b/src/contexts/RoomContext.ts @@ -31,7 +31,6 @@ const RoomContext = createContext({ canPeek: false, showApps: false, isPeeking: false, - showReadReceipts: true, showRightPanel: true, joining: false, atEndOfLiveTimeline: true, @@ -41,6 +40,11 @@ const RoomContext = createContext({ canReact: false, canReply: false, layout: Layout.Group, + showReadReceipts: true, + showRedactions: true, + showJoinLeaves: true, + showAvatarChanges: true, + showDisplaynameChanges: true, matrixClientIsReady: false, dragCounter: 0, }); From 13196b8146919f8ef781904d02c1edfd5f376dfe Mon Sep 17 00:00:00 2001 From: Robin Townsend Date: Sat, 5 Jun 2021 01:25:01 -0400 Subject: [PATCH 11/52] Prefer cached settings values in shouldHideEvent Signed-off-by: Robin Townsend --- src/shouldHideEvent.ts | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/shouldHideEvent.ts b/src/shouldHideEvent.ts index 2a47b9c417..31d610b28b 100644 --- a/src/shouldHideEvent.ts +++ b/src/shouldHideEvent.ts @@ -17,6 +17,7 @@ import {MatrixEvent} from "matrix-js-sdk/src/models/event"; import SettingsStore from "./settings/SettingsStore"; +import {IState} from "./components/structures/RoomView"; interface IDiff { isMemberEvent: boolean; @@ -47,11 +48,18 @@ function memberEventDiff(ev: MatrixEvent): IDiff { return diff; } -export default function shouldHideEvent(ev: MatrixEvent): boolean { - // Wrap getValue() for readability. Calling the SettingsStore can be - // fairly resource heavy, so the checks below should avoid hitting it - // where possible. - const isEnabled = (name) => SettingsStore.getValue(name, ev.getRoomId()); +/** + * Determines whether the given event should be hidden from timelines. + * @param ev The event + * @param ctx An optional RoomContext to pull cached settings values from to avoid + * hitting the settings store + */ +export default function shouldHideEvent(ev: MatrixEvent, ctx?: IState): boolean { + // Accessing the settings store directly can be expensive if done frequently, + // so we should prefer using cached values if a RoomContext is available + const isEnabled = ctx ? + name => ctx[name] : + name => SettingsStore.getValue(name, ev.getRoomId()); // Hide redacted events if (ev.isRedacted() && !isEnabled('showRedactions')) return true; From 3bf8e54d7f37477868c1fa229449749bd02f2894 Mon Sep 17 00:00:00 2001 From: Robin Townsend Date: Sat, 5 Jun 2021 01:25:43 -0400 Subject: [PATCH 12/52] Use cached RoomContext settings values throughout rooms Signed-off-by: Robin Townsend --- src/components/structures/MessagePanel.js | 5 ++++- src/components/structures/RoomView.tsx | 2 +- src/components/structures/TimelinePanel.js | 5 ++++- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/components/structures/MessagePanel.js b/src/components/structures/MessagePanel.js index 6709fef814..bca62e7b2f 100644 --- a/src/components/structures/MessagePanel.js +++ b/src/components/structures/MessagePanel.js @@ -26,6 +26,7 @@ import * as sdk from '../../index'; import {MatrixClientPeg} from '../../MatrixClientPeg'; import SettingsStore from '../../settings/SettingsStore'; +import RoomContext from "../../contexts/RoomContext"; import {Layout, LayoutPropType} from "../../settings/Layout"; import {_t} from "../../languageHandler"; import {haveTileForEvent} from "../views/rooms/EventTile"; @@ -152,6 +153,8 @@ export default class MessagePanel extends React.Component { enableFlair: PropTypes.bool, }; + static contextType = RoomContext; + constructor(props) { super(props); @@ -381,7 +384,7 @@ export default class MessagePanel extends React.Component { // Always show highlighted event if (this.props.highlightedEventId === mxEv.getId()) return true; - return !shouldHideEvent(mxEv); + return !shouldHideEvent(mxEv, this.context); } _readMarkerForEvent(eventId, isLastEvent) { diff --git a/src/components/structures/RoomView.tsx b/src/components/structures/RoomView.tsx index fa67013ccb..b80d909a94 100644 --- a/src/components/structures/RoomView.tsx +++ b/src/components/structures/RoomView.tsx @@ -859,7 +859,7 @@ export default class RoomView extends React.Component { // update unread count when scrolled up if (!this.state.searchResults && this.state.atEndOfLiveTimeline) { // no change - } else if (!shouldHideEvent(ev)) { + } else if (!shouldHideEvent(ev, this.state)) { this.setState((state, props) => { return {numUnreadMessages: state.numUnreadMessages + 1}; }); diff --git a/src/components/structures/TimelinePanel.js b/src/components/structures/TimelinePanel.js index 6300c7532e..c5ae2e87ba 100644 --- a/src/components/structures/TimelinePanel.js +++ b/src/components/structures/TimelinePanel.js @@ -26,6 +26,7 @@ import {EventTimeline} from "matrix-js-sdk/src/models/event-timeline"; import {TimelineWindow} from "matrix-js-sdk/src/timeline-window"; import { _t } from '../../languageHandler'; import {MatrixClientPeg} from "../../MatrixClientPeg"; +import RoomContext from "../../contexts/RoomContext"; import UserActivity from "../../UserActivity"; import Modal from "../../Modal"; import dis from "../../dispatcher/dispatcher"; @@ -122,6 +123,8 @@ class TimelinePanel extends React.Component { layout: LayoutPropType, } + static contextType = RoomContext; + // a map from room id to read marker event timestamp static roomReadMarkerTsMap = {}; @@ -1285,7 +1288,7 @@ class TimelinePanel extends React.Component { const shouldIgnore = !!ev.status || // local echo (ignoreOwn && ev.sender && ev.sender.userId == myUserId); // own message - const isWithoutTile = !haveTileForEvent(ev) || shouldHideEvent(ev); + const isWithoutTile = !haveTileForEvent(ev) || shouldHideEvent(ev, this.context); if (isWithoutTile || !node) { // don't start counting if the event should be ignored, From c24b239478aef6e4e19e3651a9f8376753f17f12 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 5 Jun 2021 13:36:25 +0000 Subject: [PATCH 13/52] Bump ws from 6.2.1 to 6.2.2 in /test/end-to-end-tests Bumps [ws](https://github.com/websockets/ws) from 6.2.1 to 6.2.2. - [Release notes](https://github.com/websockets/ws/releases) - [Commits](https://github.com/websockets/ws/commits) --- updated-dependencies: - dependency-name: ws dependency-type: indirect ... Signed-off-by: dependabot[bot] --- test/end-to-end-tests/yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/end-to-end-tests/yarn.lock b/test/end-to-end-tests/yarn.lock index 97b348fe50..bc942c4f51 100644 --- a/test/end-to-end-tests/yarn.lock +++ b/test/end-to-end-tests/yarn.lock @@ -760,9 +760,9 @@ wrappy@1: integrity sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= ws@^6.1.0: - version "6.2.1" - resolved "https://registry.yarnpkg.com/ws/-/ws-6.2.1.tgz#442fdf0a47ed64f59b6a5d8ff130f4748ed524fb" - integrity sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA== + version "6.2.2" + resolved "https://registry.yarnpkg.com/ws/-/ws-6.2.2.tgz#dd5cdbd57a9979916097652d78f1cc5faea0c32e" + integrity sha512-zmhltoSR8u1cnDsD43TX59mzoMZsLKqUweyYBAIvTngR3shc0W6aOZylZmq/7hqyVxPdi+5Ud2QInblgyE72fw== dependencies: async-limiter "~1.0.0" From 0f64f4d692f36287ace222eb13e07244fa6e1c63 Mon Sep 17 00:00:00 2001 From: Robin Townsend Date: Sat, 5 Jun 2021 10:49:44 -0400 Subject: [PATCH 14/52] Fix MessagePanel tests Signed-off-by: Robin Townsend --- test/components/structures/MessagePanel-test.js | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/test/components/structures/MessagePanel-test.js b/test/components/structures/MessagePanel-test.js index 5b466b4bb0..4f7fca1759 100644 --- a/test/components/structures/MessagePanel-test.js +++ b/test/components/structures/MessagePanel-test.js @@ -51,8 +51,20 @@ class WrappedMessagePanel extends React.Component { }; render() { + const roomContext = { + room, + roomId: room.roomId, + canReact: true, + canReply: true, + showReadReceipts: true, + showRedactions: false, + showJoinLeaves: false, + showAvatarChanges: false, + showDisplaynameChanges: true, + }; + return - + ; From 903d4d252a0c2ac6043ec24251d4c446f33d7990 Mon Sep 17 00:00:00 2001 From: Robin Townsend Date: Sun, 6 Jun 2021 23:06:56 -0400 Subject: [PATCH 15/52] Add optimized function to determine whether event has text to display Signed-off-by: Robin Townsend --- src/TextForEvent.js | 274 ++++++++++++---------- src/components/structures/MessagePanel.js | 9 +- src/components/views/rooms/EventTile.tsx | 4 +- 3 files changed, 155 insertions(+), 132 deletions(-) diff --git a/src/TextForEvent.js b/src/TextForEvent.js index 86f9ff20f4..22ce0dd9cf 100644 --- a/src/TextForEvent.js +++ b/src/TextForEvent.js @@ -21,6 +21,10 @@ import SettingsStore from "./settings/SettingsStore"; import {ALL_RULE_TYPES, ROOM_RULE_TYPES, SERVER_RULE_TYPES, USER_RULE_TYPES} from "./mjolnir/BanList"; import {WIDGET_LAYOUT_EVENT_TYPE} from "./stores/widgets/WidgetLayoutStore"; +// These functions are frequently used just to check whether an event has +// any text to display at all. For this reason they return deferred values +// to avoid the expense of looking up translations when they're not needed. + function textForMemberEvent(ev) { // XXX: SYJS-16 "sender is sometimes null for join messages" const senderName = ev.sender ? ev.sender.name : ev.getSender(); @@ -28,84 +32,84 @@ function textForMemberEvent(ev) { const prevContent = ev.getPrevContent(); const content = ev.getContent(); - const reason = content.reason ? (_t('Reason') + ': ' + content.reason) : ''; + const getReason = () => content.reason ? (_t('Reason') + ': ' + content.reason) : ''; switch (content.membership) { case 'invite': { const threePidContent = content.third_party_invite; if (threePidContent) { if (threePidContent.display_name) { - return _t('%(targetName)s accepted the invitation for %(displayName)s.', { + return () => _t('%(targetName)s accepted the invitation for %(displayName)s.', { targetName, displayName: threePidContent.display_name, }); } else { - return _t('%(targetName)s accepted an invitation.', {targetName}); + return () => _t('%(targetName)s accepted an invitation.', {targetName}); } } else { - return _t('%(senderName)s invited %(targetName)s.', {senderName, targetName}); + return () => _t('%(senderName)s invited %(targetName)s.', {senderName, targetName}); } } case 'ban': - return _t('%(senderName)s banned %(targetName)s.', {senderName, targetName}) + ' ' + reason; + return () => _t('%(senderName)s banned %(targetName)s.', {senderName, targetName}) + ' ' + getReason(); case 'join': if (prevContent && prevContent.membership === 'join') { if (prevContent.displayname && content.displayname && prevContent.displayname !== content.displayname) { - return _t('%(oldDisplayName)s changed their display name to %(displayName)s.', { + return () => _t('%(oldDisplayName)s changed their display name to %(displayName)s.', { oldDisplayName: prevContent.displayname, displayName: content.displayname, }); } else if (!prevContent.displayname && content.displayname) { - return _t('%(senderName)s set their display name to %(displayName)s.', { + return () => _t('%(senderName)s set their display name to %(displayName)s.', { senderName: ev.getSender(), displayName: content.displayname, }); } else if (prevContent.displayname && !content.displayname) { - return _t('%(senderName)s removed their display name (%(oldDisplayName)s).', { + return () => _t('%(senderName)s removed their display name (%(oldDisplayName)s).', { senderName, oldDisplayName: prevContent.displayname, }); } else if (prevContent.avatar_url && !content.avatar_url) { - return _t('%(senderName)s removed their profile picture.', {senderName}); + return () => _t('%(senderName)s removed their profile picture.', {senderName}); } else if (prevContent.avatar_url && content.avatar_url && prevContent.avatar_url !== content.avatar_url) { - return _t('%(senderName)s changed their profile picture.', {senderName}); + return () => _t('%(senderName)s changed their profile picture.', {senderName}); } else if (!prevContent.avatar_url && content.avatar_url) { - return _t('%(senderName)s set a profile picture.', {senderName}); + return () => _t('%(senderName)s set a profile picture.', {senderName}); } else if (SettingsStore.getValue("showHiddenEventsInTimeline")) { // This is a null rejoin, it will only be visible if the Labs option is enabled - return _t("%(senderName)s made no change.", {senderName}); + return () => _t("%(senderName)s made no change.", {senderName}); } else { - return ""; + return null; } } else { if (!ev.target) console.warn("Join message has no target! -- " + ev.getContent().state_key); - return _t('%(targetName)s joined the room.', {targetName}); + return () => _t('%(targetName)s joined the room.', {targetName}); } case 'leave': if (ev.getSender() === ev.getStateKey()) { if (prevContent.membership === "invite") { - return _t('%(targetName)s rejected the invitation.', {targetName}); + return () => _t('%(targetName)s rejected the invitation.', {targetName}); } else { - return _t('%(targetName)s left the room.', {targetName}); + return () => _t('%(targetName)s left the room.', {targetName}); } } else if (prevContent.membership === "ban") { - return _t('%(senderName)s unbanned %(targetName)s.', {senderName, targetName}); + return () => _t('%(senderName)s unbanned %(targetName)s.', {senderName, targetName}); } else if (prevContent.membership === "invite") { - return _t('%(senderName)s withdrew %(targetName)s\'s invitation.', { + return () => _t('%(senderName)s withdrew %(targetName)s\'s invitation.', { senderName, targetName, - }) + ' ' + reason; + }) + ' ' + getReason(); } else if (prevContent.membership === "join") { - return _t('%(senderName)s kicked %(targetName)s.', {senderName, targetName}) + ' ' + reason; + return () => _t('%(senderName)s kicked %(targetName)s.', {senderName, targetName}) + ' ' + getReason(); } else { - return ""; + return null; } } } function textForTopicEvent(ev) { const senderDisplayName = ev.sender && ev.sender.name ? ev.sender.name : ev.getSender(); - return _t('%(senderDisplayName)s changed the topic to "%(topic)s".', { + return () => _t('%(senderDisplayName)s changed the topic to "%(topic)s".', { senderDisplayName, topic: ev.getContent().topic, }); @@ -115,16 +119,16 @@ function textForRoomNameEvent(ev) { const senderDisplayName = ev.sender && ev.sender.name ? ev.sender.name : ev.getSender(); if (!ev.getContent().name || ev.getContent().name.trim().length === 0) { - return _t('%(senderDisplayName)s removed the room name.', {senderDisplayName}); + return () => _t('%(senderDisplayName)s removed the room name.', {senderDisplayName}); } if (ev.getPrevContent().name) { - return _t('%(senderDisplayName)s changed the room name from %(oldRoomName)s to %(newRoomName)s.', { + return () => _t('%(senderDisplayName)s changed the room name from %(oldRoomName)s to %(newRoomName)s.', { senderDisplayName, oldRoomName: ev.getPrevContent().name, newRoomName: ev.getContent().name, }); } - return _t('%(senderDisplayName)s changed the room name to %(roomName)s.', { + return () => _t('%(senderDisplayName)s changed the room name to %(roomName)s.', { senderDisplayName, roomName: ev.getContent().name, }); @@ -132,19 +136,23 @@ function textForRoomNameEvent(ev) { function textForTombstoneEvent(ev) { const senderDisplayName = ev.sender && ev.sender.name ? ev.sender.name : ev.getSender(); - return _t('%(senderDisplayName)s upgraded this room.', {senderDisplayName}); + return () => _t('%(senderDisplayName)s upgraded this room.', {senderDisplayName}); } function textForJoinRulesEvent(ev) { const senderDisplayName = ev.sender && ev.sender.name ? ev.sender.name : ev.getSender(); switch (ev.getContent().join_rule) { case "public": - return _t('%(senderDisplayName)s made the room public to whoever knows the link.', {senderDisplayName}); + return () => _t('%(senderDisplayName)s made the room public to whoever knows the link.', { + senderDisplayName, + }); case "invite": - return _t('%(senderDisplayName)s made the room invite only.', {senderDisplayName}); + return () => _t('%(senderDisplayName)s made the room invite only.', { + senderDisplayName, + }); default: // The spec supports "knock" and "private", however nothing implements these. - return _t('%(senderDisplayName)s changed the join rule to %(rule)s', { + return () => _t('%(senderDisplayName)s changed the join rule to %(rule)s', { senderDisplayName, rule: ev.getContent().join_rule, }); @@ -155,12 +163,12 @@ function textForGuestAccessEvent(ev) { const senderDisplayName = ev.sender && ev.sender.name ? ev.sender.name : ev.getSender(); switch (ev.getContent().guest_access) { case "can_join": - return _t('%(senderDisplayName)s has allowed guests to join the room.', {senderDisplayName}); + return () => _t('%(senderDisplayName)s has allowed guests to join the room.', {senderDisplayName}); case "forbidden": - return _t('%(senderDisplayName)s has prevented guests from joining the room.', {senderDisplayName}); + return () => _t('%(senderDisplayName)s has prevented guests from joining the room.', {senderDisplayName}); default: // There's no other options we can expect, however just for safety's sake we'll do this. - return _t('%(senderDisplayName)s changed guest access to %(rule)s', { + return () => _t('%(senderDisplayName)s changed guest access to %(rule)s', { senderDisplayName, rule: ev.getContent().guest_access, }); @@ -175,17 +183,17 @@ function textForRelatedGroupsEvent(ev) { const removed = prevGroups.filter((g) => !groups.includes(g)); if (added.length && !removed.length) { - return _t('%(senderDisplayName)s enabled flair for %(groups)s in this room.', { + return () => _t('%(senderDisplayName)s enabled flair for %(groups)s in this room.', { senderDisplayName, groups: added.join(', '), }); } else if (!added.length && removed.length) { - return _t('%(senderDisplayName)s disabled flair for %(groups)s in this room.', { + return () => _t('%(senderDisplayName)s disabled flair for %(groups)s in this room.', { senderDisplayName, groups: removed.join(', '), }); } else if (added.length && removed.length) { - return _t('%(senderDisplayName)s enabled flair for %(newGroups)s and disabled flair for ' + + return () => _t('%(senderDisplayName)s enabled flair for %(newGroups)s and disabled flair for ' + '%(oldGroups)s in this room.', { senderDisplayName, newGroups: added.join(', '), @@ -193,7 +201,7 @@ function textForRelatedGroupsEvent(ev) { }); } else { // Don't bother rendering this change (because there were no changes) - return ''; + return null; } } @@ -207,11 +215,11 @@ function textForServerACLEvent(ev) { allow_ip_literals: !(prevContent.allow_ip_literals === false), }; - let text = ""; + let getText = null; if (prev.deny.length === 0 && prev.allow.length === 0) { - text = _t("%(senderDisplayName)s set the server ACLs for this room.", {senderDisplayName}); + getText = () => _t("%(senderDisplayName)s set the server ACLs for this room.", {senderDisplayName}); } else { - text = _t("%(senderDisplayName)s changed the server ACLs for this room.", {senderDisplayName}); + getText = () => _t("%(senderDisplayName)s changed the server ACLs for this room.", {senderDisplayName}); } if (!Array.isArray(current.allow)) { @@ -220,21 +228,24 @@ function textForServerACLEvent(ev) { // If we know for sure everyone is banned, mark the room as obliterated if (current.allow.length === 0) { - return text + " " + _t("🎉 All servers are banned from participating! This room can no longer be used."); + return () => getText() + " " + + _t("🎉 All servers are banned from participating! This room can no longer be used."); } - return text; + return getText; } function textForMessageEvent(ev) { - const senderDisplayName = ev.sender && ev.sender.name ? ev.sender.name : ev.getSender(); - let message = senderDisplayName + ': ' + ev.getContent().body; - if (ev.getContent().msgtype === "m.emote") { - message = "* " + senderDisplayName + " " + message; - } else if (ev.getContent().msgtype === "m.image") { - message = _t('%(senderDisplayName)s sent an image.', {senderDisplayName}); - } - return message; + return () => { + const senderDisplayName = ev.sender && ev.sender.name ? ev.sender.name : ev.getSender(); + let message = senderDisplayName + ': ' + ev.getContent().body; + if (ev.getContent().msgtype === "m.emote") { + message = "* " + senderDisplayName + " " + message; + } else if (ev.getContent().msgtype === "m.image") { + message = _t('%(senderDisplayName)s sent an image.', {senderDisplayName}); + } + return message; + }; } function textForCanonicalAliasEvent(ev) { @@ -248,96 +259,100 @@ function textForCanonicalAliasEvent(ev) { if (!removedAltAliases.length && !addedAltAliases.length) { if (newAlias) { - return _t('%(senderName)s set the main address for this room to %(address)s.', { + return () => _t('%(senderName)s set the main address for this room to %(address)s.', { senderName: senderName, address: ev.getContent().alias, }); } else if (oldAlias) { - return _t('%(senderName)s removed the main address for this room.', { + return () => _t('%(senderName)s removed the main address for this room.', { senderName: senderName, }); } } else if (newAlias === oldAlias) { if (addedAltAliases.length && !removedAltAliases.length) { - return _t('%(senderName)s added the alternative addresses %(addresses)s for this room.', { + return () => _t('%(senderName)s added the alternative addresses %(addresses)s for this room.', { senderName: senderName, addresses: addedAltAliases.join(", "), count: addedAltAliases.length, }); } if (removedAltAliases.length && !addedAltAliases.length) { - return _t('%(senderName)s removed the alternative addresses %(addresses)s for this room.', { + return () => _t('%(senderName)s removed the alternative addresses %(addresses)s for this room.', { senderName: senderName, addresses: removedAltAliases.join(", "), count: removedAltAliases.length, }); } if (removedAltAliases.length && addedAltAliases.length) { - return _t('%(senderName)s changed the alternative addresses for this room.', { + return () => _t('%(senderName)s changed the alternative addresses for this room.', { senderName: senderName, }); } } else { // both alias and alt_aliases where modified - return _t('%(senderName)s changed the main and alternative addresses for this room.', { + return () => _t('%(senderName)s changed the main and alternative addresses for this room.', { senderName: senderName, }); } // in case there is no difference between the two events, // say something as we can't simply hide the tile from here - return _t('%(senderName)s changed the addresses for this room.', { + return () => _t('%(senderName)s changed the addresses for this room.', { senderName: senderName, }); } function textForCallAnswerEvent(event) { - const senderName = event.sender ? event.sender.name : _t('Someone'); - const supported = MatrixClientPeg.get().supportsVoip() ? '' : _t('(not supported by this browser)'); - return _t('%(senderName)s answered the call.', {senderName}) + ' ' + supported; + return () => { + const senderName = event.sender ? event.sender.name : _t('Someone'); + const supported = MatrixClientPeg.get().supportsVoip() ? '' : _t('(not supported by this browser)'); + return _t('%(senderName)s answered the call.', {senderName}) + ' ' + supported; + }; } function textForCallHangupEvent(event) { - const senderName = event.sender ? event.sender.name : _t('Someone'); + const getSenderName = () => event.sender ? event.sender.name : _t('Someone'); const eventContent = event.getContent(); - let reason = ""; + let getReason = () => ""; if (!MatrixClientPeg.get().supportsVoip()) { - reason = _t('(not supported by this browser)'); + getReason = () => _t('(not supported by this browser)'); } else if (eventContent.reason) { if (eventContent.reason === "ice_failed") { // We couldn't establish a connection at all - reason = _t('(could not connect media)'); + getReason = () => _t('(could not connect media)'); } else if (eventContent.reason === "ice_timeout") { // We established a connection but it died - reason = _t('(connection failed)'); + getReason = () => _t('(connection failed)'); } else if (eventContent.reason === "user_media_failed") { // The other side couldn't open capture devices - reason = _t("(their device couldn't start the camera / microphone)"); + getReason = () => _t("(their device couldn't start the camera / microphone)"); } else if (eventContent.reason === "unknown_error") { // An error code the other side doesn't have a way to express // (as opposed to an error code they gave but we don't know about, // in which case we show the error code) - reason = _t("(an error occurred)"); + getReason = () => _t("(an error occurred)"); } else if (eventContent.reason === "invite_timeout") { - reason = _t('(no answer)'); + getReason = () => _t('(no answer)'); } else if (eventContent.reason === "user hangup" || eventContent.reason === "user_hangup") { // workaround for https://github.com/vector-im/element-web/issues/5178 // it seems Android randomly sets a reason of "user hangup" which is // interpreted as an error code :( // https://github.com/vector-im/riot-android/issues/2623 // Also the correct hangup code as of VoIP v1 (with underscore) - reason = ''; + getReason = () => ''; } else { - reason = _t('(unknown failure: %(reason)s)', {reason: eventContent.reason}); + getReason = () => _t('(unknown failure: %(reason)s)', {reason: eventContent.reason}); } } - return _t('%(senderName)s ended the call.', {senderName}) + ' ' + reason; + return () => _t('%(senderName)s ended the call.', {senderName: getSenderName()}) + ' ' + getReason(); } function textForCallRejectEvent(event) { - const senderName = event.sender ? event.sender.name : _t('Someone'); - return _t('%(senderName)s declined the call.', {senderName}); + return () => { + const senderName = event.sender ? event.sender.name : _t('Someone'); + return _t('%(senderName)s declined the call.', {senderName}); + }; } function textForCallInviteEvent(event) { - const senderName = event.sender ? event.sender.name : _t('Someone'); + const getSenderName = () => event.sender ? event.sender.name : _t('Someone'); // FIXME: Find a better way to determine this from the event? let isVoice = true; if (event.getContent().offer && event.getContent().offer.sdp && @@ -350,13 +365,21 @@ function textForCallInviteEvent(event) { // can have a hard time translating those strings. In an effort to make translations easier // and more accurate, we break out the string-based variables to a couple booleans. if (isVoice && isSupported) { - return _t("%(senderName)s placed a voice call.", {senderName}); + return () => _t("%(senderName)s placed a voice call.", { + senderName: getSenderName(), + }); } else if (isVoice && !isSupported) { - return _t("%(senderName)s placed a voice call. (not supported by this browser)", {senderName}); + return () => _t("%(senderName)s placed a voice call. (not supported by this browser)", { + senderName: getSenderName(), + }); } else if (!isVoice && isSupported) { - return _t("%(senderName)s placed a video call.", {senderName}); + return () => _t("%(senderName)s placed a video call.", { + senderName: getSenderName(), + }); } else if (!isVoice && !isSupported) { - return _t("%(senderName)s placed a video call. (not supported by this browser)", {senderName}); + return () => _t("%(senderName)s placed a video call. (not supported by this browser)", { + senderName: getSenderName(), + }); } } @@ -364,14 +387,13 @@ function textForThreePidInviteEvent(event) { const senderName = event.sender ? event.sender.name : event.getSender(); if (!isValid3pidInvite(event)) { - const targetDisplayName = event.getPrevContent().display_name || _t("Someone"); - return _t('%(senderName)s revoked the invitation for %(targetDisplayName)s to join the room.', { + return () => _t('%(senderName)s revoked the invitation for %(targetDisplayName)s to join the room.', { senderName, - targetDisplayName, + targetDisplayName: event.getPrevContent().display_name || _t("Someone"), }); } - return _t('%(senderName)s sent an invitation to %(targetDisplayName)s to join the room.', { + return () => _t('%(senderName)s sent an invitation to %(targetDisplayName)s to join the room.', { senderName, targetDisplayName: event.getContent().display_name, }); @@ -381,17 +403,17 @@ function textForHistoryVisibilityEvent(event) { const senderName = event.sender ? event.sender.name : event.getSender(); switch (event.getContent().history_visibility) { case 'invited': - return _t('%(senderName)s made future room history visible to all room members, ' + return () => _t('%(senderName)s made future room history visible to all room members, ' + 'from the point they are invited.', {senderName}); case 'joined': - return _t('%(senderName)s made future room history visible to all room members, ' + return () => _t('%(senderName)s made future room history visible to all room members, ' + 'from the point they joined.', {senderName}); case 'shared': - return _t('%(senderName)s made future room history visible to all room members.', {senderName}); + return () => _t('%(senderName)s made future room history visible to all room members.', {senderName}); case 'world_readable': - return _t('%(senderName)s made future room history visible to anyone.', {senderName}); + return () => _t('%(senderName)s made future room history visible to anyone.', {senderName}); default: - return _t('%(senderName)s made future room history visible to unknown (%(visibility)s).', { + return () => _t('%(senderName)s made future room history visible to unknown (%(visibility)s).', { senderName, visibility: event.getContent().history_visibility, }); @@ -403,7 +425,7 @@ function textForPowerEvent(event) { const senderName = event.sender ? event.sender.name : event.getSender(); if (!event.getPrevContent() || !event.getPrevContent().users || !event.getContent() || !event.getContent().users) { - return ''; + return null; } const userDefault = event.getContent().users_default || 0; // Construct set of userIds @@ -418,35 +440,35 @@ function textForPowerEvent(event) { if (users.indexOf(userId) === -1) users.push(userId); }, ); - const diff = []; - // XXX: This is also surely broken for i18n + const diffs = []; users.forEach((userId) => { // Previous power level const from = event.getPrevContent().users[userId]; // Current power level const to = event.getContent().users[userId]; if (to !== from) { - diff.push( - _t('%(userId)s from %(fromPowerLevel)s to %(toPowerLevel)s', { - userId, - fromPowerLevel: Roles.textualPowerLevel(from, userDefault), - toPowerLevel: Roles.textualPowerLevel(to, userDefault), - }), - ); + diffs.push({ userId, from, to }); } }); - if (!diff.length) { - return ''; + if (!diffs.length) { + return null; } - return _t('%(senderName)s changed the power level of %(powerLevelDiffText)s.', { + // XXX: This is also surely broken for i18n + return () => _t('%(senderName)s changed the power level of %(powerLevelDiffText)s.', { senderName, - powerLevelDiffText: diff.join(", "), + powerLevelDiffText: diffs.map(diff => + _t('%(userId)s from %(fromPowerLevel)s to %(toPowerLevel)s', { + userId: diff.userId, + fromPowerLevel: Roles.textualPowerLevel(diff.from, userDefault), + toPowerLevel: Roles.textualPowerLevel(diff.to, userDefault), + }), + ).join(", "), }); } function textForPinnedEvent(event) { const senderName = event.sender ? event.sender.name : event.getSender(); - return _t("%(senderName)s changed the pinned messages for the room.", {senderName}); + return () => _t("%(senderName)s changed the pinned messages for the room.", {senderName}); } function textForWidgetEvent(event) { @@ -464,16 +486,16 @@ function textForWidgetEvent(event) { // equivalent to that condition. if (url) { if (prevUrl) { - return _t('%(widgetName)s widget modified by %(senderName)s', { + return () => _t('%(widgetName)s widget modified by %(senderName)s', { widgetName, senderName, }); } else { - return _t('%(widgetName)s widget added by %(senderName)s', { + return () => _t('%(widgetName)s widget added by %(senderName)s', { widgetName, senderName, }); } } else { - return _t('%(widgetName)s widget removed by %(senderName)s', { + return () => _t('%(widgetName)s widget removed by %(senderName)s', { widgetName, senderName, }); } @@ -481,7 +503,7 @@ function textForWidgetEvent(event) { function textForWidgetLayoutEvent(event) { const senderName = event.sender?.name || event.getSender(); - return _t("%(senderName)s has updated the widget layout", {senderName}); + return () => _t("%(senderName)s has updated the widget layout", {senderName}); } function textForMjolnirEvent(event) { @@ -492,74 +514,74 @@ function textForMjolnirEvent(event) { // Rule removed if (!entity) { if (USER_RULE_TYPES.includes(event.getType())) { - return _t("%(senderName)s removed the rule banning users matching %(glob)s", + return () => _t("%(senderName)s removed the rule banning users matching %(glob)s", {senderName, glob: prevEntity}); } else if (ROOM_RULE_TYPES.includes(event.getType())) { - return _t("%(senderName)s removed the rule banning rooms matching %(glob)s", + return () => _t("%(senderName)s removed the rule banning rooms matching %(glob)s", {senderName, glob: prevEntity}); } else if (SERVER_RULE_TYPES.includes(event.getType())) { - return _t("%(senderName)s removed the rule banning servers matching %(glob)s", + return () => _t("%(senderName)s removed the rule banning servers matching %(glob)s", {senderName, glob: prevEntity}); } // Unknown type. We'll say something, but we shouldn't end up here. - return _t("%(senderName)s removed a ban rule matching %(glob)s", {senderName, glob: prevEntity}); + return () => _t("%(senderName)s removed a ban rule matching %(glob)s", {senderName, glob: prevEntity}); } // Invalid rule - if (!recommendation || !reason) return _t(`%(senderName)s updated an invalid ban rule`, {senderName}); + if (!recommendation || !reason) return () => _t(`%(senderName)s updated an invalid ban rule`, {senderName}); // Rule updated if (entity === prevEntity) { if (USER_RULE_TYPES.includes(event.getType())) { - return _t("%(senderName)s updated the rule banning users matching %(glob)s for %(reason)s", + return () => _t("%(senderName)s updated the rule banning users matching %(glob)s for %(reason)s", {senderName, glob: entity, reason}); } else if (ROOM_RULE_TYPES.includes(event.getType())) { - return _t("%(senderName)s updated the rule banning rooms matching %(glob)s for %(reason)s", + return () => _t("%(senderName)s updated the rule banning rooms matching %(glob)s for %(reason)s", {senderName, glob: entity, reason}); } else if (SERVER_RULE_TYPES.includes(event.getType())) { - return _t("%(senderName)s updated the rule banning servers matching %(glob)s for %(reason)s", + return () => _t("%(senderName)s updated the rule banning servers matching %(glob)s for %(reason)s", {senderName, glob: entity, reason}); } // Unknown type. We'll say something but we shouldn't end up here. - return _t("%(senderName)s updated a ban rule matching %(glob)s for %(reason)s", + return () => _t("%(senderName)s updated a ban rule matching %(glob)s for %(reason)s", {senderName, glob: entity, reason}); } // New rule if (!prevEntity) { if (USER_RULE_TYPES.includes(event.getType())) { - return _t("%(senderName)s created a rule banning users matching %(glob)s for %(reason)s", + return () => _t("%(senderName)s created a rule banning users matching %(glob)s for %(reason)s", {senderName, glob: entity, reason}); } else if (ROOM_RULE_TYPES.includes(event.getType())) { - return _t("%(senderName)s created a rule banning rooms matching %(glob)s for %(reason)s", + return () => _t("%(senderName)s created a rule banning rooms matching %(glob)s for %(reason)s", {senderName, glob: entity, reason}); } else if (SERVER_RULE_TYPES.includes(event.getType())) { - return _t("%(senderName)s created a rule banning servers matching %(glob)s for %(reason)s", + return () => _t("%(senderName)s created a rule banning servers matching %(glob)s for %(reason)s", {senderName, glob: entity, reason}); } // Unknown type. We'll say something but we shouldn't end up here. - return _t("%(senderName)s created a ban rule matching %(glob)s for %(reason)s", + return () => _t("%(senderName)s created a ban rule matching %(glob)s for %(reason)s", {senderName, glob: entity, reason}); } // else the entity !== prevEntity - count as a removal & add if (USER_RULE_TYPES.includes(event.getType())) { - return _t( + return () => _t( "%(senderName)s changed a rule that was banning users matching %(oldGlob)s to matching " + "%(newGlob)s for %(reason)s", {senderName, oldGlob: prevEntity, newGlob: entity, reason}, ); } else if (ROOM_RULE_TYPES.includes(event.getType())) { - return _t( + return () => _t( "%(senderName)s changed a rule that was banning rooms matching %(oldGlob)s to matching " + "%(newGlob)s for %(reason)s", {senderName, oldGlob: prevEntity, newGlob: entity, reason}, ); } else if (SERVER_RULE_TYPES.includes(event.getType())) { - return _t( + return () => _t( "%(senderName)s changed a rule that was banning servers matching %(oldGlob)s to matching " + "%(newGlob)s for %(reason)s", {senderName, oldGlob: prevEntity, newGlob: entity, reason}, @@ -567,7 +589,7 @@ function textForMjolnirEvent(event) { } // Unknown type. We'll say something but we shouldn't end up here. - return _t("%(senderName)s updated a ban rule that was matching %(oldGlob)s to matching %(newGlob)s " + + return () => _t("%(senderName)s updated a ban rule that was matching %(oldGlob)s to matching %(newGlob)s " + "for %(reason)s", {senderName, oldGlob: prevEntity, newGlob: entity, reason}); } @@ -604,8 +626,12 @@ for (const evType of ALL_RULE_TYPES) { stateHandlers[evType] = textForMjolnirEvent; } +export function hasText(ev) { + const handler = (ev.isState() ? stateHandlers : handlers)[ev.getType()]; + return Boolean(handler?.(ev)); +} + export function textForEvent(ev) { const handler = (ev.isState() ? stateHandlers : handlers)[ev.getType()]; - if (handler) return handler(ev); - return ''; + return handler?.(ev)?.() || ''; } diff --git a/src/components/structures/MessagePanel.js b/src/components/structures/MessagePanel.js index bca62e7b2f..d040944833 100644 --- a/src/components/structures/MessagePanel.js +++ b/src/components/structures/MessagePanel.js @@ -30,7 +30,7 @@ import RoomContext from "../../contexts/RoomContext"; import {Layout, LayoutPropType} from "../../settings/Layout"; import {_t} from "../../languageHandler"; import {haveTileForEvent} from "../views/rooms/EventTile"; -import {textForEvent} from "../../TextForEvent"; +import {hasText} from "../../TextForEvent"; import IRCTimelineProfileResizer from "../views/elements/IRCTimelineProfileResizer"; import DMRoomMap from "../../utils/DMRoomMap"; import NewRoomIntro from "../views/rooms/NewRoomIntro"; @@ -1180,11 +1180,8 @@ class MemberGrouper { add(ev) { if (ev.getType() === 'm.room.member') { - // We'll just double check that it's worth our time to do so, through an - // ugly hack. If textForEvent returns something, we should group it for - // rendering but if it doesn't then we'll exclude it. - const renderText = textForEvent(ev); - if (!renderText || renderText.trim().length === 0) return; // quietly ignore + // We can ignore any events that don't actually have a message to display + if (!hasText(ev)) return; } this.readMarker = this.readMarker || this.panel._readMarkerForEvent( ev.getId(), diff --git a/src/components/views/rooms/EventTile.tsx b/src/components/views/rooms/EventTile.tsx index 8cec067c39..fda2906f07 100644 --- a/src/components/views/rooms/EventTile.tsx +++ b/src/components/views/rooms/EventTile.tsx @@ -25,7 +25,7 @@ import { RoomMember } from "matrix-js-sdk/src/models/room-member"; import ReplyThread from "../elements/ReplyThread"; import { _t } from '../../../languageHandler'; -import * as TextForEvent from "../../../TextForEvent"; +import { hasText } from "../../../TextForEvent"; import * as sdk from "../../../index"; import dis from '../../../dispatcher/dispatcher'; import SettingsStore from "../../../settings/SettingsStore"; @@ -1200,7 +1200,7 @@ export function haveTileForEvent(e) { const handler = getHandlerTile(e); if (handler === undefined) return false; if (handler === 'messages.TextualEvent') { - return TextForEvent.textForEvent(e) !== ''; + return hasText(e); } else if (handler === 'messages.RoomCreate') { return Boolean(e.getContent()['predecessor']); } else { From 1e574307d0a74c498429c6e2a22f07c61369550b Mon Sep 17 00:00:00 2001 From: Robin Townsend Date: Sun, 6 Jun 2021 23:08:47 -0400 Subject: [PATCH 16/52] Cache lowBandwidth setting to speed up BaseAvatar Signed-off-by: Robin Townsend --- src/components/structures/RoomView.tsx | 5 +++++ src/components/views/avatars/BaseAvatar.tsx | 15 +++++++++++---- src/contexts/RoomContext.ts | 1 + 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/components/structures/RoomView.tsx b/src/components/structures/RoomView.tsx index b80d909a94..e4dc90f141 100644 --- a/src/components/structures/RoomView.tsx +++ b/src/components/structures/RoomView.tsx @@ -182,6 +182,7 @@ export interface IState { canReact: boolean; canReply: boolean; layout: Layout; + lowBandwidth: boolean; showReadReceipts: boolean; showRedactions: boolean; showJoinLeaves: boolean; @@ -244,6 +245,7 @@ export default class RoomView extends React.Component { canReact: false, canReply: false, layout: SettingsStore.getValue("layout"), + lowBandwidth: SettingsStore.getValue("lowBandwidth"), showReadReceipts: true, showRedactions: true, showJoinLeaves: true, @@ -279,6 +281,9 @@ export default class RoomView extends React.Component { SettingsStore.watchSetting("layout", null, () => this.setState({ layout: SettingsStore.getValue("layout") }), ), + SettingsStore.watchSetting("lowBandwidth", null, () => + this.setState({ lowBandwidth: SettingsStore.getValue("lowBandwidth") }), + ), ]; } diff --git a/src/components/views/avatars/BaseAvatar.tsx b/src/components/views/avatars/BaseAvatar.tsx index 8ce05e0a55..6949c14636 100644 --- a/src/components/views/avatars/BaseAvatar.tsx +++ b/src/components/views/avatars/BaseAvatar.tsx @@ -22,6 +22,7 @@ import classNames from 'classnames'; import * as AvatarLogic from '../../../Avatar'; import SettingsStore from "../../../settings/SettingsStore"; import AccessibleButton from '../elements/AccessibleButton'; +import RoomContext from "../../../contexts/RoomContext"; import MatrixClientContext from "../../../contexts/MatrixClientContext"; import {useEventEmitter} from "../../../hooks/useEventEmitter"; import {toPx} from "../../../utils/units"; @@ -44,12 +45,12 @@ interface IProps { className?: string; } -const calculateUrls = (url, urls) => { +const calculateUrls = (url, urls, lowBandwidth) => { // work out the full set of urls to try to load. This is formed like so: // imageUrls: [ props.url, ...props.urls ] let _urls = []; - if (!SettingsStore.getValue("lowBandwidth")) { + if (!lowBandwidth) { _urls = urls || []; if (url) { @@ -63,7 +64,13 @@ const calculateUrls = (url, urls) => { }; const useImageUrl = ({url, urls}): [string, () => void] => { - const [imageUrls, setUrls] = useState(calculateUrls(url, urls)); + // Since this is a hot code path and the settings store can be slow, we + // use the cached lowBandwidth value from the room context if it exists + const roomContext = useContext(RoomContext); + const lowBandwidth = roomContext ? + roomContext.lowBandwidth : SettingsStore.getValue("lowBandwidth"); + + const [imageUrls, setUrls] = useState(calculateUrls(url, urls, lowBandwidth)); const [urlsIndex, setIndex] = useState(0); const onError = useCallback(() => { @@ -71,7 +78,7 @@ const useImageUrl = ({url, urls}): [string, () => void] => { }, []); useEffect(() => { - setUrls(calculateUrls(url, urls)); + setUrls(calculateUrls(url, urls, lowBandwidth)); setIndex(0); }, [url, JSON.stringify(urls)]); // eslint-disable-line react-hooks/exhaustive-deps diff --git a/src/contexts/RoomContext.ts b/src/contexts/RoomContext.ts index 1efa1c03e7..3464f952a6 100644 --- a/src/contexts/RoomContext.ts +++ b/src/contexts/RoomContext.ts @@ -40,6 +40,7 @@ const RoomContext = createContext({ canReact: false, canReply: false, layout: Layout.Group, + lowBandwidth: false, showReadReceipts: true, showRedactions: true, showJoinLeaves: true, From 21ff1f521fa191308852429214d2697ee35adda8 Mon Sep 17 00:00:00 2001 From: Robin Townsend Date: Sun, 6 Jun 2021 23:14:26 -0400 Subject: [PATCH 17/52] Fix i18n strings Signed-off-by: Robin Townsend --- src/i18n/strings/en_EN.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 9e85ea28c8..5108ea7fe2 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -556,8 +556,8 @@ "%(senderName)s made future room history visible to all room members.": "%(senderName)s made future room history visible to all room members.", "%(senderName)s made future room history visible to anyone.": "%(senderName)s made future room history visible to anyone.", "%(senderName)s made future room history visible to unknown (%(visibility)s).": "%(senderName)s made future room history visible to unknown (%(visibility)s).", - "%(userId)s from %(fromPowerLevel)s to %(toPowerLevel)s": "%(userId)s from %(fromPowerLevel)s to %(toPowerLevel)s", "%(senderName)s changed the power level of %(powerLevelDiffText)s.": "%(senderName)s changed the power level of %(powerLevelDiffText)s.", + "%(userId)s from %(fromPowerLevel)s to %(toPowerLevel)s": "%(userId)s from %(fromPowerLevel)s to %(toPowerLevel)s", "%(senderName)s changed the pinned messages for the room.": "%(senderName)s changed the pinned messages for the room.", "%(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", From 4185185cdc9e97231ca8f25ca4c9a5041429225c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Mon, 7 Jun 2021 19:42:48 +0200 Subject: [PATCH 18/52] Reduce number of DOM elements MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/views/messages/SenderProfile.tsx | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/components/views/messages/SenderProfile.tsx b/src/components/views/messages/SenderProfile.tsx index a51f50525d..df75de34ba 100644 --- a/src/components/views/messages/SenderProfile.tsx +++ b/src/components/views/messages/SenderProfile.tsx @@ -146,12 +146,10 @@ export default class SenderProfile extends React.Component { } return ( -
-
- { displayNameElement } - { mxidElement } - { flair } -
+
+ { displayNameElement } + { mxidElement } + { flair }
); } From b39c615abc6f2787decfcd5ed4c38ae6ea01d072 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Mon, 7 Jun 2021 19:44:58 +0200 Subject: [PATCH 19/52] Reorder MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- .../views/messages/SenderProfile.tsx | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/components/views/messages/SenderProfile.tsx b/src/components/views/messages/SenderProfile.tsx index df75de34ba..872dc132b9 100644 --- a/src/components/views/messages/SenderProfile.tsx +++ b/src/components/views/messages/SenderProfile.tsx @@ -118,18 +118,6 @@ export default class SenderProfile extends React.Component { return null; // emote message must include the name so don't duplicate it } - let flair; - if (this.props.enableFlair) { - const displayedGroups = this._getDisplayedGroups( - this.state.userGroups, this.state.relatedGroups, - ); - - flair = ; - } - const displayNameElement = ( { displayName } @@ -145,6 +133,18 @@ export default class SenderProfile extends React.Component { ); } + let flair; + if (this.props.enableFlair) { + const displayedGroups = this._getDisplayedGroups( + this.state.userGroups, this.state.relatedGroups, + ); + + flair = ; + } + return (
{ displayNameElement } From 5c62728d13ac23721c7747c10929e38fa4f2458b Mon Sep 17 00:00:00 2001 From: Germain Souquet Date: Tue, 8 Jun 2021 12:52:32 +0100 Subject: [PATCH 20/52] Migrate end to end tests pipeline to GitHub Actions --- .github/workflows/end-to-end-tests.yml | 32 ++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 .github/workflows/end-to-end-tests.yml diff --git a/.github/workflows/end-to-end-tests.yml b/.github/workflows/end-to-end-tests.yml new file mode 100644 index 0000000000..271ea06529 --- /dev/null +++ b/.github/workflows/end-to-end-tests.yml @@ -0,0 +1,32 @@ +name: "end-to-end_tests" +on: [push] +jobs: + "end-to-end_tests": + runs-on: ubuntu-latest + defaults: + run: + shell: bash + working-directory: ${{ github.workspace }} + container: + image: vectorim/element-web-ci-e2etests-env:latest + env: + CI_PACKAGE: true + GITHUB_HEAD_REF: ${{ github.head_ref }} + GITHUB_BASE_REF: ${{ github.base_ref }} + steps: + - name: Checkout code + uses: actions/checkout@v2 + - name: End-to-End tests + run: ./scripts/ci/end-to-end-tests.sh + - name: Archive logs + uses: actions/upload-artifact@v2 + with: + path: | + test/end-to-end-tests/logs/**/* + test/end-to-end-tests/synapse/installations/consent/homeserver.log + retention-days: 14 + - name: Archive performance benchmark + uses: actions/upload-artifact@v2 + with: + name: performance-entries.json + path: test/end-to-end-tests/performance-entries.json From 90982f9b3fd1345c5af5e66a46986dd11e12dd2a Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 8 Jun 2021 17:31:08 +0100 Subject: [PATCH 21/52] Fix upgrade to element home button in top left menu --- src/components/structures/HostSignupAction.tsx | 5 ++++- src/components/structures/UserMenu.tsx | 4 +--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/components/structures/HostSignupAction.tsx b/src/components/structures/HostSignupAction.tsx index 769775d549..46e158d6c8 100644 --- a/src/components/structures/HostSignupAction.tsx +++ b/src/components/structures/HostSignupAction.tsx @@ -24,13 +24,16 @@ import { HostSignupStore } from "../../stores/HostSignupStore"; import SdkConfig from "../../SdkConfig"; import {replaceableComponent} from "../../utils/replaceableComponent"; -interface IProps {} +interface IProps { + onClick?(): void; +} interface IState {} @replaceableComponent("structures.HostSignupAction") export default class HostSignupAction extends React.PureComponent { private openDialog = async () => { + this.props.onClick?.(); await HostSignupStore.instance.setHostSignupActive(true); } diff --git a/src/components/structures/UserMenu.tsx b/src/components/structures/UserMenu.tsx index fb4829f879..6a449cf1a2 100644 --- a/src/components/structures/UserMenu.tsx +++ b/src/components/structures/UserMenu.tsx @@ -366,9 +366,7 @@ export default class UserMenu extends React.Component { const mxDomain = MatrixClientPeg.get().getDomain(); const validDomains = hostSignupDomains.filter(d => (d === mxDomain || mxDomain.endsWith(`.${d}`))); if (!hostSignupConfig.domains || validDomains.length > 0) { - topSection =
- -
; + topSection = ; } } } From 7155c033f23048acab1c864e758bfed2e3344b93 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 8 Jun 2021 19:20:10 +0000 Subject: [PATCH 22/52] Bump trim-newlines from 3.0.0 to 3.0.1 Bumps [trim-newlines](https://github.com/sindresorhus/trim-newlines) from 3.0.0 to 3.0.1. - [Release notes](https://github.com/sindresorhus/trim-newlines/releases) - [Commits](https://github.com/sindresorhus/trim-newlines/commits) --- updated-dependencies: - dependency-name: trim-newlines dependency-type: indirect ... Signed-off-by: dependabot[bot] --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 97179550c9..21243f4f58 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7969,9 +7969,9 @@ tree-kill@^1.2.2: integrity sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A== trim-newlines@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-3.0.0.tgz#79726304a6a898aa8373427298d54c2ee8b1cb30" - integrity sha512-C4+gOpvmxaSMKuEf9Qc134F1ZuOHVXKRbtEflf4NTtuuJDEIJ9p5PXsalL8SkeRw+qit1Mo+yuvMPAKwWg/1hA== + version "3.0.1" + resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-3.0.1.tgz#260a5d962d8b752425b32f3a7db0dcacd176c144" + integrity sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw== trough@^1.0.0: version "1.0.5" From dbb8aa47dcbaf0558607e705727c9c8e86bc12a3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 8 Jun 2021 20:08:39 +0000 Subject: [PATCH 23/52] Bump css-what from 5.0.0 to 5.0.1 Bumps [css-what](https://github.com/fb55/css-what) from 5.0.0 to 5.0.1. - [Release notes](https://github.com/fb55/css-what/releases) - [Commits](https://github.com/fb55/css-what/compare/v5.0.0...v5.0.1) --- updated-dependencies: - dependency-name: css-what dependency-type: indirect ... Signed-off-by: dependabot[bot] --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 21243f4f58..7c12ac51d1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2721,9 +2721,9 @@ css-select@^4.1.2: nth-check "^2.0.0" css-what@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/css-what/-/css-what-5.0.0.tgz#f0bf4f8bac07582722346ab243f6a35b512cfc47" - integrity sha512-qxyKHQvgKwzwDWC/rGbT821eJalfupxYW2qbSJSAtdSTimsr/MlaGONoNLllaUPZWf8QnbcKM/kPVYUQuEKAFA== + version "5.0.1" + resolved "https://registry.yarnpkg.com/css-what/-/css-what-5.0.1.tgz#3efa820131f4669a8ac2408f9c32e7c7de9f4cad" + integrity sha512-FYDTSHb/7KXsWICVsxdmiExPjCfRC4qRFBdVwv7Ax9hMnvMmEjP9RfxTEZ3qPZGmADDn2vAKSo9UcN1jKVYscg== cssesc@^3.0.0: version "3.0.0" From a28f5754c7cd86d7b6d795c8fcfc6a7468c8e1e6 Mon Sep 17 00:00:00 2001 From: Robin Townsend Date: Tue, 8 Jun 2021 21:58:48 -0400 Subject: [PATCH 24/52] Convert TextForEvent to TypeScript Signed-off-by: Robin Townsend --- src/{TextForEvent.js => TextForEvent.ts} | 54 +++++++++++++----------- 1 file changed, 29 insertions(+), 25 deletions(-) rename src/{TextForEvent.js => TextForEvent.ts} (94%) diff --git a/src/TextForEvent.js b/src/TextForEvent.ts similarity index 94% rename from src/TextForEvent.js rename to src/TextForEvent.ts index 22ce0dd9cf..649c53664e 100644 --- a/src/TextForEvent.js +++ b/src/TextForEvent.ts @@ -25,7 +25,7 @@ import {WIDGET_LAYOUT_EVENT_TYPE} from "./stores/widgets/WidgetLayoutStore"; // any text to display at all. For this reason they return deferred values // to avoid the expense of looking up translations when they're not needed. -function textForMemberEvent(ev) { +function textForMemberEvent(ev): () => string | null { // XXX: SYJS-16 "sender is sometimes null for join messages" const senderName = ev.sender ? ev.sender.name : ev.getSender(); const targetName = ev.target ? ev.target.name : ev.getStateKey(); @@ -107,7 +107,7 @@ function textForMemberEvent(ev) { } } -function textForTopicEvent(ev) { +function textForTopicEvent(ev): () => string | null { const senderDisplayName = ev.sender && ev.sender.name ? ev.sender.name : ev.getSender(); return () => _t('%(senderDisplayName)s changed the topic to "%(topic)s".', { senderDisplayName, @@ -115,7 +115,7 @@ function textForTopicEvent(ev) { }); } -function textForRoomNameEvent(ev) { +function textForRoomNameEvent(ev): () => string | null { const senderDisplayName = ev.sender && ev.sender.name ? ev.sender.name : ev.getSender(); if (!ev.getContent().name || ev.getContent().name.trim().length === 0) { @@ -134,12 +134,12 @@ function textForRoomNameEvent(ev) { }); } -function textForTombstoneEvent(ev) { +function textForTombstoneEvent(ev): () => string | null { const senderDisplayName = ev.sender && ev.sender.name ? ev.sender.name : ev.getSender(); return () => _t('%(senderDisplayName)s upgraded this room.', {senderDisplayName}); } -function textForJoinRulesEvent(ev) { +function textForJoinRulesEvent(ev): () => string | null { const senderDisplayName = ev.sender && ev.sender.name ? ev.sender.name : ev.getSender(); switch (ev.getContent().join_rule) { case "public": @@ -159,7 +159,7 @@ function textForJoinRulesEvent(ev) { } } -function textForGuestAccessEvent(ev) { +function textForGuestAccessEvent(ev): () => string | null { const senderDisplayName = ev.sender && ev.sender.name ? ev.sender.name : ev.getSender(); switch (ev.getContent().guest_access) { case "can_join": @@ -175,7 +175,7 @@ function textForGuestAccessEvent(ev) { } } -function textForRelatedGroupsEvent(ev) { +function textForRelatedGroupsEvent(ev): () => string | null { const senderDisplayName = ev.sender && ev.sender.name ? ev.sender.name : ev.getSender(); const groups = ev.getContent().groups || []; const prevGroups = ev.getPrevContent().groups || []; @@ -205,7 +205,7 @@ function textForRelatedGroupsEvent(ev) { } } -function textForServerACLEvent(ev) { +function textForServerACLEvent(ev): () => string | null { const senderDisplayName = ev.sender && ev.sender.name ? ev.sender.name : ev.getSender(); const prevContent = ev.getPrevContent(); const current = ev.getContent(); @@ -235,7 +235,7 @@ function textForServerACLEvent(ev) { return getText; } -function textForMessageEvent(ev) { +function textForMessageEvent(ev): () => string | null { return () => { const senderDisplayName = ev.sender && ev.sender.name ? ev.sender.name : ev.getSender(); let message = senderDisplayName + ': ' + ev.getContent().body; @@ -248,7 +248,7 @@ function textForMessageEvent(ev) { }; } -function textForCanonicalAliasEvent(ev) { +function textForCanonicalAliasEvent(ev): () => string | null { const senderName = ev.sender && ev.sender.name ? ev.sender.name : ev.getSender(); const oldAlias = ev.getPrevContent().alias; const oldAltAliases = ev.getPrevContent().alt_aliases || []; @@ -299,7 +299,7 @@ function textForCanonicalAliasEvent(ev) { }); } -function textForCallAnswerEvent(event) { +function textForCallAnswerEvent(event): () => string | null { return () => { const senderName = event.sender ? event.sender.name : _t('Someone'); const supported = MatrixClientPeg.get().supportsVoip() ? '' : _t('(not supported by this browser)'); @@ -307,7 +307,7 @@ function textForCallAnswerEvent(event) { }; } -function textForCallHangupEvent(event) { +function textForCallHangupEvent(event): () => string | null { const getSenderName = () => event.sender ? event.sender.name : _t('Someone'); const eventContent = event.getContent(); let getReason = () => ""; @@ -344,14 +344,14 @@ function textForCallHangupEvent(event) { return () => _t('%(senderName)s ended the call.', {senderName: getSenderName()}) + ' ' + getReason(); } -function textForCallRejectEvent(event) { +function textForCallRejectEvent(event): () => string | null { return () => { const senderName = event.sender ? event.sender.name : _t('Someone'); return _t('%(senderName)s declined the call.', {senderName}); }; } -function textForCallInviteEvent(event) { +function textForCallInviteEvent(event): () => string | null { const getSenderName = () => event.sender ? event.sender.name : _t('Someone'); // FIXME: Find a better way to determine this from the event? let isVoice = true; @@ -383,7 +383,7 @@ function textForCallInviteEvent(event) { } } -function textForThreePidInviteEvent(event) { +function textForThreePidInviteEvent(event): () => string | null { const senderName = event.sender ? event.sender.name : event.getSender(); if (!isValid3pidInvite(event)) { @@ -399,7 +399,7 @@ function textForThreePidInviteEvent(event) { }); } -function textForHistoryVisibilityEvent(event) { +function textForHistoryVisibilityEvent(event): () => string | null { const senderName = event.sender ? event.sender.name : event.getSender(); switch (event.getContent().history_visibility) { case 'invited': @@ -421,7 +421,7 @@ function textForHistoryVisibilityEvent(event) { } // Currently will only display a change if a user's power level is changed -function textForPowerEvent(event) { +function textForPowerEvent(event): () => string | null { const senderName = event.sender ? event.sender.name : event.getSender(); if (!event.getPrevContent() || !event.getPrevContent().users || !event.getContent() || !event.getContent().users) { @@ -466,12 +466,12 @@ function textForPowerEvent(event) { }); } -function textForPinnedEvent(event) { +function textForPinnedEvent(event): () => string | null { const senderName = event.sender ? event.sender.name : event.getSender(); return () => _t("%(senderName)s changed the pinned messages for the room.", {senderName}); } -function textForWidgetEvent(event) { +function textForWidgetEvent(event): () => string | null { const senderName = event.getSender(); const {name: prevName, type: prevType, url: prevUrl} = event.getPrevContent(); const {name, type, url} = event.getContent() || {}; @@ -501,12 +501,12 @@ function textForWidgetEvent(event) { } } -function textForWidgetLayoutEvent(event) { +function textForWidgetLayoutEvent(event): () => string | null { const senderName = event.sender?.name || event.getSender(); return () => _t("%(senderName)s has updated the widget layout", {senderName}); } -function textForMjolnirEvent(event) { +function textForMjolnirEvent(event): () => string | null { const senderName = event.getSender(); const {entity: prevEntity} = event.getPrevContent(); const {entity, recommendation, reason} = event.getContent(); @@ -593,7 +593,11 @@ function textForMjolnirEvent(event) { "for %(reason)s", {senderName, oldGlob: prevEntity, newGlob: entity, reason}); } -const handlers = { +interface IHandlers { + [type: string]: (ev: any) => (() => string | null); +} + +const handlers: IHandlers = { 'm.room.message': textForMessageEvent, 'm.call.invite': textForCallInviteEvent, 'm.call.answer': textForCallAnswerEvent, @@ -601,7 +605,7 @@ const handlers = { 'm.call.reject': textForCallRejectEvent, }; -const stateHandlers = { +const stateHandlers: IHandlers = { 'm.room.canonical_alias': textForCanonicalAliasEvent, 'm.room.name': textForRoomNameEvent, 'm.room.topic': textForTopicEvent, @@ -626,12 +630,12 @@ for (const evType of ALL_RULE_TYPES) { stateHandlers[evType] = textForMjolnirEvent; } -export function hasText(ev) { +export function hasText(ev): boolean { const handler = (ev.isState() ? stateHandlers : handlers)[ev.getType()]; return Boolean(handler?.(ev)); } -export function textForEvent(ev) { +export function textForEvent(ev): string { const handler = (ev.isState() ? stateHandlers : handlers)[ev.getType()]; return handler?.(ev)?.() || ''; } From fccd71f82e9101ebbcab23d3ec04e6521958359f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Wed, 9 Jun 2021 06:23:51 +0200 Subject: [PATCH 25/52] Update styling based on review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/css/views/messages/_SenderProfile.scss | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/res/css/views/messages/_SenderProfile.scss b/res/css/views/messages/_SenderProfile.scss index 378d9e6f1a..7aaafa6ac3 100644 --- a/res/css/views/messages/_SenderProfile.scss +++ b/res/css/views/messages/_SenderProfile.scss @@ -20,8 +20,7 @@ limitations under the License. .mx_SenderProfile_mxid { font-weight: 600; - font-family: monospace; - font-size: 1rem; + font-size: 1.1rem; color: $event-timestamp-color; margin-left: 5px; } From c9b1d1aef06ece68109c46871bf633f823d41c7c Mon Sep 17 00:00:00 2001 From: Germain Date: Wed, 9 Jun 2021 10:38:28 +0100 Subject: [PATCH 26/52] Only run e2e tests on develop and PR targetting develop Co-authored-by: J. Ryan Stinnett --- .github/workflows/end-to-end-tests.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/end-to-end-tests.yml b/.github/workflows/end-to-end-tests.yml index 271ea06529..abf7813bbc 100644 --- a/.github/workflows/end-to-end-tests.yml +++ b/.github/workflows/end-to-end-tests.yml @@ -1,5 +1,9 @@ name: "end-to-end_tests" -on: [push] +on: + push: + branches: [develop] + pull_request: + branches: [develop] jobs: "end-to-end_tests": runs-on: ubuntu-latest From 85d1bb65c6046330c2fd732621d9e94ced82a072 Mon Sep 17 00:00:00 2001 From: Germain Souquet Date: Wed, 9 Jun 2021 11:40:20 +0100 Subject: [PATCH 27/52] Upgrade to React@17 --- package.json | 8 +- yarn.lock | 288 +++++++++++++++++++++++++++++++++------------------ 2 files changed, 193 insertions(+), 103 deletions(-) diff --git a/package.json b/package.json index 13047b69cf..ab855b84be 100644 --- a/package.json +++ b/package.json @@ -90,9 +90,9 @@ "qrcode": "^1.4.4", "qs": "^6.9.6", "re-resizable": "^6.9.0", - "react": "^16.14.0", + "react": "^17.0.2", "react-beautiful-dnd": "^4.0.1", - "react-dom": "^16.14.0", + "react-dom": "^17.0.2", "react-focus-lock": "^2.5.0", "react-transition-group": "^4.4.1", "resize-observer-polyfill": "^1.5.1", @@ -147,7 +147,7 @@ "chokidar": "^3.5.1", "concurrently": "^5.3.0", "enzyme": "^3.11.0", - "enzyme-adapter-react-16": "^1.15.6", + "@wojtekmaj/enzyme-adapter-react-17": "^0.6.1", "eslint": "7.18.0", "eslint-config-matrix-org": "^0.2.0", "eslint-plugin-babel": "^5.3.1", @@ -162,7 +162,7 @@ "matrix-mock-request": "^1.2.3", "matrix-react-test-utils": "^0.2.2", "matrix-web-i18n": "github:matrix-org/matrix-web-i18n", - "react-test-renderer": "^16.14.0", + "react-test-renderer": "^17.0.2", "rimraf": "^3.0.2", "stylelint": "^13.9.0", "stylelint-config-standard": "^20.0.0", diff --git a/yarn.lock b/yarn.lock index 0ff235a660..32480a156f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1752,6 +1752,31 @@ "@typescript-eslint/types" "4.14.0" eslint-visitor-keys "^2.0.0" +"@wojtekmaj/enzyme-adapter-react-17@^0.6.1": + version "0.6.1" + resolved "https://registry.yarnpkg.com/@wojtekmaj/enzyme-adapter-react-17/-/enzyme-adapter-react-17-0.6.1.tgz#28caa37118c183e5f13c4dfb68cc32cde828ecbc" + integrity sha512-xgPfzLVpN0epIHeZofahwr5qwpukEDNAbrufgeDWN6vZPtfblGCC+OZG5TlfK+A6ePVy8sBkD8S2X4tO17JKjg== + dependencies: + "@wojtekmaj/enzyme-adapter-utils" "^0.1.0" + enzyme-shallow-equal "^1.0.0" + has "^1.0.0" + object.assign "^4.1.0" + object.values "^1.1.0" + prop-types "^15.7.0" + react-is "^17.0.0" + react-test-renderer "^17.0.0" + +"@wojtekmaj/enzyme-adapter-utils@^0.1.0": + version "0.1.0" + resolved "https://registry.yarnpkg.com/@wojtekmaj/enzyme-adapter-utils/-/enzyme-adapter-utils-0.1.0.tgz#3a2a3db756111d53357e2f119a1612a969ab8c38" + integrity sha512-EYK/Vy0Y1ap0jH2UNQjOKtR/7HWkbEq8N+cwC5+yDf+Mwp5uu7j4Qg70RmWuzsA35DGGwgkop6m4pQsGwNOF2A== + dependencies: + function.prototype.name "^1.1.0" + has "^1.0.0" + object.assign "^4.1.0" + object.fromentries "^2.0.0" + prop-types "^15.7.0" + abab@^2.0.3: version "2.0.5" resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.5.tgz#c0b678fb32d60fc1219c784d6a826fe385aeb79a" @@ -1780,21 +1805,6 @@ acorn@^7.1.1, acorn@^7.4.0: resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.4.1.tgz#feaed255973d2e77555b83dbc08851a6c63520fa" integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== -airbnb-prop-types@^2.16.0: - version "2.16.0" - resolved "https://registry.yarnpkg.com/airbnb-prop-types/-/airbnb-prop-types-2.16.0.tgz#b96274cefa1abb14f623f804173ee97c13971dc2" - integrity sha512-7WHOFolP/6cS96PhKNrslCLMYAI8yB1Pp6u6XmxozQOiZbsI5ycglZr5cHhBFfuRcQQjzCMith5ZPZdYiJCxUg== - dependencies: - array.prototype.find "^2.1.1" - function.prototype.name "^1.1.2" - is-regex "^1.1.0" - object-is "^1.1.2" - object.assign "^4.1.0" - object.entries "^1.1.2" - prop-types "^15.7.2" - prop-types-exact "^1.2.0" - react-is "^16.13.1" - ajv@^6.10.0, ajv@^6.10.2, ajv@^6.12.3, ajv@^6.12.4: version "6.12.6" resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" @@ -1920,14 +1930,6 @@ array-unique@^0.3.2: resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg= -array.prototype.find@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/array.prototype.find/-/array.prototype.find-2.1.1.tgz#3baca26108ca7affb08db06bf0be6cb3115a969c" - integrity sha512-mi+MYNJYLTx2eNYy+Yh6raoQacCsNeeMUaspFPh9Y141lFSsWxxB8V9mM2ye+eqiRs917J6/pJ4M9ZPzenWckA== - dependencies: - define-properties "^1.1.3" - es-abstract "^1.17.4" - array.prototype.flat@^1.2.3: version "1.2.4" resolved "https://registry.yarnpkg.com/array.prototype.flat/-/array.prototype.flat-1.2.4.tgz#6ef638b43312bd401b4c6199fdec7e2dc9e9a123" @@ -3075,35 +3077,7 @@ entities@~2.0: resolved "https://registry.yarnpkg.com/entities/-/entities-2.0.3.tgz#5c487e5742ab93c15abb5da22759b8590ec03b7f" integrity sha512-MyoZ0jgnLvB2X3Lg5HqpFmn1kybDiIfEQmKzTb5apr51Rb+T3KdmMiqa70T+bhGnyv7bQ6WMj2QMHpGMmlrUYQ== -enzyme-adapter-react-16@^1.15.6: - version "1.15.6" - resolved "https://registry.yarnpkg.com/enzyme-adapter-react-16/-/enzyme-adapter-react-16-1.15.6.tgz#fd677a658d62661ac5afd7f7f541f141f8085901" - integrity sha512-yFlVJCXh8T+mcQo8M6my9sPgeGzj85HSHi6Apgf1Cvq/7EL/J9+1JoJmJsRxZgyTvPMAqOEpRSu/Ii/ZpyOk0g== - dependencies: - enzyme-adapter-utils "^1.14.0" - enzyme-shallow-equal "^1.0.4" - has "^1.0.3" - object.assign "^4.1.2" - object.values "^1.1.2" - prop-types "^15.7.2" - react-is "^16.13.1" - react-test-renderer "^16.0.0-0" - semver "^5.7.0" - -enzyme-adapter-utils@^1.14.0: - version "1.14.0" - resolved "https://registry.yarnpkg.com/enzyme-adapter-utils/-/enzyme-adapter-utils-1.14.0.tgz#afbb0485e8033aa50c744efb5f5711e64fbf1ad0" - integrity sha512-F/z/7SeLt+reKFcb7597IThpDp0bmzcH1E9Oabqv+o01cID2/YInlqHbFl7HzWBl4h3OdZYedtwNDOmSKkk0bg== - dependencies: - airbnb-prop-types "^2.16.0" - function.prototype.name "^1.1.3" - has "^1.0.3" - object.assign "^4.1.2" - object.fromentries "^2.0.3" - prop-types "^15.7.2" - semver "^5.7.1" - -enzyme-shallow-equal@^1.0.1, enzyme-shallow-equal@^1.0.4: +enzyme-shallow-equal@^1.0.0, enzyme-shallow-equal@^1.0.1: version "1.0.4" resolved "https://registry.yarnpkg.com/enzyme-shallow-equal/-/enzyme-shallow-equal-1.0.4.tgz#b9256cb25a5f430f9bfe073a84808c1d74fced2e" integrity sha512-MttIwB8kKxypwHvRynuC3ahyNc+cFbR8mjVIltnmzQ0uKGqmsfO4bfBuLxb0beLNPhjblUEYvEbsg+VSygvF1Q== @@ -3146,7 +3120,7 @@ error-ex@^1.2.0, error-ex@^1.3.1: dependencies: is-arrayish "^0.2.1" -es-abstract@^1.17.0-next.1, es-abstract@^1.17.4: +es-abstract@^1.17.0-next.1: version "1.17.7" resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.17.7.tgz#a4de61b2f66989fc7421676c1cb9787573ace54c" integrity sha512-VBl/gnfcJ7OercKA9MVaegWsBHFjV492syMudcnQZvt/Dw8ezpcOHYZXa/J96O8vx+g4x65YKhxOwDUh63aS5g== @@ -3183,6 +3157,28 @@ es-abstract@^1.18.0-next.1: string.prototype.trimend "^1.0.3" string.prototype.trimstart "^1.0.3" +es-abstract@^1.18.0-next.2, es-abstract@^1.18.2: + version "1.18.3" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.18.3.tgz#25c4c3380a27aa203c44b2b685bba94da31b63e0" + integrity sha512-nQIr12dxV7SSxE6r6f1l3DtAeEYdsGpps13dR0TwJg1S8gyp4ZPgy3FZcHBgbiQqnoqSTb+oC+kO4UQ0C/J8vw== + dependencies: + call-bind "^1.0.2" + es-to-primitive "^1.2.1" + function-bind "^1.1.1" + get-intrinsic "^1.1.1" + has "^1.0.3" + has-symbols "^1.0.2" + is-callable "^1.2.3" + is-negative-zero "^2.0.1" + is-regex "^1.1.3" + is-string "^1.0.6" + object-inspect "^1.10.3" + object-keys "^1.1.1" + object.assign "^4.1.2" + string.prototype.trimend "^1.0.4" + string.prototype.trimstart "^1.0.4" + unbox-primitive "^1.0.1" + es-get-iterator@^1.0.1: version "1.1.1" resolved "https://registry.yarnpkg.com/es-get-iterator/-/es-get-iterator-1.1.1.tgz#b93ddd867af16d5118e00881396533c1c6647ad9" @@ -3965,7 +3961,17 @@ function-bind@^1.1.1: resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== -function.prototype.name@^1.1.2, function.prototype.name@^1.1.3: +function.prototype.name@^1.1.0: + version "1.1.4" + resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.4.tgz#e4ea839b9d3672ae99d0efd9f38d9191c5eaac83" + integrity sha512-iqy1pIotY/RmhdFZygSSlW0wko2yxkSCKqsuv4pr8QESohpYyG/Z7B/XXvPRKTJS//960rgguE5mSRUsDdaJrQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + es-abstract "^1.18.0-next.2" + functions-have-names "^1.2.2" + +function.prototype.name@^1.1.2: version "1.1.3" resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.3.tgz#0bb034bb308e7682826f215eb6b2ae64918847fe" integrity sha512-H51qkbNSp8mtkJt+nyW1gyStBiKZxfRqySNUR99ylq6BPXHKI4SEvIlTKp4odLfjRKJV04DFWMU3G/YRlQOsag== @@ -3980,7 +3986,7 @@ functional-red-black-tree@^1.0.1: resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= -functions-have-names@^1.2.0, functions-have-names@^1.2.1: +functions-have-names@^1.2.0, functions-have-names@^1.2.1, functions-have-names@^1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.2.tgz#98d93991c39da9361f8e50b337c4f6e41f120e21" integrity sha512-bLgc3asbWdwPbx2mNk2S49kmJCuQeu0nfmaOgbs8WIyzzkw3r4htszdIi9Q9EMezDPTYuJx2wvjZ/EwgAthpnA== @@ -4004,6 +4010,15 @@ get-intrinsic@^1.0.1, get-intrinsic@^1.0.2: has "^1.0.3" has-symbols "^1.0.1" +get-intrinsic@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.1.1.tgz#15f59f376f855c446963948f0d24cd3637b4abc6" + integrity sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q== + dependencies: + function-bind "^1.1.1" + has "^1.0.3" + has-symbols "^1.0.1" + get-package-type@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" @@ -4157,6 +4172,11 @@ hard-rejection@^2.1.0: resolved "https://registry.yarnpkg.com/hard-rejection/-/hard-rejection-2.1.0.tgz#1c6eda5c1685c63942766d79bb40ae773cecd883" integrity sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA== +has-bigints@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.1.tgz#64fe6acb020673e3b78db035a5af69aa9d07b113" + integrity sha512-LSBS2LjbNBTf6287JEbEzvJgftkF5qFkmCo9hDRpAzKhUOlJ+hx8dd4USs00SgsUNwc4617J9ki5YtEClM2ffA== + has-flag@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" @@ -4172,6 +4192,11 @@ has-symbols@^1.0.1: resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.1.tgz#9f5214758a44196c406d9bd76cebf81ec2dd31e8" integrity sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg== +has-symbols@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.2.tgz#165d3070c00309752a1236a479331e3ac56f1423" + integrity sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw== + has-value@^0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f" @@ -4203,7 +4228,7 @@ has-values@^1.0.0: is-number "^3.0.0" kind-of "^4.0.0" -has@^1.0.1, has@^1.0.3: +has@^1.0.0, has@^1.0.1, has@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796" integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw== @@ -4534,6 +4559,11 @@ is-callable@^1.0.4, is-callable@^1.1.4, is-callable@^1.1.5, is-callable@^1.2.2: resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.2.tgz#c7c6715cd22d4ddb48d3e19970223aceabb080d9" integrity sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA== +is-callable@^1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.3.tgz#8b1e0500b73a1d76c70487636f368e519de8db8e" + integrity sha512-J1DcMe8UYTBSrKezuIUTUwjXsho29693unXM2YhJUTR2txK/eG47bvNa/wipPFmZFgr/N6f1GA66dv0mEyTIyQ== + is-ci@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-2.0.0.tgz#6bc6334181810e04b5c22b3d589fdca55026404c" @@ -4736,13 +4766,21 @@ is-potential-custom-element-name@^1.0.0: resolved "https://registry.yarnpkg.com/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.0.tgz#0c52e54bcca391bb2c494b21e8626d7336c6e397" integrity sha1-DFLlS8yjkbssSUsh6GJtczbG45c= -is-regex@^1.0.3, is-regex@^1.0.4, is-regex@^1.0.5, is-regex@^1.1.0, is-regex@^1.1.1: +is-regex@^1.0.3, is-regex@^1.0.4, is-regex@^1.0.5, is-regex@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.1.tgz#c6f98aacc546f6cec5468a07b7b153ab564a57b9" integrity sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg== dependencies: has-symbols "^1.0.1" +is-regex@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.1.3.tgz#d029f9aff6448b93ebbe3f33dac71511fdcbef9f" + integrity sha512-qSVXFz28HM7y+IWX6vLCsexdlvzT1PJNFSBuaQLQ5o0IEw8UDYW6/2+eCMVyIsbM8CNLX2a/QWmSpyxYEHY7CQ== + dependencies: + call-bind "^1.0.2" + has-symbols "^1.0.2" + is-regexp@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/is-regexp/-/is-regexp-2.1.0.tgz#cd734a56864e23b956bf4e7c66c396a4c0b22c2d" @@ -4768,6 +4806,11 @@ is-string@^1.0.4, is-string@^1.0.5: resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.5.tgz#40493ed198ef3ff477b8c7f92f644ec82a5cd3a6" integrity sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ== +is-string@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.6.tgz#3fe5d5992fb0d93404f32584d4b0179a71b54a5f" + integrity sha512-2gdzbKUuqtQ3lYNrUTQYoClPhm7oQu4UdpSZMp1/DGgkHBT8E2Z1l0yMdb6D4zNAxwDiMv8MdulKROJGNl0Q0w== + is-subset@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/is-subset/-/is-subset-0.1.1.tgz#8a59117d932de1de00f245fcdd39ce43f1e939a6" @@ -6081,6 +6124,11 @@ object-inspect@^1.1.0, object-inspect@^1.7.0, object-inspect@^1.8.0, object-insp resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.9.0.tgz#c90521d74e1127b67266ded3394ad6116986533a" integrity sha512-i3Bp9iTqwhaLZBxGkRfo5ZbE07BQRT7MGu8+nNgwW9ItGp1TzCTw2DLEoWwjClxBjOFI/hWljTAmYGCEwmtnOw== +object-inspect@^1.10.3: + version "1.10.3" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.10.3.tgz#c2aa7d2d09f50c99375704f7a0adf24c5782d369" + integrity sha512-e5mCJlSH7poANfC8z8S9s9S2IN5/4Zb3aZ33f5s8YqoazCFzNLloLU8r5VCG+G7WoqLvAAZoVMcy3tp/3X0Plw== + object-is@^1.0.2, object-is@^1.1.2: version "1.1.4" resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.1.4.tgz#63d6c83c00a43f4cbc9434eb9757c8a5b8565068" @@ -6121,7 +6169,17 @@ object.entries@^1.1.0, object.entries@^1.1.1, object.entries@^1.1.2: es-abstract "^1.18.0-next.1" has "^1.0.3" -object.fromentries@^2.0.2, object.fromentries@^2.0.3: +object.fromentries@^2.0.0: + version "2.0.4" + resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.4.tgz#26e1ba5c4571c5c6f0890cef4473066456a120b8" + integrity sha512-EsFBshs5RUUpQEY1D4q/m59kMfz4YJvxuNCJcv/jWwOJr34EaVnG11ZrZa0UHB3wnzV1wx8m58T4hQL8IuNXlQ== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + es-abstract "^1.18.0-next.2" + has "^1.0.3" + +object.fromentries@^2.0.2: version "2.0.3" resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.3.tgz#13cefcffa702dc67750314a3305e8cb3fad1d072" integrity sha512-IDUSMXs6LOSJBWE++L0lzIbSqHl9KDCfff2x/JSEIDtEUavUnyMYC2ZGay/04Zq4UT8lvd4xNhU4/YHKibAOlw== @@ -6138,7 +6196,16 @@ object.pick@^1.3.0: dependencies: isobject "^3.0.1" -object.values@^1.1.1, object.values@^1.1.2: +object.values@^1.1.0: + version "1.1.4" + resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.4.tgz#0d273762833e816b693a637d30073e7051535b30" + integrity sha512-TnGo7j4XSnKQoK3MfvkzqKCi0nVe/D9I9IjwTNYdb/fxYHpjrluHVOgw0AF6jrRFGMPHdfuidR09tIDiIvnaSg== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + es-abstract "^1.18.2" + +object.values@^1.1.1: version "1.1.2" resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.2.tgz#7a2015e06fcb0f546bd652486ce8583a4731c731" integrity sha512-MYC0jvJopr8EK6dPBiO8Nb9mvjdypOachO5REGk6MXzujbBrAisKo3HmdEI6kZDL6fC31Mwee/5YbtMebixeag== @@ -6588,16 +6655,7 @@ prompts@^2.0.1: kleur "^3.0.3" sisteransi "^1.0.5" -prop-types-exact@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/prop-types-exact/-/prop-types-exact-1.2.0.tgz#825d6be46094663848237e3925a98c6e944e9869" - integrity sha512-K+Tk3Kd9V0odiXFP9fwDHUYRyvK3Nun3GVyPapSIs5OBkITAm15W0CPFD/YKTkMUAbc0b9CUwRQp2ybiBIq+eA== - dependencies: - has "^1.0.3" - object.assign "^4.1.0" - reflect.ownkeys "^0.2.0" - -prop-types@^15.5.8, prop-types@^15.6.0, prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.7.2: +prop-types@^15.5.8, prop-types@^15.6.0, prop-types@^15.6.1, prop-types@^15.6.2, prop-types@^15.7.0, prop-types@^15.7.2: version "15.7.2" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.7.2.tgz#52c41e75b8c87e72b9d9360e0206b99dcbffa6c5" integrity sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ== @@ -6729,15 +6787,14 @@ react-clientside-effect@^1.2.2: dependencies: "@babel/runtime" "^7.0.0" -react-dom@^16.14.0: - version "16.14.0" - resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.14.0.tgz#7ad838ec29a777fb3c75c3a190f661cf92ab8b89" - integrity sha512-1gCeQXDLoIqMgqD3IO2Ah9bnf0w9kzhwN5q4FGnHZ67hBm9yePzB5JJAIQCc8x3pFnNlwFq4RidZggNAAkzWWw== +react-dom@^17.0.2: + version "17.0.2" + resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-17.0.2.tgz#ecffb6845e3ad8dbfcdc498f0d0a939736502c23" + integrity sha512-s4h96KtLDUQlsENhMn1ar8t2bEa+q/YAtj8pPPdIjPDGBDIVNsrD9aXNWqspUe6AzKCIG0C1HZZLqLV7qpOBGA== dependencies: loose-envify "^1.1.0" object-assign "^4.1.1" - prop-types "^15.6.2" - scheduler "^0.19.1" + scheduler "^0.20.2" react-focus-lock@^2.5.0: version "2.5.0" @@ -6751,7 +6808,12 @@ react-focus-lock@^2.5.0: use-callback-ref "^1.2.1" use-sidecar "^1.0.1" -react-is@^16.13.1, react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.1, react-is@^16.8.6: +"react-is@^16.12.0 || ^17.0.0", react-is@^17.0.0, react-is@^17.0.2: + version "17.0.2" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0" + integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== + +react-is@^16.6.0, react-is@^16.7.0, react-is@^16.8.1: version "16.13.1" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== @@ -6788,15 +6850,23 @@ react-redux@^5.0.6: react-is "^16.6.0" react-lifecycles-compat "^3.0.0" -react-test-renderer@^16.0.0-0, react-test-renderer@^16.14.0: - version "16.14.0" - resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-16.14.0.tgz#e98360087348e260c56d4fe2315e970480c228ae" - integrity sha512-L8yPjqPE5CZO6rKsKXRO/rVPiaCOy0tQQJbC+UjPNlobl5mad59lvPjwFsQHTvL03caVDIVr9x9/OSgDe6I5Eg== +react-shallow-renderer@^16.13.1: + version "16.14.1" + resolved "https://registry.yarnpkg.com/react-shallow-renderer/-/react-shallow-renderer-16.14.1.tgz#bf0d02df8a519a558fd9b8215442efa5c840e124" + integrity sha512-rkIMcQi01/+kxiTE9D3fdS959U1g7gs+/rborw++42m1O9FAQiNI/UNRZExVUoAOprn4umcXf+pFRou8i4zuBg== dependencies: object-assign "^4.1.1" - prop-types "^15.6.2" - react-is "^16.8.6" - scheduler "^0.19.1" + react-is "^16.12.0 || ^17.0.0" + +react-test-renderer@^17.0.0, react-test-renderer@^17.0.2: + version "17.0.2" + resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-17.0.2.tgz#4cd4ae5ef1ad5670fc0ef776e8cc7e1231d9866c" + integrity sha512-yaQ9cB89c17PUb0x6UfWRs7kQCorVdHlutU1boVPEsB8IDZH6n9tHxMacc3y0JoXOJUsZb/t/Mb8FUWMKaM7iQ== + dependencies: + object-assign "^4.1.1" + react-is "^17.0.2" + react-shallow-renderer "^16.13.1" + scheduler "^0.20.2" react-transition-group@^4.4.1: version "4.4.1" @@ -6808,14 +6878,13 @@ react-transition-group@^4.4.1: loose-envify "^1.4.0" prop-types "^15.6.2" -react@^16.14.0: - version "16.14.0" - resolved "https://registry.yarnpkg.com/react/-/react-16.14.0.tgz#94d776ddd0aaa37da3eda8fc5b6b18a4c9a3114d" - integrity sha512-0X2CImDkJGApiAlcf0ODKIneSwBPhqJawOa5wCtKbu7ZECrmS26NvtSILynQ66cgkT/RJ4LidJOc3bUESwmU8g== +react@^17.0.2: + version "17.0.2" + resolved "https://registry.yarnpkg.com/react/-/react-17.0.2.tgz#d0b5cc516d29eb3eee383f75b62864cfb6800037" + integrity sha512-gnhPt75i/dq/z3/6q/0asP78D0u592D5L1pd7M8P+dck6Fu/jJeL6iVVK23fptSUZj8Vjf++7wXA8UNclGQcbA== dependencies: loose-envify "^1.1.0" object-assign "^4.1.1" - prop-types "^15.6.2" read-pkg-up@^2.0.0: version "2.0.0" @@ -6923,11 +6992,6 @@ redux@^3.7.2: loose-envify "^1.1.0" symbol-observable "^1.0.3" -reflect.ownkeys@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/reflect.ownkeys/-/reflect.ownkeys-0.2.0.tgz#749aceec7f3fdf8b63f927a04809e90c5c0b3460" - integrity sha1-dJrO7H8/34tj+SegSAnpDFwLNGA= - regenerate-unicode-properties@^8.2.0: version "8.2.0" resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-8.2.0.tgz#e5de7111d655e7ba60c057dbe9ff37c87e65cdec" @@ -7266,15 +7330,15 @@ saxes@^5.0.0: dependencies: xmlchars "^2.2.0" -scheduler@^0.19.1: - version "0.19.1" - resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.19.1.tgz#4f3e2ed2c1a7d65681f4c854fa8c5a1ccb40f196" - integrity sha512-n/zwRWRYSUj0/3g/otKDRPMh6qv2SYMWNq85IEa8iZyAv8od9zDYpGSnpBEjNgcMNq6Scbu5KfIPxNF72R/2EA== +scheduler@^0.20.2: + version "0.20.2" + resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.20.2.tgz#4baee39436e34aa93b4874bddcbf0fe8b8b50e91" + integrity sha512-2eWfGgAqqWFGqtdMmcL5zCMK1U8KlXv8SQFGglL3CEtd0aDVDWgeF/YoCmvln55m5zSk3J/20hTaSBeSObsQDQ== dependencies: loose-envify "^1.1.0" object-assign "^4.1.1" -"semver@2 || 3 || 4 || 5", semver@^5.4.1, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0, semver@^5.7.0, semver@^5.7.1: +"semver@2 || 3 || 4 || 5", semver@^5.4.1, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0: version "5.7.1" resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== @@ -7622,6 +7686,14 @@ string.prototype.trimend@^1.0.1, string.prototype.trimend@^1.0.3: call-bind "^1.0.0" define-properties "^1.1.3" +string.prototype.trimend@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.4.tgz#e75ae90c2942c63504686c18b287b4a0b1a45f80" + integrity sha512-y9xCjw1P23Awk8EvTpcyL2NIr1j7wJ39f+k6lvRnSMz+mz9CGz9NYPelDk42kOz6+ql8xjfK8oYzy3jAP5QU5A== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + string.prototype.trimstart@^1.0.1, string.prototype.trimstart@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.3.tgz#9b4cb590e123bb36564401d59824298de50fd5aa" @@ -7630,6 +7702,14 @@ string.prototype.trimstart@^1.0.1, string.prototype.trimstart@^1.0.3: call-bind "^1.0.0" define-properties "^1.1.3" +string.prototype.trimstart@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.4.tgz#b36399af4ab2999b4c9c648bd7a3fb2bb26feeed" + integrity sha512-jh6e984OBfvxS50tdY2nRZnoC5/mLFKOREQfw8t5yytkoUsJRNxvI/E39qu1sD0OtWI3OC0XgKSmcWwziwYuZw== + dependencies: + call-bind "^1.0.2" + define-properties "^1.1.3" + string_decoder@^1.1.1: version "1.3.0" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" @@ -8078,6 +8158,16 @@ ua-parser-js@^0.7.18: resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.28.tgz#8ba04e653f35ce210239c64661685bf9121dec31" integrity sha512-6Gurc1n//gjp9eQNXjD9O3M/sMwVtN5S8Lv9bvOYBfKfDNiIIhqiyi01vMBO45u4zkDE420w/e0se7Vs+sIg+g== +unbox-primitive@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/unbox-primitive/-/unbox-primitive-1.0.1.tgz#085e215625ec3162574dc8859abee78a59b14471" + integrity sha512-tZU/3NqK3dA5gpE1KtyiJUrEB0lxnGkMFHptJ7q6ewdZ8s12QrODwNbhIJStmJkd1QDXa1NRA8aF2A1zk/Ypyw== + dependencies: + function-bind "^1.1.1" + has-bigints "^1.0.1" + has-symbols "^1.0.2" + which-boxed-primitive "^1.0.2" + unhomoglyph@^1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/unhomoglyph/-/unhomoglyph-1.0.6.tgz#ea41f926d0fcf598e3b8bb2980c2ddac66b081d3" @@ -8352,7 +8442,7 @@ whatwg-url@^8.0.0: tr46 "^2.0.2" webidl-conversions "^6.1.0" -which-boxed-primitive@^1.0.1: +which-boxed-primitive@^1.0.1, which-boxed-primitive@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz#13757bc89b209b049fe5d86430e21cf40a89a8e6" integrity sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg== From d492ee4d8acbf0156492cbd97e3f2f3e8153a234 Mon Sep 17 00:00:00 2001 From: Germain Souquet Date: Wed, 9 Jun 2021 11:57:29 +0100 Subject: [PATCH 28/52] Update Enzyme adapter name --- test/accessibility/RovingTabIndex-test.js | 2 +- .../structures/MessagePanel-test.js | 32 +++++++++---------- .../views/messages/TextualBody-test.js | 2 +- .../views/rooms/SendMessageComposer-test.js | 2 +- 4 files changed, 19 insertions(+), 19 deletions(-) diff --git a/test/accessibility/RovingTabIndex-test.js b/test/accessibility/RovingTabIndex-test.js index 5aa93f99f3..7af28043e0 100644 --- a/test/accessibility/RovingTabIndex-test.js +++ b/test/accessibility/RovingTabIndex-test.js @@ -16,7 +16,7 @@ limitations under the License. import '../skinned-sdk'; // Must be first for skinning to work import React from "react"; -import Adapter from "enzyme-adapter-react-16"; +import Adapter from "@wojtekmaj/enzyme-adapter-react-17"; import { configure, mount } from "enzyme"; import { diff --git a/test/components/structures/MessagePanel-test.js b/test/components/structures/MessagePanel-test.js index 5b466b4bb0..9426998160 100644 --- a/test/components/structures/MessagePanel-test.js +++ b/test/components/structures/MessagePanel-test.js @@ -32,7 +32,7 @@ import Matrix from 'matrix-js-sdk'; const test_utils = require('../../test-utils'); const mockclock = require('../../mock-clock'); -import Adapter from "enzyme-adapter-react-16"; +import Adapter from "@wojtekmaj/enzyme-adapter-react-17"; import { configure, mount } from "enzyme"; import MatrixClientContext from "../../../src/contexts/MatrixClientContext"; @@ -77,7 +77,7 @@ describe('MessagePanel', function() { DMRoomMap.makeShared(); }); - afterEach(function () { + afterEach(function() { clock.uninstall(); }); @@ -270,7 +270,7 @@ describe('MessagePanel', function() { it('should show the events', function() { const res = TestUtils.renderIntoDocument( - , + , ); // just check we have the right number of tiles for now @@ -298,8 +298,8 @@ describe('MessagePanel', function() { it('should insert the read-marker in the right place', function() { const res = TestUtils.renderIntoDocument( - , + , ); const tiles = TestUtils.scryRenderedComponentsWithType( @@ -316,8 +316,8 @@ describe('MessagePanel', function() { it('should show the read-marker that fall in summarised events after the summary', function() { const melsEvents = mkMelsEvents(); const res = TestUtils.renderIntoDocument( - , + , ); const summary = TestUtils.findRenderedDOMComponentWithClass(res, 'mx_EventListSummary'); @@ -334,8 +334,8 @@ describe('MessagePanel', function() { it('should hide the read-marker at the end of summarised events', function() { const melsEvents = mkMelsEventsOnly(); const res = TestUtils.renderIntoDocument( - , + , ); const summary = TestUtils.findRenderedDOMComponentWithClass(res, 'mx_EventListSummary'); @@ -358,9 +358,9 @@ describe('MessagePanel', function() { // first render with the RM in one place let mp = ReactDOM.render( - , parentDiv); + , parentDiv); const tiles = TestUtils.scryRenderedComponentsWithType( mp, sdk.getComponent('rooms.EventTile')); @@ -374,9 +374,9 @@ describe('MessagePanel', function() { // now move the RM mp = ReactDOM.render( - , parentDiv); + , parentDiv); // now there should be two RM containers const found = TestUtils.scryRenderedDOMComponentsWithClass(mp, 'mx_RoomView_myReadMarker_container'); @@ -451,7 +451,7 @@ describe('MessagePanel', function() { expect(isReadMarkerVisible(rm)).toBeFalsy(); }); - it('should render Date separators for the events', function () { + it('should render Date separators for the events', function() { const events = mkOneDayEvents(); const res = mount( Date: Wed, 9 Jun 2021 12:17:12 +0100 Subject: [PATCH 29/52] rename workflow --- .github/workflows/{end-to-end-tests.yml => develop.yml} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename .github/workflows/{end-to-end-tests.yml => develop.yml} (96%) diff --git a/.github/workflows/end-to-end-tests.yml b/.github/workflows/develop.yml similarity index 96% rename from .github/workflows/end-to-end-tests.yml rename to .github/workflows/develop.yml index abf7813bbc..346dcbaaba 100644 --- a/.github/workflows/end-to-end-tests.yml +++ b/.github/workflows/develop.yml @@ -1,11 +1,11 @@ -name: "end-to-end_tests" +name: Develop jobs on: push: branches: [develop] pull_request: branches: [develop] jobs: - "end-to-end_tests": + end-to-end: runs-on: ubuntu-latest defaults: run: From 4e14c612b78eec426ce88e4b9f880f9535d66365 Mon Sep 17 00:00:00 2001 From: Germain Souquet Date: Wed, 9 Jun 2021 12:17:49 +0100 Subject: [PATCH 30/52] Remove extraenous instructions --- .github/workflows/develop.yml | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/.github/workflows/develop.yml b/.github/workflows/develop.yml index 346dcbaaba..3f82e61280 100644 --- a/.github/workflows/develop.yml +++ b/.github/workflows/develop.yml @@ -7,16 +7,7 @@ on: jobs: end-to-end: runs-on: ubuntu-latest - defaults: - run: - shell: bash - working-directory: ${{ github.workspace }} - container: - image: vectorim/element-web-ci-e2etests-env:latest - env: - CI_PACKAGE: true - GITHUB_HEAD_REF: ${{ github.head_ref }} - GITHUB_BASE_REF: ${{ github.base_ref }} + container: vectorim/element-web-ci-e2etests-env:latest steps: - name: Checkout code uses: actions/checkout@v2 From 15132074e2bd899e55da8be97650e129a05a5b64 Mon Sep 17 00:00:00 2001 From: Germain Souquet Date: Wed, 9 Jun 2021 13:44:31 +0100 Subject: [PATCH 31/52] Restore copy button icon when sharing permalink --- res/css/views/dialogs/_ShareDialog.scss | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/res/css/views/dialogs/_ShareDialog.scss b/res/css/views/dialogs/_ShareDialog.scss index ce3fdd021f..4d5e1409db 100644 --- a/res/css/views/dialogs/_ShareDialog.scss +++ b/res/css/views/dialogs/_ShareDialog.scss @@ -50,7 +50,8 @@ limitations under the License. margin-left: 20px; display: inherit; } -.mx_ShareDialog_matrixto_copy > div { +.mx_ShareDialog_matrixto_copy::after { + content: ""; mask-image: url($copy-button-url); background-color: $message-action-bar-fg-color; margin-left: 5px; From ceed6ecbe87f743ddd87252a673b16fd6a7bf96a Mon Sep 17 00:00:00 2001 From: Germain Souquet Date: Wed, 9 Jun 2021 14:03:31 +0100 Subject: [PATCH 32/52] Restore Page Up/Down key bindings when focusing the composer --- src/components/structures/RoomView.tsx | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/components/structures/RoomView.tsx b/src/components/structures/RoomView.tsx index 463b71922f..fe90d2f873 100644 --- a/src/components/structures/RoomView.tsx +++ b/src/components/structures/RoomView.tsx @@ -1669,6 +1669,24 @@ export default class RoomView extends React.Component { }); }; + /** + * called by the parent component when PageUp/Down/etc is pressed. + * + * We pass it down to the scroll panel. + */ + private handleScrollKey = ev => { + let panel; + if (this.searchResultsPanel.current) { + panel = this.searchResultsPanel.current; + } else if (this.messagePanel) { + panel = this.messagePanel; + } + + if (panel) { + panel.handleScrollKey(ev); + } + }; + /** * get any current call for this room */ From 927de02a8e8e7a86eafeac59aecc779fecb9e986 Mon Sep 17 00:00:00 2001 From: Germain Souquet Date: Wed, 9 Jun 2021 14:59:55 +0100 Subject: [PATCH 33/52] Revert refreshStickyHeaders optimisations --- src/components/structures/LeftPanel.tsx | 1 + src/components/views/rooms/RoomList.tsx | 5 ++++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/components/structures/LeftPanel.tsx b/src/components/structures/LeftPanel.tsx index 5b6b9c3717..af22db1350 100644 --- a/src/components/structures/LeftPanel.tsx +++ b/src/components/structures/LeftPanel.tsx @@ -439,6 +439,7 @@ export default class LeftPanel extends React.Component { onBlur={this.onBlur} isMinimized={this.props.isMinimized} activeSpace={this.state.activeSpace} + onResize={this.refreshStickyHeaders} onListCollapse={this.refreshStickyHeaders} />; diff --git a/src/components/views/rooms/RoomList.tsx b/src/components/views/rooms/RoomList.tsx index 8b36341ed0..5a1c3a24b3 100644 --- a/src/components/views/rooms/RoomList.tsx +++ b/src/components/views/rooms/RoomList.tsx @@ -55,6 +55,7 @@ interface IProps { onKeyDown: (ev: React.KeyboardEvent) => void; onFocus: (ev: React.FocusEvent) => void; onBlur: (ev: React.FocusEvent) => void; + onResize: () => void; onListCollapse?: (isExpanded: boolean) => void; resizeNotifier: ResizeNotifier; isMinimized: boolean; @@ -404,7 +405,9 @@ export default class RoomList extends React.PureComponent { const newSublists = objectWithOnly(newLists, newListIds); const sublists = objectShallowClone(newSublists, (k, v) => arrayFastClone(v)); - this.setState({sublists, isNameFiltering}); + this.setState({sublists, isNameFiltering}, () => { + this.props.onResize(); + }); } }; From b43315c6c1c555ebce7a032dcc2f7950e999ed83 Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Wed, 9 Jun 2021 15:46:10 +0100 Subject: [PATCH 34/52] Restore read receipt animation from event to event This restores expected read receipt animation by always including the positioned parent of read receipts. I imagine there's something smarter we could be doing, but for now, at least least get back to expected behaviour. Fixes https://github.com/vector-im/element-web/issues/17561 --- src/components/views/rooms/EventTile.tsx | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/components/views/rooms/EventTile.tsx b/src/components/views/rooms/EventTile.tsx index 9e9d7ef53d..354e84bcae 100644 --- a/src/components/views/rooms/EventTile.tsx +++ b/src/components/views/rooms/EventTile.tsx @@ -644,7 +644,18 @@ export default class EventTile extends React.Component { // return early if there are no read receipts if (!this.props.readReceipts || this.props.readReceipts.length === 0) { - return null; + // We currently must include `mx_EventTile_readAvatars` in the DOM + // of all events, as it is the positioned parent of the animated + // read receipts. We can't let it unmount when a receipt moves + // events, so for now we mount it for all events. With out it, the + // animation will start from the top of the timeline (because it + // lost its container). + // See also https://github.com/vector-im/element-web/issues/17561 + return ( +
+ +
+ ); } const ReadReceiptMarker = sdk.getComponent('rooms.ReadReceiptMarker'); From e566704bdf1baf5abc81601401d6f00422117923 Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Wed, 9 Jun 2021 15:48:30 +0100 Subject: [PATCH 35/52] Remove redundant early return --- src/components/views/rooms/EventTile.tsx | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/components/views/rooms/EventTile.tsx b/src/components/views/rooms/EventTile.tsx index 354e84bcae..48118444a5 100644 --- a/src/components/views/rooms/EventTile.tsx +++ b/src/components/views/rooms/EventTile.tsx @@ -663,11 +663,7 @@ export default class EventTile extends React.Component { const receiptOffset = 15; let left = 0; - const receipts = this.props.readReceipts || []; - - if (receipts.length === 0) { - return null; - } + const receipts = this.props.readReceipts; for (let i = 0; i < receipts.length; ++i) { const receipt = receipts[i]; From c6972c4535c09e914a7aaa2c980c5e2f3fe78eb3 Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Wed, 9 Jun 2021 15:53:38 +0100 Subject: [PATCH 36/52] Fix spelling --- src/components/views/rooms/EventTile.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/rooms/EventTile.tsx b/src/components/views/rooms/EventTile.tsx index 48118444a5..0861f7fd5d 100644 --- a/src/components/views/rooms/EventTile.tsx +++ b/src/components/views/rooms/EventTile.tsx @@ -647,7 +647,7 @@ export default class EventTile extends React.Component { // We currently must include `mx_EventTile_readAvatars` in the DOM // of all events, as it is the positioned parent of the animated // read receipts. We can't let it unmount when a receipt moves - // events, so for now we mount it for all events. With out it, the + // events, so for now we mount it for all events. Without it, the // animation will start from the top of the timeline (because it // lost its container). // See also https://github.com/vector-im/element-web/issues/17561 From bd8fd246c67a4b79f7374e1cad18001080ff4741 Mon Sep 17 00:00:00 2001 From: David Baker Date: Wed, 9 Jun 2021 16:04:03 +0100 Subject: [PATCH 37/52] Add logging for which rooms calls are in --- src/CallHandler.tsx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/CallHandler.tsx b/src/CallHandler.tsx index 0d87451b5f..bf7cb3473d 100644 --- a/src/CallHandler.tsx +++ b/src/CallHandler.tsx @@ -542,6 +542,7 @@ export default class CallHandler extends EventEmitter { if (newMappedRoomId !== mappedRoomId) { this.removeCallForRoom(mappedRoomId); mappedRoomId = newMappedRoomId; + console.log("Moving call to room " + mappedRoomId); this.calls.set(mappedRoomId, call); this.emit(CallHandlerEvent.CallChangeRoom, call); } @@ -607,6 +608,7 @@ export default class CallHandler extends EventEmitter { } private removeCallForRoom(roomId: string) { + console.log("Removing call for room ", roomId); this.calls.delete(roomId); this.emit(CallHandlerEvent.CallsChanged, this.calls); } @@ -680,6 +682,7 @@ export default class CallHandler extends EventEmitter { console.log("Current turn creds expire in " + timeUntilTurnCresExpire + " ms"); const call = MatrixClientPeg.get().createCall(mappedRoomId); + console.log("Adding call for room ", roomId); this.calls.set(roomId, call); this.emit(CallHandlerEvent.CallsChanged, this.calls); if (transferee) { @@ -812,6 +815,7 @@ export default class CallHandler extends EventEmitter { } Analytics.trackEvent('voip', 'receiveCall', 'type', call.type); + console.log("Adding call for room ", mappedRoomId); this.calls.set(mappedRoomId, call) this.emit(CallHandlerEvent.CallsChanged, this.calls); this.setCallListeners(call); From b4cb275ca1e3e5b5d7d068b8cacfed85ee375ba7 Mon Sep 17 00:00:00 2001 From: Germain Souquet Date: Wed, 9 Jun 2021 17:59:41 +0100 Subject: [PATCH 38/52] Fix expanding last collapsed sticky session when zoomed in --- src/components/views/rooms/RoomSublist.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/components/views/rooms/RoomSublist.tsx b/src/components/views/rooms/RoomSublist.tsx index 0bb7381dbc..ba8bbffbcc 100644 --- a/src/components/views/rooms/RoomSublist.tsx +++ b/src/components/views/rooms/RoomSublist.tsx @@ -454,8 +454,9 @@ export default class RoomSublist extends React.Component { const sublist = possibleSticky.parentElement.parentElement; const list = sublist.parentElement.parentElement; // the scrollTop is capped at the height of the header in LeftPanel, the top header is always sticky - const isAtTop = list.scrollTop <= HEADER_HEIGHT; - const isAtBottom = list.scrollTop >= list.scrollHeight - list.offsetHeight; + const listScrollTop = Math.round(list.scrollTop); + const isAtTop = listScrollTop <= Math.round(HEADER_HEIGHT); + const isAtBottom = listScrollTop >= Math.round(list.scrollHeight - list.offsetHeight); const isStickyTop = possibleSticky.classList.contains('mx_RoomSublist_headerContainer_stickyTop'); const isStickyBottom = possibleSticky.classList.contains('mx_RoomSublist_headerContainer_stickyBottom'); From 4f649f290cafbe49c9bc7ed8ac8c20e1ae77ab35 Mon Sep 17 00:00:00 2001 From: Germain Souquet Date: Thu, 10 Jun 2021 10:46:21 +0100 Subject: [PATCH 39/52] Migrate RoomSettingsDialog to TypeScript --- ...ttingsDialog.js => RoomSettingsDialog.tsx} | 33 ++++++++++--------- 1 file changed, 18 insertions(+), 15 deletions(-) rename src/components/views/dialogs/{RoomSettingsDialog.js => RoomSettingsDialog.tsx} (87%) diff --git a/src/components/views/dialogs/RoomSettingsDialog.js b/src/components/views/dialogs/RoomSettingsDialog.tsx similarity index 87% rename from src/components/views/dialogs/RoomSettingsDialog.js rename to src/components/views/dialogs/RoomSettingsDialog.tsx index b6c4d42243..9d1608c9e4 100644 --- a/src/components/views/dialogs/RoomSettingsDialog.js +++ b/src/components/views/dialogs/RoomSettingsDialog.tsx @@ -16,7 +16,6 @@ limitations under the License. */ import React from 'react'; -import PropTypes from 'prop-types'; import TabbedView, {Tab} from "../../structures/TabbedView"; import {_t, _td} from "../../../languageHandler"; import AdvancedRoomSettingsTab from "../settings/tabs/room/AdvancedRoomSettingsTab"; @@ -39,31 +38,35 @@ export const ROOM_NOTIFICATIONS_TAB = "ROOM_NOTIFICATIONS_TAB"; export const ROOM_BRIDGES_TAB = "ROOM_BRIDGES_TAB"; export const ROOM_ADVANCED_TAB = "ROOM_ADVANCED_TAB"; +interface IProps { + roomId: string; + onFinished: (success: boolean) => void; +} + @replaceableComponent("views.dialogs.RoomSettingsDialog") -export default class RoomSettingsDialog extends React.Component { - static propTypes = { - roomId: PropTypes.string.isRequired, - onFinished: PropTypes.func.isRequired, - }; +export default class RoomSettingsDialog extends React.Component { + private dispatcherRef: string; - componentDidMount() { - this._dispatcherRef = dis.register(this._onAction); + public componentDidMount() { + this.dispatcherRef = dis.register(this.onAction); } - componentWillUnmount() { - if (this._dispatcherRef) dis.unregister(this._dispatcherRef); + public componentWillUnmount() { + if (this.dispatcherRef) { + dis.unregister(this.dispatcherRef); + } } - _onAction = (payload) => { + private onAction = (payload): void => { // When view changes below us, close the room settings // whilst the modal is open this can only be triggered when someone hits Leave Room if (payload.action === 'view_home_page') { - this.props.onFinished(); + this.props.onFinished(true); } }; - _getTabs() { - const tabs = []; + private getTabs(): Tab[] { + const tabs: Tab[] = []; tabs.push(new Tab( ROOM_GENERAL_TAB, @@ -123,7 +126,7 @@ export default class RoomSettingsDialog extends React.Component { title={_t("Room Settings - %(roomName)s", {roomName})} >
- +
); From 7f3173f17085ee598e78c20f94db916b7f297fa7 Mon Sep 17 00:00:00 2001 From: Germain Souquet Date: Thu, 10 Jun 2021 11:14:43 +0100 Subject: [PATCH 40/52] Implement unencrypted warning slate in rooms --- .../views/dialogs/RoomSettingsDialog.tsx | 6 ++- .../views/messages/EventTileBubble.tsx | 3 +- src/components/views/rooms/NewRoomIntro.tsx | 37 +++++++++++++------ src/i18n/strings/en_EN.json | 3 +- src/stores/RoomViewStore.tsx | 1 + 5 files changed, 35 insertions(+), 15 deletions(-) diff --git a/src/components/views/dialogs/RoomSettingsDialog.tsx b/src/components/views/dialogs/RoomSettingsDialog.tsx index 9d1608c9e4..1a664951c5 100644 --- a/src/components/views/dialogs/RoomSettingsDialog.tsx +++ b/src/components/views/dialogs/RoomSettingsDialog.tsx @@ -41,6 +41,7 @@ export const ROOM_ADVANCED_TAB = "ROOM_ADVANCED_TAB"; interface IProps { roomId: string; onFinished: (success: boolean) => void; + initialTabId?: string; } @replaceableComponent("views.dialogs.RoomSettingsDialog") @@ -126,7 +127,10 @@ export default class RoomSettingsDialog extends React.Component { title={_t("Room Settings - %(roomName)s", {roomName})} >
- +
); diff --git a/src/components/views/messages/EventTileBubble.tsx b/src/components/views/messages/EventTileBubble.tsx index f797a97a3d..88913ac2d4 100644 --- a/src/components/views/messages/EventTileBubble.tsx +++ b/src/components/views/messages/EventTileBubble.tsx @@ -14,13 +14,14 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React, {forwardRef, ReactNode} from "react"; +import React, {forwardRef, ReactNode, ReactChildren} from "react"; import classNames from "classnames"; interface IProps { className: string; title: string; subtitle?: ReactNode; + children?: ReactChildren; } const EventTileBubble = forwardRef(({ className, title, subtitle, children }, ref) => { diff --git a/src/components/views/rooms/NewRoomIntro.tsx b/src/components/views/rooms/NewRoomIntro.tsx index 9b003ade89..21282c415a 100644 --- a/src/components/views/rooms/NewRoomIntro.tsx +++ b/src/components/views/rooms/NewRoomIntro.tsx @@ -33,6 +33,9 @@ import {showSpaceInvite} from "../../../utils/space"; import { privateShouldBeEncrypted } from "../../../createRoom"; +import EventTileBubble from "../messages/EventTileBubble"; +import { ROOM_SECURITY_TAB } from "../dialogs/RoomSettingsDialog"; + function hasExpectedEncryptionSettings(room): boolean { const isEncrypted: boolean = room._client.isRoomEncrypted(room.roomId); const isPublic: boolean = room.getJoinRule() === "public"; @@ -174,20 +177,30 @@ const NewRoomIntro = () => { ; } + function openRoomSettings(event) { + event.preventDefault(); + dis.dispatch({ + action: "open_room_settings", + initial_tab_id: ROOM_SECURITY_TAB, + }); + } + + const sub2 = _t("Your private messages are normally encrypted, but this room isn't." + + "Usually this is because the room was created with a device or method that doesn't support " + + "encryption, like email invites. Enable encryption in settings.", {}, + { a: sub => {sub} }); + return
+ + { !hasExpectedEncryptionSettings(room) && ( + + )} + { body } - - { !hasExpectedEncryptionSettings(room) &&

- {_t("Messages in this room are not end-to-end encrypted. " + - "Messages sent in an unencrypted room can be seen by the server and third parties. " + - "Learn more about encryption.", {}, - { - a: sub => {sub}, - })} - -

}
; }; diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 8508d55eba..3ab69cdb11 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1509,7 +1509,8 @@ "Invite to just this room": "Invite to just this room", "Add a photo, so people can easily spot your room.": "Add a photo, so people can easily spot your room.", "This is the start of .": "This is the start of .", - "Messages in this room are not end-to-end encrypted. Messages sent in an unencrypted room can be seen by the server and third parties. Learn more about encryption.": "Messages in this room are not end-to-end encrypted. Messages sent in an unencrypted room can be seen by the server and third parties. Learn more about encryption.", + "Your private messages are normally encrypted, but this room isn't.Usually this is because the room was created with a device or method that doesn't support encryption, like email invites. Enable encryption in settings.": "Your private messages are normally encrypted, but this room isn't.Usually this is because the room was created with a device or method that doesn't support encryption, like email invites. Enable encryption in settings.", + "This room isn't end to end encrypted": "This room isn't end to end encrypted", "Unpin": "Unpin", "View message": "View message", "%(duration)ss": "%(duration)ss", diff --git a/src/stores/RoomViewStore.tsx b/src/stores/RoomViewStore.tsx index 8da565f603..cc3eafffcd 100644 --- a/src/stores/RoomViewStore.tsx +++ b/src/stores/RoomViewStore.tsx @@ -167,6 +167,7 @@ class RoomViewStore extends Store { const RoomSettingsDialog = sdk.getComponent("dialogs.RoomSettingsDialog"); Modal.createTrackedDialog('Room settings', '', RoomSettingsDialog, { roomId: payload.room_id || this.state.roomId, + initialTabId: payload.initial_tab_id, }, /*className=*/null, /*isPriority=*/false, /*isStatic=*/true); break; } From 0e2e327c4650d835abf8b2b0d9cee39982383144 Mon Sep 17 00:00:00 2001 From: Germain Souquet Date: Thu, 10 Jun 2021 11:19:25 +0100 Subject: [PATCH 41/52] Delete new room intro stale CSS --- res/css/views/rooms/_NewRoomIntro.scss | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/res/css/views/rooms/_NewRoomIntro.scss b/res/css/views/rooms/_NewRoomIntro.scss index 357273b597..e0cccfa885 100644 --- a/res/css/views/rooms/_NewRoomIntro.scss +++ b/res/css/views/rooms/_NewRoomIntro.scss @@ -69,24 +69,4 @@ limitations under the License. font-size: $font-15px; color: $secondary-fg-color; } - - .mx_NewRoomIntro_message:not(:first-child) { - padding-top: 1em; - margin-top: 1em; - border-top: 1px solid currentColor; - } - - .mx_NewRoomIntro_message[role=alert]::before { - --size: 25px; - content: "!"; - float: left; - border-radius: 50%; - width: var(--size); - height: var(--size); - line-height: var(--size); - text-align: center; - background: $button-danger-bg-color; - color: #fff; - margin-right: calc(var(--size) / 2); - } } From 6606a1142ded38fe85953d1cc881599491aa3054 Mon Sep 17 00:00:00 2001 From: Germain Souquet Date: Thu, 10 Jun 2021 11:29:10 +0100 Subject: [PATCH 42/52] Update unencrypted room warning copy --- src/components/views/rooms/NewRoomIntro.tsx | 8 ++++---- src/i18n/strings/en_EN.json | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/components/views/rooms/NewRoomIntro.tsx b/src/components/views/rooms/NewRoomIntro.tsx index 21282c415a..76240f6a2f 100644 --- a/src/components/views/rooms/NewRoomIntro.tsx +++ b/src/components/views/rooms/NewRoomIntro.tsx @@ -185,9 +185,9 @@ const NewRoomIntro = () => { }); } - const sub2 = _t("Your private messages are normally encrypted, but this room isn't." + - "Usually this is because the room was created with a device or method that doesn't support " + - "encryption, like email invites. Enable encryption in settings.", {}, + const sub2 = _t("Your private messages are normally encrypted, but this room isn't. "+ + "Usually this is due to an unsupported device or method being used, " + + "like email invites. Enable encryption in settings.", {}, { a: sub => {sub} }); return
@@ -195,7 +195,7 @@ const NewRoomIntro = () => { { !hasExpectedEncryptionSettings(room) && ( )} diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 3ab69cdb11..874dc11bd2 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1509,8 +1509,8 @@ "Invite to just this room": "Invite to just this room", "Add a photo, so people can easily spot your room.": "Add a photo, so people can easily spot your room.", "This is the start of .": "This is the start of .", - "Your private messages are normally encrypted, but this room isn't.Usually this is because the room was created with a device or method that doesn't support encryption, like email invites. Enable encryption in settings.": "Your private messages are normally encrypted, but this room isn't.Usually this is because the room was created with a device or method that doesn't support encryption, like email invites. Enable encryption in settings.", - "This room isn't end to end encrypted": "This room isn't end to end encrypted", + "Your private messages are normally encrypted, but this room isn't. Usually this is due to an unsupported device or method being used, like email invites. Enable encryption in settings.": "Your private messages are normally encrypted, but this room isn't. Usually this is due to an unsupported device or method being used, like email invites. Enable encryption in settings.", + "End-to-end encryption isn't enabled": "End-to-end encryption isn't enabled", "Unpin": "Unpin", "View message": "View message", "%(duration)ss": "%(duration)ss", From 9731e275f8665b59b8a50746603ad4754392848d Mon Sep 17 00:00:00 2001 From: Germain Souquet Date: Thu, 10 Jun 2021 12:15:55 +0100 Subject: [PATCH 43/52] cater for an undefined MatrixClient on rooms --- src/components/views/rooms/NewRoomIntro.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/rooms/NewRoomIntro.tsx b/src/components/views/rooms/NewRoomIntro.tsx index 76240f6a2f..390a570b26 100644 --- a/src/components/views/rooms/NewRoomIntro.tsx +++ b/src/components/views/rooms/NewRoomIntro.tsx @@ -37,7 +37,7 @@ import EventTileBubble from "../messages/EventTileBubble"; import { ROOM_SECURITY_TAB } from "../dialogs/RoomSettingsDialog"; function hasExpectedEncryptionSettings(room): boolean { - const isEncrypted: boolean = room._client.isRoomEncrypted(room.roomId); + const isEncrypted: boolean = room._client?.isRoomEncrypted(room.roomId); const isPublic: boolean = room.getJoinRule() === "public"; return isPublic || !privateShouldBeEncrypted() || isEncrypted; } From 79b8fbc2a909364972043ebe06019406247fb9b5 Mon Sep 17 00:00:00 2001 From: Germain Date: Thu, 10 Jun 2021 14:21:48 +0100 Subject: [PATCH 44/52] Add indentation for unencrypted warning text Co-authored-by: J. Ryan Stinnett --- src/components/views/rooms/NewRoomIntro.tsx | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/components/views/rooms/NewRoomIntro.tsx b/src/components/views/rooms/NewRoomIntro.tsx index 390a570b26..3bf9a9db33 100644 --- a/src/components/views/rooms/NewRoomIntro.tsx +++ b/src/components/views/rooms/NewRoomIntro.tsx @@ -185,10 +185,12 @@ const NewRoomIntro = () => { }); } - const sub2 = _t("Your private messages are normally encrypted, but this room isn't. "+ - "Usually this is due to an unsupported device or method being used, " + - "like email invites. Enable encryption in settings.", {}, - { a: sub => {sub} }); + const sub2 = _t( + "Your private messages are normally encrypted, but this room isn't. "+ + "Usually this is due to an unsupported device or method being used, " + + "like email invites. Enable encryption in settings.", {}, + { a: sub => {sub} }, + ); return
From 8174973cfed821249db3c3db61a489c79d776039 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Thu, 10 Jun 2021 15:31:27 +0200 Subject: [PATCH 45/52] Use the same styling as for mx_TextualEvent MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/css/views/messages/_SenderProfile.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/css/views/messages/_SenderProfile.scss b/res/css/views/messages/_SenderProfile.scss index 7aaafa6ac3..08644b14e3 100644 --- a/res/css/views/messages/_SenderProfile.scss +++ b/res/css/views/messages/_SenderProfile.scss @@ -21,6 +21,6 @@ limitations under the License. .mx_SenderProfile_mxid { font-weight: 600; font-size: 1.1rem; - color: $event-timestamp-color; margin-left: 5px; + opacity: 0.5; // Match mx_TextualEvent } From 5343be7814e9fd120c3779aa8a33951716cb2d0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Thu, 10 Jun 2021 18:53:56 +0200 Subject: [PATCH 46/52] Fix buggy hovering/selecting of event tiles MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/css/views/rooms/_EventTile.scss | 18 +++++++++++------- res/css/views/rooms/_GroupLayout.scss | 8 -------- 2 files changed, 11 insertions(+), 15 deletions(-) diff --git a/res/css/views/rooms/_EventTile.scss b/res/css/views/rooms/_EventTile.scss index c8b4138f27..3af266caee 100644 --- a/res/css/views/rooms/_EventTile.scss +++ b/res/css/views/rooms/_EventTile.scss @@ -16,6 +16,7 @@ limitations under the License. */ $left-gutter: 64px; +$hover-select-border: 4px; .mx_EventTile { max-width: 100%; @@ -142,8 +143,7 @@ $left-gutter: 64px; } .mx_EventTile_selected > div > a > .mx_MessageTimestamp { - left: 3px; - width: auto; + left: calc(-$hover-select-border); } .mx_EventTile:hover .mx_MessageActionBar, @@ -158,7 +158,7 @@ $left-gutter: 64px; */ .mx_EventTile_selected > .mx_EventTile_line { border-left: $accent-color 4px solid; - padding-left: 60px; + padding-left: calc($left-gutter - $hover-select-border); background-color: $event-selected-color; } @@ -171,8 +171,12 @@ $left-gutter: 64px; } } +.mx_EventTile_info .mx_EventTile_line { + padding-left: calc($left-gutter + 18px); +} + .mx_EventTile_selected.mx_EventTile_info .mx_EventTile_line { - padding-left: 78px; + padding-left: calc($left-gutter + 18px - $hover-select-border); } .mx_EventTile:hover .mx_EventTile_line, @@ -408,7 +412,7 @@ $left-gutter: 64px; .mx_EventTile:hover.mx_EventTile_verified .mx_EventTile_line, .mx_EventTile:hover.mx_EventTile_unverified .mx_EventTile_line, .mx_EventTile:hover.mx_EventTile_unknown .mx_EventTile_line { - padding-left: 60px; + padding-left: calc($left-gutter - $hover-select-border); } .mx_EventTile:hover.mx_EventTile_verified .mx_EventTile_line { @@ -426,7 +430,7 @@ $left-gutter: 64px; .mx_EventTile:hover.mx_EventTile_verified.mx_EventTile_info .mx_EventTile_line, .mx_EventTile:hover.mx_EventTile_unverified.mx_EventTile_info .mx_EventTile_line, .mx_EventTile:hover.mx_EventTile_unknown.mx_EventTile_info .mx_EventTile_line { - padding-left: 78px; + padding-left: calc($left-gutter + 18px - $hover-select-border); } /* End to end encryption stuff */ @@ -438,7 +442,7 @@ $left-gutter: 64px; .mx_EventTile:hover.mx_EventTile_verified .mx_EventTile_line > a > .mx_MessageTimestamp, .mx_EventTile:hover.mx_EventTile_unverified .mx_EventTile_line > a > .mx_MessageTimestamp, .mx_EventTile:hover.mx_EventTile_unknown .mx_EventTile_line > a > .mx_MessageTimestamp { - width: $MessageTimestamp_width_hover; + left: calc(-$hover-select-border); } // Explicit relationships so that it doesn't apply to nested EventTile components (e.g in Replies) diff --git a/res/css/views/rooms/_GroupLayout.scss b/res/css/views/rooms/_GroupLayout.scss index 818509785b..ddee81a914 100644 --- a/res/css/views/rooms/_GroupLayout.scss +++ b/res/css/views/rooms/_GroupLayout.scss @@ -24,10 +24,6 @@ $left-gutter: 64px; margin-left: $left-gutter; } - > .mx_EventTile_line { - padding-left: $left-gutter; - } - > .mx_EventTile_avatar { position: absolute; } @@ -43,10 +39,6 @@ $left-gutter: 64px; line-height: $font-22px; } } - - .mx_EventTile_info .mx_EventTile_line { - padding-left: calc($left-gutter + 18px); - } } /* Compact layout overrides */ From b1824ce579c805c8c293aee739808b71bc030614 Mon Sep 17 00:00:00 2001 From: Germain Souquet Date: Fri, 11 Jun 2021 11:34:53 +0100 Subject: [PATCH 47/52] fix HTML tag for Event Tile when not rendered in a list --- .../views/elements/EventTilePreview.tsx | 1 + src/components/views/rooms/EventTile.tsx | 150 ++++++++++-------- src/components/views/rooms/ReplyPreview.js | 1 + 3 files changed, 82 insertions(+), 70 deletions(-) diff --git a/src/components/views/elements/EventTilePreview.tsx b/src/components/views/elements/EventTilePreview.tsx index b15fbbed2b..cf3b7a6e61 100644 --- a/src/components/views/elements/EventTilePreview.tsx +++ b/src/components/views/elements/EventTilePreview.tsx @@ -128,6 +128,7 @@ export default class EventTilePreview extends React.Component { mxEvent={event} layout={this.props.layout} enableFlair={SettingsStore.getValue(UIFeature.Flair)} + as="div" />
; } diff --git a/src/components/views/rooms/EventTile.tsx b/src/components/views/rooms/EventTile.tsx index 0861f7fd5d..85b9cac2c4 100644 --- a/src/components/views/rooms/EventTile.tsx +++ b/src/components/views/rooms/EventTile.tsx @@ -1059,58 +1059,65 @@ export default class EventTile extends React.Component { switch (this.props.tileShape) { case 'notif': { const room = this.context.getRoom(this.props.mxEvent.getRoomId()); - return ( -
  • - - -
    - -
    -
  • - ); + return React.createElement(this.props.as || "li", { + "className": classes, + "aria-live": ariaLive, + "aria-atomic": true, + "data-scroll-tokens": scrollToken, + }, [ + , + , +
    + +
    , + ]); } case 'file_grid': { - return ( -
  • -
    - + return React.createElement(this.props.as || "li", { + "className": classes, + "aria-live": ariaLive, + "aria-atomic": true, + "data-scroll-tokens": scrollToken, + }, [ +
    + +
    , + +
    + { sender } + { timestamp }
    -
    -
    - { sender } - { timestamp } -
    -
    -
  • - ); + , + ]); } case 'reply': @@ -1126,27 +1133,30 @@ export default class EventTile extends React.Component { this.props.alwaysShowTimestamps || this.state.hover, ); } - return ( -
  • - { ircTimestamp } - { avatar } - { sender } - { ircPadlock } -
    - { groupTimestamp } - { groupPadlock } - { thread } - -
    -
  • - ); + return React.createElement(this.props.as || "li", { + "className": classes, + "aria-live": ariaLive, + "aria-atomic": true, + "data-scroll-tokens": scrollToken, + }, [ + ircTimestamp, + avatar, + sender, + ircPadlock, +
    + { groupTimestamp } + { groupPadlock } + { thread } + +
    , + ]); } default: { const thread = ReplyThread.makeThread( diff --git a/src/components/views/rooms/ReplyPreview.js b/src/components/views/rooms/ReplyPreview.js index 5835eb9f36..c61424a059 100644 --- a/src/components/views/rooms/ReplyPreview.js +++ b/src/components/views/rooms/ReplyPreview.js @@ -95,6 +95,7 @@ export default class ReplyPreview extends React.Component { permalinkCreator={this.props.permalinkCreator} isTwelveHour={SettingsStore.getValue("showTwelveHourTimestamps")} enableFlair={SettingsStore.getValue(UIFeature.Flair)} + as="div" />
    ; From 84679cf8ec5bfe764cda2743f69a9099fe79c38a Mon Sep 17 00:00:00 2001 From: Germain Souquet Date: Fri, 11 Jun 2021 12:19:14 +0100 Subject: [PATCH 48/52] remove legacy polyfills and unused dependencies --- package.json | 3 --- src/ContentMessages.tsx | 2 -- src/CountlyAnalytics.ts | 7 ------- src/rageshake/submit-rageshake.ts | 6 ------ src/utils/MegolmExportEncryption.js | 11 ----------- yarn.lock | 10 ---------- 6 files changed, 39 deletions(-) diff --git a/package.json b/package.json index dbb6b29b53..4dc4bda3b2 100644 --- a/package.json +++ b/package.json @@ -55,7 +55,6 @@ "dependencies": { "@babel/runtime": "^7.12.5", "await-lock": "^2.1.0", - "blueimp-canvas-to-blob": "^3.28.0", "browser-encrypt-attachment": "^0.3.0", "browser-request": "^0.3.3", "cheerio": "^1.0.0-rc.9", @@ -88,7 +87,6 @@ "png-chunks-extract": "^1.0.0", "prop-types": "^15.7.2", "qrcode": "^1.4.4", - "qs": "^6.9.6", "re-resizable": "^6.9.0", "react": "^17.0.2", "react-beautiful-dnd": "^4.0.1", @@ -99,7 +97,6 @@ "rfc4648": "^1.4.0", "sanitize-html": "^2.3.2", "tar-js": "^0.3.0", - "text-encoding-utf-8": "^1.0.2", "url": "^0.11.0", "what-input": "^5.2.10", "zxcvbn": "^4.4.2" diff --git a/src/ContentMessages.tsx b/src/ContentMessages.tsx index b21829ac63..9042d47243 100644 --- a/src/ContentMessages.tsx +++ b/src/ContentMessages.tsx @@ -28,8 +28,6 @@ import encrypt from "browser-encrypt-attachment"; import extractPngChunks from "png-chunks-extract"; import Spinner from "./components/views/elements/Spinner"; -// Polyfill for Canvas.toBlob API using Canvas.toDataURL -import "blueimp-canvas-to-blob"; import { Action } from "./dispatcher/actions"; import CountlyAnalytics from "./CountlyAnalytics"; import { diff --git a/src/CountlyAnalytics.ts b/src/CountlyAnalytics.ts index 5545ed8483..f494c1eb22 100644 --- a/src/CountlyAnalytics.ts +++ b/src/CountlyAnalytics.ts @@ -24,13 +24,6 @@ import {sleep} from "./utils/promise"; import RoomViewStore from "./stores/RoomViewStore"; import { Action } from "./dispatcher/actions"; -// polyfill textencoder if necessary -import * as TextEncodingUtf8 from 'text-encoding-utf-8'; -let TextEncoder = window.TextEncoder; -if (!TextEncoder) { - TextEncoder = TextEncodingUtf8.TextEncoder; -} - const INACTIVITY_TIME = 20; // seconds const HEARTBEAT_INTERVAL = 5_000; // ms const SESSION_UPDATE_INTERVAL = 60; // seconds diff --git a/src/rageshake/submit-rageshake.ts b/src/rageshake/submit-rageshake.ts index b2ad9fe6f6..08d8ccfd13 100644 --- a/src/rageshake/submit-rageshake.ts +++ b/src/rageshake/submit-rageshake.ts @@ -25,14 +25,8 @@ import Tar from "tar-js"; import * as rageshake from './rageshake'; -// polyfill textencoder if necessary -import * as TextEncodingUtf8 from 'text-encoding-utf-8'; import SettingsStore from "../settings/SettingsStore"; import SdkConfig from "../SdkConfig"; -let TextEncoder = window.TextEncoder; -if (!TextEncoder) { - TextEncoder = TextEncodingUtf8.TextEncoder; -} interface IOpts { label?: string; diff --git a/src/utils/MegolmExportEncryption.js b/src/utils/MegolmExportEncryption.js index 6f5c7104b1..ae6b2999fa 100644 --- a/src/utils/MegolmExportEncryption.js +++ b/src/utils/MegolmExportEncryption.js @@ -15,17 +15,6 @@ See the License for the specific language governing permissions and limitations under the License. */ -// polyfill textencoder if necessary -import * as TextEncodingUtf8 from 'text-encoding-utf-8'; -let TextEncoder = window.TextEncoder; -if (!TextEncoder) { - TextEncoder = TextEncodingUtf8.TextEncoder; -} -let TextDecoder = window.TextDecoder; -if (!TextDecoder) { - TextDecoder = TextEncodingUtf8.TextDecoder; -} - import { _t } from '../languageHandler'; import SdkConfig from '../SdkConfig'; diff --git a/yarn.lock b/yarn.lock index 2b8667c7a6..f98cb5845e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2181,11 +2181,6 @@ bluebird@^3.5.0: resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== -blueimp-canvas-to-blob@^3.28.0: - version "3.28.0" - resolved "https://registry.yarnpkg.com/blueimp-canvas-to-blob/-/blueimp-canvas-to-blob-3.28.0.tgz#c8ab4dc6bb08774a7f273798cdf94b0776adf6c8" - integrity sha512-5q+YHzgGsuHQ01iouGgJaPJXod2AzTxJXmVv90PpGrRxU7G7IqgPqWXz+PBmt3520jKKi6irWbNV87DicEa7wg== - boolbase@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" @@ -7945,11 +7940,6 @@ test-exclude@^6.0.0: glob "^7.1.4" minimatch "^3.0.4" -text-encoding-utf-8@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/text-encoding-utf-8/-/text-encoding-utf-8-1.0.2.tgz#585b62197b0ae437e3c7b5d0af27ac1021e10d13" - integrity sha512-8bw4MY9WjdsD2aMtO0OzOCY3pXGYNx2d2FfHRVUKkiCPDWjKuOlhLVASS+pD7VkLTVjW268LYJHwsnPFlBpbAg== - text-table@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" From 37484e3fc465ef5a05a5001580facbea37d10bb5 Mon Sep 17 00:00:00 2001 From: Germain Souquet Date: Fri, 11 Jun 2021 12:33:02 +0100 Subject: [PATCH 49/52] Add TextEncoder polyfill for test run --- test/setupTests.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/test/setupTests.js b/test/setupTests.js index 6d37d48987..9da412a7e8 100644 --- a/test/setupTests.js +++ b/test/setupTests.js @@ -1,6 +1,12 @@ import * as languageHandler from "../src/languageHandler"; +import { TextEncoder, TextDecoder } from 'util'; languageHandler.setLanguage('en'); languageHandler.setMissingEntryGenerator(key => key.split("|", 2)[1]); require('jest-fetch-mock').enableMocks(); + +// polyfilling TextEncoder as it is not available on JSDOM +// view https://github.com/facebook/jest/issues/9983 +global.TextEncoder = TextEncoder; +global.TextDecoder = TextDecoder; From e260e8671fab08da34f8c9abc30ec39e1aa486d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Fri, 11 Jun 2021 15:14:16 +0200 Subject: [PATCH 50/52] Inline display name into return MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/views/messages/SenderProfile.tsx | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/components/views/messages/SenderProfile.tsx b/src/components/views/messages/SenderProfile.tsx index 872dc132b9..805f842fbc 100644 --- a/src/components/views/messages/SenderProfile.tsx +++ b/src/components/views/messages/SenderProfile.tsx @@ -118,12 +118,6 @@ export default class SenderProfile extends React.Component { return null; // emote message must include the name so don't duplicate it } - const displayNameElement = ( - - { displayName } - - ); - let mxidElement; if (disambiguate) { mxidElement = ( @@ -147,7 +141,9 @@ export default class SenderProfile extends React.Component { return (
    - { displayNameElement } + + { displayName } + { mxidElement } { flair }
    From 2eafe132b8b0650b058faa4be3bbdc4e5167ff38 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Mon, 14 Jun 2021 13:31:58 -0600 Subject: [PATCH 51/52] Move various createRoom types to the js-sdk --- src/components/structures/SpaceRoomView.tsx | 3 +- .../views/dialogs/CreateRoomDialog.tsx | 21 ++++---- .../views/spaces/SpaceCreateMenu.tsx | 6 ++- src/createRoom.ts | 52 ++----------------- src/mjolnir/Mjolnir.ts | 3 +- 5 files changed, 23 insertions(+), 62 deletions(-) diff --git a/src/components/structures/SpaceRoomView.tsx b/src/components/structures/SpaceRoomView.tsx index 276f4ae6ca..3ccf2e5424 100644 --- a/src/components/structures/SpaceRoomView.tsx +++ b/src/components/structures/SpaceRoomView.tsx @@ -28,7 +28,7 @@ import RoomTopic from "../views/elements/RoomTopic"; import InlineSpinner from "../views/elements/InlineSpinner"; import {inviteMultipleToRoom, showRoomInviteDialog} from "../../RoomInvite"; import {useRoomMembers} from "../../hooks/useRoomMembers"; -import createRoom, {IOpts, Preset} from "../../createRoom"; +import createRoom, {IOpts} from "../../createRoom"; import Field from "../views/elements/Field"; import {useEventEmitter} from "../../hooks/useEventEmitter"; import withValidation from "../views/elements/Validation"; @@ -65,6 +65,7 @@ import dis from "../../dispatcher/dispatcher"; import Modal from "../../Modal"; import BetaFeedbackDialog from "../views/dialogs/BetaFeedbackDialog"; import SdkConfig from "../../SdkConfig"; +import { Preset } from "matrix-js-sdk/src/@types/partials"; interface IProps { space: Room; diff --git a/src/components/views/dialogs/CreateRoomDialog.tsx b/src/components/views/dialogs/CreateRoomDialog.tsx index cce6b6c34c..544f593708 100644 --- a/src/components/views/dialogs/CreateRoomDialog.tsx +++ b/src/components/views/dialogs/CreateRoomDialog.tsx @@ -15,22 +15,23 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React, {ChangeEvent, createRef, KeyboardEvent, SyntheticEvent} from "react"; -import {Room} from "matrix-js-sdk/src/models/room"; +import React, { ChangeEvent, createRef, KeyboardEvent, SyntheticEvent } from "react"; +import { Room } from "matrix-js-sdk/src/models/room"; import SdkConfig from '../../../SdkConfig'; -import withValidation, {IFieldState} from '../elements/Validation'; -import {_t} from '../../../languageHandler'; -import {MatrixClientPeg} from '../../../MatrixClientPeg'; -import {Key} from "../../../Keyboard"; -import {IOpts, Preset, privateShouldBeEncrypted, Visibility} from "../../../createRoom"; -import {CommunityPrototypeStore} from "../../../stores/CommunityPrototypeStore"; -import {replaceableComponent} from "../../../utils/replaceableComponent"; +import withValidation, { IFieldState } from '../elements/Validation'; +import { _t } from '../../../languageHandler'; +import { MatrixClientPeg } from '../../../MatrixClientPeg'; +import { Key } from "../../../Keyboard"; +import { IOpts, privateShouldBeEncrypted } from "../../../createRoom"; +import { CommunityPrototypeStore } from "../../../stores/CommunityPrototypeStore"; +import { replaceableComponent } from "../../../utils/replaceableComponent"; import Field from "../elements/Field"; import RoomAliasField from "../elements/RoomAliasField"; import LabelledToggleSwitch from "../elements/LabelledToggleSwitch"; import DialogButtons from "../elements/DialogButtons"; import BaseDialog from "../dialogs/BaseDialog"; +import { Preset, Visibility } from "matrix-js-sdk/src/@types/partials"; interface IProps { defaultPublic?: boolean; @@ -72,7 +73,7 @@ export default class CreateRoomDialog extends React.Component { canChangeEncryption: true, }; - MatrixClientPeg.get().doesServerForceEncryptionForPreset("private") + MatrixClientPeg.get().doesServerForceEncryptionForPreset(Preset.PrivateChat) .then(isForced => this.setState({ canChangeEncryption: !isForced })); } diff --git a/src/components/views/spaces/SpaceCreateMenu.tsx b/src/components/views/spaces/SpaceCreateMenu.tsx index 0ebf511018..6a935ab276 100644 --- a/src/components/views/spaces/SpaceCreateMenu.tsx +++ b/src/components/views/spaces/SpaceCreateMenu.tsx @@ -22,7 +22,7 @@ import FocusLock from "react-focus-lock"; import {_t} from "../../../languageHandler"; import AccessibleTooltipButton from "../elements/AccessibleTooltipButton"; import {ChevronFace, ContextMenu} from "../../structures/ContextMenu"; -import createRoom, {IStateEvent, Preset} from "../../../createRoom"; +import createRoom from "../../../createRoom"; import MatrixClientContext from "../../../contexts/MatrixClientContext"; import {SpaceAvatar} from "./SpaceBasicSettings"; import AccessibleButton from "../elements/AccessibleButton"; @@ -33,6 +33,8 @@ import {USER_LABS_TAB} from "../dialogs/UserSettingsDialog"; import Field from "../elements/Field"; import withValidation from "../elements/Validation"; import {SpaceFeedbackPrompt} from "../../structures/SpaceRoomView"; +import { Preset } from "matrix-js-sdk/src/@types/partials"; +import { ICreateRoomStateEvent } from "matrix-js-sdk/src/@types/requests"; const SpaceCreateMenuType = ({ title, description, className, onClick }) => { return ( @@ -81,7 +83,7 @@ const SpaceCreateMenu = ({ onFinished }) => { return; } - const initialState: IStateEvent[] = [ + const initialState: ICreateRoomStateEvent[] = [ { type: EventType.RoomHistoryVisibility, content: { diff --git a/src/createRoom.ts b/src/createRoom.ts index c6507b1380..2641492588 100644 --- a/src/createRoom.ts +++ b/src/createRoom.ts @@ -35,53 +35,15 @@ import { VIRTUAL_ROOM_EVENT_TYPE } from "./CallHandler"; import SpaceStore from "./stores/SpaceStore"; import { makeSpaceParentEvent } from "./utils/space"; import { Action } from "./dispatcher/actions" +import { ICreateRoomOpts } from "matrix-js-sdk/src/@types/requests"; +import { Preset, Visibility } from "matrix-js-sdk/src/@types/partials"; // we define a number of interfaces which take their names from the js-sdk /* eslint-disable camelcase */ -// TODO move these interfaces over to js-sdk once it has been typescripted enough to accept them -export enum Visibility { - Public = "public", - Private = "private", -} - -export enum Preset { - PrivateChat = "private_chat", - TrustedPrivateChat = "trusted_private_chat", - PublicChat = "public_chat", -} - -interface Invite3PID { - id_server: string; - id_access_token?: string; // this gets injected by the js-sdk - medium: string; - address: string; -} - -export interface IStateEvent { - type: string; - state_key?: string; // defaults to an empty string - content: object; -} - -interface ICreateOpts { - visibility?: Visibility; - room_alias_name?: string; - name?: string; - topic?: string; - invite?: string[]; - invite_3pid?: Invite3PID[]; - room_version?: string; - creation_content?: object; - initial_state?: IStateEvent[]; - preset?: Preset; - is_direct?: boolean; - power_level_content_override?: object; -} - export interface IOpts { dmUserId?: string; - createOpts?: ICreateOpts; + createOpts?: ICreateRoomOpts; spinner?: boolean; guestAccess?: boolean; encryption?: boolean; @@ -91,12 +53,6 @@ export interface IOpts { parentSpace?: Room; } -export interface IInvite3PID { - id_server: string, - medium: 'email', - address: string, -} - /** * Create a new room, and switch to it. * @@ -136,7 +92,7 @@ export default function createRoom(opts: IOpts): Promise { const defaultPreset = opts.dmUserId ? Preset.TrustedPrivateChat : Preset.PrivateChat; // set some defaults for the creation - const createOpts = opts.createOpts || {}; + const createOpts: ICreateRoomOpts = opts.createOpts || {}; createOpts.preset = createOpts.preset || defaultPreset; createOpts.visibility = createOpts.visibility || Visibility.Private; if (opts.dmUserId && createOpts.invite === undefined) { diff --git a/src/mjolnir/Mjolnir.ts b/src/mjolnir/Mjolnir.ts index 891438bbb9..f1fe816642 100644 --- a/src/mjolnir/Mjolnir.ts +++ b/src/mjolnir/Mjolnir.ts @@ -20,6 +20,7 @@ import SettingsStore from "../settings/SettingsStore"; import {_t} from "../languageHandler"; import dis from "../dispatcher/dispatcher"; import {SettingLevel} from "../settings/SettingLevel"; +import { Preset } from "matrix-js-sdk/src/@types/partials"; // TODO: Move this and related files to the js-sdk or something once finalized. @@ -86,7 +87,7 @@ export class Mjolnir { const resp = await MatrixClientPeg.get().createRoom({ name: _t("My Ban List"), topic: _t("This is your list of users/servers you have blocked - don't leave the room!"), - preset: "private_chat", + preset: Preset.PrivateChat, }); personalRoomId = resp['room_id']; await SettingsStore.setValue( From 951fbf4a1a06f52d40b2eefc21937a3383e983ea Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Mon, 14 Jun 2021 13:38:43 -0600 Subject: [PATCH 52/52] fix import --- src/components/views/dialogs/InviteDialog.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/dialogs/InviteDialog.tsx b/src/components/views/dialogs/InviteDialog.tsx index b006205f11..d90214110f 100644 --- a/src/components/views/dialogs/InviteDialog.tsx +++ b/src/components/views/dialogs/InviteDialog.tsx @@ -31,7 +31,6 @@ import Modal from "../../../Modal"; import {humanizeTime} from "../../../utils/humanize"; import createRoom, { canEncryptToAllUsers, ensureDMExists, findDMForUser, privateShouldBeEncrypted, - IInvite3PID, } from "../../../createRoom"; import {inviteMultipleToRoom, showCommunityInviteDialog} from "../../../RoomInvite"; import {Key} from "../../../Keyboard"; @@ -50,6 +49,7 @@ import {getAddressType} from "../../../UserAddress"; import BaseAvatar from '../avatars/BaseAvatar'; import AccessibleButton from '../elements/AccessibleButton'; import { compare } from '../../../utils/strings'; +import { IInvite3PID } from "matrix-js-sdk/src/@types/requests"; // we have a number of types defined from the Matrix spec which can't reasonably be altered here. /* eslint-disable camelcase */