From f420c8598516b4f939b7ab90f5fa263db2aaa94a Mon Sep 17 00:00:00 2001 From: Jaiwanth Date: Mon, 15 Feb 2021 19:13:09 +0530 Subject: [PATCH 01/63] Added invite option to room's context menu Signed-off-by: Jaiwanth --- res/css/views/rooms/_RoomTile.scss | 4 ++++ src/components/views/rooms/RoomTile.tsx | 17 ++++++++++++++++- 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/res/css/views/rooms/_RoomTile.scss b/res/css/views/rooms/_RoomTile.scss index 8eca3f1efa..377b207490 100644 --- a/res/css/views/rooms/_RoomTile.scss +++ b/res/css/views/rooms/_RoomTile.scss @@ -188,6 +188,10 @@ limitations under the License. .mx_RoomTile_iconSettings::before { mask-image: url('$(res)/img/element-icons/settings.svg'); } + + .mx_RoomTile_iconInvite::before { + mask-image: url('$(res)/img/element-icons/room/invite.svg'); + } .mx_RoomTile_iconSignOut::before { mask-image: url('$(res)/img/element-icons/leave.svg'); diff --git a/src/components/views/rooms/RoomTile.tsx b/src/components/views/rooms/RoomTile.tsx index 835447dc18..3a34a0daaa 100644 --- a/src/components/views/rooms/RoomTile.tsx +++ b/src/components/views/rooms/RoomTile.tsx @@ -331,6 +331,17 @@ export default class RoomTile extends React.PureComponent { this.setState({generalMenuPosition: null}); // hide the menu }; + private onInviteClick = (ev: ButtonEvent) => { + ev.preventDefault(); + ev.stopPropagation(); + + dis.dispatch({ + action: 'view_invite', + roomId: this.props.room.roomId, + }); + this.setState({generalMenuPosition: null}); // hide the menu + }; + private async saveNotifState(ev: ButtonEvent, newState: Volume) { ev.preventDefault(); ev.stopPropagation(); @@ -470,7 +481,11 @@ export default class RoomTile extends React.PureComponent { label={lowPriorityLabel} iconClassName="mx_RoomTile_iconArrowDown" /> - + Date: Mon, 15 Feb 2021 20:52:19 +0530 Subject: [PATCH 02/63] Check whether user has permission to invite Signed-off-by: Jaiwanth --- res/css/views/rooms/_RoomTile.scss | 2 +- src/components/views/rooms/RoomTile.tsx | 22 +++++++++++++++++----- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/res/css/views/rooms/_RoomTile.scss b/res/css/views/rooms/_RoomTile.scss index 377b207490..72d29dfd4c 100644 --- a/res/css/views/rooms/_RoomTile.scss +++ b/res/css/views/rooms/_RoomTile.scss @@ -188,7 +188,7 @@ limitations under the License. .mx_RoomTile_iconSettings::before { mask-image: url('$(res)/img/element-icons/settings.svg'); } - + .mx_RoomTile_iconInvite::before { mask-image: url('$(res)/img/element-icons/room/invite.svg'); } diff --git a/src/components/views/rooms/RoomTile.tsx b/src/components/views/rooms/RoomTile.tsx index 3a34a0daaa..f168235335 100644 --- a/src/components/views/rooms/RoomTile.tsx +++ b/src/components/views/rooms/RoomTile.tsx @@ -462,6 +462,16 @@ export default class RoomTile extends React.PureComponent { const isLowPriority = roomTags.includes(DefaultTagID.LowPriority); const lowPriorityLabel = _t("Low Priority"); + const inRoom = this.props.room && this.props.room.getMyMembership() === "join"; + const userId = MatrixClientPeg.get().getUserId(); + let canInvite = inRoom; + const powerLevels = this.props.room.currentState + .getStateEvents("m.room.power_levels", "") + ?.getContent(); + const me = this.props.room.getMember(userId); + if (powerLevels && me && powerLevels.invite > me.powerLevel) { + canInvite = false; + } contextMenu = { label={lowPriorityLabel} iconClassName="mx_RoomTile_iconArrowDown" /> - + {canInvite ? ( + + ) : null} Date: Tue, 16 Feb 2021 18:59:22 +0530 Subject: [PATCH 03/63] Update src/components/views/rooms/RoomTile.tsx --- src/components/views/rooms/RoomTile.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/rooms/RoomTile.tsx b/src/components/views/rooms/RoomTile.tsx index f168235335..3894f557fc 100644 --- a/src/components/views/rooms/RoomTile.tsx +++ b/src/components/views/rooms/RoomTile.tsx @@ -462,7 +462,7 @@ export default class RoomTile extends React.PureComponent { const isLowPriority = roomTags.includes(DefaultTagID.LowPriority); const lowPriorityLabel = _t("Low Priority"); - const inRoom = this.props.room && this.props.room.getMyMembership() === "join"; + const inRoom = this.props.room.getMyMembership() === "join"; const userId = MatrixClientPeg.get().getUserId(); let canInvite = inRoom; const powerLevels = this.props.room.currentState From 21b9ab9d8595ffbdb881e8c4e4ae33d9829d0dcb Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 16 Feb 2021 15:17:51 -0700 Subject: [PATCH 04/63] Add an optional config option to make the welcome page the login page This is commonly requested by deployments with custom setups or those using SSO. Instead of having them all ship the same welcome.html with redirect code in it, we can offer a built-in redirect. Note that this doesn't actually redirect and instead just replaces the view. This is to make the change less invasive as otherwise it involves changing the routing layers. --- src/components/structures/MatrixChat.tsx | 6 ++++-- src/utils/{pages.js => pages.ts} | 17 +++++++++++------ 2 files changed, 15 insertions(+), 8 deletions(-) rename src/utils/{pages.js => pages.ts} (68%) diff --git a/src/components/structures/MatrixChat.tsx b/src/components/structures/MatrixChat.tsx index 5045e44182..35e08e8d37 100644 --- a/src/components/structures/MatrixChat.tsx +++ b/src/components/structures/MatrixChat.tsx @@ -82,6 +82,7 @@ import {UIFeature} from "../../settings/UIFeature"; import { CommunityPrototypeStore } from "../../stores/CommunityPrototypeStore"; import DialPadModal from "../views/voip/DialPadModal"; import { showToast as showMobileGuideToast } from '../../toasts/MobileGuideToast'; +import { shouldUseLoginForWelcome } from "../../utils/pages"; /** constants for MatrixChat.state.view */ export enum Views { @@ -1988,7 +1989,7 @@ export default class MatrixChat extends React.PureComponent { ); } - } else if (this.state.view === Views.WELCOME) { + } else if (this.state.view === Views.WELCOME && !shouldUseLoginForWelcome(SdkConfig.get())) { const Welcome = sdk.getComponent('auth.Welcome'); view = ; } else if (this.state.view === Views.REGISTER && SettingsStore.getValue(UIFeature.Registration)) { @@ -2020,7 +2021,8 @@ export default class MatrixChat extends React.PureComponent { {...this.getServerProperties()} /> ); - } else if (this.state.view === Views.LOGIN) { + } else if (this.state.view === Views.LOGIN + || (this.state.view === Views.WELCOME && shouldUseLoginForWelcome(SdkConfig.get()))) { const showPasswordReset = SettingsStore.getValue(UIFeature.PasswordReset); const Login = sdk.getComponent('structures.auth.Login'); view = ( diff --git a/src/utils/pages.js b/src/utils/pages.ts similarity index 68% rename from src/utils/pages.js rename to src/utils/pages.ts index d63ca3f2c7..bae76be29d 100644 --- a/src/utils/pages.js +++ b/src/utils/pages.ts @@ -1,5 +1,5 @@ /* -Copyright 2019 New Vector Ltd +Copyright 2019, 2021 The Matrix.org Foundation C.I.C. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -14,12 +14,12 @@ See the License for the specific language governing permissions and limitations under the License. */ -export function getHomePageUrl(appConfig) { +import { ConfigOptions } from "../SdkConfig"; + +export function getHomePageUrl(appConfig: ConfigOptions): string | null { const pagesConfig = appConfig.embeddedPages; - let pageUrl = null; - if (pagesConfig) { - pageUrl = pagesConfig.homeUrl; - } + let pageUrl = pagesConfig?.homeUrl; + if (!pageUrl) { // This is a deprecated config option for the home page // (despite the name, given we also now have a welcome @@ -29,3 +29,8 @@ export function getHomePageUrl(appConfig) { return pageUrl; } + +export function shouldUseLoginForWelcome(appConfig: ConfigOptions): boolean { + const pagesConfig = appConfig.embeddedPages; + return pagesConfig?.loginForWelcome === true; +} From 6106e01ab496f16ac4c36911f67f8df817cf2025 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 8 Mar 2021 09:39:07 +0000 Subject: [PATCH 05/63] Collapse redactions into an event list summary --- src/components/structures/MessagePanel.js | 93 ++++++++++++++++++++++- src/i18n/strings/en_EN.json | 2 + 2 files changed, 94 insertions(+), 1 deletion(-) diff --git a/src/components/structures/MessagePanel.js b/src/components/structures/MessagePanel.js index 161227a139..28214aada9 100644 --- a/src/components/structures/MessagePanel.js +++ b/src/components/structures/MessagePanel.js @@ -1001,6 +1001,97 @@ class CreationGrouper { } } +class RedactionGrouper { + static canStartGroup = function(panel, ev) { + return panel._shouldShowEvent(ev) && ev.isRedacted(); + } + + constructor(panel, ev, prevEvent, lastShownEvent) { + this.panel = panel; + this.readMarker = panel._readMarkerForEvent( + ev.getId(), + ev === lastShownEvent, + ); + this.events = [ev]; + this.prevEvent = prevEvent; + this.lastShownEvent = lastShownEvent; + } + + shouldGroup(ev) { + if (this.panel._wantsDateSeparator(this.events[0], ev.getDate())) { + return false; + } + return ev.isRedacted(); + } + + add(ev) { + this.readMarker = this.readMarker || this.panel._readMarkerForEvent( + ev.getId(), + ev === this.lastShownEvent, + ); + this.events.push(ev); + } + + getTiles() { + if (!this.events || !this.events.length) return []; + + const DateSeparator = sdk.getComponent('messages.DateSeparator'); + const EventListSummary = sdk.getComponent('views.elements.EventListSummary'); + + const panel = this.panel; + const ret = []; + const lastShownEvent = this.lastShownEvent; + + if (panel._wantsDateSeparator(this.prevEvent, this.events[0].getDate())) { + const ts = this.events[0].getTs(); + ret.push( +
  • , + ); + } + + const key = "redactioneventlistsummary-" + ( + this.prevEvent ? this.events[0].getId() : "initial" + ); + + const senders = new Set(); + let eventTiles = this.events.map((e) => { + senders.add(e.sender); + // In order to prevent DateSeparators from appearing in the expanded form, + // render each member event as if the previous one was itself. + // This way, the timestamp of the previous event === the + // timestamp of the current event, and no DateSeparator is inserted. + return panel._getTilesForEvent(e, e, e === lastShownEvent); + }).reduce((a, b) => a.concat(b), []); + + if (eventTiles.length === 0) { + eventTiles = null; + } + + ret.push( + + { eventTiles } + , + ); + + if (this.readMarker) { + ret.push(this.readMarker); + } + + return ret; + } + + getNewPrevEvent() { + return this.events[0]; + } +} + // Wrap consecutive member events in a ListSummary, ignore if redacted class MemberGrouper { static canStartGroup = function(panel, ev) { @@ -1111,4 +1202,4 @@ class MemberGrouper { } // all the grouper classes that we use -const groupers = [CreationGrouper, MemberGrouper]; +const groupers = [CreationGrouper, MemberGrouper, RedactionGrouper]; diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index daa38b9b3e..e78792ec51 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -2541,6 +2541,8 @@ "Logout": "Logout", "%(creator)s created this DM.": "%(creator)s created this DM.", "%(creator)s created and configured the room.": "%(creator)s created and configured the room.", + "%(count)s messages deleted.|other": "%(count)s messages deleted.", + "%(count)s messages deleted.|one": "%(count)s message deleted.", "Your Communities": "Your Communities", "Did you know: you can use communities to filter your %(brand)s experience!": "Did you know: you can use communities to filter your %(brand)s experience!", "To set up a filter, drag a community avatar over to the filter panel on the far left hand side of the screen. You can click on an avatar in the filter panel at any time to see only the rooms and people associated with that community.": "To set up a filter, drag a community avatar over to the filter panel on the far left hand side of the screen. You can click on an avatar in the filter panel at any time to see only the rooms and people associated with that community.", From 681529aa3cb54b630fd02a25613d4f9e7379c536 Mon Sep 17 00:00:00 2001 From: RiotRobot Date: Wed, 10 Mar 2021 17:26:35 +0000 Subject: [PATCH 06/63] Upgrade matrix-js-sdk to 9.9.0-rc.1 --- package.json | 2 +- yarn.lock | 7 ++++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 7ed1b272da..e7fff438ff 100644 --- a/package.json +++ b/package.json @@ -80,7 +80,7 @@ "katex": "^0.12.0", "linkifyjs": "^2.1.9", "lodash": "^4.17.20", - "matrix-js-sdk": "github:matrix-org/matrix-js-sdk#develop", + "matrix-js-sdk": "9.9.0-rc.1", "matrix-widget-api": "^0.1.0-beta.13", "minimist": "^1.2.5", "pako": "^2.0.3", diff --git a/yarn.lock b/yarn.lock index f99ea5900d..5c78e70590 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5572,9 +5572,10 @@ mathml-tag-names@^2.1.3: resolved "https://registry.yarnpkg.com/mathml-tag-names/-/mathml-tag-names-2.1.3.tgz#4ddadd67308e780cf16a47685878ee27b736a0a3" integrity sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg== -"matrix-js-sdk@github:matrix-org/matrix-js-sdk#develop": - version "9.8.0" - resolved "https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/fb73ab687826e4d05fb8b424ab013a771213f84f" +matrix-js-sdk@9.9.0-rc.1: + version "9.9.0-rc.1" + resolved "https://registry.yarnpkg.com/matrix-js-sdk/-/matrix-js-sdk-9.9.0-rc.1.tgz#5ee28aee89a87ccdf742d1512bc44ec54454e94f" + integrity sha512-A3pY5CyCNE5+QdpYL/C7FGU8KjBojXRWbtWsbeTMFwZEuzkIYlCtev1i07NbN3kbF83R3TtOfSCxQITm1C5jwg== dependencies: "@babel/runtime" "^7.12.5" another-json "^0.2.0" From 89f561a1ee645873e0a93fe1a6bc8967560ae8ab Mon Sep 17 00:00:00 2001 From: RiotRobot Date: Wed, 10 Mar 2021 17:31:17 +0000 Subject: [PATCH 07/63] Prepare changelog for v3.16.0-rc.1 --- CHANGELOG.md | 107 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 107 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c31eedf93b..d4ffeb5fbf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,110 @@ +Changes in [3.16.0-rc.1](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v3.16.0-rc.1) (2021-03-10) +=============================================================================================================== +[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v3.15.0...v3.16.0-rc.1) + + * Upgrade to JS SDK 9.9.0-rc.1 + * Translations update from Weblate + [\#5743](https://github.com/matrix-org/matrix-react-sdk/pull/5743) + * Document behaviour of showReadReceipts=false for sent receipts + [\#5739](https://github.com/matrix-org/matrix-react-sdk/pull/5739) + * Tweak sent marker code style + [\#5741](https://github.com/matrix-org/matrix-react-sdk/pull/5741) + * Fix sent markers disappearing for edits/reactions + [\#5737](https://github.com/matrix-org/matrix-react-sdk/pull/5737) + * Ignore to-device decryption in the room list store + [\#5740](https://github.com/matrix-org/matrix-react-sdk/pull/5740) + * Spaces suggested rooms support + [\#5736](https://github.com/matrix-org/matrix-react-sdk/pull/5736) + * Add tooltips to sent/sending receipts + [\#5738](https://github.com/matrix-org/matrix-react-sdk/pull/5738) + * Remove a bunch of useless 'use strict' definitions + [\#5735](https://github.com/matrix-org/matrix-react-sdk/pull/5735) + * [SK-1] Fix types for replaceableComponent + [\#5732](https://github.com/matrix-org/matrix-react-sdk/pull/5732) + * [SK-2] Make debugging skinning problems easier + [\#5733](https://github.com/matrix-org/matrix-react-sdk/pull/5733) + * Support sending invite reasons with /invite command + [\#5695](https://github.com/matrix-org/matrix-react-sdk/pull/5695) + * Fix clicking on the avatar for opening member info requires pixel-perfect + accuracy + [\#5717](https://github.com/matrix-org/matrix-react-sdk/pull/5717) + * Display decrypted and encrypted event source on the same dialog + [\#5713](https://github.com/matrix-org/matrix-react-sdk/pull/5713) + * Fix units of TURN server expiry time + [\#5730](https://github.com/matrix-org/matrix-react-sdk/pull/5730) + * Display room name in pills instead of address + [\#5624](https://github.com/matrix-org/matrix-react-sdk/pull/5624) + * Refresh UI for file uploads + [\#5723](https://github.com/matrix-org/matrix-react-sdk/pull/5723) + * UI refresh for uploaded files + [\#5719](https://github.com/matrix-org/matrix-react-sdk/pull/5719) + * Improve message sending states to match new designs + [\#5699](https://github.com/matrix-org/matrix-react-sdk/pull/5699) + * Add clipboard write permission for widgets + [\#5725](https://github.com/matrix-org/matrix-react-sdk/pull/5725) + * Fix widget resizing + [\#5722](https://github.com/matrix-org/matrix-react-sdk/pull/5722) + * Option for audio streaming + [\#5707](https://github.com/matrix-org/matrix-react-sdk/pull/5707) + * Show a specific error for hs_disabled + [\#5576](https://github.com/matrix-org/matrix-react-sdk/pull/5576) + * Add Edge to the targets list + [\#5721](https://github.com/matrix-org/matrix-react-sdk/pull/5721) + * File drop UI fixes and improvements + [\#5505](https://github.com/matrix-org/matrix-react-sdk/pull/5505) + * Fix Bottom border of state counters is white on the dark theme + [\#5715](https://github.com/matrix-org/matrix-react-sdk/pull/5715) + * Trim spurious whitespace of nicknames + [\#5332](https://github.com/matrix-org/matrix-react-sdk/pull/5332) + * Ensure HostSignupDialog border colour matches light theme + [\#5716](https://github.com/matrix-org/matrix-react-sdk/pull/5716) + * Don't place another call if there's already one ongoing + [\#5712](https://github.com/matrix-org/matrix-react-sdk/pull/5712) + * Space room hierarchies + [\#5706](https://github.com/matrix-org/matrix-react-sdk/pull/5706) + * Iterate Space view and right panel + [\#5705](https://github.com/matrix-org/matrix-react-sdk/pull/5705) + * Add a scroll to bottom on message sent setting + [\#5692](https://github.com/matrix-org/matrix-react-sdk/pull/5692) + * Add .tmp files to gitignore + [\#5708](https://github.com/matrix-org/matrix-react-sdk/pull/5708) + * Initial Space Room View and Creation UX + [\#5704](https://github.com/matrix-org/matrix-react-sdk/pull/5704) + * Add multi language spell check + [\#5452](https://github.com/matrix-org/matrix-react-sdk/pull/5452) + * Fix tetris effect (holes) in read receipts + [\#5697](https://github.com/matrix-org/matrix-react-sdk/pull/5697) + * Fixed edit for markdown images + [\#5703](https://github.com/matrix-org/matrix-react-sdk/pull/5703) + * Iterate Space Panel + [\#5702](https://github.com/matrix-org/matrix-react-sdk/pull/5702) + * Fix read receipts for compact layout + [\#5700](https://github.com/matrix-org/matrix-react-sdk/pull/5700) + * Space Store and Space Panel for Room List filtering + [\#5689](https://github.com/matrix-org/matrix-react-sdk/pull/5689) + * Log when turn creds expire + [\#5691](https://github.com/matrix-org/matrix-react-sdk/pull/5691) + * Null check for maxHeight in call view + [\#5690](https://github.com/matrix-org/matrix-react-sdk/pull/5690) + * Autocomplete invited users + [\#5687](https://github.com/matrix-org/matrix-react-sdk/pull/5687) + * Add send message button + [\#5535](https://github.com/matrix-org/matrix-react-sdk/pull/5535) + * Move call buttons to the room header + [\#5693](https://github.com/matrix-org/matrix-react-sdk/pull/5693) + * Use the default SSSS key if the default is set + [\#5638](https://github.com/matrix-org/matrix-react-sdk/pull/5638) + * Initial Spaces feature flag + [\#5668](https://github.com/matrix-org/matrix-react-sdk/pull/5668) + * Clean up code edge cases and add helpers + [\#5667](https://github.com/matrix-org/matrix-react-sdk/pull/5667) + * Clean up widgets when leaving the room + [\#5684](https://github.com/matrix-org/matrix-react-sdk/pull/5684) + * Fix read receipts? + [\#5567](https://github.com/matrix-org/matrix-react-sdk/pull/5567) + * Fix MAU usage alerts + [\#5678](https://github.com/matrix-org/matrix-react-sdk/pull/5678) + Changes in [3.15.0](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v3.15.0) (2021-03-01) ===================================================================================================== [Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v3.15.0-rc.1...v3.15.0) From ad1f9edba8297495bf54675bf7c3555fa0b197b6 Mon Sep 17 00:00:00 2001 From: RiotRobot Date: Wed, 10 Mar 2021 17:31:19 +0000 Subject: [PATCH 08/63] v3.16.0-rc.1 --- package.json | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index e7fff438ff..080b16b4ea 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "matrix-react-sdk", - "version": "3.15.0", + "version": "3.16.0-rc.1", "description": "SDK for matrix.org using React", "author": "matrix.org", "repository": { @@ -27,7 +27,7 @@ "matrix-gen-i18n": "scripts/gen-i18n.js", "matrix-prune-i18n": "scripts/prune-i18n.js" }, - "main": "./src/index.js", + "main": "./lib/index.js", "matrix_src_main": "./src/index.js", "matrix_lib_main": "./lib/index.js", "matrix_lib_typings": "./lib/index.d.ts", @@ -189,5 +189,6 @@ "transformIgnorePatterns": [ "/node_modules/(?!matrix-js-sdk).+$" ] - } + }, + "typings": "./lib/index.d.ts" } From 79ba898b3d6fc1264e785d24fbdfb3a8a9020854 Mon Sep 17 00:00:00 2001 From: RiotRobot Date: Wed, 10 Mar 2021 18:08:55 +0000 Subject: [PATCH 09/63] Prepare changelog for v3.16.0-rc.2 --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d4ffeb5fbf..0b130d1c70 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +Changes in [3.16.0-rc.2](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v3.16.0-rc.2) (2021-03-10) +=============================================================================================================== +[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v3.16.0-rc.1...v3.16.0-rc.2) + + * Fixed incorrect build output in rc.1 + Changes in [3.16.0-rc.1](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v3.16.0-rc.1) (2021-03-10) =============================================================================================================== [Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v3.15.0...v3.16.0-rc.1) From 71a3847c35aac3ad58bf49b5e15d986b55d32464 Mon Sep 17 00:00:00 2001 From: RiotRobot Date: Wed, 10 Mar 2021 18:08:56 +0000 Subject: [PATCH 10/63] v3.16.0-rc.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 080b16b4ea..ddc352104c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "matrix-react-sdk", - "version": "3.16.0-rc.1", + "version": "3.16.0-rc.2", "description": "SDK for matrix.org using React", "author": "matrix.org", "repository": { From baab0a0952327efa256de0b43bfba7447694b99c Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Wed, 10 Mar 2021 16:47:27 -0700 Subject: [PATCH 11/63] Change read receipt drift to be non-fractional I suspect this is what is causing issues in Firefox for read receipts not falling down. --- src/components/views/rooms/ReadReceiptMarker.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/views/rooms/ReadReceiptMarker.js b/src/components/views/rooms/ReadReceiptMarker.js index ba2b3064fd..0000d767df 100644 --- a/src/components/views/rooms/ReadReceiptMarker.js +++ b/src/components/views/rooms/ReadReceiptMarker.js @@ -156,14 +156,14 @@ export default class ReadReceiptMarker extends React.PureComponent { // then shift to the rightmost column, // and then it will drop down to its resting position // - // XXX: We use a fractional left value to trick velocity-animate into actually animating. + // XXX: We use a small left value to trick velocity-animate into actually animating. // This is a very annoying bug where if it thinks there's no change to `left` then it'll // skip applying it, thus making our read receipt at +14px instead of +0px like it // should be. This does cause a tiny amount of drift for read receipts, however with a // value so small it's not perceived by a user. // Note: Any smaller values (or trying to interchange units) might cause read receipts to // fail to fall down or cause gaps. - startStyles.push({ top: startTopOffset+'px', left: '0.001px' }); + startStyles.push({ top: startTopOffset+'px', left: '1px' }); enterTransitionOpts.push({ duration: bounce ? Math.min(Math.log(Math.abs(startTopOffset)) * 200, 3000) : 300, easing: bounce ? 'easeOutBounce' : 'easeOutCubic', From 37b6162c2550eadec74dfb93cc54cdedc58fe274 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Thu, 11 Mar 2021 08:29:03 +0100 Subject: [PATCH 12/63] Use resourceId in formatted body MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/editor/serialize.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/editor/serialize.ts b/src/editor/serialize.ts index c1f4da306b..40ff4b59f5 100644 --- a/src/editor/serialize.ts +++ b/src/editor/serialize.ts @@ -34,6 +34,10 @@ export function mdSerialize(model: EditorModel) { case "at-room-pill": return html + part.text; case "room-pill": + // Here we use the resourceId for compatibility with non-rich text clients + // See https://github.com/vector-im/element-web/issues/16660 + return html + + `[${part.resourceId.replace(/[[\\\]]/g, c => "\\" + c)}](${makeGenericPermalink(part.resourceId)})`; case "user-pill": return html + `[${part.text.replace(/[[\\\]]/g, c => "\\" + c)}](${makeGenericPermalink(part.resourceId)})`; From 5b65f367d3a9203d0383a5257e622a7e176ba000 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Thu, 11 Mar 2021 18:50:35 +0100 Subject: [PATCH 13/63] Use resourceId in plain body MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/editor/serialize.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/editor/serialize.ts b/src/editor/serialize.ts index 40ff4b59f5..d43cb4f38d 100644 --- a/src/editor/serialize.ts +++ b/src/editor/serialize.ts @@ -101,6 +101,9 @@ export function textSerialize(model: EditorModel) { case "at-room-pill": return text + part.text; case "room-pill": + // Here we use the resourceId for compatibility with non-rich text clients + // See https://github.com/vector-im/element-web/issues/16660 + return text + `${part.resourceId}`; case "user-pill": return text + `${part.text}`; } From 02051a39ff126d8ea4f328ed705ec5d2fc573d24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Fri, 12 Mar 2021 13:55:14 +0100 Subject: [PATCH 14/63] Hangup all calls on logout MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/CallHandler.tsx | 5 +++++ src/components/structures/MatrixChat.tsx | 1 + 2 files changed, 6 insertions(+) diff --git a/src/CallHandler.tsx b/src/CallHandler.tsx index d2564e637b..ce779f12a5 100644 --- a/src/CallHandler.tsx +++ b/src/CallHandler.tsx @@ -788,6 +788,11 @@ export default class CallHandler { // don't remove the call yet: let the hangup event handler do it (otherwise it will throw // the hangup event away) break; + case 'hangup_all': + for (const call of this.calls.values()) { + call.hangup(CallErrorCode.UserHangup, false); + } + break; case 'answer': { if (!this.calls.has(payload.room_id)) { return; // no call to answer diff --git a/src/components/structures/MatrixChat.tsx b/src/components/structures/MatrixChat.tsx index 0272633e8f..f0a3778a2b 100644 --- a/src/components/structures/MatrixChat.tsx +++ b/src/components/structures/MatrixChat.tsx @@ -582,6 +582,7 @@ export default class MatrixChat extends React.PureComponent { } break; case 'logout': + dis.dispatch({action: "hangup_all"}); Lifecycle.logout(); break; case 'require_registration': From 6210614ce7138f871818df4941cdd04d65e6cc1d Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 12 Mar 2021 17:37:15 +0000 Subject: [PATCH 15/63] Rebuild space panel layout to resolve weirdness with selections and nesting --- res/css/structures/_SpacePanel.scss | 42 ++++++++--------------------- 1 file changed, 11 insertions(+), 31 deletions(-) diff --git a/res/css/structures/_SpacePanel.scss b/res/css/structures/_SpacePanel.scss index 9937117086..4ced807f66 100644 --- a/res/css/structures/_SpacePanel.scss +++ b/res/css/structures/_SpacePanel.scss @@ -16,9 +16,8 @@ limitations under the License. $topLevelHeight: 32px; $nestedHeight: 24px; -$gutterSize: 17px; -$activeStripeSize: 4px; -$activeBorderTransparentGap: 2px; +$gutterSize: 16px; +$activeBorderTransparentGap: 1px; $activeBackgroundColor: $roomtile-selected-bg-color; $activeBorderColor: $secondary-fg-color; @@ -102,14 +101,11 @@ $activeBorderColor: $secondary-fg-color; &.mx_SpaceButton_active { &:not(.mx_SpaceButton_narrow) .mx_SpaceButton_selectionWrapper { background-color: $activeBackgroundColor; - border-radius: 8px; } - &.mx_SpaceButton_narrow { - .mx_BaseAvatar, .mx_SpaceButton_avatarPlaceholder { - border: 2px $activeBorderColor solid; - border-radius: 11px; - } + &.mx_SpaceButton_narrow .mx_SpaceButton_selectionWrapper { + padding: $activeBorderTransparentGap; + border: 3px $activeBorderColor solid; } } @@ -117,6 +113,8 @@ $activeBorderColor: $secondary-fg-color; display: flex; flex: 1; align-items: center; + border-radius: 12px; + padding: 4px; } .mx_SpaceButton_name { @@ -133,8 +131,8 @@ $activeBorderColor: $secondary-fg-color; } .mx_SpaceButton_toggleCollapse { - width: calc($gutterSize - $activeStripeSize); - margin-left: 1px; + width: $gutterSize; + margin-right: -4px; height: 20px; mask-position: center; mask-size: 20px; @@ -172,11 +170,6 @@ $activeBorderColor: $secondary-fg-color; } } - .mx_SpaceButton_avatarPlaceholder { - border: $activeBorderTransparentGap transparent solid; - padding: $activeBorderTransparentGap; - } - &.mx_SpaceButton_new .mx_SpaceButton_icon { background-color: $accent-color; transition: all .1s ease-in-out; // TODO transition @@ -196,21 +189,8 @@ $activeBorderColor: $secondary-fg-color; } } - .mx_BaseAvatar { - /* moving the border-radius to this element from _image - element so we can add a border to it without the initials being displaced */ - overflow: hidden; - border: 2px transparent solid; - padding: $activeBorderTransparentGap; - - .mx_BaseAvatar_initial { - top: $activeBorderTransparentGap; - left: $activeBorderTransparentGap; - } - - .mx_BaseAvatar_image { - border-radius: 8px; - } + .mx_BaseAvatar_image { + border-radius: 8px; } .mx_SpaceButton_menuButton { From c8d7b0e1b9baca45b0c1570370403b037b64d728 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Fri, 12 Mar 2021 14:56:47 -0700 Subject: [PATCH 16/63] Collapse copyright --- src/components/structures/MatrixChat.tsx | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/components/structures/MatrixChat.tsx b/src/components/structures/MatrixChat.tsx index 99397640ed..d3e0778e82 100644 --- a/src/components/structures/MatrixChat.tsx +++ b/src/components/structures/MatrixChat.tsx @@ -1,8 +1,5 @@ /* -Copyright 2015, 2016 OpenMarket Ltd -Copyright 2017 Vector Creations Ltd -Copyright 2017-2019 New Vector Ltd -Copyright 2019, 2020 The Matrix.org Foundation C.I.C. +Copyright 2015-2019-2021 The Matrix.org Foundation C.I.C. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. From 75cfd9a8f596af114f20011896d47a00a4c4082e Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Fri, 12 Mar 2021 15:01:05 -0700 Subject: [PATCH 17/63] Redirect to login properly --- src/components/structures/MatrixChat.tsx | 31 +++++++++++++----------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/src/components/structures/MatrixChat.tsx b/src/components/structures/MatrixChat.tsx index d3e0778e82..dab75be46e 100644 --- a/src/components/structures/MatrixChat.tsx +++ b/src/components/structures/MatrixChat.tsx @@ -604,12 +604,7 @@ export default class MatrixChat extends React.PureComponent { if (payload.screenAfterLogin) { this.screenAfterLogin = payload.screenAfterLogin; } - this.setStateForNewView({ - view: Views.LOGIN, - }); - this.notifyNewScreen('login'); - ThemeController.isLogin = true; - this.themeWatcher.recheck(); + this.viewLogin(); break; case 'start_password_recovery': this.setStateForNewView({ @@ -973,6 +968,9 @@ export default class MatrixChat extends React.PureComponent { } private viewWelcome() { + if (shouldUseLoginForWelcome(SdkConfig.get())) { + return this.viewLogin(); + } this.setStateForNewView({ view: Views.WELCOME, }); @@ -981,6 +979,16 @@ export default class MatrixChat extends React.PureComponent { this.themeWatcher.recheck(); } + private viewLogin(otherState?: any) { + this.setStateForNewView({ + view: Views.LOGIN, + ...otherState, + }); + this.notifyNewScreen('login'); + ThemeController.isLogin = true; + this.themeWatcher.recheck(); + } + private viewHome(justRegistered = false) { // The home page requires the "logged in" view, so we'll set that. this.setStateForNewView({ @@ -1296,17 +1304,13 @@ export default class MatrixChat extends React.PureComponent { * Called when the session is logged out */ private onLoggedOut() { - this.notifyNewScreen('login'); - this.setStateForNewView({ - view: Views.LOGIN, + this.viewLogin({ ready: false, collapseLhs: false, currentRoomId: null, }); this.subTitleStatus = ''; this.setPageSubtitle(); - ThemeController.isLogin = true; - this.themeWatcher.recheck(); } /** @@ -2011,7 +2015,7 @@ export default class MatrixChat extends React.PureComponent { ); } - } else if (this.state.view === Views.WELCOME && !shouldUseLoginForWelcome(SdkConfig.get())) { + } else if (this.state.view === Views.WELCOME) { const Welcome = sdk.getComponent('auth.Welcome'); view = ; } else if (this.state.view === Views.REGISTER && SettingsStore.getValue(UIFeature.Registration)) { @@ -2043,8 +2047,7 @@ export default class MatrixChat extends React.PureComponent { {...this.getServerProperties()} /> ); - } else if (this.state.view === Views.LOGIN - || (this.state.view === Views.WELCOME && shouldUseLoginForWelcome(SdkConfig.get()))) { + } else if (this.state.view === Views.LOGIN) { const showPasswordReset = SettingsStore.getValue(UIFeature.PasswordReset); const Login = sdk.getComponent('structures.auth.Login'); view = ( From 9b6d9b3bf1a81926658dd8a6c59c385c85575c66 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Fri, 12 Mar 2021 15:02:39 -0700 Subject: [PATCH 18/63] years are difficult --- src/components/structures/MatrixChat.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/structures/MatrixChat.tsx b/src/components/structures/MatrixChat.tsx index dab75be46e..c5620c7ba2 100644 --- a/src/components/structures/MatrixChat.tsx +++ b/src/components/structures/MatrixChat.tsx @@ -1,5 +1,5 @@ /* -Copyright 2015-2019-2021 The Matrix.org Foundation C.I.C. +Copyright 2015-2021 The Matrix.org Foundation C.I.C. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. From 4811154f25db75bbba8ee3dfd0ba09256a81c496 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Fri, 12 Mar 2021 15:24:16 -0700 Subject: [PATCH 19/63] Remove now-unused assets and CSS from CompleteSecurity step --- .../structures/auth/_CompleteSecurity.scss | 44 ------------------- res/img/feather-customised/monitor.svg | 5 --- res/img/feather-customised/smartphone.svg | 4 -- 3 files changed, 53 deletions(-) delete mode 100644 res/img/feather-customised/monitor.svg delete mode 100644 res/img/feather-customised/smartphone.svg diff --git a/res/css/structures/auth/_CompleteSecurity.scss b/res/css/structures/auth/_CompleteSecurity.scss index f742be70e4..80e7aaada0 100644 --- a/res/css/structures/auth/_CompleteSecurity.scss +++ b/res/css/structures/auth/_CompleteSecurity.scss @@ -26,50 +26,6 @@ limitations under the License. position: relative; } -.mx_CompleteSecurity_clients { - width: max-content; - margin: 36px auto 0; - - .mx_CompleteSecurity_clients_desktop, .mx_CompleteSecurity_clients_mobile { - position: relative; - width: 160px; - text-align: center; - padding-top: 64px; - display: inline-block; - - &::before { - content: ''; - position: absolute; - height: 48px; - width: 48px; - left: 56px; - top: 0; - background-color: $muted-fg-color; - mask-repeat: no-repeat; - mask-size: contain; - } - } - - .mx_CompleteSecurity_clients_desktop { - margin-right: 56px; - } - - .mx_CompleteSecurity_clients_desktop::before { - mask-image: url('$(res)/img/feather-customised/monitor.svg'); - } - - .mx_CompleteSecurity_clients_mobile::before { - mask-image: url('$(res)/img/feather-customised/smartphone.svg'); - } - - p { - margin-top: 16px; - font-size: $font-12px; - color: $muted-fg-color; - text-align: center; - } -} - .mx_CompleteSecurity_heroIcon { width: 128px; height: 128px; diff --git a/res/img/feather-customised/monitor.svg b/res/img/feather-customised/monitor.svg deleted file mode 100644 index 231811d5a6..0000000000 --- a/res/img/feather-customised/monitor.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/res/img/feather-customised/smartphone.svg b/res/img/feather-customised/smartphone.svg deleted file mode 100644 index fde78c82e2..0000000000 --- a/res/img/feather-customised/smartphone.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - From 830080c23ec2dbb798ae1fbe6d7bb7ff399ce424 Mon Sep 17 00:00:00 2001 From: Tulir Asokan Date: Sun, 14 Mar 2021 19:34:33 +0200 Subject: [PATCH 20/63] Add details and summary to allowed HTML tags Implements MSC2184 (https://github.com/matrix-org/matrix-doc/pull/2184) Signed-off-by: Tulir Asokan --- src/HtmlUtils.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/HtmlUtils.tsx b/src/HtmlUtils.tsx index 59b596a5da..1dc342fac5 100644 --- a/src/HtmlUtils.tsx +++ b/src/HtmlUtils.tsx @@ -237,6 +237,7 @@ const sanitizeHtmlParams: IExtendedSanitizeOptions = { 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'blockquote', 'p', 'a', 'ul', 'ol', 'sup', 'sub', 'nl', 'li', 'b', 'i', 'u', 'strong', 'em', 'strike', 'code', 'hr', 'br', 'div', 'table', 'thead', 'caption', 'tbody', 'tr', 'th', 'td', 'pre', 'span', 'img', + 'details', 'summary', ], allowedAttributes: { // custom ones first: From f9eace1729bbe52d65378c8d66d02820d59d357a Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 15 Mar 2021 11:59:46 +0000 Subject: [PATCH 21/63] Rebuild alignment of the menu buttons and notification badges --- res/css/structures/_SpacePanel.scss | 63 +++++++++++++++++++++-------- 1 file changed, 46 insertions(+), 17 deletions(-) diff --git a/res/css/structures/_SpacePanel.scss b/res/css/structures/_SpacePanel.scss index 4ced807f66..f489d7b8b5 100644 --- a/res/css/structures/_SpacePanel.scss +++ b/res/css/structures/_SpacePanel.scss @@ -62,21 +62,26 @@ $activeBorderColor: $secondary-fg-color; } .mx_AutoHideScrollbar { - padding: 16px 12px 16px 0; + padding: 16px 0; } .mx_SpaceButton_toggleCollapse { cursor: pointer; } - .mx_SpaceItem.collapsed { - .mx_SpaceButton { - .mx_NotificationBadge { - right: -4px; - top: -4px; - } - } + .mx_SpaceTreeLevel { + display: flex; + flex-direction: column; + max-width: 250px; + flex-grow: 1; + } + .mx_SpaceItem { + display: inline-flex; + flex-flow: wrap; + } + + .mx_SpaceItem.collapsed { & > .mx_SpaceButton > .mx_SpaceButton_toggleCollapse { transform: rotate(-90deg); } @@ -88,15 +93,16 @@ $activeBorderColor: $secondary-fg-color; .mx_SpaceItem:not(.hasSubSpaces) > .mx_SpaceButton { margin-left: $gutterSize; + min-width: 40px; } .mx_SpaceButton { border-radius: 8px; - position: relative; margin-bottom: 2px; display: flex; align-items: center; - padding: 4px; + padding: 4px 4px 4px 0; + width: 100%; &.mx_SpaceButton_active { &:not(.mx_SpaceButton_narrow) .mx_SpaceButton_selectionWrapper { @@ -110,6 +116,7 @@ $activeBorderColor: $secondary-fg-color; } .mx_SpaceButton_selectionWrapper { + position: relative; display: flex; flex: 1; align-items: center; @@ -117,12 +124,23 @@ $activeBorderColor: $secondary-fg-color; padding: 4px; } + &:not(.mx_SpaceButton_narrow) { + .mx_SpaceButton_selectionWrapper { + width: 100%; + padding-right: 16px; + overflow: hidden; + } + } + + &.mx_SpaceButton_narrow { + margin-right: 10px; + } + .mx_SpaceButton_name { flex: 1; margin-left: 8px; white-space: nowrap; display: block; - max-width: 150px; text-overflow: ellipsis; overflow: hidden; padding-right: 8px; @@ -199,8 +217,9 @@ $activeBorderColor: $secondary-fg-color; height: 20px; margin-top: auto; margin-bottom: auto; - position: relative; display: none; + position: absolute; + right: 4px; &::before { top: 2px; @@ -219,9 +238,8 @@ $activeBorderColor: $secondary-fg-color; } .mx_SpacePanel_badgeContainer { + position: absolute; height: 16px; - // don't set width so that it takes no space when there is no badge to show - margin: auto 0; // vertically align // Create a flexbox to make aligning dot badges easier display: flex; @@ -241,14 +259,25 @@ $activeBorderColor: $secondary-fg-color; &.collapsed { .mx_SpaceButton { .mx_SpacePanel_badgeContainer { - position: absolute; - right: 0px; - top: 2px; + right: -3px; + top: -3px; + } + + &.mx_SpaceButton_active .mx_SpacePanel_badgeContainer { + // when we draw the selection border we move the relative bounds of our parent + // so update our position within the bounds of the parent to maintain position overall + right: -6px; + top: -6px; } } } &:not(.collapsed) { + .mx_SpacePanel_badgeContainer { + position: absolute; + right: 4px; + } + .mx_SpaceButton:hover, .mx_SpaceButton:focus-within, .mx_SpaceButton_hasMenuOpen { From 272f06d57a84c714f92ad6570a6f8d506aeccec9 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 15 Mar 2021 12:16:48 +0000 Subject: [PATCH 22/63] Fix Space Panel exploding with cyclic hierarchies --- .../views/spaces/SpaceTreeLevel.tsx | 23 ++++++++++++++----- 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/src/components/views/spaces/SpaceTreeLevel.tsx b/src/components/views/spaces/SpaceTreeLevel.tsx index 04d6c02208..83bc2296e7 100644 --- a/src/components/views/spaces/SpaceTreeLevel.tsx +++ b/src/components/views/spaces/SpaceTreeLevel.tsx @@ -51,6 +51,7 @@ interface IItemProps { isNested?: boolean; isPanelCollapsed?: boolean; onExpand?: Function; + parents?: Set; } interface IItemState { @@ -299,7 +300,8 @@ export class SpaceItem extends React.PureComponent { const isNarrow = this.props.isPanelCollapsed; const collapsed = this.state.collapsed || forceCollapsed; - const childSpaces = SpaceStore.instance.getChildSpaces(space.roomId); + const childSpaces = SpaceStore.instance.getChildSpaces(space.roomId) + .filter(s => !this.props.parents?.has(s.roomId)); const isActive = activeSpaces.includes(space); const itemClasses = classNames({ "mx_SpaceItem": true, @@ -312,11 +314,17 @@ export class SpaceItem extends React.PureComponent { mx_SpaceButton_narrow: isNarrow, }); const notificationState = SpaceStore.instance.getNotificationState(space.roomId); - const childItems = childSpaces && !collapsed ? : null; + + let childItems; + if (childSpaces && !collapsed) { + childItems = ; + } + let notifBadge; if (notificationState) { notifBadge =
    @@ -383,12 +391,14 @@ interface ITreeLevelProps { spaces: Room[]; activeSpaces: Room[]; isNested?: boolean; + parents: Set; } const SpaceTreeLevel: React.FC = ({ spaces, activeSpaces, isNested, + parents, }) => { return
      {spaces.map(s => { @@ -397,6 +407,7 @@ const SpaceTreeLevel: React.FC = ({ activeSpaces={activeSpaces} space={s} isNested={isNested} + parents={parents} />); })}
    ; From 62983ca48efd4ec4087a5d29cce3b11acce6d34a Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 15 Mar 2021 12:57:35 +0000 Subject: [PATCH 23/63] Fix space panel alignments when no scrollbar is present --- res/css/structures/_SpacePanel.scss | 5 +---- res/css/structures/_SpaceRoomDirectory.scss | 1 + 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/res/css/structures/_SpacePanel.scss b/res/css/structures/_SpacePanel.scss index f489d7b8b5..afcf48ec4d 100644 --- a/res/css/structures/_SpacePanel.scss +++ b/res/css/structures/_SpacePanel.scss @@ -35,6 +35,7 @@ $activeBorderColor: $secondary-fg-color; .mx_SpacePanel_spaceTreeWrapper { flex: 1; + overflow-y: scroll; } .mx_SpacePanel_toggleCollapse { @@ -132,10 +133,6 @@ $activeBorderColor: $secondary-fg-color; } } - &.mx_SpaceButton_narrow { - margin-right: 10px; - } - .mx_SpaceButton_name { flex: 1; margin-left: 8px; diff --git a/res/css/structures/_SpaceRoomDirectory.scss b/res/css/structures/_SpaceRoomDirectory.scss index c96398594f..b14e92a1af 100644 --- a/res/css/structures/_SpaceRoomDirectory.scss +++ b/res/css/structures/_SpaceRoomDirectory.scss @@ -132,6 +132,7 @@ limitations under the License. height: min-content; margin-left: auto; margin-right: 16px; + display: inline-flex; } } From da627dab20656757fa46d5a95fe8717571852011 Mon Sep 17 00:00:00 2001 From: RiotRobot Date: Mon, 15 Mar 2021 14:38:57 +0000 Subject: [PATCH 24/63] Upgrade matrix-js-sdk to 9.9.0 --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index ddc352104c..7324d5b352 100644 --- a/package.json +++ b/package.json @@ -80,7 +80,7 @@ "katex": "^0.12.0", "linkifyjs": "^2.1.9", "lodash": "^4.17.20", - "matrix-js-sdk": "9.9.0-rc.1", + "matrix-js-sdk": "9.9.0", "matrix-widget-api": "^0.1.0-beta.13", "minimist": "^1.2.5", "pako": "^2.0.3", diff --git a/yarn.lock b/yarn.lock index 5c78e70590..aaee47b876 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5572,10 +5572,10 @@ mathml-tag-names@^2.1.3: resolved "https://registry.yarnpkg.com/mathml-tag-names/-/mathml-tag-names-2.1.3.tgz#4ddadd67308e780cf16a47685878ee27b736a0a3" integrity sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg== -matrix-js-sdk@9.9.0-rc.1: - version "9.9.0-rc.1" - resolved "https://registry.yarnpkg.com/matrix-js-sdk/-/matrix-js-sdk-9.9.0-rc.1.tgz#5ee28aee89a87ccdf742d1512bc44ec54454e94f" - integrity sha512-A3pY5CyCNE5+QdpYL/C7FGU8KjBojXRWbtWsbeTMFwZEuzkIYlCtev1i07NbN3kbF83R3TtOfSCxQITm1C5jwg== +matrix-js-sdk@9.9.0: + version "9.9.0" + resolved "https://registry.yarnpkg.com/matrix-js-sdk/-/matrix-js-sdk-9.9.0.tgz#30c46419c026fcad0ff25aea9417b77921f64c6d" + integrity sha512-rgy9b8D+GzjK3wfdmxDr42fxNV13fK12cvQD1qnsFzqPyJGeg++cazH/+7HxL/uuW/WQR6HAmfc7wo9VQegWtg== dependencies: "@babel/runtime" "^7.12.5" another-json "^0.2.0" From 2ef9f3327e1c2635a8c9cb4e8df8a48521ac3e4b Mon Sep 17 00:00:00 2001 From: RiotRobot Date: Mon, 15 Mar 2021 14:45:35 +0000 Subject: [PATCH 25/63] Prepare changelog for v3.16.0 --- CHANGELOG.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0b130d1c70..ea4b015ee9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,12 @@ +Changes in [3.16.0](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v3.16.0) (2021-03-15) +===================================================================================================== +[Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v3.16.0-rc.2...v3.16.0) + + * [Release] Change read receipt drift to be non-fractional + [\#5746](https://github.com/matrix-org/matrix-react-sdk/pull/5746) + * [Release] Properly gate SpaceRoomView behind labs + [\#5750](https://github.com/matrix-org/matrix-react-sdk/pull/5750) + Changes in [3.16.0-rc.2](https://github.com/matrix-org/matrix-react-sdk/releases/tag/v3.16.0-rc.2) (2021-03-10) =============================================================================================================== [Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v3.16.0-rc.1...v3.16.0-rc.2) From 44fd41cb1e769038914025c836c8d241ad2b4be6 Mon Sep 17 00:00:00 2001 From: RiotRobot Date: Mon, 15 Mar 2021 14:45:36 +0000 Subject: [PATCH 26/63] v3.16.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 7324d5b352..5bbb2a0a15 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "matrix-react-sdk", - "version": "3.16.0-rc.2", + "version": "3.16.0", "description": "SDK for matrix.org using React", "author": "matrix.org", "repository": { From 1d1263c39a6b261bb5c6fde9a244377e16b4a1e5 Mon Sep 17 00:00:00 2001 From: RiotRobot Date: Mon, 15 Mar 2021 14:46:57 +0000 Subject: [PATCH 27/63] Resetting package fields for development --- package.json | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 2841e01824..ab450f0526 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ "matrix-gen-i18n": "scripts/gen-i18n.js", "matrix-prune-i18n": "scripts/prune-i18n.js" }, - "main": "./lib/index.js", + "main": "./src/index.js", "matrix_src_main": "./src/index.js", "matrix_lib_main": "./lib/index.js", "matrix_lib_typings": "./lib/index.d.ts", @@ -190,6 +190,5 @@ "transformIgnorePatterns": [ "/node_modules/(?!matrix-js-sdk).+$" ] - }, - "typings": "./lib/index.d.ts" + } } From a696c09e1ec11c43312360def847e582971d9014 Mon Sep 17 00:00:00 2001 From: RiotRobot Date: Mon, 15 Mar 2021 14:47:08 +0000 Subject: [PATCH 28/63] Reset matrix-js-sdk back to develop branch --- package.json | 2 +- yarn.lock | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index ab450f0526..f8b4287197 100644 --- a/package.json +++ b/package.json @@ -80,7 +80,7 @@ "katex": "^0.12.0", "linkifyjs": "^2.1.9", "lodash": "^4.17.20", - "matrix-js-sdk": "9.9.0", + "matrix-js-sdk": "github:matrix-org/matrix-js-sdk#develop", "matrix-widget-api": "^0.1.0-beta.13", "minimist": "^1.2.5", "pako": "^2.0.3", diff --git a/yarn.lock b/yarn.lock index 275c315921..58686248f7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5587,10 +5587,9 @@ mathml-tag-names@^2.1.3: resolved "https://registry.yarnpkg.com/mathml-tag-names/-/mathml-tag-names-2.1.3.tgz#4ddadd67308e780cf16a47685878ee27b736a0a3" integrity sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg== -matrix-js-sdk@9.9.0: +"matrix-js-sdk@github:matrix-org/matrix-js-sdk#develop": version "9.9.0" - resolved "https://registry.yarnpkg.com/matrix-js-sdk/-/matrix-js-sdk-9.9.0.tgz#30c46419c026fcad0ff25aea9417b77921f64c6d" - integrity sha512-rgy9b8D+GzjK3wfdmxDr42fxNV13fK12cvQD1qnsFzqPyJGeg++cazH/+7HxL/uuW/WQR6HAmfc7wo9VQegWtg== + resolved "https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/cd38fb9b4c349eb31feac14e806e710bf6431b72" dependencies: "@babel/runtime" "^7.12.5" another-json "^0.2.0" From 3154dd6cd441b7710a525e75512e1665adf60746 Mon Sep 17 00:00:00 2001 From: RiotRobot Date: Mon, 15 Mar 2021 14:48:40 +0000 Subject: [PATCH 29/63] Add SDK version to changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ea4b015ee9..38b1a2572f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ Changes in [3.16.0](https://github.com/matrix-org/matrix-react-sdk/releases/tag/ ===================================================================================================== [Full Changelog](https://github.com/matrix-org/matrix-react-sdk/compare/v3.16.0-rc.2...v3.16.0) + * Upgrade to JS SDK 9.9.0 * [Release] Change read receipt drift to be non-fractional [\#5746](https://github.com/matrix-org/matrix-react-sdk/pull/5746) * [Release] Properly gate SpaceRoomView behind labs From f575625c7a5a5ec8984ae40aa5a97b3436249260 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Timo=20K=C3=B6sters?= Date: Mon, 15 Mar 2021 23:13:16 +0100 Subject: [PATCH 30/63] fix: make room directory correct when using a homeserver with explicit port Server names are allowed to contain ':' to specify a port, see https://matrix.org/docs/spec/appendices#server-name User ids on the other hand are not allowed to contain ':', even historical user ids, see https://matrix.org/docs/spec/appendices#historical-user-ids Therefore we can use change the regex to make sure the localpart is not allowed to contain ':'. --- src/MatrixClientPeg.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/MatrixClientPeg.ts b/src/MatrixClientPeg.ts index 98ca446532..de1d573d40 100644 --- a/src/MatrixClientPeg.ts +++ b/src/MatrixClientPeg.ts @@ -261,7 +261,7 @@ class _MatrixClientPeg implements IMatrixClientPeg { } public getHomeserverName(): string { - const matches = /^@.+:(.+)$/.exec(this.matrixClient.credentials.userId); + const matches = /^@[^:]+:(.+)$/.exec(this.matrixClient.credentials.userId); if (matches === null || matches.length < 1) { throw new Error("Failed to derive homeserver name from user ID!"); } From baaceccfdaeae98bd08cf477356aea6ceed0b12e Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 16 Mar 2021 10:53:27 +0000 Subject: [PATCH 31/63] Rearrange dom hierarchy to put space panel at same level as LeftPanel so they don't steal horizontal space from each other --- res/css/structures/_LeftPanel.scss | 2 +- res/css/structures/_MatrixChat.scss | 2 +- src/components/structures/LeftPanel.tsx | 7 +------ src/components/structures/LoggedInView.tsx | 23 +++++++--------------- 4 files changed, 10 insertions(+), 24 deletions(-) diff --git a/res/css/structures/_LeftPanel.scss b/res/css/structures/_LeftPanel.scss index f1f27014ee..c4bbdefb44 100644 --- a/res/css/structures/_LeftPanel.scss +++ b/res/css/structures/_LeftPanel.scss @@ -19,7 +19,7 @@ $roomListCollapsedWidth: 68px; .mx_LeftPanel { background-color: $roomlist-bg-color; - min-width: 260px; + min-width: 200px; max-width: 50%; // Create a row-based flexbox for the GroupFilterPanel and the room list diff --git a/res/css/structures/_MatrixChat.scss b/res/css/structures/_MatrixChat.scss index 812a7f8472..a220c5d505 100644 --- a/res/css/structures/_MatrixChat.scss +++ b/res/css/structures/_MatrixChat.scss @@ -66,7 +66,7 @@ limitations under the License. } /* not the left panel, and not the resize handle, so the roomview/groupview/... */ -.mx_MatrixChat > :not(.mx_LeftPanel):not(.mx_ResizeHandle) { +.mx_MatrixChat > :not(.mx_LeftPanel):not(.mx_SpacePanel):not(.mx_ResizeHandle) { background-color: $primary-bg-color; flex: 1 1 0; diff --git a/src/components/structures/LeftPanel.tsx b/src/components/structures/LeftPanel.tsx index 82dd9443cc..077dbbb29c 100644 --- a/src/components/structures/LeftPanel.tsx +++ b/src/components/structures/LeftPanel.tsx @@ -39,7 +39,6 @@ import { OwnProfileStore } from "../../stores/OwnProfileStore"; import { MatrixClientPeg } from "../../MatrixClientPeg"; import RoomListNumResults from "../views/rooms/RoomListNumResults"; import LeftPanelWidget from "./LeftPanelWidget"; -import SpacePanel from "../views/spaces/SpacePanel"; interface IProps { isMinimized: boolean; @@ -390,11 +389,7 @@ export default class LeftPanel extends React.Component { public render(): React.ReactNode { let leftLeftPanel; - // Currently TagPanel.enableTagPanel is disabled when Legacy Communities are disabled so for now - // ignore it and force the rendering of SpacePanel if that Labs flag is enabled. - if (SettingsStore.getValue("feature_spaces")) { - leftLeftPanel = ; - } else if (this.state.showGroupFilterPanel) { + if (this.state.showGroupFilterPanel) { leftLeftPanel = (
    diff --git a/src/components/structures/LoggedInView.tsx b/src/components/structures/LoggedInView.tsx index 3e6d56fd54..b7212ddb25 100644 --- a/src/components/structures/LoggedInView.tsx +++ b/src/components/structures/LoggedInView.tsx @@ -56,6 +56,7 @@ import Modal from "../../Modal"; import { ICollapseConfig } from "../../resizer/distributors/collapse"; import HostSignupContainer from '../views/host_signup/HostSignupContainer'; import { IOpts } from "../../createRoom"; +import SpacePanel from "../views/spaces/SpacePanel"; // We need to fetch each pinned message individually (if we don't already have it) // so each pinned message may trigger a request. Limit the number per room for sanity. @@ -227,14 +228,7 @@ class LoggedInView extends React.Component { let size; let collapsed; const collapseConfig: ICollapseConfig = { - // TODO: the space panel currently does not have a fixed width, - // just the headers at each level have a max-width of 150px - // Taking 222px for the space panel for now, - // so this will look slightly off for now, - // depending on the depth of your space tree. - // To fix this, we'll need to turn toggleSize - // into a callback so it can be measured when starting the resize operation - toggleSize: 222 + 68, + toggleSize: 200, onCollapsed: (_collapsed) => { collapsed = _collapsed; if (_collapsed) { @@ -668,13 +662,6 @@ class LoggedInView extends React.Component { bodyClasses += ' mx_MatrixChat_useCompactLayout'; } - const leftPanel = ( - - ); - return (
    {
    - { leftPanel } + { SettingsStore.getValue("feature_spaces") ? : null } + { pageElement }
    From 11cd791c7606ecafa2af93826d69b36cc48c1d27 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 16 Mar 2021 11:11:44 +0000 Subject: [PATCH 32/63] Tweak left panel min widths for better flexibility --- res/css/structures/_LeftPanel.scss | 3 ++- src/components/structures/LoggedInView.tsx | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/res/css/structures/_LeftPanel.scss b/res/css/structures/_LeftPanel.scss index c4bbdefb44..10357117da 100644 --- a/res/css/structures/_LeftPanel.scss +++ b/res/css/structures/_LeftPanel.scss @@ -19,7 +19,8 @@ $roomListCollapsedWidth: 68px; .mx_LeftPanel { background-color: $roomlist-bg-color; - min-width: 200px; + // TODO decrease this once Spaces launches as it'll no longer need to include the 56px Community Panel + min-width: 206px; max-width: 50%; // Create a row-based flexbox for the GroupFilterPanel and the room list diff --git a/src/components/structures/LoggedInView.tsx b/src/components/structures/LoggedInView.tsx index b7212ddb25..3c7e79b89d 100644 --- a/src/components/structures/LoggedInView.tsx +++ b/src/components/structures/LoggedInView.tsx @@ -228,7 +228,8 @@ class LoggedInView extends React.Component { let size; let collapsed; const collapseConfig: ICollapseConfig = { - toggleSize: 200, + // TODO decrease this once Spaces launches as it'll no longer need to include the 56px Community Panel + toggleSize: 206 - 50, onCollapsed: (_collapsed) => { collapsed = _collapsed; if (_collapsed) { From 67e2ae99880aa2d45b6bcf8f1fb8d2b202153f7a Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 16 Mar 2021 15:42:03 +0000 Subject: [PATCH 33/63] Make some dispatches async It looks like these dispatches were being dispatched while already dispatching, causing soft crashes (although I can't see where the first dispatch was from). Either way, seems like these didn't need to be sync. --- src/components/structures/LoggedInView.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/structures/LoggedInView.tsx b/src/components/structures/LoggedInView.tsx index 15e90a383a..936eb819ba 100644 --- a/src/components/structures/LoggedInView.tsx +++ b/src/components/structures/LoggedInView.tsx @@ -240,10 +240,10 @@ class LoggedInView extends React.Component { onCollapsed: (_collapsed) => { collapsed = _collapsed; if (_collapsed) { - dis.dispatch({action: "hide_left_panel"}, true); + dis.dispatch({action: "hide_left_panel"}); window.localStorage.setItem("mx_lhs_size", '0'); } else { - dis.dispatch({action: "show_left_panel"}, true); + dis.dispatch({action: "show_left_panel"}); } }, onResized: (_size) => { From 5ce3a05867e839eb41f66773899c176c3cbd80dc Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 16 Mar 2021 16:34:28 +0000 Subject: [PATCH 34/63] Update _SpacePanel.scss --- res/css/structures/_SpacePanel.scss | 2 ++ 1 file changed, 2 insertions(+) diff --git a/res/css/structures/_SpacePanel.scss b/res/css/structures/_SpacePanel.scss index afcf48ec4d..d3e7d7efee 100644 --- a/res/css/structures/_SpacePanel.scss +++ b/res/css/structures/_SpacePanel.scss @@ -147,6 +147,8 @@ $activeBorderColor: $secondary-fg-color; .mx_SpaceButton_toggleCollapse { width: $gutterSize; + // negative margin to place it correctly even with the complex + // 4px selection border each space button has when active margin-right: -4px; height: 20px; mask-position: center; From 451a3aaa3f6ccd7679c3bee5a57f2c6c0fb4fc62 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 16 Mar 2021 13:50:43 -0600 Subject: [PATCH 35/63] Work around more cases where a rageshake server might not be present We already do this for a number of other places (slash commands, help section of settings, etc) - these places appear to have been missed, though. --- .../views/dialogs/FeedbackDialog.js | 23 ++++++---- .../views/dialogs/RoomUpgradeWarningDialog.js | 42 ++++++++++++------- src/i18n/strings/en_EN.json | 5 ++- 3 files changed, 46 insertions(+), 24 deletions(-) diff --git a/src/components/views/dialogs/FeedbackDialog.js b/src/components/views/dialogs/FeedbackDialog.js index cbe26af6cc..d80a935573 100644 --- a/src/components/views/dialogs/FeedbackDialog.js +++ b/src/components/views/dialogs/FeedbackDialog.js @@ -100,6 +100,20 @@ export default (props) => { ); } + let bugReports = null; + if (SdkConfig.get().bug_report_endpoint_url) { + bugReports = ( +

    { + _t("PRO TIP: If you start a bug, please submit debug logs " + + "to help us track down the problem.", {}, { + debugLogsLink: sub => ( + {sub} + ), + }) + }

    + ); + } + return ( { }, }) }

    -

    { - _t("PRO TIP: If you start a bug, please submit debug logs " + - "to help us track down the problem.", {}, { - debugLogsLink: sub => ( - {sub} - ), - }) - }

    + {bugReports}
    { countlyFeedbackSection } } diff --git a/src/components/views/dialogs/RoomUpgradeWarningDialog.js b/src/components/views/dialogs/RoomUpgradeWarningDialog.js index 452ac56dff..c6ef2c6ae2 100644 --- a/src/components/views/dialogs/RoomUpgradeWarningDialog.js +++ b/src/components/views/dialogs/RoomUpgradeWarningDialog.js @@ -82,6 +82,33 @@ export default class RoomUpgradeWarningDialog extends React.Component { const title = this.state.isPrivate ? _t("Upgrade private room") : _t("Upgrade public room"); + let bugReports = ( +

    + {_t( + "This usually only affects how the room is processed on the server. If you're " + + "having problems with your %(brand)s, please report a bug.", {brand}, + )} +

    + ); + if (SdkConfig.get().bug_report_endpoint_url) { + bugReports = ( +

    + {_t( + "This usually only affects how the room is processed on the server. If you're " + + "having problems with your %(brand)s, please report a bug.", + { + brand, + }, + { + "a": (sub) => { + return {sub}; + }, + }, + )} +

    + ); + } + return ( -

    - {_t( - "This usually only affects how the room is processed on the server. If you're " + - "having problems with your %(brand)s, please report a bug.", - { - brand, - }, - { - "a": (sub) => { - return {sub}; - }, - }, - )} -

    + {bugReports}

    {_t( "You'll upgrade this room from to .", diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 63449eb99f..07d292a0e7 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -2150,10 +2150,10 @@ "Add comment": "Add comment", "Comment": "Comment", "There are two ways you can provide feedback and help us improve %(brand)s.": "There are two ways you can provide feedback and help us improve %(brand)s.", + "PRO TIP: If you start a bug, please submit debug logs to help us track down the problem.": "PRO TIP: If you start a bug, please submit debug logs to help us track down the problem.", "Feedback": "Feedback", "Report a bug": "Report a bug", "Please view existing bugs on Github first. No match? Start a new one.": "Please view existing bugs on Github first. No match? Start a new one.", - "PRO TIP: If you start a bug, please submit debug logs to help us track down the problem.": "PRO TIP: If you start a bug, please submit debug logs to help us track down the problem.", "Send feedback": "Send feedback", "Confirm abort of host creation": "Confirm abort of host creation", "Are you sure you wish to abort creation of the host? The process cannot be continued.": "Are you sure you wish to abort creation of the host? The process cannot be continued.", @@ -2269,8 +2269,9 @@ "Automatically invite users": "Automatically invite users", "Upgrade private room": "Upgrade private room", "Upgrade public room": "Upgrade public room", - "Upgrading a room is an advanced action and is usually recommended when a room is unstable due to bugs, missing features or security vulnerabilities.": "Upgrading a room is an advanced action and is usually recommended when a room is unstable due to bugs, missing features or security vulnerabilities.", + "This usually only affects how the room is processed on the server. If you're having problems with your %(brand)s, please report a bug.": "This usually only affects how the room is processed on the server. If you're having problems with your %(brand)s, please report a bug.", "This usually only affects how the room is processed on the server. If you're having problems with your %(brand)s, please report a bug.": "This usually only affects how the room is processed on the server. If you're having problems with your %(brand)s, please report a bug.", + "Upgrading a room is an advanced action and is usually recommended when a room is unstable due to bugs, missing features or security vulnerabilities.": "Upgrading a room is an advanced action and is usually recommended when a room is unstable due to bugs, missing features or security vulnerabilities.", "You'll upgrade this room from to .": "You'll upgrade this room from to .", "Resend": "Resend", "You're all caught up.": "You're all caught up.", From c285b79a8dedc3a64a428d15cd4a4b8d50b59c97 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 16 Mar 2021 14:19:49 -0600 Subject: [PATCH 36/63] Add possibility to delay rageshake persistence in app startup Even if the app takes a moment to set up persistence, this will still capture any logs before persistence happened. --- src/rageshake/rageshake.js | 32 +++++++++++++++++++++++++++----- 1 file changed, 27 insertions(+), 5 deletions(-) diff --git a/src/rageshake/rageshake.js b/src/rageshake/rageshake.js index 8eb77bb3ae..b886f369df 100644 --- a/src/rageshake/rageshake.js +++ b/src/rageshake/rageshake.js @@ -434,15 +434,37 @@ function selectQuery(store, keyRange, resultMapper) { /** * Configure rage shaking support for sending bug reports. * Modifies globals. + * @param {boolean} setUpPersistence When true (default), the persistence will + * be set up immediately for the logs. * @return {Promise} Resolves when set up. */ -export function init() { +export function init(setUpPersistence = true) { if (global.mx_rage_initPromise) { return global.mx_rage_initPromise; } global.mx_rage_logger = new ConsoleLogger(); global.mx_rage_logger.monkeyPatch(window.console); + if (setUpPersistence) { + return tryInitStorage(); + } + + global.mx_rage_initPromise = Promise.resolve(); + return global.mx_rage_initPromise; +} + +/** + * Try to start up the rageshake storage for logs. If not possible (client unsupported) + * then this no-ops. + * @return {Promise} Resolves when complete. + */ +export function tryInitStorage() { + if (global.mx_rage_initStoragePromise) { + return global.mx_rage_initStoragePromise; + } + + console.log("Configuring rageshake persistence..."); + // just *accessing* indexedDB throws an exception in firefox with // indexeddb disabled. let indexedDB; @@ -452,11 +474,11 @@ export function init() { if (indexedDB) { global.mx_rage_store = new IndexedDBLogStore(indexedDB, global.mx_rage_logger); - global.mx_rage_initPromise = global.mx_rage_store.connect(); - return global.mx_rage_initPromise; + global.mx_rage_initStoragePromise = global.mx_rage_store.connect(); + return global.mx_rage_initStoragePromise; } - global.mx_rage_initPromise = Promise.resolve(); - return global.mx_rage_initPromise; + global.mx_rage_initStoragePromise = Promise.resolve(); + return global.mx_rage_initStoragePromise; } export function flush() { From f6a87386bc2f0b15892920dd19d85820b9eeb647 Mon Sep 17 00:00:00 2001 From: David Baker Date: Wed, 17 Mar 2021 19:09:43 +0000 Subject: [PATCH 37/63] Fix username showing instead of display name in Jitsi widgets If you opened element and entered a jitsi conference straight away in the room you landed in, your jitsi display name would be your matrix username rather than your display name. This was because OwnProfileStore was still busy fetching your profile from the server while the room, and therefore jitsi widget, was rendered. Blocking these widgets loading on this profile fetch completing isn't really an option, so store the profile data in localstorage and seed OwnProfileStore with the values from there. Bonus: the name in the top left will now be your display name as soon as the app is loaded, rather than being your username for the first several seconds after you load the app. Fixes https://github.com/vector-im/element-web/issues/16577 --- src/stores/OwnProfileStore.ts | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/stores/OwnProfileStore.ts b/src/stores/OwnProfileStore.ts index 8983380fec..15884b8ad6 100644 --- a/src/stores/OwnProfileStore.ts +++ b/src/stores/OwnProfileStore.ts @@ -28,13 +28,22 @@ interface IState { avatarUrl?: string; } +const KEY_DISPLAY_NAME = "mx_profile_displayname"; +const KEY_AVATAR_URL = "mx_profile_avatar_url"; + export class OwnProfileStore extends AsyncStoreWithClient { private static internalInstance = new OwnProfileStore(); private monitoredUser: User; private constructor() { - super(defaultDispatcher, {}); + // seed from localstorage because otherwise we won't get these values until a whole network + // round-trip after the client is ready, and we often load widgets in that time, and we'd + // and up passing them an incorrect display name + super(defaultDispatcher, { + displayName: window.localStorage.getItem(KEY_DISPLAY_NAME), + avatarUrl: window.localStorage.getItem(KEY_AVATAR_URL), + }); } public static get instance(): OwnProfileStore { @@ -73,7 +82,11 @@ export class OwnProfileStore extends AsyncStoreWithClient { public getHttpAvatarUrl(size = 0): string { if (!this.avatarMxc) return null; const adjustedSize = size > 1 ? size : undefined; // don't let negatives or zero through - return this.matrixClient.mxcUrlToHttp(this.avatarMxc, adjustedSize, adjustedSize); + + // XXX: We use MatrixClientPeg here rather than this.matrixClient because otherwise we'll + // race because our data stores aren't set up consistently enough that this will all be + // initialised with a store before these getter methods are called. + return MatrixClientPeg.get().mxcUrlToHttp(this.avatarMxc, adjustedSize, adjustedSize); } protected async onNotReady() { @@ -110,6 +123,8 @@ export class OwnProfileStore extends AsyncStoreWithClient { // We specifically do not use the User object we stored for profile info as it // could easily be wrong (such as per-room instead of global profile). const profileInfo = await this.matrixClient.getProfileInfo(this.matrixClient.getUserId()); + if (profileInfo.displayname) window.localStorage.setItem(KEY_DISPLAY_NAME, profileInfo.displayname); + if (profileInfo.avatar_url) window.localStorage.setItem(KEY_AVATAR_URL, profileInfo.avatar_url); await this.updateState({displayName: profileInfo.displayname, avatarUrl: profileInfo.avatar_url}); }; From 2b608aa919eaa82827a4372ffa1e9dbf6a609f45 Mon Sep 17 00:00:00 2001 From: David Baker Date: Wed, 17 Mar 2021 19:18:32 +0000 Subject: [PATCH 38/63] Remove blank line --- src/stores/OwnProfileStore.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/stores/OwnProfileStore.ts b/src/stores/OwnProfileStore.ts index 6927ecc153..398532a5c8 100644 --- a/src/stores/OwnProfileStore.ts +++ b/src/stores/OwnProfileStore.ts @@ -82,7 +82,6 @@ export class OwnProfileStore extends AsyncStoreWithClient { */ public getHttpAvatarUrl(size = 0): string { if (!this.avatarMxc) return null; - const media = mediaFromMxc(this.avatarMxc); if (!size || size <= 0) { return media.srcHttp; From 84b6027d41e055e342653fab3e5198b37e96396e Mon Sep 17 00:00:00 2001 From: David Baker Date: Thu, 18 Mar 2021 16:41:22 +0000 Subject: [PATCH 39/63] Log error when failing to list usermedia devices So we can debug why this might be failing --- src/components/views/settings/tabs/user/VoiceUserSettingsTab.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/views/settings/tabs/user/VoiceUserSettingsTab.js b/src/components/views/settings/tabs/user/VoiceUserSettingsTab.js index bc6fe796b8..d8adab55f6 100644 --- a/src/components/views/settings/tabs/user/VoiceUserSettingsTab.js +++ b/src/components/views/settings/tabs/user/VoiceUserSettingsTab.js @@ -84,6 +84,7 @@ export default class VoiceUserSettingsTab extends React.Component { } } if (error) { + console.log("Failed to list userMedia devices", error); const brand = SdkConfig.get().brand; const ErrorDialog = sdk.getComponent('dialogs.ErrorDialog'); Modal.createTrackedDialog('No media permissions', '', ErrorDialog, { From a4a3b4da78d6562e8d1f11e91fe58c8b6d58945c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Thu, 18 Mar 2021 18:01:50 +0100 Subject: [PATCH 40/63] jumpToLiveTimeline() on scroll_to_bottom MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/structures/TimelinePanel.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/components/structures/TimelinePanel.js b/src/components/structures/TimelinePanel.js index f32b8ed0a9..84193eae4c 100644 --- a/src/components/structures/TimelinePanel.js +++ b/src/components/structures/TimelinePanel.js @@ -463,6 +463,9 @@ class TimelinePanel extends React.Component { } }); } + if (payload.action === "scroll_to_bottom") { + this.jumpToLiveTimeline(); + } }; onRoomTimeline = (ev, room, toStartOfTimeline, removed, data) => { From 7ac8fdea6448bd5e1a386fec2ae2fb412b69d689 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Thu, 18 Mar 2021 18:02:33 +0100 Subject: [PATCH 41/63] Don't call scrollToBottom() on scroll_to_bottom() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/structures/MessagePanel.js | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/components/structures/MessagePanel.js b/src/components/structures/MessagePanel.js index 439bdbcef5..f4334ffe87 100644 --- a/src/components/structures/MessagePanel.js +++ b/src/components/structures/MessagePanel.js @@ -23,7 +23,6 @@ import classNames from 'classnames'; import shouldHideEvent from '../../shouldHideEvent'; import {wantsDateSeparator} from '../../DateUtils'; import * as sdk from '../../index'; -import dis from "../../dispatcher/dispatcher"; import {MatrixClientPeg} from '../../MatrixClientPeg'; import SettingsStore from '../../settings/SettingsStore'; @@ -210,13 +209,11 @@ export default class MessagePanel extends React.Component { componentDidMount() { this._isMounted = true; - this.dispatcherRef = dis.register(this.onAction); } componentWillUnmount() { this._isMounted = false; SettingsStore.unwatchSetting(this._showTypingNotificationsWatcherRef); - dis.unregister(this.dispatcherRef); } componentDidUpdate(prevProps, prevState) { @@ -229,14 +226,6 @@ export default class MessagePanel extends React.Component { } } - onAction = (payload) => { - switch (payload.action) { - case "scroll_to_bottom": - this.scrollToBottom(); - break; - } - } - onShowTypingNotificationsChange = () => { this.setState({ showTypingNotifications: SettingsStore.getValue("showTypingNotifications"), From 720ac9d837332dc4ba6db9371b39c013d774d0ef Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 18 Mar 2021 14:45:14 -0600 Subject: [PATCH 42/63] Replace type declaration in Registration.tsx --- src/components/structures/auth/Registration.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/structures/auth/Registration.tsx b/src/components/structures/auth/Registration.tsx index 32bdddb82a..9d004de2ec 100644 --- a/src/components/structures/auth/Registration.tsx +++ b/src/components/structures/auth/Registration.tsx @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import Matrix from 'matrix-js-sdk'; +import {createClient} from 'matrix-js-sdk/src/matrix'; import React, {ReactNode} from 'react'; import {MatrixClient} from "matrix-js-sdk/src/client"; @@ -181,7 +181,7 @@ export default class Registration extends React.Component { } const {hsUrl, isUrl} = serverConfig; - const cli = Matrix.createClient({ + const cli = createClient({ baseUrl: hsUrl, idBaseUrl: isUrl, }); From 1d9d0cd7be99f49da2b8a46f11d3777aca223fff Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 18 Mar 2021 20:50:34 -0600 Subject: [PATCH 43/63] Convert a bunch more js-sdk imports to absolute paths Turns out a lot of the typescript warnings about improper warnings were correct. TypeScript appears to be pulling in two copies of the js-sdk when we do this, which can lead to type conflicts (or worse: the wrong code entirely). We fix this at the webpack level by explicitly importing from `src`, but some alternative build structures have broken tests because of this - jest ends up pulling in the "wrong" js-sdk, breaking things. --- src/CallMediaHandler.js | 14 +++++++------- src/IdentityAuthClient.js | 3 ++- src/Lifecycle.ts | 7 +++---- src/Login.ts | 6 +++--- src/PasswordReset.js | 4 ++-- src/Resend.js | 2 +- src/ScalarAuthClient.js | 4 ++-- src/ScalarMessaging.js | 2 +- .../views/dialogs/security/ExportE2eKeysDialog.js | 2 +- .../views/dialogs/security/ImportE2eKeysDialog.js | 2 +- src/components/structures/FilePanel.js | 2 +- src/components/structures/GroupView.js | 2 +- src/components/structures/InteractiveAuth.js | 2 +- src/components/structures/MatrixChat.tsx | 5 ++--- src/components/structures/RoomStatusBar.js | 4 ++-- src/components/structures/TimelinePanel.js | 6 +++--- src/components/structures/UserView.js | 7 ++++--- .../context_menus/GroupInviteTileContextMenu.js | 2 +- .../views/context_menus/MessageContextMenu.js | 2 +- .../views/dialogs/ConfirmUserActionDialog.js | 2 +- src/components/views/dialogs/DevtoolsDialog.js | 3 ++- src/components/views/dialogs/ReportEventDialog.js | 2 +- .../dialogs/TabbedIntegrationManagerDialog.js | 2 +- src/components/views/dialogs/TermsDialog.js | 10 +++++----- .../dialogs/security/RestoreKeyBackupDialog.js | 2 +- src/components/views/elements/Pill.js | 3 ++- src/components/views/elements/ReplyThread.js | 2 +- .../views/messages/EditHistoryMessage.js | 2 +- src/components/views/messages/MessageActionBar.js | 2 +- .../views/room_settings/RelatedGroupSettings.js | 2 +- src/components/views/rooms/EditMessageComposer.js | 2 +- src/components/views/rooms/EventTile.js | 2 +- src/components/views/rooms/ThirdPartyMemberInfo.js | 2 +- .../settings/tabs/user/GeneralUserSettingsTab.js | 2 +- src/indexing/EventIndex.js | 3 ++- src/utils/AutoDiscoveryUtils.js | 2 +- src/utils/EventUtils.js | 2 +- src/utils/IdentityServerUtils.js | 2 +- src/utils/StorageManager.js | 7 ++++--- src/utils/createMatrixClient.js | 13 ++++++++----- 40 files changed, 77 insertions(+), 70 deletions(-) diff --git a/src/CallMediaHandler.js b/src/CallMediaHandler.js index 8d56467c57..7c7940cab5 100644 --- a/src/CallMediaHandler.js +++ b/src/CallMediaHandler.js @@ -14,9 +14,9 @@ limitations under the License. */ -import * as Matrix from 'matrix-js-sdk'; import SettingsStore from "./settings/SettingsStore"; import {SettingLevel} from "./settings/SettingLevel"; +import {setMatrixCallAudioInput, setMatrixCallAudioOutput, setMatrixCallVideoInput} from "matrix-js-sdk/src/matrix"; export default { hasAnyLabeledDevices: async function() { @@ -54,24 +54,24 @@ export default { const audioDeviceId = SettingsStore.getValue("webrtc_audioinput"); const videoDeviceId = SettingsStore.getValue("webrtc_videoinput"); - Matrix.setMatrixCallAudioOutput(audioOutDeviceId); - Matrix.setMatrixCallAudioInput(audioDeviceId); - Matrix.setMatrixCallVideoInput(videoDeviceId); + setMatrixCallAudioOutput(audioOutDeviceId); + setMatrixCallAudioInput(audioDeviceId); + setMatrixCallVideoInput(videoDeviceId); }, setAudioOutput: function(deviceId) { SettingsStore.setValue("webrtc_audiooutput", null, SettingLevel.DEVICE, deviceId); - Matrix.setMatrixCallAudioOutput(deviceId); + setMatrixCallAudioOutput(deviceId); }, setAudioInput: function(deviceId) { SettingsStore.setValue("webrtc_audioinput", null, SettingLevel.DEVICE, deviceId); - Matrix.setMatrixCallAudioInput(deviceId); + setMatrixCallAudioInput(deviceId); }, setVideoInput: function(deviceId) { SettingsStore.setValue("webrtc_videoinput", null, SettingLevel.DEVICE, deviceId); - Matrix.setMatrixCallVideoInput(deviceId); + setMatrixCallVideoInput(deviceId); }, getAudioOutput: function() { diff --git a/src/IdentityAuthClient.js b/src/IdentityAuthClient.js index d3bfee2380..9c6449f1e3 100644 --- a/src/IdentityAuthClient.js +++ b/src/IdentityAuthClient.js @@ -14,7 +14,8 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { createClient, SERVICE_TYPES } from 'matrix-js-sdk'; +import { SERVICE_TYPES } from 'matrix-js-sdk/src/service-types'; +import { createClient } from 'matrix-js-sdk/src/matrix' import {MatrixClientPeg} from './MatrixClientPeg'; import Modal from './Modal'; diff --git a/src/Lifecycle.ts b/src/Lifecycle.ts index 7780d4c87a..b0a1292ba1 100644 --- a/src/Lifecycle.ts +++ b/src/Lifecycle.ts @@ -17,8 +17,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -// @ts-ignore - XXX: tsc doesn't like this: our js-sdk imports are complex so this isn't surprising -import Matrix from 'matrix-js-sdk'; +import { createClient } from 'matrix-js-sdk/src/matrix'; import { InvalidStoreError } from "matrix-js-sdk/src/errors"; import { MatrixClient } from "matrix-js-sdk/src/client"; import {decryptAES, encryptAES} from "matrix-js-sdk/src/crypto/aes"; @@ -219,7 +218,7 @@ export function attemptTokenLogin( button: _t("Try again"), onFinished: tryAgain => { if (tryAgain) { - const cli = Matrix.createClient({ + const cli = createClient({ baseUrl: homeserver, idBaseUrl: identityServer, }); @@ -276,7 +275,7 @@ function registerAsGuest( console.log(`Doing guest login on ${hsUrl}`); // create a temporary MatrixClient to do the login - const client = Matrix.createClient({ + const client = createClient({ baseUrl: hsUrl, }); diff --git a/src/Login.ts b/src/Login.ts index aecc0493c7..db3c4c11e4 100644 --- a/src/Login.ts +++ b/src/Login.ts @@ -19,7 +19,7 @@ limitations under the License. */ // @ts-ignore - XXX: tsc doesn't like this: our js-sdk imports are complex so this isn't surprising -import Matrix from "matrix-js-sdk"; +import {createClient} from "matrix-js-sdk/src/matrix"; import { MatrixClient } from "matrix-js-sdk/src/client"; import { IMatrixClientCreds } from "./MatrixClientPeg"; import SecurityCustomisations from "./customisations/Security"; @@ -115,7 +115,7 @@ export default class Login { */ public createTemporaryClient(): MatrixClient { if (this.tempClient) return this.tempClient; // use memoization - return this.tempClient = Matrix.createClient({ + return this.tempClient = createClient({ baseUrl: this.hsUrl, idBaseUrl: this.isUrl, }); @@ -210,7 +210,7 @@ export async function sendLoginRequest( loginType: string, loginParams: ILoginParams, ): Promise { - const client = Matrix.createClient({ + const client = createClient({ baseUrl: hsUrl, idBaseUrl: isUrl, }); diff --git a/src/PasswordReset.js b/src/PasswordReset.js index b38a9de960..6fe6ca82cc 100644 --- a/src/PasswordReset.js +++ b/src/PasswordReset.js @@ -15,7 +15,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import * as Matrix from 'matrix-js-sdk'; +import { createClient } from 'matrix-js-sdk/src/matrix'; import { _t } from './languageHandler'; /** @@ -32,7 +32,7 @@ export default class PasswordReset { * @param {string} identityUrl The URL to the IS which has linked the email -> mxid mapping. */ constructor(homeserverUrl, identityUrl) { - this.client = Matrix.createClient({ + this.client = createClient({ baseUrl: homeserverUrl, idBaseUrl: identityUrl, }); diff --git a/src/Resend.js b/src/Resend.js index 5638313306..bf69e59c1a 100644 --- a/src/Resend.js +++ b/src/Resend.js @@ -17,7 +17,7 @@ limitations under the License. import {MatrixClientPeg} from './MatrixClientPeg'; import dis from './dispatcher/dispatcher'; -import { EventStatus } from 'matrix-js-sdk'; +import { EventStatus } from 'matrix-js-sdk/src/models/event'; export default class Resend { static resendUnsentEvents(room) { diff --git a/src/ScalarAuthClient.js b/src/ScalarAuthClient.js index 1ea9d39e2f..200b4fd7b9 100644 --- a/src/ScalarAuthClient.js +++ b/src/ScalarAuthClient.js @@ -21,9 +21,9 @@ import { Service, startTermsFlow, TermsNotSignedError } from './Terms'; import {MatrixClientPeg} from "./MatrixClientPeg"; import request from "browser-request"; -import * as Matrix from 'matrix-js-sdk'; import SdkConfig from "./SdkConfig"; import {WidgetType} from "./widgets/WidgetType"; +import {SERVICE_TYPES} from "matrix-js-sdk/src/service-types"; // The version of the integration manager API we're intending to work with const imApiVersion = "1.1"; @@ -153,7 +153,7 @@ export default class ScalarAuthClient { parsedImRestUrl.path = ''; parsedImRestUrl.pathname = ''; return startTermsFlow([new Service( - Matrix.SERVICE_TYPES.IM, + SERVICE_TYPES.IM, parsedImRestUrl.format(), token, )], this.termsInteractionCallback).then(() => { diff --git a/src/ScalarMessaging.js b/src/ScalarMessaging.js index 896e27d92c..3f75b3788c 100644 --- a/src/ScalarMessaging.js +++ b/src/ScalarMessaging.js @@ -237,7 +237,7 @@ Example: */ import {MatrixClientPeg} from './MatrixClientPeg'; -import { MatrixEvent } from 'matrix-js-sdk'; +import { MatrixEvent } from 'matrix-js-sdk/src/models/event'; import dis from './dispatcher/dispatcher'; import WidgetUtils from './utils/WidgetUtils'; import RoomViewStore from './stores/RoomViewStore'; diff --git a/src/async-components/views/dialogs/security/ExportE2eKeysDialog.js b/src/async-components/views/dialogs/security/ExportE2eKeysDialog.js index 4dd296a8f1..eeb68b94bd 100644 --- a/src/async-components/views/dialogs/security/ExportE2eKeysDialog.js +++ b/src/async-components/views/dialogs/security/ExportE2eKeysDialog.js @@ -19,7 +19,7 @@ import React, {createRef} from 'react'; import PropTypes from 'prop-types'; import { _t } from '../../../../languageHandler'; -import { MatrixClient } from 'matrix-js-sdk'; +import { MatrixClient } from 'matrix-js-sdk/src/client'; import * as MegolmExportEncryption from '../../../../utils/MegolmExportEncryption'; import * as sdk from '../../../../index'; diff --git a/src/async-components/views/dialogs/security/ImportE2eKeysDialog.js b/src/async-components/views/dialogs/security/ImportE2eKeysDialog.js index e7bae3578b..670cb28b94 100644 --- a/src/async-components/views/dialogs/security/ImportE2eKeysDialog.js +++ b/src/async-components/views/dialogs/security/ImportE2eKeysDialog.js @@ -17,7 +17,7 @@ limitations under the License. import React, {createRef} from 'react'; import PropTypes from 'prop-types'; -import { MatrixClient } from 'matrix-js-sdk'; +import { MatrixClient } from 'matrix-js-sdk/src/client'; import * as MegolmExportEncryption from '../../../../utils/MegolmExportEncryption'; import * as sdk from '../../../../index'; import { _t } from '../../../../languageHandler'; diff --git a/src/components/structures/FilePanel.js b/src/components/structures/FilePanel.js index 9f5a0b6211..32db5c251c 100644 --- a/src/components/structures/FilePanel.js +++ b/src/components/structures/FilePanel.js @@ -18,7 +18,7 @@ limitations under the License. import React from 'react'; import PropTypes from 'prop-types'; -import {Filter} from 'matrix-js-sdk'; +import {Filter} from 'matrix-js-sdk/src/filter'; import * as sdk from '../../index'; import {MatrixClientPeg} from '../../MatrixClientPeg'; import EventIndexPeg from "../../indexing/EventIndexPeg"; diff --git a/src/components/structures/GroupView.js b/src/components/structures/GroupView.js index f05d8d0758..b006b323fb 100644 --- a/src/components/structures/GroupView.js +++ b/src/components/structures/GroupView.js @@ -35,7 +35,7 @@ import GroupStore from '../../stores/GroupStore'; import FlairStore from '../../stores/FlairStore'; import { showGroupAddRoomDialog } from '../../GroupAddressPicker'; import {makeGroupPermalink, makeUserPermalink} from "../../utils/permalinks/Permalinks"; -import {Group} from "matrix-js-sdk"; +import {Group} from "matrix-js-sdk/src/models/group"; import {allSettled, sleep} from "../../utils/promise"; import RightPanelStore from "../../stores/RightPanelStore"; import AutoHideScrollbar from "./AutoHideScrollbar"; diff --git a/src/components/structures/InteractiveAuth.js b/src/components/structures/InteractiveAuth.js index 9b61f71fd7..d419c9de6e 100644 --- a/src/components/structures/InteractiveAuth.js +++ b/src/components/structures/InteractiveAuth.js @@ -15,7 +15,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import {InteractiveAuth} from "matrix-js-sdk"; +import {InteractiveAuth} from "matrix-js-sdk/src/interactive-auth"; import React, {createRef} from 'react'; import PropTypes from 'prop-types'; diff --git a/src/components/structures/MatrixChat.tsx b/src/components/structures/MatrixChat.tsx index f0a3778a2b..a1117551fd 100644 --- a/src/components/structures/MatrixChat.tsx +++ b/src/components/structures/MatrixChat.tsx @@ -18,8 +18,7 @@ limitations under the License. */ import React, { createRef } from 'react'; -// @ts-ignore - XXX: no idea why this import fails -import * as Matrix from "matrix-js-sdk"; +import { createClient } from "matrix-js-sdk/src/matrix"; import { InvalidStoreError } from "matrix-js-sdk/src/errors"; import { RoomMember } from "matrix-js-sdk/src/models/room-member"; import { MatrixEvent } from "matrix-js-sdk/src/models/event"; @@ -1649,7 +1648,7 @@ export default class MatrixChat extends React.PureComponent { let cli = MatrixClientPeg.get(); if (!cli) { const {hsUrl, isUrl} = this.props.serverConfig; - cli = Matrix.createClient({ + cli = createClient({ baseUrl: hsUrl, idBaseUrl: isUrl, }); diff --git a/src/components/structures/RoomStatusBar.js b/src/components/structures/RoomStatusBar.js index 8b70998be0..54b6fee233 100644 --- a/src/components/structures/RoomStatusBar.js +++ b/src/components/structures/RoomStatusBar.js @@ -16,7 +16,6 @@ limitations under the License. import React from 'react'; import PropTypes from 'prop-types'; -import Matrix from 'matrix-js-sdk'; import { _t, _td } from '../../languageHandler'; import {MatrixClientPeg} from '../../MatrixClientPeg'; import Resend from '../../Resend'; @@ -24,6 +23,7 @@ import dis from '../../dispatcher/dispatcher'; import {messageForResourceLimitError, messageForSendError} from '../../utils/ErrorUtils'; import {Action} from "../../dispatcher/actions"; import {replaceableComponent} from "../../utils/replaceableComponent"; +import {EventStatus} from "matrix-js-sdk/src/models/event"; const STATUS_BAR_HIDDEN = 0; const STATUS_BAR_EXPANDED = 1; @@ -32,7 +32,7 @@ const STATUS_BAR_EXPANDED_LARGE = 2; function getUnsentMessages(room) { if (!room) { return []; } return room.getPendingEvents().filter(function(ev) { - return ev.status === Matrix.EventStatus.NOT_SENT; + return ev.status === EventStatus.NOT_SENT; }); } diff --git a/src/components/structures/TimelinePanel.js b/src/components/structures/TimelinePanel.js index f32b8ed0a9..ce13bb43fa 100644 --- a/src/components/structures/TimelinePanel.js +++ b/src/components/structures/TimelinePanel.js @@ -22,8 +22,8 @@ import {LayoutPropType} from "../../settings/Layout"; import React, {createRef} from 'react'; import ReactDOM from "react-dom"; import PropTypes from 'prop-types'; -import {EventTimeline} from "matrix-js-sdk"; -import * as Matrix from "matrix-js-sdk"; +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 UserActivity from "../../UserActivity"; @@ -1007,7 +1007,7 @@ class TimelinePanel extends React.Component { * returns a promise which will resolve when the load completes. */ _loadTimeline(eventId, pixelOffset, offsetBase) { - this._timelineWindow = new Matrix.TimelineWindow( + this._timelineWindow = new TimelineWindow( MatrixClientPeg.get(), this.props.timelineSet, {windowLimit: this.props.timelineCap}); diff --git a/src/components/structures/UserView.js b/src/components/structures/UserView.js index dc05193ece..6b472783bb 100644 --- a/src/components/structures/UserView.js +++ b/src/components/structures/UserView.js @@ -17,13 +17,14 @@ limitations under the License. import React from "react"; import PropTypes from "prop-types"; -import Matrix from "matrix-js-sdk"; import {MatrixClientPeg} from "../../MatrixClientPeg"; import * as sdk from "../../index"; import Modal from '../../Modal'; import { _t } from '../../languageHandler'; import HomePage from "./HomePage"; import {replaceableComponent} from "../../utils/replaceableComponent"; +import {MatrixEvent} from "matrix-js-sdk/src/models/event"; +import {RoomMember} from "matrix-js-sdk/src/models/room-member"; @replaceableComponent("structures.UserView") export default class UserView extends React.Component { @@ -68,8 +69,8 @@ export default class UserView extends React.Component { this.setState({loading: false}); return; } - const fakeEvent = new Matrix.MatrixEvent({type: "m.room.member", content: profileInfo}); - const member = new Matrix.RoomMember(null, this.props.userId); + const fakeEvent = new MatrixEvent({type: "m.room.member", content: profileInfo}); + const member = new RoomMember(null, this.props.userId); member.setMembershipEvent(fakeEvent); this.setState({member, loading: false}); } diff --git a/src/components/views/context_menus/GroupInviteTileContextMenu.js b/src/components/views/context_menus/GroupInviteTileContextMenu.js index 11a9d90ac2..15078326b3 100644 --- a/src/components/views/context_menus/GroupInviteTileContextMenu.js +++ b/src/components/views/context_menus/GroupInviteTileContextMenu.js @@ -20,7 +20,7 @@ import PropTypes from 'prop-types'; import * as sdk from '../../../index'; import { _t } from '../../../languageHandler'; import Modal from '../../../Modal'; -import {Group} from 'matrix-js-sdk'; +import {Group} from 'matrix-js-sdk/src/models/group'; import GroupStore from "../../../stores/GroupStore"; import {MenuItem} from "../../structures/ContextMenu"; import {replaceableComponent} from "../../../utils/replaceableComponent"; diff --git a/src/components/views/context_menus/MessageContextMenu.js b/src/components/views/context_menus/MessageContextMenu.js index daabf224dc..56f070ba36 100644 --- a/src/components/views/context_menus/MessageContextMenu.js +++ b/src/components/views/context_menus/MessageContextMenu.js @@ -19,7 +19,7 @@ limitations under the License. import React from 'react'; import PropTypes from 'prop-types'; -import {EventStatus} from 'matrix-js-sdk'; +import {EventStatus} from 'matrix-js-sdk/src/models/event'; import {MatrixClientPeg} from '../../../MatrixClientPeg'; import dis from '../../../dispatcher/dispatcher'; diff --git a/src/components/views/dialogs/ConfirmUserActionDialog.js b/src/components/views/dialogs/ConfirmUserActionDialog.js index 8cfd28986b..8059b9172a 100644 --- a/src/components/views/dialogs/ConfirmUserActionDialog.js +++ b/src/components/views/dialogs/ConfirmUserActionDialog.js @@ -16,7 +16,7 @@ limitations under the License. import React from 'react'; import PropTypes from 'prop-types'; -import { MatrixClient } from 'matrix-js-sdk'; +import { MatrixClient } from 'matrix-js-sdk/src/client'; import * as sdk from '../../../index'; import { _t } from '../../../languageHandler'; import { GroupMemberType } from '../../../groups'; diff --git a/src/components/views/dialogs/DevtoolsDialog.js b/src/components/views/dialogs/DevtoolsDialog.js index dfcc5d6dfb..9f5513e0a3 100644 --- a/src/components/views/dialogs/DevtoolsDialog.js +++ b/src/components/views/dialogs/DevtoolsDialog.js @@ -19,7 +19,6 @@ import PropTypes from 'prop-types'; import * as sdk from '../../../index'; import SyntaxHighlight from '../elements/SyntaxHighlight'; import { _t } from '../../../languageHandler'; -import { Room, MatrixEvent } from "matrix-js-sdk"; import Field from "../elements/Field"; import MatrixClientContext from "../../../contexts/MatrixClientContext"; import {useEventEmitter} from "../../../hooks/useEventEmitter"; @@ -39,6 +38,8 @@ import SettingsStore, {LEVEL_ORDER} from "../../../settings/SettingsStore"; import Modal from "../../../Modal"; import ErrorDialog from "./ErrorDialog"; import {replaceableComponent} from "../../../utils/replaceableComponent"; +import {Room} from "matrix-js-sdk/src/models/room"; +import {MatrixEvent} from "matrix-js-sdk/src/models/event"; class GenericEditor extends React.PureComponent { // static propTypes = {onBack: PropTypes.func.isRequired}; diff --git a/src/components/views/dialogs/ReportEventDialog.js b/src/components/views/dialogs/ReportEventDialog.js index 67ed0f8f53..5454b97287 100644 --- a/src/components/views/dialogs/ReportEventDialog.js +++ b/src/components/views/dialogs/ReportEventDialog.js @@ -18,7 +18,7 @@ import React, {PureComponent} from 'react'; import * as sdk from '../../../index'; import { _t } from '../../../languageHandler'; import PropTypes from "prop-types"; -import {MatrixEvent} from "matrix-js-sdk"; +import {MatrixEvent} from "matrix-js-sdk/src/models/event"; import {MatrixClientPeg} from "../../../MatrixClientPeg"; import SdkConfig from '../../../SdkConfig'; import Markdown from '../../../Markdown'; diff --git a/src/components/views/dialogs/TabbedIntegrationManagerDialog.js b/src/components/views/dialogs/TabbedIntegrationManagerDialog.js index 07e29adcff..618b0b4347 100644 --- a/src/components/views/dialogs/TabbedIntegrationManagerDialog.js +++ b/src/components/views/dialogs/TabbedIntegrationManagerDialog.js @@ -17,7 +17,7 @@ limitations under the License. import React from 'react'; import PropTypes from 'prop-types'; import {IntegrationManagers} from "../../../integrations/IntegrationManagers"; -import {Room} from "matrix-js-sdk"; +import {Room} from "matrix-js-sdk/src/models/room"; import * as sdk from '../../../index'; import {dialogTermsInteractionCallback, TermsNotSignedError} from "../../../Terms"; import classNames from 'classnames'; diff --git a/src/components/views/dialogs/TermsDialog.js b/src/components/views/dialogs/TermsDialog.js index 72e6c3f3a0..e8625ec6cb 100644 --- a/src/components/views/dialogs/TermsDialog.js +++ b/src/components/views/dialogs/TermsDialog.js @@ -20,8 +20,8 @@ import PropTypes from 'prop-types'; import * as sdk from '../../../index'; import { _t, pickBestLanguage } from '../../../languageHandler'; -import Matrix from 'matrix-js-sdk'; import {replaceableComponent} from "../../../utils/replaceableComponent"; +import {SERVICE_TYPES} from "matrix-js-sdk/src/service-types"; class TermsCheckbox extends React.PureComponent { static propTypes = { @@ -85,22 +85,22 @@ export default class TermsDialog extends React.PureComponent { _nameForServiceType(serviceType, host) { switch (serviceType) { - case Matrix.SERVICE_TYPES.IS: + case SERVICE_TYPES.IS: return

    {_t("Identity Server")}
    ({host})
    ; - case Matrix.SERVICE_TYPES.IM: + case SERVICE_TYPES.IM: return
    {_t("Integration Manager")}
    ({host})
    ; } } _summaryForServiceType(serviceType) { switch (serviceType) { - case Matrix.SERVICE_TYPES.IS: + case SERVICE_TYPES.IS: return
    {_t("Find others by phone or email")}
    {_t("Be found by phone or email")}
    ; - case Matrix.SERVICE_TYPES.IM: + case SERVICE_TYPES.IM: return
    {_t("Use bots, bridges, widgets and sticker packs")}
    ; diff --git a/src/components/views/dialogs/security/RestoreKeyBackupDialog.js b/src/components/views/dialogs/security/RestoreKeyBackupDialog.js index ca28ca094c..1fafe03d95 100644 --- a/src/components/views/dialogs/security/RestoreKeyBackupDialog.js +++ b/src/components/views/dialogs/security/RestoreKeyBackupDialog.js @@ -19,7 +19,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import * as sdk from '../../../../index'; import {MatrixClientPeg} from '../../../../MatrixClientPeg'; -import { MatrixClient } from 'matrix-js-sdk'; +import { MatrixClient } from 'matrix-js-sdk/src/client'; import { _t } from '../../../../languageHandler'; import { accessSecretStorage } from '../../../../SecurityManager'; diff --git a/src/components/views/elements/Pill.js b/src/components/views/elements/Pill.js index e61d312305..d6c8aeb811 100644 --- a/src/components/views/elements/Pill.js +++ b/src/components/views/elements/Pill.js @@ -17,7 +17,8 @@ import React from 'react'; import * as sdk from '../../../index'; import dis from '../../../dispatcher/dispatcher'; import classNames from 'classnames'; -import { Room, RoomMember } from 'matrix-js-sdk'; +import { Room } from 'matrix-js-sdk/src/models/room'; +import { RoomMember } from 'matrix-js-sdk/src/models/room-member' import PropTypes from 'prop-types'; import {MatrixClientPeg} from '../../../MatrixClientPeg'; import FlairStore from "../../../stores/FlairStore"; diff --git a/src/components/views/elements/ReplyThread.js b/src/components/views/elements/ReplyThread.js index 2e0cc50435..870803995d 100644 --- a/src/components/views/elements/ReplyThread.js +++ b/src/components/views/elements/ReplyThread.js @@ -21,7 +21,7 @@ import {_t} from '../../../languageHandler'; import PropTypes from 'prop-types'; import dis from '../../../dispatcher/dispatcher'; import {wantsDateSeparator} from '../../../DateUtils'; -import {MatrixEvent} from 'matrix-js-sdk'; +import {MatrixEvent} from 'matrix-js-sdk/src/models/event'; import {makeUserPermalink, RoomPermalinkCreator} from "../../../utils/permalinks/Permalinks"; import SettingsStore from "../../../settings/SettingsStore"; import {LayoutPropType} from "../../../settings/Layout"; diff --git a/src/components/views/messages/EditHistoryMessage.js b/src/components/views/messages/EditHistoryMessage.js index a298f7eb68..e2eda1e12a 100644 --- a/src/components/views/messages/EditHistoryMessage.js +++ b/src/components/views/messages/EditHistoryMessage.js @@ -19,7 +19,7 @@ import PropTypes from 'prop-types'; import * as HtmlUtils from '../../../HtmlUtils'; import { editBodyDiffToHtml } from '../../../utils/MessageDiffUtils'; import {formatTime} from '../../../DateUtils'; -import {MatrixEvent} from 'matrix-js-sdk'; +import {MatrixEvent} from 'matrix-js-sdk/src/models/event'; import {pillifyLinks, unmountPills} from '../../../utils/pillify'; import { _t } from '../../../languageHandler'; import * as sdk from '../../../index'; diff --git a/src/components/views/messages/MessageActionBar.js b/src/components/views/messages/MessageActionBar.js index c33debe3f5..5a6e7d87b7 100644 --- a/src/components/views/messages/MessageActionBar.js +++ b/src/components/views/messages/MessageActionBar.js @@ -18,7 +18,7 @@ limitations under the License. import React, {useEffect} from 'react'; import PropTypes from 'prop-types'; -import { EventStatus } from 'matrix-js-sdk'; +import { EventStatus } from 'matrix-js-sdk/src/models/event'; import { _t } from '../../../languageHandler'; import * as sdk from '../../../index'; diff --git a/src/components/views/room_settings/RelatedGroupSettings.js b/src/components/views/room_settings/RelatedGroupSettings.js index f82e238722..272ecd1228 100644 --- a/src/components/views/room_settings/RelatedGroupSettings.js +++ b/src/components/views/room_settings/RelatedGroupSettings.js @@ -16,7 +16,7 @@ limitations under the License. import React from 'react'; import PropTypes from 'prop-types'; -import {MatrixEvent} from 'matrix-js-sdk'; +import {MatrixEvent} from 'matrix-js-sdk/src/models/event'; import * as sdk from '../../../index'; import { _t } from '../../../languageHandler'; import Modal from '../../../Modal'; diff --git a/src/components/views/rooms/EditMessageComposer.js b/src/components/views/rooms/EditMessageComposer.js index 6ecb2bd549..be04a50798 100644 --- a/src/components/views/rooms/EditMessageComposer.js +++ b/src/components/views/rooms/EditMessageComposer.js @@ -27,7 +27,7 @@ import {parseEvent} from '../../../editor/deserialize'; import {PartCreator} from '../../../editor/parts'; import EditorStateTransfer from '../../../utils/EditorStateTransfer'; import classNames from 'classnames'; -import {EventStatus} from 'matrix-js-sdk'; +import {EventStatus} from 'matrix-js-sdk/src/models/event'; import BasicMessageComposer from "./BasicMessageComposer"; import {Key, isOnlyCtrlOrCmdKeyEvent} from "../../../Keyboard"; import MatrixClientContext from "../../../contexts/MatrixClientContext"; diff --git a/src/components/views/rooms/EventTile.js b/src/components/views/rooms/EventTile.js index cbe3252c2b..644d64d322 100644 --- a/src/components/views/rooms/EventTile.js +++ b/src/components/views/rooms/EventTile.js @@ -28,7 +28,7 @@ import * as sdk from "../../../index"; import dis from '../../../dispatcher/dispatcher'; import SettingsStore from "../../../settings/SettingsStore"; import {Layout, LayoutPropType} from "../../../settings/Layout"; -import {EventStatus} from 'matrix-js-sdk'; +import {EventStatus} from 'matrix-js-sdk/src/models/event'; import {formatTime} from "../../../DateUtils"; import {MatrixClientPeg} from '../../../MatrixClientPeg'; import {ALL_RULE_TYPES} from "../../../mjolnir/BanList"; diff --git a/src/components/views/rooms/ThirdPartyMemberInfo.js b/src/components/views/rooms/ThirdPartyMemberInfo.js index 5e2d82a1b2..67b86538c9 100644 --- a/src/components/views/rooms/ThirdPartyMemberInfo.js +++ b/src/components/views/rooms/ThirdPartyMemberInfo.js @@ -17,7 +17,7 @@ limitations under the License. import React from 'react'; import PropTypes from 'prop-types'; import {MatrixClientPeg} from "../../../MatrixClientPeg"; -import {MatrixEvent} from "matrix-js-sdk"; +import {MatrixEvent} from "matrix-js-sdk/src/models/event"; import {_t} from "../../../languageHandler"; import dis from "../../../dispatcher/dispatcher"; import * as sdk from "../../../index"; diff --git a/src/components/views/settings/tabs/user/GeneralUserSettingsTab.js b/src/components/views/settings/tabs/user/GeneralUserSettingsTab.js index 314acf5d65..b7dbfa4a3b 100644 --- a/src/components/views/settings/tabs/user/GeneralUserSettingsTab.js +++ b/src/components/views/settings/tabs/user/GeneralUserSettingsTab.js @@ -32,7 +32,7 @@ import * as sdk from "../../../../.."; import Modal from "../../../../../Modal"; import dis from "../../../../../dispatcher/dispatcher"; import {Service, startTermsFlow} from "../../../../../Terms"; -import {SERVICE_TYPES} from "matrix-js-sdk"; +import {SERVICE_TYPES} from "matrix-js-sdk/src/service-types"; import IdentityAuthClient from "../../../../../IdentityAuthClient"; import {abbreviateUrl} from "../../../../../utils/UrlUtils"; import { getThreepidsWithBindStatus } from '../../../../../boundThreepids'; diff --git a/src/indexing/EventIndex.js b/src/indexing/EventIndex.js index fa263a2a55..2dcdb9e3a3 100644 --- a/src/indexing/EventIndex.js +++ b/src/indexing/EventIndex.js @@ -16,7 +16,8 @@ limitations under the License. import PlatformPeg from "../PlatformPeg"; import {MatrixClientPeg} from "../MatrixClientPeg"; -import {EventTimeline, RoomMember} from 'matrix-js-sdk'; +import {RoomMember} from 'matrix-js-sdk/src/models/room-member'; +import {EventTimeline} from 'matrix-js-sdk/src/models/event-timeline'; import {sleep} from "../utils/promise"; import SettingsStore from "../settings/SettingsStore"; import {EventEmitter} from "events"; diff --git a/src/utils/AutoDiscoveryUtils.js b/src/utils/AutoDiscoveryUtils.js index 18b6451d3e..614aa4cea8 100644 --- a/src/utils/AutoDiscoveryUtils.js +++ b/src/utils/AutoDiscoveryUtils.js @@ -16,7 +16,7 @@ limitations under the License. */ import React from 'react'; -import {AutoDiscovery} from "matrix-js-sdk"; +import {AutoDiscovery} from "matrix-js-sdk/src/autodiscovery"; import {_t, _td, newTranslatableError} from "../languageHandler"; import {makeType} from "./TypeUtils"; import SdkConfig from '../SdkConfig'; diff --git a/src/utils/EventUtils.js b/src/utils/EventUtils.js index 6558a11ed4..be21896417 100644 --- a/src/utils/EventUtils.js +++ b/src/utils/EventUtils.js @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { EventStatus } from 'matrix-js-sdk'; +import { EventStatus } from 'matrix-js-sdk/src/models/event'; import {MatrixClientPeg} from '../MatrixClientPeg'; import shouldHideEvent from "../shouldHideEvent"; /** diff --git a/src/utils/IdentityServerUtils.js b/src/utils/IdentityServerUtils.js index 093d4eeabf..5ece308954 100644 --- a/src/utils/IdentityServerUtils.js +++ b/src/utils/IdentityServerUtils.js @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { SERVICE_TYPES } from 'matrix-js-sdk'; +import { SERVICE_TYPES } from 'matrix-js-sdk/src/service-types'; import SdkConfig from '../SdkConfig'; import {MatrixClientPeg} from '../MatrixClientPeg'; diff --git a/src/utils/StorageManager.js b/src/utils/StorageManager.js index c90281bacf..23c27a2d1c 100644 --- a/src/utils/StorageManager.js +++ b/src/utils/StorageManager.js @@ -14,9 +14,10 @@ See the License for the specific language governing permissions and limitations under the License. */ -import Matrix from 'matrix-js-sdk'; import {LocalStorageCryptoStore} from 'matrix-js-sdk/src/crypto/store/localStorage-crypto-store'; import Analytics from '../Analytics'; +import {IndexedDBStore} from "matrix-js-sdk/src/store/indexeddb"; +import {IndexedDBCryptoStore} from "matrix-js-sdk/src/crypto/store/indexeddb-crypto-store"; const localStorage = window.localStorage; @@ -132,7 +133,7 @@ export async function checkConsistency() { async function checkSyncStore() { let exists = false; try { - exists = await Matrix.IndexedDBStore.exists( + exists = await IndexedDBStore.exists( indexedDB, SYNC_STORE_NAME, ); log(`Sync store using IndexedDB contains data? ${exists}`); @@ -148,7 +149,7 @@ async function checkSyncStore() { async function checkCryptoStore() { let exists = false; try { - exists = await Matrix.IndexedDBCryptoStore.exists( + exists = await IndexedDBCryptoStore.exists( indexedDB, CRYPTO_STORE_NAME, ); log(`Crypto store using IndexedDB contains data? ${exists}`); diff --git a/src/utils/createMatrixClient.js b/src/utils/createMatrixClient.js index c8ff35a584..f5e196d846 100644 --- a/src/utils/createMatrixClient.js +++ b/src/utils/createMatrixClient.js @@ -14,7 +14,10 @@ See the License for the specific language governing permissions and limitations under the License. */ -import * as Matrix from 'matrix-js-sdk'; +import {createClient} from "matrix-js-sdk/src/matrix"; +import {IndexedDBCryptoStore} from "matrix-js-sdk/src/crypto/store/indexeddb-crypto-store"; +import {WebStorageSessionStore} from "matrix-js-sdk/src/store/session/webstorage"; +import {IndexedDBStore} from "matrix-js-sdk/src/store/indexeddb"; const localStorage = window.localStorage; @@ -44,7 +47,7 @@ export default function createMatrixClient(opts) { }; if (indexedDB && localStorage) { - storeOpts.store = new Matrix.IndexedDBStore({ + storeOpts.store = new IndexedDBStore({ indexedDB: indexedDB, dbName: "riot-web-sync", localStorage: localStorage, @@ -53,18 +56,18 @@ export default function createMatrixClient(opts) { } if (localStorage) { - storeOpts.sessionStore = new Matrix.WebStorageSessionStore(localStorage); + storeOpts.sessionStore = new WebStorageSessionStore(localStorage); } if (indexedDB) { - storeOpts.cryptoStore = new Matrix.IndexedDBCryptoStore( + storeOpts.cryptoStore = new IndexedDBCryptoStore( indexedDB, "matrix-js-sdk:crypto", ); } opts = Object.assign(storeOpts, opts); - return Matrix.createClient(opts); + return createClient(opts); } createMatrixClient.indexedDbWorkerScript = null; From be8ee1a576fb9eff9d8753736ae3e6183378110a Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 18 Mar 2021 21:05:51 -0600 Subject: [PATCH 44/63] Appease the linter --- src/IdentityAuthClient.js | 2 +- src/components/views/elements/Pill.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/IdentityAuthClient.js b/src/IdentityAuthClient.js index 9c6449f1e3..1687adf13b 100644 --- a/src/IdentityAuthClient.js +++ b/src/IdentityAuthClient.js @@ -15,7 +15,7 @@ limitations under the License. */ import { SERVICE_TYPES } from 'matrix-js-sdk/src/service-types'; -import { createClient } from 'matrix-js-sdk/src/matrix' +import { createClient } from 'matrix-js-sdk/src/matrix'; import {MatrixClientPeg} from './MatrixClientPeg'; import Modal from './Modal'; diff --git a/src/components/views/elements/Pill.js b/src/components/views/elements/Pill.js index d6c8aeb811..a8e16813e6 100644 --- a/src/components/views/elements/Pill.js +++ b/src/components/views/elements/Pill.js @@ -18,7 +18,7 @@ import * as sdk from '../../../index'; import dis from '../../../dispatcher/dispatcher'; import classNames from 'classnames'; import { Room } from 'matrix-js-sdk/src/models/room'; -import { RoomMember } from 'matrix-js-sdk/src/models/room-member' +import { RoomMember } from 'matrix-js-sdk/src/models/room-member'; import PropTypes from 'prop-types'; import {MatrixClientPeg} from '../../../MatrixClientPeg'; import FlairStore from "../../../stores/FlairStore"; From c2cc0862179b987fa0367fcbb5169391cb6a50a2 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 18 Mar 2021 21:24:02 -0600 Subject: [PATCH 45/63] Import the js-sdk at the index so the `request` object gets set Now that all the imports are replaced, we need to set up the js-sdk somewhere. Might as well do it in the index of the react-sdk where callers will be relying on those skin functions. --- src/index.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/index.js b/src/index.js index 008e15ad90..e360c04f4f 100644 --- a/src/index.js +++ b/src/index.js @@ -28,3 +28,7 @@ export function resetSkin() { export function getComponent(componentName) { return Skinner.getComponent(componentName); } + +// Import the js-sdk so the proper `request` object can be set. This does some +// magic with the browser injection to make all subsequent imports work fine. +import "matrix-js-sdk"; From 1d43c8b7913690f6558cab26322d8443e3a5f1fd Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 18 Mar 2021 23:28:59 -0600 Subject: [PATCH 46/63] Remove forgotten rooms from the room list once forgotten Fixes https://github.com/vector-im/element-web/issues/15559 This isn't exactly perfect as an implementation: if the user refreshes immediately after forgetting then there is a good chance the room re-appears because of the sync accumulator. At the very least this change makes it so in *most* cases the room goes away, which is probably good enough until https://github.com/vector-im/element-web/issues/14038 can be implemented properly. --- src/components/structures/MatrixChat.tsx | 8 ++++++++ src/stores/room-list/RoomListStore.ts | 12 ++++++++++++ 2 files changed, 20 insertions(+) diff --git a/src/components/structures/MatrixChat.tsx b/src/components/structures/MatrixChat.tsx index f0a3778a2b..98c7421f9d 100644 --- a/src/components/structures/MatrixChat.tsx +++ b/src/components/structures/MatrixChat.tsx @@ -85,6 +85,8 @@ import { showToast as showMobileGuideToast } from '../../toasts/MobileGuideToast import SpaceStore from "../../stores/SpaceStore"; import SpaceRoomDirectory from "./SpaceRoomDirectory"; import {replaceableComponent} from "../../utils/replaceableComponent"; +import RoomListStore from "../../stores/room-list/RoomListStore"; +import {RoomUpdateCause} from "../../stores/room-list/models"; /** constants for MatrixChat.state.view */ export enum Views { @@ -1140,11 +1142,17 @@ export default class MatrixChat extends React.PureComponent { } private forgetRoom(roomId: string) { + const room = MatrixClientPeg.get().getRoom(roomId); MatrixClientPeg.get().forget(roomId).then(() => { // Switch to home page if we're currently viewing the forgotten room if (this.state.currentRoomId === roomId) { dis.dispatch({ action: "view_home_page" }); } + + // We have to manually update the room list because the forgotten room will not + // be notified to us, therefore the room list will have no other way of knowing + // the room is forgotten. + RoomListStore.instance.manualRoomUpdate(room, RoomUpdateCause.RoomRemoved); }).catch((err) => { const errCode = err.errcode || _td("unknown error code"); Modal.createTrackedDialog("Failed to forget room", '', ErrorDialog, { diff --git a/src/stores/room-list/RoomListStore.ts b/src/stores/room-list/RoomListStore.ts index 3f415f946d..074c2e569d 100644 --- a/src/stores/room-list/RoomListStore.ts +++ b/src/stores/room-list/RoomListStore.ts @@ -655,6 +655,18 @@ export class RoomListStoreClass extends AsyncStoreWithClient { if (!algorithmTags) return [DefaultTagID.Untagged]; return algorithmTags; } + + /** + * Manually update a room with a given cause. This should only be used if the + * room list store would otherwise be incapable of doing the update itself. Note + * that this may race with the room list's regular operation. + * @param {Room} room The room to update. + * @param {RoomUpdateCause} cause The cause to update for. + */ + public async manualRoomUpdate(room: Room, cause: RoomUpdateCause) { + await this.handleRoomUpdate(room, cause); + this.updateFn.trigger(); + } } export default class RoomListStore { From 4a734ef70ba322bbd726a711eb4e0f1d802f0677 Mon Sep 17 00:00:00 2001 From: David Baker Date: Fri, 19 Mar 2021 11:29:14 +0000 Subject: [PATCH 47/63] Clear displayname / vatar if they're unset --- src/stores/OwnProfileStore.ts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/stores/OwnProfileStore.ts b/src/stores/OwnProfileStore.ts index 398532a5c8..bb45456f1e 100644 --- a/src/stores/OwnProfileStore.ts +++ b/src/stores/OwnProfileStore.ts @@ -124,8 +124,16 @@ export class OwnProfileStore extends AsyncStoreWithClient { // We specifically do not use the User object we stored for profile info as it // could easily be wrong (such as per-room instead of global profile). const profileInfo = await this.matrixClient.getProfileInfo(this.matrixClient.getUserId()); - if (profileInfo.displayname) window.localStorage.setItem(KEY_DISPLAY_NAME, profileInfo.displayname); - if (profileInfo.avatar_url) window.localStorage.setItem(KEY_AVATAR_URL, profileInfo.avatar_url); + if (profileInfo.displayname) { + window.localStorage.setItem(KEY_DISPLAY_NAME, profileInfo.displayname); + } else { + window.localStorage.removeItem(KEY_DISPLAY_NAME); + } + if (profileInfo.avatar_url) { + window.localStorage.setItem(KEY_AVATAR_URL, profileInfo.avatar_url); + } else { + window.localStorage.removeItem(KEY_AVATAR_URL); + } await this.updateState({displayName: profileInfo.displayname, avatarUrl: profileInfo.avatar_url}); }; From d04c8b3fb417fc9cc3eedcc78d3d8b4a84de035a Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 19 Mar 2021 11:36:36 +0000 Subject: [PATCH 48/63] Improve discovery of rooms in a space --- res/css/structures/_SpaceRoomDirectory.scss | 271 +++++--- res/img/element-icons/warning-badge.svg | 6 +- .../structures/SpaceRoomDirectory.tsx | 635 +++++++++--------- src/components/structures/SpaceRoomView.tsx | 9 +- .../views/elements/TextWithTooltip.js | 10 +- src/i18n/strings/en_EN.json | 32 +- 6 files changed, 527 insertions(+), 436 deletions(-) diff --git a/res/css/structures/_SpaceRoomDirectory.scss b/res/css/structures/_SpaceRoomDirectory.scss index b14e92a1af..b20554166a 100644 --- a/res/css/structures/_SpaceRoomDirectory.scss +++ b/res/css/structures/_SpaceRoomDirectory.scss @@ -31,7 +31,8 @@ limitations under the License. display: flex; .mx_BaseAvatar { - margin-right: 16px; + margin-right: 12px; + align-self: center; } .mx_BaseAvatar_image { @@ -47,6 +48,7 @@ limitations under the License. } > div { + font-weight: 400; color: $secondary-fg-color; font-size: $font-15px; line-height: $font-24px; @@ -55,38 +57,71 @@ limitations under the License. } .mx_Dialog_content { - // TODO fix scrollbar - //display: flex; - //flex-direction: column; - //height: calc(100% - 80px); - .mx_AccessibleButton_kind_link { padding: 0; } .mx_SearchBox { - margin: 24px 0 28px; + margin: 24px 0 16px; + } + + .mx_SpaceRoomDirectory_noResults { + text-align: center; + + > div { + font-size: $font-15px; + line-height: $font-24px; + color: $secondary-fg-color; + } } .mx_SpaceRoomDirectory_listHeader { display: flex; - font-size: $font-12px; - line-height: $font-15px; - color: $secondary-fg-color; + min-height: 32px; + align-items: center; + font-size: $font-15px; + line-height: $font-24px; + color: $primary-fg-color; - .mx_FormButton { - margin-bottom: 8px; + .mx_AccessibleButton { + padding: 2px 8px; + font-weight: normal; + + & + .mx_AccessibleButton { + margin-left: 16px; + } } > span { - margin: auto 0 0 auto; + margin-left: auto; + } + } + + .mx_SpaceRoomDirectory_error { + position: relative; + font-weight: $font-semi-bold; + color: $notice-primary-color; + font-size: $font-15px; + line-height: $font-18px; + margin: 20px auto 12px; + padding-left: 24px; + width: max-content; + + &::before { + content: ""; + position: absolute; + height: 16px; + width: 16px; + left: 0; + background-image: url("$(res)/img/element-icons/warning-badge.svg"); } } } } .mx_SpaceRoomDirectory_list { - margin-top: 8px; + margin-top: 16px; + padding-bottom: 40px; .mx_SpaceRoomDirectory_roomCount { > h3 { @@ -106,115 +141,128 @@ limitations under the License. } .mx_SpaceRoomDirectory_subspace { - margin-top: 8px; - - .mx_SpaceRoomDirectory_subspace_info { - display: flex; - flex-direction: row; - align-items: center; - margin-bottom: 8px; - color: $secondary-fg-color; - font-weight: $font-semi-bold; - font-size: $font-12px; - line-height: $font-15px; - - .mx_BaseAvatar { - margin-right: 12px; - vertical-align: middle; - } - - .mx_BaseAvatar_image { - border-radius: 8px; - } - - .mx_SpaceRoomDirectory_actions { - text-align: right; - height: min-content; - margin-left: auto; - margin-right: 16px; - display: inline-flex; - } - } - - .mx_SpaceRoomDirectory_subspace_children { - margin-left: 12px; - border-left: 2px solid $space-button-outline-color; - padding-left: 24px; + .mx_BaseAvatar_image { + border-radius: 8px; } } - .mx_SpaceRoomDirectory_roomTile { - padding: 16px; - border-radius: 8px; - border: 1px solid $space-button-outline-color; - margin: 8px 0 16px; - display: flex; - min-height: 76px; - box-sizing: border-box; + .mx_SpaceRoomDirectory_subspace_toggle { + position: absolute; + left: -1px; + top: 10px; + height: 16px; + width: 16px; + border-radius: 4px; + background-color: $primary-bg-color; - &.mx_AccessibleButton:hover { - background-color: rgba(141, 151, 165, 0.1); + &::before { + content: ''; + position: absolute; + top: 0; + left: 0; + height: 16px; + width: 16px; + mask-repeat: no-repeat; + mask-position: center; + background-color: $tertiary-fg-color; + mask-size: 16px; + transform: rotate(270deg); + mask-image: url('$(res)/img/feather-customised/chevron-down.svg'); } + &.mx_SpaceRoomDirectory_subspace_toggle_shown::before { + transform: rotate(0deg); + } + } + + .mx_SpaceRoomDirectory_subspace_children { + position: relative; + padding-left: 12px; + } + + .mx_SpaceRoomDirectory_roomTile { + position: relative; + padding: 6px 16px; + border-radius: 8px; + min-height: 56px; + box-sizing: border-box; + + display: grid; + grid-template-columns: 20px auto max-content; + grid-column-gap: 8px; + align-items: center; + .mx_BaseAvatar { - margin-right: 16px; - margin-top: 6px; + grid-row: 1; + grid-column: 1; + } + + .mx_SpaceRoomDirectory_roomTile_name { + font-weight: $font-semi-bold; + font-size: $font-15px; + line-height: $font-18px; + grid-row: 1; + grid-column: 2; + + .mx_InfoTooltip { + display: inline; + margin-left: 12px; + color: $tertiary-fg-color; + font-size: $font-12px; + line-height: $font-15px; + + .mx_InfoTooltip_icon { + margin-right: 4px; + } + } } .mx_SpaceRoomDirectory_roomTile_info { - display: inline-block; - font-size: $font-15px; - flex-grow: 1; - height: min-content; - margin: auto 0; - - .mx_SpaceRoomDirectory_roomTile_name { - font-weight: $font-semi-bold; - line-height: $font-18px; - } - .mx_SpaceRoomDirectory_roomTile_topic { - line-height: $font-24px; - color: $secondary-fg-color; - } - } - - .mx_SpaceRoomDirectory_roomTile_memberCount { - position: relative; - margin: auto 0 auto 24px; - padding: 0 0 0 28px; - line-height: $font-24px; - display: inline-block; - width: 32px; - - &::before { - position: absolute; - content: ''; - width: 24px; - height: 24px; - top: 0; - left: 0; - mask-position: center; - mask-repeat: no-repeat; - mask-size: contain; - background-color: $secondary-fg-color; - mask-image: url('$(res)/img/element-icons/community-members.svg'); - } + font-size: $font-12px; + line-height: $font-15px; + color: $tertiary-fg-color; + grid-row: 2; + grid-column: 1/3; } .mx_SpaceRoomDirectory_actions { - width: 180px; text-align: right; - margin-left: 28px; - display: inline-flex; - align-items: center; + margin-left: 20px; + grid-column: 3; + grid-row: 1/3; .mx_AccessibleButton { - vertical-align: middle; + padding: 6px 18px; - & + .mx_AccessibleButton { - margin-left: 24px; - } + display: none; } + + .mx_Checkbox { + display: inline-flex; + vertical-align: middle; + margin-left: 12px; + } + } + + &:hover { + background-color: $groupFilterPanel-bg-color; + + .mx_AccessibleButton { + display: inline-block; + } + } + } + + .mx_SpaceRoomDirectory_roomTile, + .mx_SpaceRoomDirectory_subspace_children { + &::before { + content: ""; + position: absolute; + background-color: $groupFilterPanel-bg-color; + width: 1px; + height: 100%; + left: 6px; + top: 0; } } @@ -226,4 +274,17 @@ limitations under the License. color: $secondary-fg-color; } } + + > hr { + border: none; + height: 1px; + background-color: rgba(141, 151, 165, 0.2); + margin: 20px 0; + } + + .mx_SpaceRoomDirectory_createRoom { + display: block; + margin: 16px auto 0; + width: max-content; + } } diff --git a/res/img/element-icons/warning-badge.svg b/res/img/element-icons/warning-badge.svg index ac5991f221..1ae4e40ffe 100644 --- a/res/img/element-icons/warning-badge.svg +++ b/res/img/element-icons/warning-badge.svg @@ -1,5 +1,5 @@ - - - + + + diff --git a/src/components/structures/SpaceRoomDirectory.tsx b/src/components/structures/SpaceRoomDirectory.tsx index 9ee16558d3..0a53f38238 100644 --- a/src/components/structures/SpaceRoomDirectory.tsx +++ b/src/components/structures/SpaceRoomDirectory.tsx @@ -14,27 +14,30 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React, {useMemo, useRef, useState} from "react"; +import React, {useMemo, useState} from "react"; import Room from "matrix-js-sdk/src/models/room"; -import MatrixEvent from "matrix-js-sdk/src/models/event"; import {EventType, RoomType} from "matrix-js-sdk/src/@types/event"; +import classNames from "classnames"; +import {sortBy} from "lodash"; import {MatrixClientPeg} from "../../MatrixClientPeg"; import dis from "../../dispatcher/dispatcher"; import {_t} from "../../languageHandler"; import AccessibleButton from "../views/elements/AccessibleButton"; import BaseDialog from "../views/dialogs/BaseDialog"; -import FormButton from "../views/elements/FormButton"; +import Spinner from "../views/elements/Spinner"; import SearchBox from "./SearchBox"; import RoomAvatar from "../views/avatars/RoomAvatar"; import RoomName from "../views/elements/RoomName"; import {useAsyncMemo} from "../../hooks/useAsyncMemo"; -import {shouldShowSpaceSettings} from "../../utils/space"; import {EnhancedMap} from "../../utils/maps"; import StyledCheckbox from "../views/elements/StyledCheckbox"; import AutoHideScrollbar from "./AutoHideScrollbar"; import BaseAvatar from "../views/avatars/BaseAvatar"; import {mediaFromMxc} from "../../customisations/Media"; +import InfoTooltip from "../views/elements/InfoTooltip"; +import TextWithTooltip from "../views/elements/TextWithTooltip"; +import {useStateToggle} from "../../hooks/useStateToggle"; interface IProps { space: Room; @@ -72,215 +75,98 @@ export interface ISpaceSummaryEvent { } /* eslint-enable camelcase */ -interface ISubspaceProps { - space: ISpaceSummaryRoom; - event?: MatrixEvent; +interface ITileProps { + room: ISpaceSummaryRoom; editing?: boolean; - onPreviewClick?(): void; - queueAction?(action: IAction): void; - onJoinClick?(): void; + suggested?: boolean; + selected?: boolean; + numChildRooms?: number; + hasPermissions?: boolean; + onViewRoomClick(autoJoin: boolean): void; + onToggleClick?(): void; } -const SubSpace: React.FC = ({ - space, +const Tile: React.FC = ({ + room, editing, - event, - queueAction, - onJoinClick, - onPreviewClick, + suggested, + selected, + hasPermissions, + onToggleClick, + onViewRoomClick, + numChildRooms, children, }) => { - const name = space.name || space.canonical_alias || space.aliases?.[0] || _t("Unnamed Space"); + const name = room.name || room.canonical_alias || room.aliases?.[0] + || (room.room_type === RoomType.Space ? _t("Unnamed Space") : _t("Unnamed Room")); - const evContent = event?.getContent(); - const [suggested, _setSuggested] = useState(evContent?.suggested); - const [removed, _setRemoved] = useState(!evContent?.via); - - const cli = MatrixClientPeg.get(); - const cliRoom = cli.getRoom(space.room_id); - const myMembership = cliRoom?.getMyMembership(); - - // TODO DRY code - let actions; - if (editing && queueAction) { - if (event && cli.getRoom(event.getRoomId())?.currentState.maySendStateEvent(event.getType(), cli.getUserId())) { - const setSuggested = () => { - _setSuggested(v => { - queueAction({ - event, - removed, - suggested: !v, - }); - return !v; - }); - }; - - const setRemoved = () => { - _setRemoved(v => { - queueAction({ - event, - removed: !v, - suggested, - }); - return !v; - }); - }; - - if (removed) { - actions = - - ; - } else { - actions = - - - ; - } - } else { - actions = - { _t("No permissions")} - ; - } - // TODO confirm remove from space click behaviour here - } else { - if (myMembership === "join") { - actions = - { _t("You're in this space")} - ; - } else if (onJoinClick) { - actions = - - { _t("Preview") } - - - - } - } - - let url: string; - if (space.avatar_url) { - url = mediaFromMxc(space.avatar_url).getSquareThumbnailHttp(Math.floor(24 * window.devicePixelRatio)); - } - - return
    -
    - - { name } - -
    - { actions } -
    -
    -
    - { children } -
    -
    -}; - -interface IAction { - event: MatrixEvent; - suggested: boolean; - removed: boolean; -} - -interface IRoomTileProps { - room: ISpaceSummaryRoom; - event?: MatrixEvent; - editing?: boolean; - onPreviewClick(): void; - queueAction?(action: IAction): void; - onJoinClick?(): void; -} - -const RoomTile = ({ room, event, editing, queueAction, onPreviewClick, onJoinClick }: IRoomTileProps) => { - const name = room.name || room.canonical_alias || room.aliases?.[0] || _t("Unnamed Room"); - - const evContent = event?.getContent(); - const [suggested, _setSuggested] = useState(evContent?.suggested); - const [removed, _setRemoved] = useState(!evContent?.via); + const [showChildren, toggleShowChildren] = useStateToggle(true); const cli = MatrixClientPeg.get(); const cliRoom = cli.getRoom(room.room_id); const myMembership = cliRoom?.getMyMembership(); - let actions; - if (editing && queueAction) { - if (event && cli.getRoom(event.getRoomId())?.currentState.maySendStateEvent(event.getType(), cli.getUserId())) { - const setSuggested = () => { - _setSuggested(v => { - queueAction({ - event, - removed, - suggested: !v, - }); - return !v; - }); - }; + const onPreviewClick = () => onViewRoomClick(false); + const onJoinClick = () => onViewRoomClick(true); - const setRemoved = () => { - _setRemoved(v => { - queueAction({ - event, - removed: !v, - suggested, - }); - return !v; - }); - }; + let button; + if (myMembership === "join") { + button = + { _t("Open") } + ; + } else if (onJoinClick) { + button = + { _t("Join") } + ; + } - if (removed) { - actions = - - ; - } else { - actions = - - - ; - } + let checkbox; + if (onToggleClick) { + if (hasPermissions) { + checkbox = ; } else { - actions = - { _t("No permissions")} - ; - } - // TODO confirm remove from space click behaviour here - } else { - if (myMembership === "join") { - actions = - { _t("You're in this room")} - ; - } else if (onJoinClick) { - actions = - - { _t("Preview") } - - - + checkbox = { ev.stopPropagation() }} + > + + ; } } let url: string; if (room.avatar_url) { - url = mediaFromMxc(room.avatar_url).getSquareThumbnailHttp(Math.floor(32 * window.devicePixelRatio)); + url = mediaFromMxc(room.avatar_url).getSquareThumbnailHttp(Math.floor(20 * window.devicePixelRatio)); + } + + let description = _t("%(count)s members", { count: room.num_joined_members }); + if (numChildRooms) { + description += " · " + _t("%(count)s rooms", { count: numChildRooms }); + } + if (room.topic) { + description += " · " + room.topic; + } + + let suggestedSection; + if (suggested) { + suggestedSection = + { _t("Suggested") } + ; } const content = - + +
    + { name } + { suggestedSection } +
    -
    - { name } -
    -
    - { room.topic } -
    + { description }
    -
    - { room.num_joined_members } -
    -
    - { actions } + { button } + { checkbox }
    ; @@ -290,9 +176,38 @@ const RoomTile = ({ room, event, editing, queueAction, onPreviewClick, onJoinCli
    } - return - { content } - ; + let childToggle; + let childSection; + if (children) { + // the chevron is purposefully a div rather than a button as it should be ignored for a11y + childToggle =
    { + ev.stopPropagation(); + toggleShowChildren(); + }} + />; + if (showChildren) { + childSection =
    + { children } +
    ; + } + } + + return <> + + { content } + { childToggle } + + { childSection } + ; }; export const showRoom = (room: ISpaceSummaryRoom, viaServers?: string[], autoJoin = false) => { @@ -325,88 +240,77 @@ export const showRoom = (room: ISpaceSummaryRoom, viaServers?: string[], autoJoi interface IHierarchyLevelProps { spaceId: string; rooms: Map; - editing?: boolean; - relations: EnhancedMap; + relations: EnhancedMap>; parents: Set; - queueAction?(action: IAction): void; - onPreviewClick(roomId: string): void; - onRemoveFromSpaceClick?(roomId: string): void; - onJoinClick?(roomId: string): void; + selectedMap?: Map>; + onViewRoomClick(roomId: string, autoJoin: boolean): void; + onToggleClick?(parentId: string, childId: string): void; } export const HierarchyLevel = ({ spaceId, rooms, - editing, relations, parents, - onPreviewClick, - onJoinClick, - queueAction, + selectedMap, + onViewRoomClick, + onToggleClick, }: IHierarchyLevelProps) => { const cli = MatrixClientPeg.get(); const space = cli.getRoom(spaceId); - // TODO respect order - const [subspaces, childRooms] = relations.get(spaceId)?.reduce((result, roomId: string) => { - if (!rooms.has(roomId)) return result; // TODO wat + const hasPermissions = space?.currentState.maySendStateEvent(EventType.SpaceChild, cli.getUserId()) + + const sortedChildren = sortBy([...relations.get(spaceId)?.values()], ev => ev.content.order || null); + const [subspaces, childRooms] = sortedChildren.reduce((result, ev: ISpaceSummaryEvent) => { + const roomId = ev.state_key; + if (!rooms.has(roomId)) return result; result[rooms.get(roomId).room_type === RoomType.Space ? 0 : 1].push(roomId); return result; }, [[], []]) || [[], []]; - // Don't render this subspace if it has no rooms we can show - // TODO this is broken - as a space may have subspaces we still need to show - // if (!childRooms.length) return null; - - const userId = cli.getUserId(); - const newParents = new Set(parents).add(spaceId); return { childRooms.map(roomId => ( - { - onPreviewClick(roomId); + suggested={relations.get(spaceId)?.get(roomId)?.content.suggested} + selected={selectedMap?.get(spaceId)?.has(roomId)} + onViewRoomClick={(autoJoin) => { + onViewRoomClick(roomId, autoJoin); }} - onJoinClick={onJoinClick ? () => { - onJoinClick(roomId); - } : undefined} + hasPermissions={hasPermissions} + onToggleClick={onToggleClick ? () => onToggleClick(spaceId, roomId) : undefined} /> )) } { subspaces.filter(roomId => !newParents.has(roomId)).map(roomId => ( - { - onPreviewClick(roomId); - }} - onJoinClick={() => { - onJoinClick(roomId); + room={rooms.get(roomId)} + numChildRooms={Array.from(relations.get(roomId)?.values() || []) + .filter(ev => rooms.get(ev.state_key)?.room_type !== RoomType.Space).length} + suggested={relations.get(spaceId)?.get(roomId)?.content.suggested} + selected={selectedMap?.get(spaceId)?.has(roomId)} + onViewRoomClick={(autoJoin) => { + onViewRoomClick(roomId, autoJoin); }} + hasPermissions={hasPermissions} + onToggleClick={onToggleClick ? () => onToggleClick(spaceId, roomId) : undefined} > - + )) } @@ -415,8 +319,8 @@ export const HierarchyLevel = ({ const SpaceRoomDirectory: React.FC = ({ space, initialText = "", onFinished }) => { // TODO pagination const cli = MatrixClientPeg.get(); + const userId = cli.getUserId(); const [query, setQuery] = useState(initialText); - const [isEditing, setIsEditing] = useState(false); const onCreateRoomClick = () => { dis.dispatch({ @@ -426,51 +330,19 @@ const SpaceRoomDirectory: React.FC = ({ space, initialText = "", onFinis onFinished(); }; - // stored within a ref as we don't need to re-render when it changes - const pendingActions = useRef(new Map()); + const [selected, setSelected] = useState(new Map>()); // Map> - let adminButton; - if (shouldShowSpaceSettings(cli, space)) { // TODO this is an imperfect test - const onManageButtonClicked = () => { - setIsEditing(true); - }; - - const onSaveButtonClicked = () => { - // TODO setBusy - pendingActions.current.forEach(({event, suggested, removed}) => { - const content = { - ...event.getContent(), - suggested, - }; - - if (removed) { - delete content["via"]; - } - - cli.sendStateEvent(event.getRoomId(), event.getType(), content, event.getStateKey()); - }); - setIsEditing(false); - }; - - if (isEditing) { - adminButton = - - { _t("Promoted to users") } - ; - } else { - adminButton = ; - } - } - - const [rooms, relations, viaMap] = useAsyncMemo(async () => { + const [rooms, parentChildMap, childParentMap, viaMap] = useAsyncMemo(async () => { try { const data = await cli.getSpaceSummary(space.roomId); - const parentChildRelations = new EnhancedMap(); + const parentChildRelations = new EnhancedMap>(); + const childParentRelations = new EnhancedMap>(); const viaMap = new EnhancedMap>(); data.events.map((ev: ISpaceSummaryEvent) => { if (ev.type === EventType.SpaceChild) { - parentChildRelations.getOrCreate(ev.room_id, []).push(ev.state_key); + parentChildRelations.getOrCreate(ev.room_id, new Map()).set(ev.state_key, ev); + childParentRelations.getOrCreate(ev.state_key, new Set()).add(ev.room_id); } if (Array.isArray(ev.content["via"])) { const set = viaMap.getOrCreate(ev.state_key, new Set()); @@ -478,7 +350,7 @@ const SpaceRoomDirectory: React.FC = ({ space, initialText = "", onFinis } }); - return [data.rooms, parentChildRelations, viaMap]; + return [data.rooms as ISpaceSummaryRoom[], parentChildRelations, childParentRelations, viaMap]; } catch (e) { console.error(e); // TODO } @@ -488,54 +360,204 @@ const SpaceRoomDirectory: React.FC = ({ space, initialText = "", onFinis const roomsMap = useMemo(() => { if (!rooms) return null; - const lcQuery = query.toLowerCase(); + const lcQuery = query.toLowerCase().trim(); - const filteredRooms = rooms.filter(r => { - return r.room_type === RoomType.Space // always include spaces to allow filtering of sub-space rooms - || r.name?.toLowerCase().includes(lcQuery) - || r.topic?.toLowerCase().includes(lcQuery); + const roomsMap = new Map(rooms.map(r => [r.room_id, r])); + if (!lcQuery) return roomsMap; + + const directMatches = rooms.filter(r => { + return r.name?.toLowerCase().includes(lcQuery) || r.topic?.toLowerCase().includes(lcQuery); }); - return new Map(filteredRooms.map(r => [r.room_id, r])); - // const root = rooms.get(space.roomId); - }, [rooms, query]); + // Walk back up the tree to find all parents of the direct matches to show their place in the hierarchy + const visited = new Set(); + const queue = [...directMatches.map(r => r.room_id)]; + while (queue.length) { + const roomId = queue.pop(); + visited.add(roomId); + childParentMap.get(roomId)?.forEach(parentId => { + if (!visited.has(parentId)) { + queue.push(parentId); + } + }); + } + + // Remove any mappings for rooms which were not visited in the walk + Array.from(roomsMap.keys()).forEach(roomId => { + if (!visited.has(roomId)) { + roomsMap.delete(roomId); + } + }); + return roomsMap; + }, [rooms, childParentMap, query]); const title = - +

    { _t("Explore rooms") }

    ; + const explanation = - _t("If you can't find the room you're looking for, ask for an invite or Create a new room.", null, + _t("If you can't find the room you're looking for, ask for an invite or create a new room.", null, {a: sub => { return {sub}; }}, ); + const [error, setError] = useState(""); + const [removing, setRemoving] = useState(false); + const [saving, setSaving] = useState(false); + let content; if (roomsMap) { - content = - { - pendingActions.current.set(action.event.room_id, action); - }} - onPreviewClick={roomId => { - showRoom(roomsMap.get(roomId), Array.from(viaMap.get(roomId) || []), false); - onFinished(); - }} - onJoinClick={(roomId) => { - showRoom(roomsMap.get(roomId), Array.from(viaMap.get(roomId) || []), true); - onFinished(); - }} - /> - ; + const numRooms = Array.from(roomsMap.values()).filter(r => r.room_type !== RoomType.Space).length; + const numSpaces = roomsMap.size - numRooms - 1; // -1 at the end to exclude the space we are looking at + + let countsStr; + if (numSpaces > 1) { + countsStr = _t("%(count)s rooms and %(numSpaces)s spaces", { count: numRooms, numSpaces }); + } else if (numSpaces > 0) { + countsStr = _t("%(count)s rooms and 1 space", { count: numRooms, numSpaces }); + } else { + countsStr = _t("%(count)s rooms", { count: numRooms, numSpaces }); + } + + let editSection; + if (space.getMyMembership() === "join" && space.currentState.maySendStateEvent(EventType.SpaceChild, userId)) { + const selectedRelations = Array.from(selected.keys()).flatMap(parentId => { + return [...selected.get(parentId).values()].map(childId => [parentId, childId]) as [string, string][]; + }); + + let buttons; + if (selectedRelations.length) { + const selectionAllSuggested = selectedRelations.every(([parentId, childId]) => { + return parentChildMap.get(parentId)?.get(childId)?.content.suggested; + }); + + const disabled = removing || saving; + + buttons = <> + { + setRemoving(true); + try { + for (const [parentId, childId] of selectedRelations) { + await cli.sendStateEvent(parentId, EventType.SpaceChild, {}, childId); + parentChildMap.get(parentId).get(childId).content = {}; + parentChildMap.set(parentId, new Map(parentChildMap.get(parentId))); + } + } catch (e) { + setError(_t("Failed to remove some rooms. Try again later")); + } + setRemoving(false); + }} + kind="danger_outline" + disabled={disabled} + > + { removing ? _t("Removing...") : _t("Remove") } + + { + setSaving(true); + try { + for (const [parentId, childId] of selectedRelations) { + const suggested = !selectionAllSuggested; + const existingContent = parentChildMap.get(parentId)?.get(childId)?.content; + if (!existingContent || existingContent.suggested === suggested) continue; + + const content = { + ...existingContent, + suggested: !selectionAllSuggested, + }; + + await cli.sendStateEvent(parentId, EventType.SpaceChild, content, childId); + + parentChildMap.get(parentId).get(childId).content = content; + parentChildMap.set(parentId, new Map(parentChildMap.get(parentId))); + } + } catch (e) { + setError("Failed to update some suggestions. Try again later"); + } + setSaving(false); + }} + kind="primary_outline" + disabled={disabled} + > + { saving + ? _t("Saving...") + : (selectionAllSuggested ? _t("Mark as not suggested") : _t("Mark as suggested")) + } + + ; + } + + editSection = + { buttons } + ; + } + + let results; + if (roomsMap.size) { + results = <> + { + setError(""); + if (!selected.has(parentId)) { + setSelected(new Map(selected.set(parentId, new Set([childId])))); + return; + } + + const parentSet = selected.get(parentId); + if (!parentSet.has(childId)) { + setSelected(new Map(selected.set(parentId, new Set([...parentSet, childId])))); + return; + } + + parentSet.delete(childId); + setSelected(new Map(selected.set(parentId, new Set(parentSet)))); + }} + onViewRoomClick={(roomId, autoJoin) => { + showRoom(roomsMap.get(roomId), Array.from(viaMap.get(roomId) || []), autoJoin); + onFinished(); + }} + /> +
    + ; + } else { + results =
    +

    { _t("No results found") }

    +
    { _t("You may want to try a different search or check for typos.") }
    +
    ; + } + + content = <> +
    + { countsStr } + { editSection } +
    + { error &&
    + { error } +
    } + + { results } + + { _t("Create room") } + + + ; + } else { + content = ; } // TODO loading state/error state @@ -546,13 +568,10 @@ const SpaceRoomDirectory: React.FC = ({ space, initialText = "", onFinis -
    - { adminButton } -
    { content }
    diff --git a/src/components/structures/SpaceRoomView.tsx b/src/components/structures/SpaceRoomView.tsx index 0b0f2a2ac9..2b4168a983 100644 --- a/src/components/structures/SpaceRoomView.tsx +++ b/src/components/structures/SpaceRoomView.tsx @@ -230,10 +230,10 @@ const SpaceLanding = ({ space }) => { try { const data = await cli.getSpaceSummary(space.roomId, undefined, myMembership !== "join"); - const parentChildRelations = new EnhancedMap(); + const parentChildRelations = new EnhancedMap>(); data.events.map((ev: ISpaceSummaryEvent) => { if (ev.type === EventType.SpaceChild) { - parentChildRelations.getOrCreate(ev.room_id, []).push(ev.state_key); + parentChildRelations.getOrCreate(ev.room_id, new Map()).set(ev.state_key, ev); } }); @@ -257,11 +257,10 @@ const SpaceLanding = ({ space }) => { { - showRoom(roomsMap.get(roomId), [], false); // TODO + onViewRoomClick={(roomId, autoJoin) => { + showRoom(roomsMap.get(roomId), [], autoJoin); }} /> ; diff --git a/src/components/views/elements/TextWithTooltip.js b/src/components/views/elements/TextWithTooltip.js index e4ad234ae2..0bd491768c 100644 --- a/src/components/views/elements/TextWithTooltip.js +++ b/src/components/views/elements/TextWithTooltip.js @@ -46,12 +46,14 @@ export default class TextWithTooltip extends React.Component { render() { const Tooltip = sdk.getComponent("elements.Tooltip"); + const {class: className, children, tooltip, tooltipClass, ...props} = this.props; + return ( - - {this.props.children} + + {children} {this.state.hover && } ); diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 07d292a0e7..e4d197470d 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -2603,20 +2603,30 @@ "Drop file here to upload": "Drop file here to upload", "You have %(count)s unread notifications in a prior version of this room.|other": "You have %(count)s unread notifications in a prior version of this room.", "You have %(count)s unread notifications in a prior version of this room.|one": "You have %(count)s unread notification in a prior version of this room.", - "Undo": "Undo", - "Remove from Space": "Remove from Space", - "No permissions": "No permissions", - "You're in this space": "You're in this space", - "You're in this room": "You're in this room", - "Save changes": "Save changes", - "Promoted to users": "Promoted to users", - "Manage rooms": "Manage rooms", - "Find a room...": "Find a room...", + "Open": "Open", + "You don't have permission": "You don't have permission", + "%(count)s members|other": "%(count)s members", + "%(count)s members|one": "%(count)s member", + "%(count)s rooms|other": "%(count)s rooms", + "%(count)s rooms|one": "%(count)s room", + "This room is suggested as a good one to join": "This room is suggested as a good one to join", + "Suggested": "Suggested", + "If you can't find the room you're looking for, ask for an invite or create a new room.": "If you can't find the room you're looking for, ask for an invite or create a new room.", + "%(count)s rooms and %(numSpaces)s spaces|other": "%(count)s rooms and %(numSpaces)s spaces", + "%(count)s rooms and %(numSpaces)s spaces|one": "%(count)s room and %(numSpaces)s spaces", + "%(count)s rooms and 1 space|other": "%(count)s rooms and 1 space", + "%(count)s rooms and 1 space|one": "%(count)s room and 1 space", + "Failed to remove some rooms. Try again later": "Failed to remove some rooms. Try again later", + "Removing...": "Removing...", + "Mark as not suggested": "Mark as not suggested", + "Mark as suggested": "Mark as suggested", + "No results found": "No results found", + "You may want to try a different search or check for typos.": "You may want to try a different search or check for typos.", + "Create room": "Create room", + "Search names and description": "Search names and description", " invites you": " invites you", "Public space": "Public space", "Private space": "Private space", - "%(count)s members|other": "%(count)s members", - "%(count)s members|one": "%(count)s member", "Add existing rooms & spaces": "Add existing rooms & spaces", "Default Rooms": "Default Rooms", "Your server does not support showing space hierarchies.": "Your server does not support showing space hierarchies.", From 90d87122bc45d3082d4322f8f06085f71377086b Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 16 Mar 2021 11:39:06 +0000 Subject: [PATCH 49/63] Tweak copy on space creation flows --- src/components/structures/SpaceRoomView.tsx | 14 ++++++++++---- src/components/views/spaces/SpaceCreateMenu.tsx | 4 ++-- src/i18n/strings/en_EN.json | 10 +++++----- 3 files changed, 17 insertions(+), 11 deletions(-) diff --git a/src/components/structures/SpaceRoomView.tsx b/src/components/structures/SpaceRoomView.tsx index 0b0f2a2ac9..17ab75e707 100644 --- a/src/components/structures/SpaceRoomView.tsx +++ b/src/components/structures/SpaceRoomView.tsx @@ -369,7 +369,7 @@ const SpaceSetupFirstRooms = ({ space, title, description, onFinished }) => { let buttonLabel = _t("Skip for now"); if (roomNames.some(name => name.trim())) { onClick = onNextClick; - buttonLabel = busy ? _t("Creating rooms...") : _t("Next") + buttonLabel = busy ? _t("Creating rooms...") : _t("Continue") } return
    @@ -391,8 +391,14 @@ const SpaceSetupFirstRooms = ({ space, title, description, onFinished }) => { const SpaceSetupPublicShare = ({ space, onFinished }) => { return
    -

    { _t("Share your public space") }

    -
    { _t("At the moment only you can see it.") }
    +

    { _t("Invite people") }

    +
    + { + _t("It's just you at the moment.") + } { + _t("%(spaceName)s will be even better with others", { spaceName: space.name }) + } +
    @@ -610,7 +616,7 @@ export default class SpaceRoomView extends React.PureComponent { return this.setState({ phase: Phase.PublicShare })} />; case Phase.PublicShare: diff --git a/src/components/views/spaces/SpaceCreateMenu.tsx b/src/components/views/spaces/SpaceCreateMenu.tsx index 88098d1b66..ca5b25f128 100644 --- a/src/components/views/spaces/SpaceCreateMenu.tsx +++ b/src/components/views/spaces/SpaceCreateMenu.tsx @@ -140,9 +140,9 @@ const SpaceCreateMenu = ({ onFinished }) => {

    { - _t("Give it a photo, name and description to help you identify it.") + _t("Add some details to help people recognise it.") } { - _t("You can change these at any point.") + _t("You can change these anytime.") }

    diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 07d292a0e7..e3ccc1794d 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -998,8 +998,8 @@ "Go back": "Go back", "Your public space": "Your public space", "Your private space": "Your private space", - "Give it a photo, name and description to help you identify it.": "Give it a photo, name and description to help you identify it.", - "You can change these at any point.": "You can change these at any point.", + "Add some details to help people recognise it.": "Add some details to help people recognise it.", + "You can change these anytime.": "You can change these anytime.", "Creating...": "Creating...", "Create": "Create", "Expand space panel": "Expand space panel", @@ -2629,8 +2629,8 @@ "Failed to create initial space rooms": "Failed to create initial space rooms", "Skip for now": "Skip for now", "Creating rooms...": "Creating rooms...", - "At the moment only you can see it.": "At the moment only you can see it.", - "Finish": "Finish", + "It's just you at the moment.": "It's just you at the moment.", + "%(spaceName)s will be even better with others": "%(spaceName)s will be even better with others", "Who are you working with?": "Who are you working with?", "Ensure the right people have access to the space.": "Ensure the right people have access to the space.", "Just Me": "Just Me", @@ -2642,7 +2642,7 @@ "Invite by username": "Invite by username", "Inviting...": "Inviting...", "What are some things you want to discuss?": "What are some things you want to discuss?", - "We'll create rooms for each topic.": "We'll create rooms for each topic.", + "We'll create a room for each of them. You can add more later too.": "We'll create a room for each of them. You can add more later too.", "What projects are you working on?": "What projects are you working on?", "We'll create rooms for each of them. You can add existing rooms after setup.": "We'll create rooms for each of them. You can add existing rooms after setup.", "Tried to load a specific point in this room's timeline, but you do not have permission to view the message in question.": "Tried to load a specific point in this room's timeline, but you do not have permission to view the message in question.", From c6f6d24b32b22fc1a77fc306a117e7209438ce76 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 16 Mar 2021 14:18:45 +0000 Subject: [PATCH 50/63] Iterate space creation and previews --- src/components/structures/SpaceRoomView.tsx | 87 ++++++++++++++++++--- src/i18n/strings/en_EN.json | 1 + src/stores/SpaceStore.tsx | 35 ++++++--- 3 files changed, 100 insertions(+), 23 deletions(-) diff --git a/src/components/structures/SpaceRoomView.tsx b/src/components/structures/SpaceRoomView.tsx index 17ab75e707..cfa261bb9b 100644 --- a/src/components/structures/SpaceRoomView.tsx +++ b/src/components/structures/SpaceRoomView.tsx @@ -17,6 +17,7 @@ limitations under the License. import React, {RefObject, useContext, useRef, useState} from "react"; import {EventType, RoomType} from "matrix-js-sdk/src/@types/event"; import {Room} from "matrix-js-sdk/src/models/room"; +import {EventSubscription} from "fbemitter"; import MatrixClientContext from "../../contexts/MatrixClientContext"; import RoomAvatar from "../views/avatars/RoomAvatar"; @@ -42,7 +43,6 @@ import ErrorBoundary from "../views/elements/ErrorBoundary"; import {ActionPayload} from "../../dispatcher/payloads"; import RightPanel from "./RightPanel"; import RightPanelStore from "../../stores/RightPanelStore"; -import {EventSubscription} from "fbemitter"; import {RightPanelPhases} from "../../stores/RightPanelStorePhases"; import {SetRightPanelPhasePayload} from "../../dispatcher/payloads/SetRightPanelPhasePayload"; import {useStateArray} from "../../hooks/useStateArray"; @@ -54,6 +54,7 @@ import {EnhancedMap} from "../../utils/maps"; import AutoHideScrollbar from "./AutoHideScrollbar"; import MemberAvatar from "../views/avatars/MemberAvatar"; import {useStateToggle} from "../../hooks/useStateToggle"; +import SpaceStore from "../../stores/SpaceStore"; interface IProps { space: Room; @@ -66,6 +67,7 @@ interface IProps { interface IState { phase: Phase; showRightPanel: boolean; + myMembership: string; } enum Phase { @@ -98,6 +100,8 @@ const SpacePreview = ({ space, onJoinButtonClicked, onRejectButtonClicked }) => const cli = useContext(MatrixClientContext); const myMembership = useMyRoomMembership(space); + const [busy, setBusy] = useState(false); + let inviterSection; let joinButtons; if (myMembership === "invite") { @@ -121,11 +125,35 @@ const SpacePreview = ({ space, onJoinButtonClicked, onRejectButtonClicked }) => } joinButtons = <> - - + { + setBusy(true); + onRejectButtonClicked(); + }} /> + { + setBusy(true); + onJoinButtonClicked(); + }} + /> ; } else { - joinButtons = + joinButtons = ( + { + setBusy(true); + onJoinButtonClicked(); + }} + /> + ) + } + + if (busy) { + joinButtons = ; } let visibilitySection; @@ -403,7 +431,7 @@ const SpaceSetupPublicShare = ({ space, onFinished }) => {
    - +
    ; }; @@ -553,17 +581,26 @@ export default class SpaceRoomView extends React.PureComponent { this.state = { phase, showRightPanel: RightPanelStore.getSharedInstance().isOpenForRoom, + myMembership: this.props.space.getMyMembership(), }; this.dispatcherRef = defaultDispatcher.register(this.onAction); this.rightPanelStoreToken = RightPanelStore.getSharedInstance().addListener(this.onRightPanelStoreUpdate); + this.context.on("Room.myMembership", this.onMyMembership); } componentWillUnmount() { defaultDispatcher.unregister(this.dispatcherRef); this.rightPanelStoreToken.remove(); + this.context.off("Room.myMembership", this.onMyMembership); } + private onMyMembership = (room: Room, myMembership: string) => { + if (room.roomId === this.props.space.roomId) { + this.setState({ myMembership }); + } + }; + private onRightPanelStoreUpdate = () => { this.setState({ showRightPanel: RightPanelStore.getSharedInstance().isOpenForRoom, @@ -600,10 +637,43 @@ export default class SpaceRoomView extends React.PureComponent { } }; + private goToFirstRoom = async () => { + const childRooms = SpaceStore.instance.getChildRooms(this.props.space.roomId); + if (childRooms.length) { + const room = childRooms[0]; + defaultDispatcher.dispatch({ + action: "view_room", + room_id: room.roomId, + }); + return; + } + + let suggestedRooms = SpaceStore.instance.suggestedRooms; + if (SpaceStore.instance.activeSpace !== this.props.space) { + // the space store has the suggested rooms loaded for a different space, fetch the right ones + suggestedRooms = (await SpaceStore.instance.fetchSuggestedRooms(this.props.space, 1)).rooms; + } + + if (suggestedRooms.length) { + const room = suggestedRooms[0]; + defaultDispatcher.dispatch({ + action: "view_room", + room_id: room.room_id, + oobData: { + avatarUrl: room.avatar_url, + name: room.name || room.canonical_alias || room.aliases.pop() || _t("Empty room"), + }, + }); + return; + } + + this.setState({ phase: Phase.Landing }); + }; + private renderBody() { switch (this.state.phase) { case Phase.Landing: - if (this.props.space.getMyMembership() === "join") { + if (this.state.myMembership === "join") { return ; } else { return { onFinished={() => this.setState({ phase: Phase.PublicShare })} />; case Phase.PublicShare: - return this.setState({ phase: Phase.Landing })} - />; + return ; case Phase.PrivateScope: return { } if (space) { - try { - const data: { - rooms: ISpaceSummaryRoom[]; - events: ISpaceSummaryEvent[]; - } = await this.matrixClient.getSpaceSummary(space.roomId, 0, true, false, MAX_SUGGESTED_ROOMS); - if (this._activeSpace === space) { - this._suggestedRooms = data.rooms.filter(roomInfo => { - return roomInfo.room_type !== RoomType.Space && !this.matrixClient.getRoom(roomInfo.room_id); - }); - this.emit(SUGGESTED_ROOMS, this._suggestedRooms); - } - } catch (e) { - console.error(e); + const data = await this.fetchSuggestedRooms(space); + if (this._activeSpace === space) { + this._suggestedRooms = data.rooms.filter(roomInfo => { + return roomInfo.room_type !== RoomType.Space && !this.matrixClient.getRoom(roomInfo.room_id); + }); + this.emit(SUGGESTED_ROOMS, this._suggestedRooms); } } } + public fetchSuggestedRooms = async (space: Room, limit = MAX_SUGGESTED_ROOMS) => { + try { + const data: { + rooms: ISpaceSummaryRoom[]; + events: ISpaceSummaryEvent[]; + } = await this.matrixClient.getSpaceSummary(space.roomId, 0, true, false, limit); + return data; + } catch (e) { + console.error(e); + } + return { + rooms: [], + events: [], + }; + }; + public addRoomToSpace(space: Room, roomId: string, via: string[], suggested = false, autoJoin = false) { return this.matrixClient.sendStateEvent(space.roomId, EventType.SpaceChild, { via, From 31ce19373be030ec2869195545828eadb567cd0a Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 16 Mar 2021 16:16:51 +0000 Subject: [PATCH 51/63] Fix space panel spacings --- res/css/structures/_SpacePanel.scss | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/res/css/structures/_SpacePanel.scss b/res/css/structures/_SpacePanel.scss index d3e7d7efee..ffe67ce6ab 100644 --- a/res/css/structures/_SpacePanel.scss +++ b/res/css/structures/_SpacePanel.scss @@ -63,7 +63,7 @@ $activeBorderColor: $secondary-fg-color; } .mx_AutoHideScrollbar { - padding: 16px 0; + padding: 8px 0 16px; } .mx_SpaceButton_toggleCollapse { @@ -99,7 +99,6 @@ $activeBorderColor: $secondary-fg-color; .mx_SpaceButton { border-radius: 8px; - margin-bottom: 2px; display: flex; align-items: center; padding: 4px 4px 4px 0; From 88b7c8f53dd8d043abbc4f589cb190e9e7c8b9d9 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 18 Mar 2021 13:17:30 +0000 Subject: [PATCH 52/63] Fix add existing to space dialog showing all spaces additionally as rooms --- src/components/views/dialogs/AddExistingToSpaceDialog.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/views/dialogs/AddExistingToSpaceDialog.tsx b/src/components/views/dialogs/AddExistingToSpaceDialog.tsx index 66efaefd9d..500637244a 100644 --- a/src/components/views/dialogs/AddExistingToSpaceDialog.tsx +++ b/src/components/views/dialogs/AddExistingToSpaceDialog.tsx @@ -69,6 +69,7 @@ const AddExistingToSpaceDialog: React.FC = ({ matrixClient: cli, space, const existingRoomsSet = new Set(existingRooms); const rooms = cli.getVisibleRooms().filter(room => { return !existingRoomsSet.has(room) // not already in space + && !room.isSpaceRoom() // not a space itself && room.name.toLowerCase().includes(lcQuery) // contains query && !DMRoomMap.shared().getUserIdForRoomId(room.roomId); // not a DM }); From 3718d550c5b97d722247c03d4a9bfafc03158b1c Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 19 Mar 2021 11:42:53 +0000 Subject: [PATCH 53/63] Fix space creation menu shade width --- res/css/views/spaces/_SpaceCreateMenu.scss | 5 +---- src/components/views/spaces/SpacePanel.tsx | 10 ++++++++-- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/res/css/views/spaces/_SpaceCreateMenu.scss b/res/css/views/spaces/_SpaceCreateMenu.scss index 2a11ec9f23..be387d2c26 100644 --- a/res/css/views/spaces/_SpaceCreateMenu.scss +++ b/res/css/views/spaces/_SpaceCreateMenu.scss @@ -14,10 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -// TODO: the space panel currently does not have a fixed width, -// just the headers at each level have a max-width of 150px -// so this will look slightly off for now. We should probably use css grid for the whole main layout... -$spacePanelWidth: 200px; +$spacePanelWidth: 71px; .mx_SpaceCreateMenu_wrapper { // background blur everything except SpacePanel diff --git a/src/components/views/spaces/SpacePanel.tsx b/src/components/views/spaces/SpacePanel.tsx index 48e2c86b2c..bacf1bd929 100644 --- a/src/components/views/spaces/SpacePanel.tsx +++ b/src/components/views/spaces/SpacePanel.tsx @@ -220,13 +220,19 @@ const SpacePanel = () => { { + openMenu(); + if (!isPanelCollapsed) setPanelCollapsed(true); + }} isNarrow={isPanelCollapsed} /> setPanelCollapsed(!isPanelCollapsed)} + onClick={() => { + setPanelCollapsed(!isPanelCollapsed); + if (menuDisplayed) closeMenu(); + }} title={expandCollapseButtonTitle} /> { contextMenu } From 2b4c670b8964cf52c4762331f9e566527fdb1ad0 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 19 Mar 2021 11:46:44 +0000 Subject: [PATCH 54/63] Fix favourites not showing up in home until a refresh --- src/stores/SpaceStore.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/stores/SpaceStore.tsx b/src/stores/SpaceStore.tsx index a329ee78e1..b82acfd0ed 100644 --- a/src/stores/SpaceStore.tsx +++ b/src/stores/SpaceStore.tsx @@ -394,7 +394,9 @@ export class SpaceStoreClass extends AsyncStoreWithClient { private onRoomAccountData = (ev: MatrixEvent, room: Room, lastEvent: MatrixEvent) => { if (ev.getType() === EventType.Tag && !room.isSpaceRoom()) { // If the room was in favourites and now isn't or the opposite then update its position in the trees - if (!!ev.getContent()[DefaultTagID.Favourite] !== !!lastEvent.getContent()[DefaultTagID.Favourite]) { + const oldTags = lastEvent.getContent()?.tags; + const newTags = ev.getContent()?.tags; + if (!!oldTags[DefaultTagID.Favourite] !== !!newTags[DefaultTagID.Favourite]) { this.onRoomUpdate(room); } } From 1fbbb67e7484115139ecb87d7299c7913b8994d4 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 19 Mar 2021 13:16:36 +0000 Subject: [PATCH 55/63] Consolidate button styles in Space creation flows --- res/css/structures/_SpaceRoomView.scss | 105 ++++++++---------- res/css/views/spaces/_SpaceCreateMenu.scss | 44 +------- res/css/views/spaces/_SpacePublicShare.scss | 33 +----- src/components/structures/SpaceRoomView.tsx | 41 +++---- .../views/spaces/SpacePublicShare.tsx | 5 +- 5 files changed, 66 insertions(+), 162 deletions(-) diff --git a/res/css/structures/_SpaceRoomView.scss b/res/css/structures/_SpaceRoomView.scss index 60abe36c29..a7ce630b96 100644 --- a/res/css/structures/_SpaceRoomView.scss +++ b/res/css/structures/_SpaceRoomView.scss @@ -16,6 +16,51 @@ limitations under the License. $SpaceRoomViewInnerWidth: 428px; +@define-mixin SpacePillButton { + position: relative; + padding: 16px 32px 16px 72px; + width: 432px; + box-sizing: border-box; + border-radius: 8px; + border: 1px solid $input-darker-bg-color; + font-size: $font-15px; + margin: 20px 0; + + > h3 { + font-weight: $font-semi-bold; + margin: 0 0 4px; + } + + > span { + color: $secondary-fg-color; + } + + &::before { + position: absolute; + content: ''; + width: 32px; + height: 32px; + top: 24px; + left: 20px; + mask-position: center; + mask-repeat: no-repeat; + mask-size: 24px; + background-color: $tertiary-fg-color; + } + + &:hover { + border-color: $accent-color; + + &::before { + background-color: $accent-color; + } + + > span { + color: $primary-fg-color; + } + } +} + .mx_SpaceRoomView { .mx_MainSplit > div:first-child { padding: 80px 60px; @@ -331,64 +376,8 @@ $SpaceRoomViewInnerWidth: 428px; } .mx_SpaceRoomView_privateScope { - .mx_RadioButton { - width: $SpaceRoomViewInnerWidth; - border-radius: 8px; - border: 1px solid $space-button-outline-color; - padding: 16px 16px 16px 72px; - margin-top: 36px; - cursor: pointer; - box-sizing: border-box; - position: relative; - - > div:first-of-type { - // hide radio dot - display: none; - } - - .mx_RadioButton_content { - margin: 0; - - > h3 { - margin: 0 0 4px; - font-size: $font-15px; - font-weight: $font-semi-bold; - line-height: $font-18px; - } - - > div { - color: $secondary-fg-color; - font-size: $font-15px; - line-height: $font-24px; - } - } - - &::before { - content: ""; - position: absolute; - height: 32px; - width: 32px; - top: 24px; - left: 20px; - background-color: $secondary-fg-color; - mask-repeat: no-repeat; - mask-position: center; - mask-size: contain; - } - } - - .mx_RadioButton_checked { - border-color: $accent-color; - - .mx_RadioButton_content { - > div { - color: $primary-fg-color; - } - } - - &::before { - background-color: $accent-color; - } + .mx_AccessibleButton { + @mixin SpacePillButton; } .mx_SpaceRoomView_privateScope_justMeButton::before { diff --git a/res/css/views/spaces/_SpaceCreateMenu.scss b/res/css/views/spaces/_SpaceCreateMenu.scss index be387d2c26..bea39e2389 100644 --- a/res/css/views/spaces/_SpaceCreateMenu.scss +++ b/res/css/views/spaces/_SpaceCreateMenu.scss @@ -45,53 +45,11 @@ $spacePanelWidth: 71px; } .mx_SpaceCreateMenuType { - position: relative; - padding: 16px 32px 16px 72px; - width: 432px; - box-sizing: border-box; - border-radius: 8px; - border: 1px solid $input-darker-bg-color; - font-size: $font-15px; - margin: 20px 0; - - > h3 { - font-weight: $font-semi-bold; - margin: 0 0 4px; - } - - > span { - color: $secondary-fg-color; - } - - &::before { - position: absolute; - content: ''; - width: 32px; - height: 32px; - top: 24px; - left: 20px; - mask-position: center; - mask-repeat: no-repeat; - mask-size: 32px; - background-color: $tertiary-fg-color; - } - - &:hover { - border-color: $accent-color; - - &::before { - background-color: $accent-color; - } - - > span { - color: $primary-fg-color; - } - } + @mixin SpacePillButton; } .mx_SpaceCreateMenuType_public::before { mask-image: url('$(res)/img/globe.svg'); - mask-size: 26px; } .mx_SpaceCreateMenuType_private::before { mask-image: url('$(res)/img/element-icons/lock.svg'); diff --git a/res/css/views/spaces/_SpacePublicShare.scss b/res/css/views/spaces/_SpacePublicShare.scss index 9ba0549ae3..373fa94e00 100644 --- a/res/css/views/spaces/_SpacePublicShare.scss +++ b/res/css/views/spaces/_SpacePublicShare.scss @@ -16,38 +16,7 @@ limitations under the License. .mx_SpacePublicShare { .mx_AccessibleButton { - border: 1px solid $space-button-outline-color; - box-sizing: border-box; - border-radius: 8px; - padding: 12px 24px 12px 52px; - margin-top: 16px; - width: $SpaceRoomViewInnerWidth; - font-size: $font-15px; - line-height: $font-24px; - position: relative; - display: flex; - - > span { - color: #368bd6; - margin-left: auto; - } - - &:hover { - background-color: rgba(141, 151, 165, 0.1); - } - - &::before { - content: ""; - position: absolute; - width: 30px; - height: 30px; - mask-repeat: no-repeat; - mask-size: contain; - mask-position: center; - background: $muted-fg-color; - left: 12px; - top: 9px; - } + @mixin SpacePillButton; &.mx_SpacePublicShare_shareButton::before { mask-image: url('$(res)/img/element-icons/link.svg'); diff --git a/src/components/structures/SpaceRoomView.tsx b/src/components/structures/SpaceRoomView.tsx index cfa261bb9b..23cd2f898c 100644 --- a/src/components/structures/SpaceRoomView.tsx +++ b/src/components/structures/SpaceRoomView.tsx @@ -32,7 +32,6 @@ import {useRoomMembers} from "../../hooks/useRoomMembers"; import createRoom, {IOpts, Preset} from "../../createRoom"; import Field from "../views/elements/Field"; import {useEventEmitter} from "../../hooks/useEventEmitter"; -import StyledRadioGroup from "../views/elements/StyledRadioGroup"; import withValidation from "../views/elements/Validation"; import * as Email from "../../email"; import defaultDispatcher from "../../dispatcher/dispatcher"; @@ -443,32 +442,20 @@ const SpaceSetupPrivateScope = ({ onFinished }) => {

    { _t("Who are you working with?") }

    { _t("Ensure the right people have access to the space.") }
    - -

    { _t("Just Me") }

    -
    { _t("A private space just for you") }
    - , - }, { - value: "meAndMyTeammates", - className: "mx_SpaceRoomView_privateScope_meAndMyTeammatesButton", - label: -

    { _t("Me and my teammates") }

    -
    { _t("A private space for you and your teammates") }
    -
    , - }, - ]} - /> - -
    - onFinished(option !== "justMe")} /> -
    + { onFinished(false) }} + > +

    { _t("Just me") }

    +
    { _t("A private space to organise your rooms") }
    +
    + { onFinished(true) }} + > +

    { _t("Me and my teammates") }

    +
    { _t("A private space for you and your teammates") }
    +
    ; }; diff --git a/src/components/views/spaces/SpacePublicShare.tsx b/src/components/views/spaces/SpacePublicShare.tsx index 3930c1db16..07257d295f 100644 --- a/src/components/views/spaces/SpacePublicShare.tsx +++ b/src/components/views/spaces/SpacePublicShare.tsx @@ -47,7 +47,7 @@ const SpacePublicShare = ({ space, onFinished }: IProps) => { } }} > - { _t("Share invite link") } +

    { _t("Share invite link") }

    { copiedText } { onFinished(); }} > - { _t("Invite by email or username") } +

    { _t("Invite people") }

    + { _t("Invite with email or username") }
    ; }; From 76dffdcb2c0671ea30d0c77a9b1374fe9c455cf4 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 19 Mar 2021 13:20:04 +0000 Subject: [PATCH 56/63] Tweak space creation copy and auto focus fields --- src/components/structures/SpaceRoomView.tsx | 41 +++++++++++-------- .../views/spaces/SpaceCreateMenu.tsx | 2 +- .../views/spaces/SpacePublicShare.tsx | 2 +- src/i18n/strings/en_EN.json | 25 +++++------ 4 files changed, 40 insertions(+), 30 deletions(-) diff --git a/src/components/structures/SpaceRoomView.tsx b/src/components/structures/SpaceRoomView.tsx index 23cd2f898c..f0789e1e21 100644 --- a/src/components/structures/SpaceRoomView.tsx +++ b/src/components/structures/SpaceRoomView.tsx @@ -364,6 +364,7 @@ const SpaceSetupFirstRooms = ({ space, title, description, onFinished }) => { placeholder={placeholders[i]} value={roomNames[i]} onChange={ev => setRoomName(i, ev.target.value)} + autoFocus={i === 2} />; }); @@ -418,13 +419,9 @@ const SpaceSetupFirstRooms = ({ space, title, description, onFinished }) => { const SpaceSetupPublicShare = ({ space, onFinished }) => { return
    -

    { _t("Invite people") }

    -
    - { - _t("It's just you at the moment.") - } { - _t("%(spaceName)s will be even better with others", { spaceName: space.name }) - } +

    { _t("Share %(name)s", { name: space.name }) }

    +
    + { _t("It's just you at the moment, it will be even better with others.") }
    @@ -435,12 +432,12 @@ const SpaceSetupPublicShare = ({ space, onFinished }) => {
    ; }; -const SpaceSetupPrivateScope = ({ onFinished }) => { - const [option, setOption] = useState(null); - +const SpaceSetupPrivateScope = ({ space, onFinished }) => { return

    { _t("Who are you working with?") }

    -
    { _t("Ensure the right people have access to the space.") }
    +
    + { _t("Make sure the right people have access to %(name)s", { name: space.name }) } +
    { onChange={ev => setEmailAddress(i, ev.target.value)} ref={fieldRefs[i]} onValidate={validateEmailRules} + autoFocus={i === 0} />; }); @@ -522,9 +520,18 @@ const SpaceSetupPrivateInvite = ({ space, onFinished }) => { setBusy(false); }; + let onClick = onFinished; + let buttonLabel = _t("Skip for now"); + if (emailAddresses.some(name => name.trim())) { + onClick = onNextClick; + buttonLabel = busy ? _t("Inviting...") : _t("Continue") + } + return

    { _t("Invite your teammates") }

    -
    { _t("Ensure the right people have access to the space.") }
    +
    + { _t("Make sure the right people have access. You can invite more later.") } +
    { error &&
    { error }
    } { fields } @@ -539,8 +546,7 @@ const SpaceSetupPrivateInvite = ({ space, onFinished }) => {
    - {_t("Skip for now")} - +
    ; }; @@ -673,7 +679,8 @@ export default class SpaceRoomView extends React.PureComponent { return this.setState({ phase: Phase.PublicShare })} />; case Phase.PublicShare: @@ -681,6 +688,7 @@ export default class SpaceRoomView extends React.PureComponent { case Phase.PrivateScope: return { this.setState({ phase: invite ? Phase.PrivateInvite : Phase.PrivateCreateRooms }); }} @@ -694,7 +702,8 @@ export default class SpaceRoomView extends React.PureComponent { return this.setState({ phase: Phase.Landing })} />; } diff --git a/src/components/views/spaces/SpaceCreateMenu.tsx b/src/components/views/spaces/SpaceCreateMenu.tsx index ca5b25f128..879cf929e0 100644 --- a/src/components/views/spaces/SpaceCreateMenu.tsx +++ b/src/components/views/spaces/SpaceCreateMenu.tsx @@ -108,7 +108,7 @@ const SpaceCreateMenu = ({ onFinished }) => { body =

    { _t("Create a space") }

    { _t("Spaces are new ways to group rooms and people. " + - "To join an existing space you’ll need an invite") }

    + "To join an existing space you'll need an invite.") }

    { const success = await copyPlaintext(permalinkCreator.forRoom()); const text = success ? _t("Copied!") : _t("Failed to copy"); setCopiedText(text); - await sleep(10); + await sleep(5000); if (copiedText === text) { // if the text hasn't changed by another click then clear it after some time setCopiedText(_t("Click to copy")); } diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 4be6e06459..5ba787422f 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -989,7 +989,7 @@ "Name": "Name", "Description": "Description", "Create a space": "Create a space", - "Spaces are new ways to group rooms and people. To join an existing space you’ll need an invite": "Spaces are new ways to group rooms and people. To join an existing space you’ll need an invite", + "Spaces are new ways to group rooms and people. To join an existing space you'll need an invite.": "Spaces are new ways to group rooms and people. To join an existing space you'll need an invite.", "Public": "Public", "Open space for anyone, best for communities": "Open space for anyone, best for communities", "Private": "Private", @@ -1009,10 +1009,10 @@ "Copied!": "Copied!", "Failed to copy": "Failed to copy", "Share invite link": "Share invite link", - "Invite by email or username": "Invite by email or username", + "Invite people": "Invite people", + "Invite with email or username": "Invite with email or username", "Invite members": "Invite members", "Share your public space": "Share your public space", - "Invite people": "Invite people", "Settings": "Settings", "Leave space": "Leave space", "New room": "New room", @@ -2629,23 +2629,24 @@ "Failed to create initial space rooms": "Failed to create initial space rooms", "Skip for now": "Skip for now", "Creating rooms...": "Creating rooms...", - "It's just you at the moment.": "It's just you at the moment.", - "%(spaceName)s will be even better with others": "%(spaceName)s will be even better with others", + "Share %(name)s": "Share %(name)s", + "It's just you at the moment, it will be even better with others.": "It's just you at the moment, it will be even better with others.", "Go to my first room": "Go to my first room", "Who are you working with?": "Who are you working with?", - "Ensure the right people have access to the space.": "Ensure the right people have access to the space.", - "Just Me": "Just Me", - "A private space just for you": "A private space just for you", + "Make sure the right people have access to %(name)s": "Make sure the right people have access to %(name)s", + "Just me": "Just me", + "A private space to organise your rooms": "A private space to organise your rooms", "Me and my teammates": "Me and my teammates", "A private space for you and your teammates": "A private space for you and your teammates", "Failed to invite the following users to your space: %(csvUsers)s": "Failed to invite the following users to your space: %(csvUsers)s", - "Invite your teammates": "Invite your teammates", - "Invite by username": "Invite by username", "Inviting...": "Inviting...", + "Invite your teammates": "Invite your teammates", + "Make sure the right people have access. You can invite more later.": "Make sure the right people have access. You can invite more later.", + "Invite by username": "Invite by username", "What are some things you want to discuss?": "What are some things you want to discuss?", - "We'll create a room for each of them. You can add more later too.": "We'll create a room for each of them. You can add more later too.", + "Let's create a room for each of them. You can add more later too, including already existing ones.": "Let's create a room for each of them. You can add more later too, including already existing ones.", "What projects are you working on?": "What projects are you working on?", - "We'll create rooms for each of them. You can add existing rooms after setup.": "We'll create rooms for each of them. You can add existing rooms after setup.", + "We'll create rooms for each of them. You can add more later too, including already existing ones.": "We'll create rooms for each of them. You can add more later too, including already existing ones.", "Tried to load a specific point in this room's timeline, but you do not have permission to view the message in question.": "Tried to load a specific point in this room's timeline, but you do not have permission to view the message in question.", "Tried to load a specific point in this room's timeline, but was unable to find it.": "Tried to load a specific point in this room's timeline, but was unable to find it.", "Failed to load timeline position": "Failed to load timeline position", From 70e0b77fc4db3f9ac6a83b903e15fa25fa0be907 Mon Sep 17 00:00:00 2001 From: Jaiwanth Date: Fri, 19 Mar 2021 21:12:47 +0530 Subject: [PATCH 57/63] Changed invite phrases in context menu and invite dialog --- src/components/views/dialogs/InviteDialog.tsx | 4 +++- src/components/views/rooms/RoomTile.tsx | 2 +- src/i18n/strings/en_EN.json | 2 ++ 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/components/views/dialogs/InviteDialog.tsx b/src/components/views/dialogs/InviteDialog.tsx index 9aef421d5a..de0b5b237b 100644 --- a/src/components/views/dialogs/InviteDialog.tsx +++ b/src/components/views/dialogs/InviteDialog.tsx @@ -1256,7 +1256,9 @@ export default class InviteDialog extends React.PureComponent { {canInvite ? ( ) : null} diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 07d292a0e7..34a346fef0 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1601,6 +1601,7 @@ "Favourited": "Favourited", "Favourite": "Favourite", "Low Priority": "Low Priority", + "Invite People": "Invite People", "Leave Room": "Leave Room", "Room options": "Room options", "%(count)s unread messages including mentions.|other": "%(count)s unread messages including mentions.", @@ -2201,6 +2202,7 @@ "Go": "Go", "Invite to %(spaceName)s": "Invite to %(spaceName)s", "Unnamed Space": "Unnamed Space", + "Invite to %(roomName)s": "Invite to %(roomName)s", "Invite someone using their name, email address, username (like ) or share this space.": "Invite someone using their name, email address, username (like ) or share this space.", "Invite someone using their name, username (like ) or share this space.": "Invite someone using their name, username (like ) or share this space.", "Invite someone using their name, email address, username (like ) or share this room.": "Invite someone using their name, email address, username (like ) or share this room.", From 495cbadb08f7ab9cf7f961459d3dc843297e9faf Mon Sep 17 00:00:00 2001 From: Jaiwanth Date: Fri, 19 Mar 2021 22:46:19 +0530 Subject: [PATCH 58/63] Update src/components/views/rooms/RoomTile.tsx --- src/components/views/rooms/RoomTile.tsx | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/components/views/rooms/RoomTile.tsx b/src/components/views/rooms/RoomTile.tsx index 7eaad9e204..79db460275 100644 --- a/src/components/views/rooms/RoomTile.tsx +++ b/src/components/views/rooms/RoomTile.tsx @@ -464,16 +464,8 @@ export default class RoomTile extends React.PureComponent { const isLowPriority = roomTags.includes(DefaultTagID.LowPriority); const lowPriorityLabel = _t("Low Priority"); - const inRoom = this.props.room.getMyMembership() === "join"; const userId = MatrixClientPeg.get().getUserId(); - let canInvite = inRoom; - const powerLevels = this.props.room.currentState - .getStateEvents("m.room.power_levels", "") - ?.getContent(); - const me = this.props.room.getMember(userId); - if (powerLevels && me && powerLevels.invite > me.powerLevel) { - canInvite = false; - } + const canInvite = this.props.room.canInvite(userId); contextMenu = Date: Sun, 21 Mar 2021 14:11:45 +0200 Subject: [PATCH 59/63] fix make btns in verify dailog respect system font --- res/css/_common.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/res/css/_common.scss b/res/css/_common.scss index 36a81e6651..0093bde0ab 100644 --- a/res/css/_common.scss +++ b/res/css/_common.scss @@ -395,6 +395,7 @@ input[type=text]:focus, input[type=password]:focus, textarea:focus { border: 1px solid $accent-color; color: $accent-color; background-color: $button-secondary-bg-color; + font-family: inherit; } .mx_Dialog button:last-child { From 497caf5645874d5614eb43d220dc8a18eda6149f Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Mon, 22 Mar 2021 13:22:16 +0000 Subject: [PATCH 60/63] Fix redaction event list summaries breaking sender profiles --- src/components/structures/MessagePanel.js | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/components/structures/MessagePanel.js b/src/components/structures/MessagePanel.js index 63f23f22f3..0a9af413d8 100644 --- a/src/components/structures/MessagePanel.js +++ b/src/components/structures/MessagePanel.js @@ -1044,6 +1044,10 @@ class RedactionGrouper { } shouldGroup(ev) { + // absorb hidden events so that they do not break up streams of messages & redaction events being grouped + if (!this.panel._shouldShowEvent(ev)) { + return true; + } if (this.panel._wantsDateSeparator(this.events[0], ev.getDate())) { return false; } @@ -1055,6 +1059,9 @@ class RedactionGrouper { ev.getId(), ev === this.lastShownEvent, ); + if (!this.panel._shouldShowEvent(ev)) { + return; + } this.events.push(ev); } @@ -1080,13 +1087,9 @@ class RedactionGrouper { ); const senders = new Set(); - let eventTiles = this.events.map((e) => { + let eventTiles = this.events.map((e, i) => { senders.add(e.sender); - // In order to prevent DateSeparators from appearing in the expanded form, - // render each member event as if the previous one was itself. - // This way, the timestamp of the previous event === the - // timestamp of the current event, and no DateSeparator is inserted. - return panel._getTilesForEvent(e, e, e === lastShownEvent); + return panel._getTilesForEvent(i === 0 ? this.prevEvent : this.events[i - 1], e, e === lastShownEvent); }).reduce((a, b) => a.concat(b), []); if (eventTiles.length === 0) { From cfbcf12e1d6ffe5fa078976182cd847faf4c2530 Mon Sep 17 00:00:00 2001 From: Germain Souquet Date: Mon, 22 Mar 2021 15:02:28 +0000 Subject: [PATCH 61/63] docs: update file extensions in CIDER editor documentation --- docs/ciderEditor.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/ciderEditor.md b/docs/ciderEditor.md index f522dc2fc4..379b6f5b51 100644 --- a/docs/ciderEditor.md +++ b/docs/ciderEditor.md @@ -21,14 +21,14 @@ caret nodes (more on that later). For these reasons it doesn't use `innerText`, `textContent` or anything similar. The model addresses any content in the editor within as an offset within this string. The caret position is thus also converted from a position in the DOM tree -to an offset in the content string. This happens in `getCaretOffsetAndText` in `dom.js`. +to an offset in the content string. This happens in `getCaretOffsetAndText` in `dom.ts`. Once the content string and caret offset is calculated, it is passed to the `update()` method of the model. The model first calculates the same content string of its current parts, basically just concatenating their text. It then looks for differences between the current and the new content string. The diffing algorithm is very basic, and assumes there is only one change around the caret offset, -so this should be very inexpensive. See `diff.js` for details. +so this should be very inexpensive. See `diff.ts` for details. The result of the diffing is the strings that were added and/or removed from the current content. These differences are then applied to the parts, @@ -51,7 +51,7 @@ which relate poorly to text input or changes, and don't need the `beforeinput` e which isn't broadly supported yet. Once the parts of the model are updated, the DOM of the editor is then reconciled -with the new model state, see `renderModel` in `render.js` for this. +with the new model state, see `renderModel` in `render.ts` for this. If the model didn't reject the input and didn't make any additional changes, this won't make any changes to the DOM at all, and should thus be fairly efficient. From 0db31dfeaeb656418c5f2690eb73a3b13ded285c Mon Sep 17 00:00:00 2001 From: Germain Souquet Date: Mon, 22 Mar 2021 15:05:22 +0000 Subject: [PATCH 62/63] fix: CIDER formatting buttons on Safari --- res/css/views/rooms/_MessageComposerFormatBar.scss | 2 ++ src/components/views/rooms/MessageComposerFormatBar.js | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/res/css/views/rooms/_MessageComposerFormatBar.scss b/res/css/views/rooms/_MessageComposerFormatBar.scss index d97c49630a..b305e91db0 100644 --- a/res/css/views/rooms/_MessageComposerFormatBar.scss +++ b/res/css/views/rooms/_MessageComposerFormatBar.scss @@ -60,6 +60,8 @@ limitations under the License. width: 27px; height: 24px; box-sizing: border-box; + background: none; + vertical-align: middle; } .mx_MessageComposerFormatBar_button::after { diff --git a/src/components/views/rooms/MessageComposerFormatBar.js b/src/components/views/rooms/MessageComposerFormatBar.js index d2539b1ef4..fc0f785b08 100644 --- a/src/components/views/rooms/MessageComposerFormatBar.js +++ b/src/components/views/rooms/MessageComposerFormatBar.js @@ -85,8 +85,8 @@ class FormatButton extends React.PureComponent { return ( Date: Mon, 22 Mar 2021 19:52:09 -0400 Subject: [PATCH 63/63] don't overwrite callback with undefined if no customization provided --- src/MatrixClientPeg.ts | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/MatrixClientPeg.ts b/src/MatrixClientPeg.ts index de1d573d40..7db5ed1a4e 100644 --- a/src/MatrixClientPeg.ts +++ b/src/MatrixClientPeg.ts @@ -296,10 +296,11 @@ class _MatrixClientPeg implements IMatrixClientPeg { // These are always installed regardless of the labs flag so that // cross-signing features can toggle on without reloading and also be // accessed immediately after login. - const customisedCallbacks = { - getDehydrationKey: SecurityCustomisations.getDehydrationKey, - }; - Object.assign(opts.cryptoCallbacks, crossSigningCallbacks, customisedCallbacks); + Object.assign(opts.cryptoCallbacks, crossSigningCallbacks); + if (SecurityCustomisations.getDehydrationKey) { + opts.cryptoCallbacks.getDehydrationKey = + SecurityCustomisations.getDehydrationKey; + } this.matrixClient = createMatrixClient(opts);