From 54cd385e6d5bf07c0e47dee2e448e9d9c4f14245 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Fri, 27 Aug 2021 13:31:43 +0200 Subject: [PATCH 01/65] Improve AUX panel behaviour MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/css/structures/_RoomView.scss | 1 - src/components/structures/RoomView.tsx | 34 ------------------- src/components/views/rooms/AuxPanel.tsx | 32 ++--------------- src/components/views/voip/CallViewForRoom.tsx | 7 +--- 4 files changed, 3 insertions(+), 71 deletions(-) diff --git a/res/css/structures/_RoomView.scss b/res/css/structures/_RoomView.scss index 86c2efeb4a..fd9c4a14fc 100644 --- a/res/css/structures/_RoomView.scss +++ b/res/css/structures/_RoomView.scss @@ -89,7 +89,6 @@ limitations under the License. margin: 0px auto; overflow: auto; - flex: 0 0 auto; } .mx_RoomView_auxPanel_fullHeight { diff --git a/src/components/structures/RoomView.tsx b/src/components/structures/RoomView.tsx index 474b99262d..ef5600eed2 100644 --- a/src/components/structures/RoomView.tsx +++ b/src/components/structures/RoomView.tsx @@ -78,7 +78,6 @@ import { objectHasDiff } from "../../utils/objects"; import SpaceRoomView from "./SpaceRoomView"; import { IOpts } from "../../createRoom"; import { replaceableComponent } from "../../utils/replaceableComponent"; -import UIStore from "../../stores/UIStore"; import EditorStateTransfer from "../../utils/EditorStateTransfer"; import { throttle } from "lodash"; import ErrorDialog from '../views/dialogs/ErrorDialog'; @@ -156,7 +155,6 @@ export interface IState { // used by componentDidUpdate to avoid unnecessary checks atEndOfLiveTimelineInit: boolean; showTopUnreadMessagesBar: boolean; - auxPanelMaxHeight?: number; statusBarVisible: boolean; // We load this later by asking the js-sdk to suggest a version for us. // This object is the result of Room#getRecommendedVersion() @@ -563,10 +561,6 @@ export default class RoomView extends React.Component { }); window.addEventListener('beforeunload', this.onPageUnload); - if (this.props.resizeNotifier) { - this.props.resizeNotifier.on("middlePanelResized", this.onResize); - } - this.onResize(); } shouldComponentUpdate(nextProps, nextState) { @@ -654,9 +648,6 @@ export default class RoomView extends React.Component { } window.removeEventListener('beforeunload', this.onPageUnload); - if (this.props.resizeNotifier) { - this.props.resizeNotifier.removeListener("middlePanelResized", this.onResize); - } // Remove RoomStore listener if (this.roomStoreToken) { @@ -1617,28 +1608,6 @@ export default class RoomView extends React.Component { }; } - private onResize = () => { - // It seems flexbox doesn't give us a way to constrain the auxPanel height to have - // a minimum of the height of the video element, whilst also capping it from pushing out the page - // so we have to do it via JS instead. In this implementation we cap the height by putting - // a maxHeight on the underlying remote video tag. - - // header + footer + status + give us at least 120px of scrollback at all times. - let auxPanelMaxHeight = UIStore.instance.windowHeight - - (54 + // height of RoomHeader - 36 + // height of the status area - 51 + // minimum height of the message composer - 120); // amount of desired scrollback - - // XXX: this is a bit of a hack and might possibly cause the video to push out the page anyway - // but it's better than the video going missing entirely - if (auxPanelMaxHeight < 50) auxPanelMaxHeight = 50; - - if (this.state.auxPanelMaxHeight !== auxPanelMaxHeight) { - this.setState({ auxPanelMaxHeight }); - } - }; - private onStatusBarVisible = () => { if (this.unmounted || this.state.statusBarVisible) return; this.setState({ statusBarVisible: true }); @@ -1926,11 +1895,8 @@ export default class RoomView extends React.Component { const auxPanel = ( { aux } diff --git a/src/components/views/rooms/AuxPanel.tsx b/src/components/views/rooms/AuxPanel.tsx index 4a62d6711e..7d3877025c 100644 --- a/src/components/views/rooms/AuxPanel.tsx +++ b/src/components/views/rooms/AuxPanel.tsx @@ -35,16 +35,6 @@ interface IProps { room: Room; userId: string; showApps: boolean; // Render apps - - // maxHeight attribute for the aux panel and the video - // therein - maxHeight: number; - - // a callback which is called when the content of the aux panel changes - // content in a way that is likely to make it change size. - onResize: () => void; - fullHeight: boolean; - resizeNotifier: ResizeNotifier; } @@ -92,13 +82,6 @@ export default class AuxPanel extends React.Component { return objectHasDiff(this.props, nextProps) || objectHasDiff(this.state, nextState); } - componentDidUpdate(prevProps, prevState) { - // most changes are likely to cause a resize - if (this.props.onResize) { - this.props.onResize(); - } - } - private rateLimitedUpdate = throttle(() => { this.setState({ counters: this.computeCounters() }); }, 500, { leading: true, trailing: true }); @@ -138,7 +121,6 @@ export default class AuxPanel extends React.Component { const callView = ( ); @@ -148,7 +130,6 @@ export default class AuxPanel extends React.Component { appsDrawer = ; @@ -204,21 +185,12 @@ export default class AuxPanel extends React.Component { } } - const classes = classNames({ - "mx_RoomView_auxPanel": true, - "mx_RoomView_auxPanel_fullHeight": this.props.fullHeight, - }); - const style: React.CSSProperties = {}; - if (!this.props.fullHeight) { - style.maxHeight = this.props.maxHeight; - } - return ( - + { stateViews } + { this.props.children } { appsDrawer } { callView } - { this.props.children } ); } diff --git a/src/components/views/voip/CallViewForRoom.tsx b/src/components/views/voip/CallViewForRoom.tsx index a5aa3e7734..b0a6f17095 100644 --- a/src/components/views/voip/CallViewForRoom.tsx +++ b/src/components/views/voip/CallViewForRoom.tsx @@ -27,9 +27,6 @@ interface IProps { // What room we should display the call for roomId: string; - // maxHeight style attribute for the video panel - maxVideoHeight?: number; - resizeNotifier: ResizeNotifier; } @@ -99,14 +96,12 @@ export default class CallViewForRoom extends React.Component { public render() { if (!this.state.call) return null; - // We subtract 8 as it the margin-bottom of the mx_CallViewForRoom_ResizeWrapper - const maxHeight = this.props.maxVideoHeight - 8; return (
Date: Fri, 27 Aug 2021 14:07:36 +0200 Subject: [PATCH 02/65] Remove unused import MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/views/rooms/AuxPanel.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/views/rooms/AuxPanel.tsx b/src/components/views/rooms/AuxPanel.tsx index 7d3877025c..7afa29624a 100644 --- a/src/components/views/rooms/AuxPanel.tsx +++ b/src/components/views/rooms/AuxPanel.tsx @@ -15,7 +15,6 @@ limitations under the License. */ import React from 'react'; -import classNames from 'classnames'; import { lexicographicCompare } from 'matrix-js-sdk/src/utils'; import { Room } from 'matrix-js-sdk/src/models/room'; From 2fc36628308bbc4a3949faf93f837c0272d4843c Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Fri, 17 Sep 2021 09:38:59 +0100 Subject: [PATCH 03/65] Fix space admin check false positive on multiple admins --- src/components/views/dialogs/LeaveSpaceDialog.tsx | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/components/views/dialogs/LeaveSpaceDialog.tsx b/src/components/views/dialogs/LeaveSpaceDialog.tsx index 841fa14407..d26c0a8b6a 100644 --- a/src/components/views/dialogs/LeaveSpaceDialog.tsx +++ b/src/components/views/dialogs/LeaveSpaceDialog.tsx @@ -131,8 +131,13 @@ interface IProps { } const isOnlyAdmin = (room: Room): boolean => { - return !room.getJoinedMembers().some(member => { - return member.userId !== room.client.credentials.userId && member.powerLevelNorm === 100; + const userId = room.client.getUserId(); + if (room.getMember(userId).powerLevelNorm !== 100) { + return false; // user is not an admin + } + return room.getJoinedMembers().every(member => { + // return true if every other member has a lower power level (we are highest) + return member.userId === userId || member.powerLevelNorm < 100; }); }; From 308f6b1d868b7383d95a2e4c94d0d004d1b26bfc Mon Sep 17 00:00:00 2001 From: RiotRobot Date: Tue, 21 Sep 2021 09:39:41 +0100 Subject: [PATCH 04/65] Upgrade matrix-js-sdk to 13.0.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 3e3d9383c4..0498ee948e 100644 --- a/package.json +++ b/package.json @@ -82,7 +82,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": "13.0.0-rc.1", "matrix-widget-api": "^0.1.0-beta.16", "minimist": "^1.2.5", "opus-recorder": "^8.0.3", diff --git a/yarn.lock b/yarn.lock index 546e762224..f5931c8c6e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5806,9 +5806,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 "12.5.0" - resolved "https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/f84905b00398072b592addfb1dae64c8f3a07fa2" +matrix-js-sdk@13.0.0-rc.1: + version "13.0.0-rc.1" + resolved "https://registry.yarnpkg.com/matrix-js-sdk/-/matrix-js-sdk-13.0.0-rc.1.tgz#12deab353862852acae8342108d30ce080d364da" + integrity sha512-dfqJwXmG1+Ky2geaNADWYb7mwB2IfLFTE+T4q16gCoh2HM0W5yTMvi+kiJs0QspWFXICTps7eBSSq0827QNU8A== dependencies: "@babel/runtime" "^7.12.5" another-json "^0.2.0" From afbd52e28b9d9cd36775d44b61a383f83c311d5c Mon Sep 17 00:00:00 2001 From: RiotRobot Date: Tue, 21 Sep 2021 09:41:49 +0100 Subject: [PATCH 05/65] Prepare changelog for v3.31.0-rc.1 --- CHANGELOG.md | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9a445a4041..d2da9dad17 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,52 @@ +Changes in [3.31.0-rc.1](https://github.com/vector-im/element-desktop/releases/tag/v3.31.0-rc.1) (2021-09-21) +============================================================================================================= + +## ✨ Features + * Say Joining space instead of Joining room where we know its a space ([\#6818](https://github.com/matrix-org/matrix-react-sdk/pull/6818)). Fixes vector-im/element-web#19064 and vector-im/element-web#19064. + * Add warning that some spaces may not be relinked to the newly upgraded room ([\#6805](https://github.com/matrix-org/matrix-react-sdk/pull/6805)). Fixes vector-im/element-web#18858 and vector-im/element-web#18858. + * Delabs Spaces, iterate some copy and move communities/space toggle to preferences ([\#6594](https://github.com/matrix-org/matrix-react-sdk/pull/6594)). Fixes vector-im/element-web#18088, vector-im/element-web#18524 vector-im/element-web#18088 and vector-im/element-web#18088. + * Show "Message" in the user info panel instead of "Start chat" ([\#6319](https://github.com/matrix-org/matrix-react-sdk/pull/6319)). Fixes vector-im/element-web#17877 and vector-im/element-web#17877. Contributed by [SimonBrandner](https://github.com/SimonBrandner). + * Fix space keyboard shortcuts conflicting with native zoom shortcuts ([\#6804](https://github.com/matrix-org/matrix-react-sdk/pull/6804)). + * Replace plain text emoji at the end of a line ([\#6784](https://github.com/matrix-org/matrix-react-sdk/pull/6784)). Fixes vector-im/element-web#18833 and vector-im/element-web#18833. Contributed by [SimonBrandner](https://github.com/SimonBrandner). + * Simplify Space Panel layout and fix some edge cases ([\#6800](https://github.com/matrix-org/matrix-react-sdk/pull/6800)). Fixes vector-im/element-web#18694 and vector-im/element-web#18694. + * Show unsent message warning on Space Panel buttons ([\#6778](https://github.com/matrix-org/matrix-react-sdk/pull/6778)). Fixes vector-im/element-web#18891 and vector-im/element-web#18891. + * Hide mute/unmute button in UserInfo for Spaces as it makes no sense ([\#6790](https://github.com/matrix-org/matrix-react-sdk/pull/6790)). Fixes vector-im/element-web#19007 and vector-im/element-web#19007. + * Fix automatic field population in space create menu not validating ([\#6792](https://github.com/matrix-org/matrix-react-sdk/pull/6792)). Fixes vector-im/element-web#19005 and vector-im/element-web#19005. + * Optimize input label transition on focus ([\#6783](https://github.com/matrix-org/matrix-react-sdk/pull/6783)). Fixes vector-im/element-web#12876 and vector-im/element-web#12876. Contributed by [MadLittleMods](https://github.com/MadLittleMods). + * Adapt and re-use the RolesRoomSettingsTab for Spaces ([\#6779](https://github.com/matrix-org/matrix-react-sdk/pull/6779)). Fixes vector-im/element-web#18908 vector-im/element-web#18909 and vector-im/element-web#18908. + * Deduplicate join rule management between rooms and spaces ([\#6724](https://github.com/matrix-org/matrix-react-sdk/pull/6724)). Fixes vector-im/element-web#18798 and vector-im/element-web#18798. + * Add config option to turn on in-room event sending timing metrics ([\#6766](https://github.com/matrix-org/matrix-react-sdk/pull/6766)). + * Improve the upgrade for restricted user experience ([\#6764](https://github.com/matrix-org/matrix-react-sdk/pull/6764)). Fixes vector-im/element-web#18677 and vector-im/element-web#18677. + * Improve tooltips on space quick actions and explore button ([\#6760](https://github.com/matrix-org/matrix-react-sdk/pull/6760)). Fixes vector-im/element-web#18528 and vector-im/element-web#18528. + * Make space members and user info behave more expectedly ([\#6765](https://github.com/matrix-org/matrix-react-sdk/pull/6765)). Fixes vector-im/element-web#17018 and vector-im/element-web#17018. + * hide no-op m.room.encryption events and better word param changes ([\#6747](https://github.com/matrix-org/matrix-react-sdk/pull/6747)). Fixes vector-im/element-web#18597 and vector-im/element-web#18597. + * Respect m.space.parent relations if they hold valid permissions ([\#6746](https://github.com/matrix-org/matrix-react-sdk/pull/6746)). Fixes vector-im/element-web#10935 and vector-im/element-web#10935. + * Space panel accessibility improvements ([\#6744](https://github.com/matrix-org/matrix-react-sdk/pull/6744)). Fixes vector-im/element-web#18892 and vector-im/element-web#18892. + +## 🐛 Bug Fixes + * Revert Firefox composer deletion hacks ([\#6844](https://github.com/matrix-org/matrix-react-sdk/pull/6844)). Fixes vector-im/element-web#19103 and vector-im/element-web#19103. Contributed by [SimonBrandner](https://github.com/SimonBrandner). + * Fix accessing field on oobData which may be undefined ([\#6830](https://github.com/matrix-org/matrix-react-sdk/pull/6830)). Fixes vector-im/element-web#19085 and vector-im/element-web#19085. + * Fix pill deletion on Firefox 78 ([\#6832](https://github.com/matrix-org/matrix-react-sdk/pull/6832)). Fixes vector-im/element-web#19077 and vector-im/element-web#19077. Contributed by [SimonBrandner](https://github.com/SimonBrandner). + * Fix reactions aria-label not being a string and thus being read as [Object object] ([\#6828](https://github.com/matrix-org/matrix-react-sdk/pull/6828)). + * Fix missing null guard in space hierarchy pagination ([\#6821](https://github.com/matrix-org/matrix-react-sdk/pull/6821)). Fixes matrix-org/element-web-rageshakes#6299 and matrix-org/element-web-rageshakes#6299. + * Fix checks to show prompt to start new chats ([\#6812](https://github.com/matrix-org/matrix-react-sdk/pull/6812)). + * Fix room list scroll jumps ([\#6777](https://github.com/matrix-org/matrix-react-sdk/pull/6777)). Fixes vector-im/element-web#17460 vector-im/element-web#18440 and vector-im/element-web#17460. Contributed by [robintown](https://github.com/robintown). + * Fix various message bubble alignment issues ([\#6785](https://github.com/matrix-org/matrix-react-sdk/pull/6785)). Fixes vector-im/element-web#18293, vector-im/element-web#18294 vector-im/element-web#18305 and vector-im/element-web#18293. Contributed by [robintown](https://github.com/robintown). + * Make message bubble font size consistent ([\#6795](https://github.com/matrix-org/matrix-react-sdk/pull/6795)). Contributed by [robintown](https://github.com/robintown). + * Fix edge cases around joining new room which does not belong to active space ([\#6797](https://github.com/matrix-org/matrix-react-sdk/pull/6797)). Fixes vector-im/element-web#19025 and vector-im/element-web#19025. + * Fix edge case space issues around creation and initial view ([\#6798](https://github.com/matrix-org/matrix-react-sdk/pull/6798)). Fixes vector-im/element-web#19023 and vector-im/element-web#19023. + * Stop spinner on space preview if the join fails ([\#6803](https://github.com/matrix-org/matrix-react-sdk/pull/6803)). Fixes vector-im/element-web#19034 and vector-im/element-web#19034. + * Fix emoji picker and stickerpicker not appearing correctly when opened ([\#6793](https://github.com/matrix-org/matrix-react-sdk/pull/6793)). Fixes vector-im/element-web#19012 and vector-im/element-web#19012. Contributed by [Palid](https://github.com/Palid). + * Fix autocomplete not having y-scroll ([\#6794](https://github.com/matrix-org/matrix-react-sdk/pull/6794)). Fixes vector-im/element-web#18997 and vector-im/element-web#18997. Contributed by [Palid](https://github.com/Palid). + * Fix broken edge case with public space creation with no alias ([\#6791](https://github.com/matrix-org/matrix-react-sdk/pull/6791)). Fixes vector-im/element-web#19003 and vector-im/element-web#19003. + * Redirect from /#/welcome to /#/home if already logged in ([\#6786](https://github.com/matrix-org/matrix-react-sdk/pull/6786)). Fixes vector-im/element-web#18990 and vector-im/element-web#18990. Contributed by [aaronraimist](https://github.com/aaronraimist). + * Fix build issues from two conflicting PRs landing without merge conflict ([\#6780](https://github.com/matrix-org/matrix-react-sdk/pull/6780)). + * Render guest settings only in public rooms/spaces ([\#6693](https://github.com/matrix-org/matrix-react-sdk/pull/6693)). Fixes vector-im/element-web#18776 and vector-im/element-web#18776. Contributed by [SimonBrandner](https://github.com/SimonBrandner). + * Fix message bubble corners being wrong in the presence of hidden events ([\#6776](https://github.com/matrix-org/matrix-react-sdk/pull/6776)). Fixes vector-im/element-web#18124 and vector-im/element-web#18124. Contributed by [robintown](https://github.com/robintown). + * Debounce read marker update on scroll ([\#6771](https://github.com/matrix-org/matrix-react-sdk/pull/6771)). Fixes vector-im/element-web#18961 and vector-im/element-web#18961. + * Use cursor:pointer on space panel buttons ([\#6770](https://github.com/matrix-org/matrix-react-sdk/pull/6770)). Fixes vector-im/element-web#18951 and vector-im/element-web#18951. + * Fix regressed tab view buttons in space update toast ([\#6761](https://github.com/matrix-org/matrix-react-sdk/pull/6761)). Fixes vector-im/element-web#18781 and vector-im/element-web#18781. + Changes in [3.30.0](https://github.com/vector-im/element-desktop/releases/tag/v3.30.0) (2021-09-14) =================================================================================================== From 531622f3b5946b88255b3ee4a72e0d16826e718b Mon Sep 17 00:00:00 2001 From: RiotRobot Date: Tue, 21 Sep 2021 09:41:50 +0100 Subject: [PATCH 06/65] v3.31.0-rc.1 --- package.json | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 0498ee948e..edf4c88c61 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "matrix-react-sdk", - "version": "3.30.0", + "version": "3.31.0-rc.1", "description": "SDK for matrix.org using React", "author": "matrix.org", "repository": { @@ -25,7 +25,7 @@ "bin": { "reskindex": "scripts/reskindex.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", @@ -210,5 +210,6 @@ "coverageReporters": [ "text" ] - } + }, + "typings": "./lib/index.d.ts" } From 1265fe6b86234d8a2d1d4c1c335cedc3fc1d971f Mon Sep 17 00:00:00 2001 From: Germain Souquet Date: Wed, 22 Sep 2021 10:48:24 +0100 Subject: [PATCH 07/65] Set comprehensive set of props for thread's MessagePanel --- src/components/structures/ThreadView.tsx | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/components/structures/ThreadView.tsx b/src/components/structures/ThreadView.tsx index dda4c06417..bb31c32877 100644 --- a/src/components/structures/ThreadView.tsx +++ b/src/components/structures/ThreadView.tsx @@ -133,15 +133,22 @@ export default class ThreadView extends React.Component { { this.state.thread && ( empty
} alwaysShowTimestamps={true} layout={Layout.Group} hideThreadedMessages={false} + hidden={false} + showReactions={true} + className="mx_RoomView_messagePanel mx_GroupLayout" + permalinkCreator={this.props.permalinkCreator} + membersLoaded={true} /> ) } Date: Wed, 22 Sep 2021 11:55:25 +0100 Subject: [PATCH 08/65] Fix spacing for message composer buttons --- res/css/views/rooms/_MessageComposer.scss | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/res/css/views/rooms/_MessageComposer.scss b/res/css/views/rooms/_MessageComposer.scss index 9ba966c083..c20dd43daf 100644 --- a/res/css/views/rooms/_MessageComposer.scss +++ b/res/css/views/rooms/_MessageComposer.scss @@ -185,16 +185,26 @@ limitations under the License. } } +.mx_ContextualMenu { + .mx_MessageComposer_button { + padding-left: calc(var(--size) + 6px); + } +} + .mx_MessageComposer_button { --size: 26px; position: relative; - margin-right: 6px; cursor: pointer; height: var(--size); line-height: var(--size); width: auto; - padding-left: calc(var(--size) + 5px); + padding-left: var(--size); border-radius: 100%; + margin-right: 6px; + + &:last-child { + margin-right: auto; + } &::before { content: ''; From d323bc4c0771c0ffd1b22293e9a724d5bdf47a93 Mon Sep 17 00:00:00 2001 From: videogame hacker Date: Wed, 22 Sep 2021 13:16:05 +0100 Subject: [PATCH 09/65] Fix Notifications settings view when threepids is undefined Signed-off-by: Charlotte Som --- src/components/views/settings/Notifications.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/settings/Notifications.tsx b/src/components/views/settings/Notifications.tsx index 766ba2c9c0..2f3f7abe4b 100644 --- a/src/components/views/settings/Notifications.tsx +++ b/src/components/views/settings/Notifications.tsx @@ -480,7 +480,7 @@ export default class Notifications extends React.PureComponent { return masterSwitch; } - const emailSwitches = this.state.threepids.filter(t => t.medium === ThreepidMedium.Email) + const emailSwitches = (this.state.threepids || []).filter(t => t.medium === ThreepidMedium.Email) .map(e => p.kind === "email" && p.pushkey === e.address)} From 9c69869f63f3fe933d482a6bdad2650a4ad61c78 Mon Sep 17 00:00:00 2001 From: Germain Souquet Date: Wed, 22 Sep 2021 11:55:25 +0100 Subject: [PATCH 10/65] Fix spacing for message composer buttons --- res/css/views/rooms/_MessageComposer.scss | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/res/css/views/rooms/_MessageComposer.scss b/res/css/views/rooms/_MessageComposer.scss index 9ba966c083..c20dd43daf 100644 --- a/res/css/views/rooms/_MessageComposer.scss +++ b/res/css/views/rooms/_MessageComposer.scss @@ -185,16 +185,26 @@ limitations under the License. } } +.mx_ContextualMenu { + .mx_MessageComposer_button { + padding-left: calc(var(--size) + 6px); + } +} + .mx_MessageComposer_button { --size: 26px; position: relative; - margin-right: 6px; cursor: pointer; height: var(--size); line-height: var(--size); width: auto; - padding-left: calc(var(--size) + 5px); + padding-left: var(--size); border-radius: 100%; + margin-right: 6px; + + &:last-child { + margin-right: auto; + } &::before { content: ''; From 672cdf5330d0207767f94e49bfefad00fec9ac72 Mon Sep 17 00:00:00 2001 From: RiotRobot Date: Wed, 22 Sep 2021 14:35:24 +0100 Subject: [PATCH 11/65] Prepare changelog for v3.31.0-rc.2 --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index d2da9dad17..71ddb1c5fe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +Changes in [3.31.0-rc.2](https://github.com/vector-im/element-desktop/releases/tag/v3.31.0-rc.2) (2021-09-22) +============================================================================================================= + +## 🐛 Bug Fixes + * Fix spacing for message composer buttons ([\#6854](https://github.com/matrix-org/matrix-react-sdk/pull/6854)). + Changes in [3.31.0-rc.1](https://github.com/vector-im/element-desktop/releases/tag/v3.31.0-rc.1) (2021-09-21) ============================================================================================================= From a22e2ef874767da9250f97913642c66f87f61006 Mon Sep 17 00:00:00 2001 From: RiotRobot Date: Wed, 22 Sep 2021 14:35:24 +0100 Subject: [PATCH 12/65] v3.31.0-rc.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index edf4c88c61..9593f74aae 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "matrix-react-sdk", - "version": "3.31.0-rc.1", + "version": "3.31.0-rc.2", "description": "SDK for matrix.org using React", "author": "matrix.org", "repository": { From 96c16354cac485cb0a750d93c949cd737feda4a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Wed, 22 Sep 2021 15:47:15 +0200 Subject: [PATCH 13/65] Fix soft crash MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/views/rooms/AppsDrawer.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/rooms/AppsDrawer.tsx b/src/components/views/rooms/AppsDrawer.tsx index 5a14e6b315..4c6b8db02e 100644 --- a/src/components/views/rooms/AppsDrawer.tsx +++ b/src/components/views/rooms/AppsDrawer.tsx @@ -317,7 +317,7 @@ const PersistentVResizer: React.FC = ({ }); return { From a9f7ab785cae4604914b67df70ed2be5e7a3afb7 Mon Sep 17 00:00:00 2001 From: Germain Souquet Date: Thu, 23 Sep 2021 10:59:33 +0100 Subject: [PATCH 14/65] Change CIDER state persistence key to cater for threads Extending the CIDER state persistence to threads and make sure that SendMessageComposer can restore drafts for specific threads This also prevents the thread's replyToEvent to leaking in the room composer --- .../views/rooms/SendMessageComposer.tsx | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/src/components/views/rooms/SendMessageComposer.tsx b/src/components/views/rooms/SendMessageComposer.tsx index 05c41f74bf..4138b00bdf 100644 --- a/src/components/views/rooms/SendMessageComposer.tsx +++ b/src/components/views/rooms/SendMessageComposer.tsx @@ -164,6 +164,20 @@ export default class SendMessageComposer extends React.Component { window.addEventListener("beforeunload", this.saveStoredEditorState); } + public componentDidUpdate(prevProps: IProps): void { + const replyToEventChanged = this.props.replyToEvent !== prevProps.replyToEvent; + if (replyToEventChanged) { + this.model.reset([]); + } + + if (this.props.replyInThread && this.props.replyToEvent && (!prevProps.replyToEvent || replyToEventChanged)) { + const partCreator = new CommandPartCreator(this.props.room, this.context); + const parts = this.restoreStoredEditorState(partCreator) || []; + this.model.reset(parts); + this.editorRef.current?.focus(); + } + } + private onKeyDown = (event: KeyboardEvent): void => { // ignore any keypress while doing IME compositions if (this.editorRef.current?.isComposing(event)) { @@ -484,7 +498,12 @@ export default class SendMessageComposer extends React.Component { } private get editorStateKey() { - return `mx_cider_state_${this.props.room.roomId}`; + let key = `mx_cider_state_${this.props.room.roomId}`; + const thread = this.props.replyToEvent?.getThread(); + if (thread) { + key += `_${thread.id}`; + } + return key; } private clearStoredEditorState(): void { @@ -492,6 +511,10 @@ export default class SendMessageComposer extends React.Component { } private restoreStoredEditorState(partCreator: PartCreator): Part[] { + if (this.props.replyInThread && !this.props.replyToEvent) { + return null; + } + const json = localStorage.getItem(this.editorStateKey); if (json) { try { From bd599c96a3b8cd02120e43ecf29c260221b1d49f Mon Sep 17 00:00:00 2001 From: Germain Souquet Date: Thu, 23 Sep 2021 14:43:39 +0100 Subject: [PATCH 15/65] Add React lifecycle methods publicity and return types --- src/components/structures/RightPanel.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/structures/RightPanel.tsx b/src/components/structures/RightPanel.tsx index 32a875557c..a108f376bd 100644 --- a/src/components/structures/RightPanel.tsx +++ b/src/components/structures/RightPanel.tsx @@ -143,14 +143,14 @@ export default class RightPanel extends React.Component { return rps.roomPanelPhase; } - componentDidMount() { + public componentDidMount(): void { this.dispatcherRef = dis.register(this.onAction); const cli = this.context; cli.on("RoomState.members", this.onRoomStateMember); this.initGroupStore(this.props.groupId); } - componentWillUnmount() { + public componentWillUnmount(): void { dis.unregister(this.dispatcherRef); if (this.context) { this.context.removeListener("RoomState.members", this.onRoomStateMember); @@ -159,7 +159,7 @@ export default class RightPanel extends React.Component { } // TODO: [REACT-WARNING] Replace with appropriate lifecycle event - UNSAFE_componentWillReceiveProps(newProps) { // eslint-disable-line + public UNSAFE_componentWillReceiveProps(newProps: IProps): void { // eslint-disable-line if (newProps.groupId !== this.props.groupId) { this.unregisterGroupStore(); this.initGroupStore(newProps.groupId); From 070f279ae7746b211dca319dd9edf8c4f5ccf5c0 Mon Sep 17 00:00:00 2001 From: Germain Souquet Date: Thu, 23 Sep 2021 14:44:03 +0100 Subject: [PATCH 16/65] Close thread view when changing room --- src/components/structures/RightPanel.tsx | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/components/structures/RightPanel.tsx b/src/components/structures/RightPanel.tsx index a108f376bd..be5ed52a78 100644 --- a/src/components/structures/RightPanel.tsx +++ b/src/components/structures/RightPanel.tsx @@ -54,6 +54,7 @@ import { throttle } from 'lodash'; import SpaceStore from "../../stores/SpaceStore"; import { RoomPermalinkCreator } from '../../utils/permalinks/Permalinks'; import { E2EStatus } from '../../utils/ShieldUtils'; +import { SetRightPanelPhasePayload } from '../../dispatcher/payloads/SetRightPanelPhasePayload'; interface IProps { room?: Room; // if showing panels for a given room, this is set @@ -196,6 +197,15 @@ export default class RightPanel extends React.Component { }; private onAction = (payload: ActionPayload) => { + const isChangingRoom = payload.action === 'view_room'; + const isViewingThread = this.state.phase === RightPanelPhases.ThreadView; + if (isChangingRoom && isViewingThread) { + dis.dispatch({ + action: Action.SetRightPanelPhase, + phase: RightPanelPhases.ThreadPanel, + }); + } + if (payload.action === Action.AfterRightPanelPhaseChange) { this.setState({ phase: payload.phase, From 82d685e410f118d318fcca35965e7717247ab654 Mon Sep 17 00:00:00 2001 From: Germain Souquet Date: Fri, 24 Sep 2021 11:06:07 +0100 Subject: [PATCH 17/65] Only switch to ThreadPanel when roomId changes --- src/components/structures/RightPanel.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/structures/RightPanel.tsx b/src/components/structures/RightPanel.tsx index be5ed52a78..f626bb67d9 100644 --- a/src/components/structures/RightPanel.tsx +++ b/src/components/structures/RightPanel.tsx @@ -197,7 +197,7 @@ export default class RightPanel extends React.Component { }; private onAction = (payload: ActionPayload) => { - const isChangingRoom = payload.action === 'view_room'; + const isChangingRoom = payload.action === 'view_room' && payload.room_id !== this.props.room.roomId; const isViewingThread = this.state.phase === RightPanelPhases.ThreadView; if (isChangingRoom && isViewingThread) { dis.dispatch({ From 836e427dc8fd275c418acac6e142f4726e8c5454 Mon Sep 17 00:00:00 2001 From: Germain Souquet Date: Fri, 24 Sep 2021 11:13:57 +0100 Subject: [PATCH 18/65] Scope editor reset when reply changed for thread view --- src/components/views/rooms/SendMessageComposer.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/rooms/SendMessageComposer.tsx b/src/components/views/rooms/SendMessageComposer.tsx index 4138b00bdf..cc27ccf153 100644 --- a/src/components/views/rooms/SendMessageComposer.tsx +++ b/src/components/views/rooms/SendMessageComposer.tsx @@ -165,7 +165,7 @@ export default class SendMessageComposer extends React.Component { } public componentDidUpdate(prevProps: IProps): void { - const replyToEventChanged = this.props.replyToEvent !== prevProps.replyToEvent; + const replyToEventChanged = this.props.replyInThread && (this.props.replyToEvent !== prevProps.replyToEvent); if (replyToEventChanged) { this.model.reset([]); } From bb7319f632d4fa6cf48c8bad94a17fe4d1670cf9 Mon Sep 17 00:00:00 2001 From: Germain Souquet Date: Fri, 24 Sep 2021 11:44:11 +0100 Subject: [PATCH 19/65] move timestamp to the right in thread's context --- res/css/views/rooms/_EventTile.scss | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/res/css/views/rooms/_EventTile.scss b/res/css/views/rooms/_EventTile.scss index 4495ec4f29..470851654b 100644 --- a/res/css/views/rooms/_EventTile.scss +++ b/res/css/views/rooms/_EventTile.scss @@ -732,6 +732,11 @@ $hover-select-border: 4px; margin-top: 0; padding-bottom: 5px; margin-bottom: 5px; + + .mx_MessageTimestamp { + left: auto; + right: 0; + } } .mx_MessageComposer_sendMessage { From 8348add67fb647e54d2dfb1218f2367176076f66 Mon Sep 17 00:00:00 2001 From: Germain Souquet Date: Fri, 24 Sep 2021 14:19:11 +0100 Subject: [PATCH 20/65] Add MessageActionBar to ThreadView --- src/components/structures/ThreadView.tsx | 2 +- .../views/messages/MessageActionBar.tsx | 7 +++- src/components/views/rooms/EventTile.tsx | 37 +++++++++++++++++++ 3 files changed, 44 insertions(+), 2 deletions(-) diff --git a/src/components/structures/ThreadView.tsx b/src/components/structures/ThreadView.tsx index bb31c32877..180a870cd5 100644 --- a/src/components/structures/ThreadView.tsx +++ b/src/components/structures/ThreadView.tsx @@ -139,7 +139,7 @@ export default class ThreadView extends React.Component { sendReadReceiptOnLoad={false} // No RR support in thread's MVP timelineSet={this.state?.thread?.timelineSet} showUrlPreview={true} - tileShape={TileShape.Notif} + tileShape={TileShape.Thread} empty={
empty
} alwaysShowTimestamps={true} layout={Layout.Group} diff --git a/src/components/views/messages/MessageActionBar.tsx b/src/components/views/messages/MessageActionBar.tsx index f76fa32ddc..f7c58e27dc 100644 --- a/src/components/views/messages/MessageActionBar.tsx +++ b/src/components/views/messages/MessageActionBar.tsx @@ -133,12 +133,17 @@ interface IMessageActionBarProps { getTile: () => any; // TODO: FIXME, haven't figured out what the return type is here getReplyThread?: () => ReplyThread; onFocusChange?: (menuDisplayed: boolean) => void; + isInThreadTimeline?: boolean; } @replaceableComponent("views.messages.MessageActionBar") export default class MessageActionBar extends React.PureComponent { public static contextType = RoomContext; + public static defaultProps = { + isInThreadTimeline: false, + }; + public componentDidMount(): void { if (this.props.mxEvent.status && this.props.mxEvent.status !== EventStatus.SENT) { this.props.mxEvent.on("Event.status", this.onSent); @@ -283,7 +288,7 @@ export default class MessageActionBar extends React.PureComponent { } } + const isInThreadTimeline = this.props.tileShape === TileShape.Thread; const actionBar = !isEditing ? { getTile={this.getTile} getReplyThread={this.getReplyThread} onFocusChange={this.onActionBarFocusChange} + isInThreadTimeline={isInThreadTimeline} /> : undefined; const showTimestamp = this.props.mxEvent.getTs() @@ -1160,6 +1163,40 @@ export default class EventTile extends React.Component { , ]); } + case TileShape.Thread: { + const room = this.context.getRoom(this.props.mxEvent.getRoomId()); + return React.createElement(this.props.as || "li", { + "className": classes, + "aria-live": ariaLive, + "aria-atomic": true, + "data-scroll-tokens": scrollToken, + }, [ + , + , +
+ + { actionBar } +
, + ]); + } case TileShape.FileGrid: { return React.createElement(this.props.as || "li", { "className": classes, From 9f26cca2562e22fe97b1f3ef6b53514e3ec6c04f Mon Sep 17 00:00:00 2001 From: David Baker Date: Fri, 24 Sep 2021 14:35:00 +0100 Subject: [PATCH 21/65] Fix the User View Extreme confusion between users and members, and presumably we were passing one as the other and they were similar enough that it worked. UserView still makes fake member objects but this at least fixes the user view in the right panel. Fixes https://github.com/vector-im/element-web/issues/19158 --- src/components/structures/RightPanel.tsx | 9 ++++----- src/components/structures/UserView.tsx | 4 ++-- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/components/structures/RightPanel.tsx b/src/components/structures/RightPanel.tsx index f626bb67d9..5d9d2a0b6a 100644 --- a/src/components/structures/RightPanel.tsx +++ b/src/components/structures/RightPanel.tsx @@ -18,7 +18,6 @@ limitations under the License. import React from 'react'; import { Room } from "matrix-js-sdk/src/models/room"; import { RoomState } from "matrix-js-sdk/src/models/room-state"; -import { User } from "matrix-js-sdk/src/models/user"; import { RoomMember } from "matrix-js-sdk/src/models/room-member"; import { MatrixEvent } from "matrix-js-sdk/src/models/event"; import { VerificationRequest } from "matrix-js-sdk/src/crypto/verification/request/VerificationRequest"; @@ -59,7 +58,7 @@ import { SetRightPanelPhasePayload } from '../../dispatcher/payloads/SetRightPan interface IProps { room?: Room; // if showing panels for a given room, this is set groupId?: string; // if showing panels for a given group, this is set - user?: User; // used if we know the user ahead of opening the panel + member?: RoomMember; // used if we know the room member ahead of opening the panel resizeNotifier: ResizeNotifier; permalinkCreator?: RoomPermalinkCreator; e2eStatus?: E2EStatus; @@ -100,10 +99,10 @@ export default class RightPanel extends React.Component { // Helper function to split out the logic for getPhaseFromProps() and the constructor // as both are called at the same time in the constructor. - private getUserForPanel() { + private getUserForPanel(): RoomMember { if (this.state && this.state.member) return this.state.member; const lastParams = RightPanelStore.getSharedInstance().roomPanelPhaseParams; - return this.props.user || lastParams['member']; + return this.props.member || lastParams['member']; } // gets the current phase from the props and also maybe the store @@ -225,7 +224,7 @@ export default class RightPanel extends React.Component { // XXX: There are three different ways of 'closing' this panel depending on what state // things are in... this knows far more than it should do about the state of the rest // of the app and is generally a bit silly. - if (this.props.user) { + if (this.props.member) { // If we have a user prop then we're displaying a user from the 'user' page type // in LoggedInView, so need to change the page type to close the panel (we switch // to the home page which is not obviously the correct thing to do, but I'm not sure diff --git a/src/components/structures/UserView.tsx b/src/components/structures/UserView.tsx index 0b686995fd..32168e8449 100644 --- a/src/components/structures/UserView.tsx +++ b/src/components/structures/UserView.tsx @@ -86,8 +86,8 @@ export default class UserView extends React.Component { public render(): JSX.Element { if (this.state.loading) { return ; - } else if (this.state.member?.user) { - const panel = ; + } else if (this.state.member) { + const panel = ; return ( ); From bffebb9304099822e06838917ed2894a6638ebc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Fri, 24 Sep 2021 20:37:59 +0200 Subject: [PATCH 22/65] Convert index to TS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/usercontent/{index.js => index.ts} | 34 ++++++++++++-------------- 1 file changed, 16 insertions(+), 18 deletions(-) rename src/usercontent/{index.js => index.ts} (61%) diff --git a/src/usercontent/index.js b/src/usercontent/index.ts similarity index 61% rename from src/usercontent/index.js rename to src/usercontent/index.ts index c03126ec80..f143f16720 100644 --- a/src/usercontent/index.js +++ b/src/usercontent/index.ts @@ -1,5 +1,5 @@ let hasCalled = false; -function remoteRender(event) { +function remoteRender(event: MessageEvent): void { const data = event.data; // If we're handling secondary calls, start from scratch @@ -11,10 +11,11 @@ function remoteRender(event) { const img = document.createElement("span"); // we'll mask it as an image img.id = "img"; - const a = document.createElement("a"); + const a: HTMLAnchorElement = document.createElement("a"); a.id = "a"; a.rel = "noreferrer noopener"; a.download = data.download; + // @ts-ignore a.style = data.style; a.style.fontFamily = "Arial, Helvetica, Sans-Serif"; a.href = window.URL.createObjectURL(data.blob); @@ -23,24 +24,21 @@ function remoteRender(event) { // Apply image style after so we can steal the anchor's colour. // Style copied from a rendered version of mx_MFileBody_download_icon - img.style = (data.imgStyle || "" + - "width: 12px; height: 12px;" + - "-webkit-mask-size: 12px;" + - "mask-size: 12px;" + - "-webkit-mask-position: center;" + - "mask-position: center;" + - "-webkit-mask-repeat: no-repeat;" + - "mask-repeat: no-repeat;" + - "display: inline-block;") + "" + - - // Always add these styles - `-webkit-mask-image: url('${data.imgSrc}');` + - `mask-image: url('${data.imgSrc}');` + - `background-color: ${a.style.color};`; + // @ts-ignore + img.style = data.imgStyle ?? ""; + img.style.width = "12px"; + img.style.height = "12px"; + img.style.webkitMaskSize = "12px"; + img.style.webkitMaskPosition = "center"; + img.style.webkitMaskRepeat = "no-repeat"; + img.style.display = "inline-block"; + img.style.webkitMaskImage = `url('${data.imgSrc}')`; + img.style.backgroundColor = `${a.style.color}`; const body = document.body; // Don't display scrollbars if the link takes more than one line to display. - body.style = "margin: 0px; overflow: hidden"; + body.style .margin = "0px"; + body.style.overflow = "hidden"; body.appendChild(a); if (event.data.auto) { @@ -48,7 +46,7 @@ function remoteRender(event) { } } -window.onmessage = function(e) { +window.onmessage = function(e: MessageEvent): void { if (e.origin === window.location.origin) { if (e.data.blob) remoteRender(e); } From 27f74b3ebd0a72e192e2d9bc7c07de00813b47c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Sat, 25 Sep 2021 07:53:38 +0200 Subject: [PATCH 23/65] Remove unnecessary type MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/usercontent/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/usercontent/index.ts b/src/usercontent/index.ts index f143f16720..82e8ee0d81 100644 --- a/src/usercontent/index.ts +++ b/src/usercontent/index.ts @@ -11,7 +11,7 @@ function remoteRender(event: MessageEvent): void { const img = document.createElement("span"); // we'll mask it as an image img.id = "img"; - const a: HTMLAnchorElement = document.createElement("a"); + const a = document.createElement("a"); a.id = "a"; a.rel = "noreferrer noopener"; a.download = data.download; From 1a2476609c0bea8505063eae00a08b5c37930c68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Sat, 25 Sep 2021 07:55:40 +0200 Subject: [PATCH 24/65] Fix code to be equivalent to the previous one MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/usercontent/index.ts | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/src/usercontent/index.ts b/src/usercontent/index.ts index 82e8ee0d81..eaa8c86ec6 100644 --- a/src/usercontent/index.ts +++ b/src/usercontent/index.ts @@ -24,16 +24,19 @@ function remoteRender(event: MessageEvent): void { // Apply image style after so we can steal the anchor's colour. // Style copied from a rendered version of mx_MFileBody_download_icon - // @ts-ignore - img.style = data.imgStyle ?? ""; - img.style.width = "12px"; - img.style.height = "12px"; - img.style.webkitMaskSize = "12px"; - img.style.webkitMaskPosition = "center"; - img.style.webkitMaskRepeat = "no-repeat"; - img.style.display = "inline-block"; - img.style.webkitMaskImage = `url('${data.imgSrc}')`; - img.style.backgroundColor = `${a.style.color}`; + if (data.imgStyle) { + // @ts-ignore + img.style = data.imgStyle; + } else { + img.style.width = "12px"; + img.style.height = "12px"; + img.style.webkitMaskSize = "12px"; + img.style.webkitMaskPosition = "center"; + img.style.webkitMaskRepeat = "no-repeat"; + img.style.display = "inline-block"; + img.style.webkitMaskImage = `url('${data.imgSrc}')`; + img.style.backgroundColor = `${a.style.color}`; + } const body = document.body; // Don't display scrollbars if the link takes more than one line to display. From 3450e91e32cc830a07226f7a2cfe12a0cb8a22b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Sat, 25 Sep 2021 09:58:01 +0200 Subject: [PATCH 25/65] Improve typing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/usercontent/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/usercontent/index.ts b/src/usercontent/index.ts index eaa8c86ec6..df551e88e6 100644 --- a/src/usercontent/index.ts +++ b/src/usercontent/index.ts @@ -8,10 +8,10 @@ function remoteRender(event: MessageEvent): void { } hasCalled = true; - const img = document.createElement("span"); // we'll mask it as an image + const img: HTMLSpanElement = document.createElement("span"); // we'll mask it as an image img.id = "img"; - const a = document.createElement("a"); + const a: HTMLAnchorElement = document.createElement("a"); a.id = "a"; a.rel = "noreferrer noopener"; a.download = data.download; From bee85a0bcd16bae07a53272484901f95dc6dd5f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Fri, 24 Sep 2021 19:55:52 +0200 Subject: [PATCH 26/65] Convert EditHistoryMessage to TS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- ...storyMessage.js => EditHistoryMessage.tsx} | 85 ++++++++++--------- .../views/messages/RedactedBody.tsx | 7 +- 2 files changed, 53 insertions(+), 39 deletions(-) rename src/components/views/messages/{EditHistoryMessage.js => EditHistoryMessage.tsx} (73%) diff --git a/src/components/views/messages/EditHistoryMessage.js b/src/components/views/messages/EditHistoryMessage.tsx similarity index 73% rename from src/components/views/messages/EditHistoryMessage.js rename to src/components/views/messages/EditHistoryMessage.tsx index 2c6a567f6b..1abed87b76 100644 --- a/src/components/views/messages/EditHistoryMessage.js +++ b/src/components/views/messages/EditHistoryMessage.tsx @@ -15,107 +15,112 @@ limitations under the License. */ import React, { createRef } from 'react'; -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/src/models/event'; +import { EventStatus, MatrixEvent } from 'matrix-js-sdk/src/models/event'; import { pillifyLinks, unmountPills } from '../../../utils/pillify'; import { _t } from '../../../languageHandler'; -import * as sdk from '../../../index'; import { MatrixClientPeg } from '../../../MatrixClientPeg'; import Modal from '../../../Modal'; import classNames from 'classnames'; import RedactedBody from "./RedactedBody"; import { replaceableComponent } from "../../../utils/replaceableComponent"; +import AccessibleButton from "../elements/AccessibleButton"; +import ConfirmAndWaitRedactDialog from "../dialogs/ConfirmAndWaitRedactDialog"; +import ViewSource from "../../structures/ViewSource"; function getReplacedContent(event) { const originalContent = event.getOriginalContent(); return originalContent["m.new_content"] || originalContent; } -@replaceableComponent("views.messages.EditHistoryMessage") -export default class EditHistoryMessage extends React.PureComponent { - static propTypes = { - // the message event being edited - mxEvent: PropTypes.instanceOf(MatrixEvent).isRequired, - previousEdit: PropTypes.instanceOf(MatrixEvent), - isBaseEvent: PropTypes.bool, - }; +interface IProps { + // the message event being edited + mxEvent: MatrixEvent; + previousEdit?: MatrixEvent; + isBaseEvent?: boolean; + isTwelveHour?: boolean; +} - constructor(props) { +interface IState { + canRedact: boolean; + sendStatus: EventStatus; +} + +@replaceableComponent("views.messages.EditHistoryMessage") +export default class EditHistoryMessage extends React.PureComponent { + private content = createRef(); + private pills: Element[] = []; + + constructor(props: IProps) { super(props); + const cli = MatrixClientPeg.get(); const { userId } = cli.credentials; const event = this.props.mxEvent; const room = cli.getRoom(event.getRoomId()); if (event.localRedactionEvent()) { - event.localRedactionEvent().on("status", this._onAssociatedStatusChanged); + event.localRedactionEvent().on("status", this.onAssociatedStatusChanged); } const canRedact = room.currentState.maySendRedactionForEvent(event, userId); this.state = { canRedact, sendStatus: event.getAssociatedStatus() }; - - this._content = createRef(); - this._pills = []; } - _onAssociatedStatusChanged = () => { + private onAssociatedStatusChanged = (): void => { this.setState({ sendStatus: this.props.mxEvent.getAssociatedStatus() }); }; - _onRedactClick = async () => { + private onRedactClick = async (): Promise => { const event = this.props.mxEvent; const cli = MatrixClientPeg.get(); - const ConfirmAndWaitRedactDialog = sdk.getComponent("dialogs.ConfirmAndWaitRedactDialog"); Modal.createTrackedDialog('Confirm Redact Dialog', 'Edit history', ConfirmAndWaitRedactDialog, { redact: () => cli.redactEvent(event.getRoomId(), event.getId()), }, 'mx_Dialog_confirmredact'); }; - _onViewSourceClick = () => { - const ViewSource = sdk.getComponent('structures.ViewSource'); + private onViewSourceClick = (): void => { Modal.createTrackedDialog('View Event Source', 'Edit history', ViewSource, { mxEvent: this.props.mxEvent, }, 'mx_Dialog_viewsource'); }; - pillifyLinks() { + private pillifyLinks(): void { // not present for redacted events - if (this._content.current) { - pillifyLinks(this._content.current.children, this.props.mxEvent, this._pills); + if (this.content.current) { + pillifyLinks(this.content.current.children, this.props.mxEvent, this.pills); } } - componentDidMount() { + public componentDidMount(): void { this.pillifyLinks(); } - componentWillUnmount() { - unmountPills(this._pills); + public componentWillUnmount(): void { + unmountPills(this.pills); const event = this.props.mxEvent; if (event.localRedactionEvent()) { - event.localRedactionEvent().off("status", this._onAssociatedStatusChanged); + event.localRedactionEvent().off("status", this.onAssociatedStatusChanged); } } - componentDidUpdate() { + public componentDidUpdate(): void { this.pillifyLinks(); } - _renderActionBar() { - const AccessibleButton = sdk.getComponent('elements.AccessibleButton'); + private renderActionBar(): JSX.Element { // hide the button when already redacted let redactButton; if (!this.props.mxEvent.isRedacted() && !this.props.isBaseEvent && this.state.canRedact) { redactButton = ( - + { _t("Remove") } ); } const viewSourceButton = ( - + { _t("View Source") } ); @@ -128,7 +133,7 @@ export default class EditHistoryMessage extends React.PureComponent { ); } - render() { + public render(): JSX.Element { const { mxEvent } = this.props; const content = getReplacedContent(mxEvent); let contentContainer; @@ -139,18 +144,22 @@ export default class EditHistoryMessage extends React.PureComponent { if (this.props.previousEdit) { contentElements = editBodyDiffToHtml(getReplacedContent(this.props.previousEdit), content); } else { - contentElements = HtmlUtils.bodyToHtml(content, null, { stripReplyFallback: true }); + contentElements = HtmlUtils.bodyToHtml( + content, + null, + { stripReplyFallback: true, returnString: false }, + ); } if (mxEvent.getContent().msgtype === "m.emote") { const name = mxEvent.sender ? mxEvent.sender.name : mxEvent.getSender(); contentContainer = ( -
*  +
{ name }  { contentElements }
); } else { - contentContainer =
{ contentElements }
; + contentContainer =
{ contentElements }
; } } @@ -167,7 +176,7 @@ export default class EditHistoryMessage extends React.PureComponent {
{ timestamp } { contentContainer } - { this._renderActionBar() } + { this.renderActionBar() }
diff --git a/src/components/views/messages/RedactedBody.tsx b/src/components/views/messages/RedactedBody.tsx index c2e137c97b..7202e3a2e5 100644 --- a/src/components/views/messages/RedactedBody.tsx +++ b/src/components/views/messages/RedactedBody.tsx @@ -21,8 +21,13 @@ import MatrixClientContext from "../../../contexts/MatrixClientContext"; import { formatFullDate } from "../../../DateUtils"; import SettingsStore from "../../../settings/SettingsStore"; import { IBodyProps } from "./IBodyProps"; +import { MatrixEvent } from "matrix-js-sdk/src/models/event"; -const RedactedBody = React.forwardRef(({ mxEvent }, ref) => { +interface IProps { + mxEvent: MatrixEvent; +} + +const RedactedBody = React.forwardRef(({ mxEvent }, ref) => { const cli: MatrixClient = useContext(MatrixClientContext); let text = _t("Message deleted"); From ff1c1fbc7852b7b86aeb3bd899a974f638c6e14e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Fri, 24 Sep 2021 19:58:28 +0200 Subject: [PATCH 27/65] Convert MjolnirBody to TS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- .../{MjolnirBody.js => MjolnirBody.tsx} | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) rename src/components/views/messages/{MjolnirBody.js => MjolnirBody.tsx} (73%) diff --git a/src/components/views/messages/MjolnirBody.js b/src/components/views/messages/MjolnirBody.tsx similarity index 73% rename from src/components/views/messages/MjolnirBody.js rename to src/components/views/messages/MjolnirBody.tsx index 23f255b569..c21a9a353e 100644 --- a/src/components/views/messages/MjolnirBody.js +++ b/src/components/views/messages/MjolnirBody.tsx @@ -15,22 +15,22 @@ limitations under the License. */ import React from 'react'; -import PropTypes from 'prop-types'; import { _t } from '../../../languageHandler'; import { replaceableComponent } from "../../../utils/replaceableComponent"; +import { MatrixEvent } from "matrix-js-sdk/src/models/event"; + +interface IProps { + mxEvent: MatrixEvent; + onMessageAllowed: () => void; +} @replaceableComponent("views.messages.MjolnirBody") -export default class MjolnirBody extends React.Component { - static propTypes = { - mxEvent: PropTypes.object.isRequired, - onMessageAllowed: PropTypes.func.isRequired, - }; - - constructor() { - super(); +export default class MjolnirBody extends React.Component { + constructor(props: IProps) { + super(props); } - _onAllowClick = (e) => { + private onAllowClick = (e: React.MouseEvent): void => { e.preventDefault(); e.stopPropagation(); @@ -39,11 +39,11 @@ export default class MjolnirBody extends React.Component { this.props.onMessageAllowed(); }; - render() { + public render(): JSX.Element { return (
{ _t( "You have ignored this user, so their message is hidden. Show anyways.", - {}, { a: (sub) => { sub } }, + {}, { a: (sub) => { sub } }, ) }
); } From 421fa1ab69b2d9f9cb512f5395d6fbd34301312b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Fri, 24 Sep 2021 20:06:00 +0200 Subject: [PATCH 28/65] Convert RoomAvatarEvent to TS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- ...RoomAvatarEvent.js => RoomAvatarEvent.tsx} | 23 +++++++++---------- 1 file changed, 11 insertions(+), 12 deletions(-) rename src/components/views/messages/{RoomAvatarEvent.js => RoomAvatarEvent.tsx} (88%) diff --git a/src/components/views/messages/RoomAvatarEvent.js b/src/components/views/messages/RoomAvatarEvent.tsx similarity index 88% rename from src/components/views/messages/RoomAvatarEvent.js rename to src/components/views/messages/RoomAvatarEvent.tsx index 9832332311..c29460b002 100644 --- a/src/components/views/messages/RoomAvatarEvent.js +++ b/src/components/views/messages/RoomAvatarEvent.tsx @@ -17,23 +17,24 @@ limitations under the License. */ import React from 'react'; -import PropTypes from 'prop-types'; import { MatrixClientPeg } from '../../../MatrixClientPeg'; import { _t } from '../../../languageHandler'; -import * as sdk from '../../../index'; import Modal from '../../../Modal'; import AccessibleButton from '../elements/AccessibleButton'; import { replaceableComponent } from "../../../utils/replaceableComponent"; import { mediaFromMxc } from "../../../customisations/Media"; +import { MatrixEvent } from "matrix-js-sdk/src/models/event"; +import RoomAvatar from "../avatars/RoomAvatar"; +import ImageView from "../elements/ImageView"; + +interface IProps { + /* the MatrixEvent to show */ + mxEvent: MatrixEvent; +} @replaceableComponent("views.messages.RoomAvatarEvent") -export default class RoomAvatarEvent extends React.Component { - static propTypes = { - /* the MatrixEvent to show */ - mxEvent: PropTypes.object.isRequired, - }; - - onAvatarClick = () => { +export default class RoomAvatarEvent extends React.Component { + private onAvatarClick = (): void => { const cli = MatrixClientPeg.get(); const ev = this.props.mxEvent; const httpUrl = mediaFromMxc(ev.getContent().url).srcHttp; @@ -44,7 +45,6 @@ export default class RoomAvatarEvent extends React.Component { roomName: room ? room.name : '', }); - const ImageView = sdk.getComponent("elements.ImageView"); const params = { src: httpUrl, name: text, @@ -52,10 +52,9 @@ export default class RoomAvatarEvent extends React.Component { Modal.createDialog(ImageView, params, "mx_Dialog_lightbox", null, true); }; - render() { + public render(): JSX.Element { const ev = this.props.mxEvent; const senderDisplayName = ev.sender && ev.sender.name ? ev.sender.name : ev.getSender(); - const RoomAvatar = sdk.getComponent("avatars.RoomAvatar"); if (!ev.getContent().url || ev.getContent().url.trim().length === 0) { return ( From c287d15fa0b314cbe52d3ab552ad144c64e75fdf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Fri, 24 Sep 2021 20:08:15 +0200 Subject: [PATCH 29/65] Convert RoomCreate to TS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- .../{RoomCreate.js => RoomCreate.tsx} | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) rename src/components/views/messages/{RoomCreate.js => RoomCreate.tsx} (85%) diff --git a/src/components/views/messages/RoomCreate.js b/src/components/views/messages/RoomCreate.tsx similarity index 85% rename from src/components/views/messages/RoomCreate.js rename to src/components/views/messages/RoomCreate.tsx index a0bc8daa64..c846ba5632 100644 --- a/src/components/views/messages/RoomCreate.js +++ b/src/components/views/messages/RoomCreate.tsx @@ -16,7 +16,6 @@ limitations under the License. */ import React from 'react'; -import PropTypes from 'prop-types'; import dis from '../../../dispatcher/dispatcher'; import { RoomPermalinkCreator } from '../../../utils/permalinks/Permalinks'; @@ -24,15 +23,16 @@ import { _t } from '../../../languageHandler'; import { MatrixClientPeg } from '../../../MatrixClientPeg'; import EventTileBubble from "./EventTileBubble"; import { replaceableComponent } from "../../../utils/replaceableComponent"; +import { MatrixEvent } from "matrix-js-sdk/src/models/event"; + +interface IProps { + /* the MatrixEvent to show */ + mxEvent: MatrixEvent; +} @replaceableComponent("views.messages.RoomCreate") -export default class RoomCreate extends React.Component { - static propTypes = { - /* the MatrixEvent to show */ - mxEvent: PropTypes.object.isRequired, - }; - - _onLinkClicked = e => { +export default class RoomCreate extends React.Component { + private onLinkClicked = (e: React.MouseEvent): void => { e.preventDefault(); const predecessor = this.props.mxEvent.getContent()['predecessor']; @@ -45,7 +45,7 @@ export default class RoomCreate extends React.Component { }); }; - render() { + public render(): JSX.Element { const predecessor = this.props.mxEvent.getContent()['predecessor']; if (predecessor === undefined) { return
; // We should never have been instantiated in this case @@ -55,7 +55,7 @@ export default class RoomCreate extends React.Component { permalinkCreator.load(); const predecessorPermalink = permalinkCreator.forEvent(predecessor['event_id']); const link = ( - + { _t("Click here to see older messages.") } ); From fb5c18caa0e9bb0c700e1bf95da17b054e2da079 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Fri, 24 Sep 2021 20:04:03 +0200 Subject: [PATCH 30/65] Convert MKeyVerificationConclusion to TS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- ...sion.js => MKeyVerificationConclusion.tsx} | 47 ++++++++++--------- src/components/views/rooms/EventTile.tsx | 4 +- 2 files changed, 26 insertions(+), 25 deletions(-) rename src/components/views/messages/{MKeyVerificationConclusion.js => MKeyVerificationConclusion.tsx} (74%) diff --git a/src/components/views/messages/MKeyVerificationConclusion.js b/src/components/views/messages/MKeyVerificationConclusion.tsx similarity index 74% rename from src/components/views/messages/MKeyVerificationConclusion.js rename to src/components/views/messages/MKeyVerificationConclusion.tsx index a5f12df47d..3e2ae9fa9b 100644 --- a/src/components/views/messages/MKeyVerificationConclusion.js +++ b/src/components/views/messages/MKeyVerificationConclusion.tsx @@ -16,44 +16,50 @@ limitations under the License. import React from 'react'; import classNames from 'classnames'; -import PropTypes from 'prop-types'; import { MatrixClientPeg } from '../../../MatrixClientPeg'; import { _t } from '../../../languageHandler'; -import { getNameForEventRoom, userLabelForEventRoom } - from '../../../utils/KeyVerificationStateObserver'; +import { getNameForEventRoom, userLabelForEventRoom } from '../../../utils/KeyVerificationStateObserver'; import EventTileBubble from "./EventTileBubble"; import { replaceableComponent } from "../../../utils/replaceableComponent"; +import { MatrixEvent } from "matrix-js-sdk/src/models/event"; +import { VerificationRequest } from "matrix-js-sdk/src/crypto/verification/request/VerificationRequest"; +import { EventType } from "matrix-js-sdk/src/@types/event"; + +interface IProps { + /* the MatrixEvent to show */ + mxEvent: MatrixEvent; +} @replaceableComponent("views.messages.MKeyVerificationConclusion") -export default class MKeyVerificationConclusion extends React.Component { - constructor(props) { +export default class MKeyVerificationConclusion extends React.Component { + constructor(props: IProps) { super(props); } - componentDidMount() { + public componentDidMount(): void { const request = this.props.mxEvent.verificationRequest; if (request) { - request.on("change", this._onRequestChanged); + request.on("change", this.onRequestChanged); } - MatrixClientPeg.get().on("userTrustStatusChanged", this._onTrustChanged); + MatrixClientPeg.get().on("userTrustStatusChanged", this.onTrustChanged); } - componentWillUnmount() { + public componentWillUnmount(): void { const request = this.props.mxEvent.verificationRequest; if (request) { - request.off("change", this._onRequestChanged); + request.off("change", this.onRequestChanged); } const cli = MatrixClientPeg.get(); if (cli) { - cli.removeListener("userTrustStatusChanged", this._onTrustChanged); + cli.removeListener("userTrustStatusChanged", this.onTrustChanged); } } - _onRequestChanged = () => { + private onRequestChanged = () => { this.forceUpdate(); }; - _onTrustChanged = (userId, status) => { + private onTrustChanged = (userId: string): void => { const { mxEvent } = this.props; const request = mxEvent.verificationRequest; if (!request || request.otherUserId !== userId) { @@ -62,17 +68,17 @@ export default class MKeyVerificationConclusion extends React.Component { this.forceUpdate(); }; - _shouldRender(mxEvent, request) { + public static shouldRender(mxEvent: MatrixEvent, request: VerificationRequest): boolean { // normally should not happen if (!request) { return false; } // .cancel event that was sent after the verification finished, ignore - if (mxEvent.getType() === "m.key.verification.cancel" && !request.cancelled) { + if (mxEvent.getType() === EventType.KeyVerificationCancel && !request.cancelled) { return false; } // .done event that was sent after the verification cancelled, ignore - if (mxEvent.getType() === "m.key.verification.done" && !request.done) { + if (mxEvent.getType() === EventType.KeyVerificationDone && !request.done) { return false; } @@ -89,11 +95,11 @@ export default class MKeyVerificationConclusion extends React.Component { return true; } - render() { + public render(): JSX.Element { const { mxEvent } = this.props; const request = mxEvent.verificationRequest; - if (!this._shouldRender(mxEvent, request)) { + if (!MKeyVerificationConclusion.shouldRender(mxEvent, request)) { return null; } @@ -129,8 +135,3 @@ export default class MKeyVerificationConclusion extends React.Component { return null; } } - -MKeyVerificationConclusion.propTypes = { - /* the MatrixEvent to show */ - mxEvent: PropTypes.object.isRequired, -}; diff --git a/src/components/views/rooms/EventTile.tsx b/src/components/views/rooms/EventTile.tsx index e1f0eb5368..d1ac06b199 100644 --- a/src/components/views/rooms/EventTile.tsx +++ b/src/components/views/rooms/EventTile.tsx @@ -58,6 +58,7 @@ import ReactionsRow from '../messages/ReactionsRow'; import { getEventDisplayInfo } from '../../../utils/EventUtils'; import { RightPanelPhases } from "../../../stores/RightPanelStorePhases"; import SettingsStore from "../../../settings/SettingsStore"; +import MKeyVerificationConclusion from "../messages/MKeyVerificationConclusion"; const eventTileTypes = { [EventType.RoomMessage]: 'messages.MessageEvent', @@ -144,8 +145,7 @@ export function getHandlerTile(ev) { // XXX: This is extremely a hack. Possibly these components should have an interface for // declining to render? if (type === "m.key.verification.cancel" || type === "m.key.verification.done") { - const MKeyVerificationConclusion = sdk.getComponent("messages.MKeyVerificationConclusion"); - if (!MKeyVerificationConclusion.prototype._shouldRender.call(null, ev, ev.request)) { + if (!MKeyVerificationConclusion.shouldRender(ev, ev.request)) { return; } } From 755bfb4562ef1faba9c2bd415bb2d906ca72a446 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Sun, 26 Sep 2021 15:02:07 +0200 Subject: [PATCH 31/65] Convert DirectoryUtils to TS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/utils/{DirectoryUtils.js => DirectoryUtils.ts} | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) rename src/utils/{DirectoryUtils.js => DirectoryUtils.ts} (81%) diff --git a/src/utils/DirectoryUtils.js b/src/utils/DirectoryUtils.ts similarity index 81% rename from src/utils/DirectoryUtils.js rename to src/utils/DirectoryUtils.ts index 577a6441f8..255ae0e3fd 100644 --- a/src/utils/DirectoryUtils.js +++ b/src/utils/DirectoryUtils.ts @@ -14,9 +14,12 @@ See the License for the specific language governing permissions and limitations under the License. */ +import { IInstance } from "matrix-js-sdk/src/client"; +import { Protocols } from "../components/views/directory/NetworkDropdown"; + // Find a protocol 'instance' with a given instance_id // in the supplied protocols dict -export function instanceForInstanceId(protocols, instanceId) { +export function instanceForInstanceId(protocols: Protocols, instanceId: string): IInstance { if (!instanceId) return null; for (const proto of Object.keys(protocols)) { if (!protocols[proto].instances && protocols[proto].instances instanceof Array) continue; @@ -28,7 +31,7 @@ export function instanceForInstanceId(protocols, instanceId) { // given an instance_id, return the name of the protocol for // that instance ID in the supplied protocols dict -export function protocolNameForInstanceId(protocols, instanceId) { +export function protocolNameForInstanceId(protocols: Protocols, instanceId: string): string { if (!instanceId) return null; for (const proto of Object.keys(protocols)) { if (!protocols[proto].instances && protocols[proto].instances instanceof Array) continue; From 0c6bf950f6d84bd5bea5a6a22a3ff09228fce1e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Sun, 26 Sep 2021 15:03:50 +0200 Subject: [PATCH 32/65] Convert HostingLink to TS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/utils/{HostingLink.js => HostingLink.ts} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename src/utils/{HostingLink.js => HostingLink.ts} (91%) diff --git a/src/utils/HostingLink.js b/src/utils/HostingLink.ts similarity index 91% rename from src/utils/HostingLink.js rename to src/utils/HostingLink.ts index 134e045ca2..f8c0f12c3f 100644 --- a/src/utils/HostingLink.js +++ b/src/utils/HostingLink.ts @@ -17,7 +17,7 @@ limitations under the License. import SdkConfig from '../SdkConfig'; import { MatrixClientPeg } from '../MatrixClientPeg'; -export function getHostingLink(campaign) { +export function getHostingLink(campaign: string): string { const hostingLink = SdkConfig.get().hosting_signup_link; if (!hostingLink) return null; if (!campaign) return hostingLink; @@ -27,7 +27,7 @@ export function getHostingLink(campaign) { try { const hostingUrl = new URL(hostingLink); hostingUrl.searchParams.set("utm_campaign", campaign); - return hostingUrl.format(); + return hostingUrl.toString(); } catch (e) { return hostingLink; } From aa10cf45a5876880474ba82a07096732d5f1d45b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Sun, 26 Sep 2021 15:05:51 +0200 Subject: [PATCH 33/65] Convert KeyVerificationStateObserver to TS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- ...cationStateObserver.js => KeyVerificationStateObserver.ts} | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename src/utils/{KeyVerificationStateObserver.js => KeyVerificationStateObserver.ts} (86%) diff --git a/src/utils/KeyVerificationStateObserver.js b/src/utils/KeyVerificationStateObserver.ts similarity index 86% rename from src/utils/KeyVerificationStateObserver.js rename to src/utils/KeyVerificationStateObserver.ts index 023cdf3a75..e4fae3f3c8 100644 --- a/src/utils/KeyVerificationStateObserver.js +++ b/src/utils/KeyVerificationStateObserver.ts @@ -17,14 +17,14 @@ limitations under the License. import { MatrixClientPeg } from '../MatrixClientPeg'; import { _t } from '../languageHandler'; -export function getNameForEventRoom(userId, roomId) { +export function getNameForEventRoom(userId: string, roomId: string): string { const client = MatrixClientPeg.get(); const room = client.getRoom(roomId); const member = room && room.getMember(userId); return member ? member.name : userId; } -export function userLabelForEventRoom(userId, roomId) { +export function userLabelForEventRoom(userId: string, roomId: string): string { const name = getNameForEventRoom(userId, roomId); if (name !== userId) { return _t("%(name)s (%(userId)s)", { name, userId }); From ee344efb3d725b7c4b0c1a4acdd0c2e707b9ee38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Sun, 26 Sep 2021 15:20:42 +0200 Subject: [PATCH 34/65] Convert MegolmExportEncryption to TS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- ...ncryption.js => MegolmExportEncryption.ts} | 34 +++++++++++-------- 1 file changed, 20 insertions(+), 14 deletions(-) rename src/utils/{MegolmExportEncryption.js => MegolmExportEncryption.ts} (90%) diff --git a/src/utils/MegolmExportEncryption.js b/src/utils/MegolmExportEncryption.ts similarity index 90% rename from src/utils/MegolmExportEncryption.js rename to src/utils/MegolmExportEncryption.ts index 8e7ee2005d..4fab271015 100644 --- a/src/utils/MegolmExportEncryption.js +++ b/src/utils/MegolmExportEncryption.ts @@ -26,17 +26,17 @@ const subtleCrypto = window.crypto.subtle || window.crypto.webkitSubtle; * Make an Error object which has a friendlyText property which is already * translated and suitable for showing to the user. * - * @param {string} msg message for the exception + * @param {string} message message for the exception * @param {string} friendlyText * @returns {Error} */ -function friendlyError(msg, friendlyText) { - const e = new Error(msg); - e.friendlyText = friendlyText; - return e; +function friendlyError( + message: string, friendlyText: string, +): { message: string, friendlyText: string } { + return { message, friendlyText }; } -function cryptoFailMsg() { +function cryptoFailMsg(): string { return _t('Your browser does not support the required cryptography extensions'); } @@ -49,7 +49,7 @@ function cryptoFailMsg() { * * */ -export async function decryptMegolmKeyFile(data, password) { +export async function decryptMegolmKeyFile(data: ArrayBuffer, password: string): Promise { const body = unpackMegolmKeyFile(data); const brand = SdkConfig.get().brand; @@ -124,7 +124,11 @@ export async function decryptMegolmKeyFile(data, password) { * key-derivation function. * @return {Promise} promise for encrypted output */ -export async function encryptMegolmKeyFile(data, password, options) { +export async function encryptMegolmKeyFile( + data: string, + password: string, + options?: { kdf_rounds?: number }, // eslint-disable-line camelcase +): Promise { options = options || {}; const kdfRounds = options.kdf_rounds || 500000; @@ -196,7 +200,7 @@ export async function encryptMegolmKeyFile(data, password, options) { * @param {String} password password * @return {Promise<[CryptoKey, CryptoKey]>} promise for [aes key, hmac key] */ -async function deriveKeys(salt, iterations, password) { +async function deriveKeys(salt: Uint8Array, iterations: number, password: string): Promise<[CryptoKey, CryptoKey]> { const start = new Date(); let key; @@ -229,7 +233,7 @@ async function deriveKeys(salt, iterations, password) { } const now = new Date(); - logger.log("E2e import/export: deriveKeys took " + (now - start) + "ms"); + logger.log("E2e import/export: deriveKeys took " + (now.getTime() - start.getTime()) + "ms"); const aesKey = keybits.slice(0, 32); const hmacKey = keybits.slice(32); @@ -271,7 +275,7 @@ const TRAILER_LINE = '-----END MEGOLM SESSION DATA-----'; * @param {ArrayBuffer} data input file * @return {Uint8Array} unbase64ed content */ -function unpackMegolmKeyFile(data) { +function unpackMegolmKeyFile(data: ArrayBuffer): Uint8Array { // parse the file as a great big String. This should be safe, because there // should be no non-ASCII characters, and it means that we can do string // comparisons to find the header and footer, and feed it into window.atob. @@ -279,6 +283,7 @@ function unpackMegolmKeyFile(data) { // look for the start line let lineStart = 0; + // eslint-disable-next-line no-constant-condition while (1) { const lineEnd = fileStr.indexOf('\n', lineStart); if (lineEnd < 0) { @@ -297,6 +302,7 @@ function unpackMegolmKeyFile(data) { const dataStart = lineStart; // look for the end line + // eslint-disable-next-line no-constant-condition while (1) { const lineEnd = fileStr.indexOf('\n', lineStart); const line = fileStr.slice(lineStart, lineEnd < 0 ? undefined : lineEnd).trim(); @@ -324,7 +330,7 @@ function unpackMegolmKeyFile(data) { * @param {Uint8Array} data raw data * @return {ArrayBuffer} formatted file */ -function packMegolmKeyFile(data) { +function packMegolmKeyFile(data: Uint8Array): ArrayBuffer { // we split into lines before base64ing, because encodeBase64 doesn't deal // terribly well with large arrays. const LINE_LENGTH = (72 * 4 / 3); @@ -347,7 +353,7 @@ function packMegolmKeyFile(data) { * @param {Uint8Array} uint8Array The data to encode. * @return {string} The base64. */ -function encodeBase64(uint8Array) { +function encodeBase64(uint8Array: Uint8Array): string { // Misinterpt the Uint8Array as Latin-1. // window.btoa expects a unicode string with codepoints in the range 0-255. const latin1String = String.fromCharCode.apply(null, uint8Array); @@ -360,7 +366,7 @@ function encodeBase64(uint8Array) { * @param {string} base64 The base64 to decode. * @return {Uint8Array} The decoded data. */ -function decodeBase64(base64) { +function decodeBase64(base64: string): Uint8Array { // window.atob returns a unicode string with codepoints in the range 0-255. const latin1String = window.atob(base64); // Encode the string as a Uint8Array From b08e05ebe7c8217e5b06d01298ca18958281b5c9 Mon Sep 17 00:00:00 2001 From: Robin Townsend Date: Sun, 26 Sep 2021 10:24:46 -0400 Subject: [PATCH 35/65] Fix pills being cut off in message bubble layout Signed-off-by: Robin Townsend --- res/css/views/elements/_RichText.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/css/views/elements/_RichText.scss b/res/css/views/elements/_RichText.scss index b9d845ea7a..1043fd08d1 100644 --- a/res/css/views/elements/_RichText.scss +++ b/res/css/views/elements/_RichText.scss @@ -18,7 +18,7 @@ a.mx_Pill { text-overflow: ellipsis; white-space: nowrap; overflow: hidden; - max-width: calc(100% - 1ch); + max-width: 100%; } .mx_Pill { From 2584b5bc6bfecd80bc548135fd971beb480f1482 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Sun, 26 Sep 2021 16:04:28 +0200 Subject: [PATCH 36/65] Convert rageshake to TS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/@types/global.d.ts | 10 +++ src/rageshake/{rageshake.js => rageshake.ts} | 79 +++++++++++--------- 2 files changed, 52 insertions(+), 37 deletions(-) rename src/rageshake/{rageshake.js => rageshake.ts} (91%) diff --git a/src/@types/global.d.ts b/src/@types/global.d.ts index 8ad93fa960..06bb822f36 100644 --- a/src/@types/global.d.ts +++ b/src/@types/global.d.ts @@ -49,6 +49,7 @@ import PerformanceMonitor from "../performance"; import UIStore from "../stores/UIStore"; import { SetupEncryptionStore } from "../stores/SetupEncryptionStore"; import { RoomScrollStateStore } from "../stores/RoomScrollStateStore"; +import { ConsoleLogger, IndexedDBLogStore } from "../rageshake/rageshake"; /* eslint-disable @typescript-eslint/naming-convention */ @@ -223,6 +224,15 @@ declare global { ) => string; isReady: () => boolean; }; + + // eslint-disable-next-line no-var, camelcase + var mx_rage_logger: ConsoleLogger; + // eslint-disable-next-line no-var, camelcase + var mx_rage_initPromise: Promise; + // eslint-disable-next-line no-var, camelcase + var mx_rage_initStoragePromise: Promise; + // eslint-disable-next-line no-var, camelcase + var mx_rage_store: IndexedDBLogStore; } /* eslint-enable @typescript-eslint/naming-convention */ diff --git a/src/rageshake/rageshake.js b/src/rageshake/rageshake.ts similarity index 91% rename from src/rageshake/rageshake.js rename to src/rageshake/rageshake.ts index 0dc36e3a9f..acf77c31c0 100644 --- a/src/rageshake/rageshake.js +++ b/src/rageshake/rageshake.ts @@ -46,12 +46,10 @@ const FLUSH_RATE_MS = 30 * 1000; const MAX_LOG_SIZE = 1024 * 1024 * 5; // 5 MB // A class which monkey-patches the global console and stores log lines. -class ConsoleLogger { - constructor() { - this.logs = ""; - } +export class ConsoleLogger { + private logs = ""; - monkeyPatch(consoleObj) { + public monkeyPatch(consoleObj: Console): void { // Monkey-patch console logging const consoleFunctionsToLevels = { log: "I", @@ -69,14 +67,14 @@ class ConsoleLogger { }); } - log(level, ...args) { + private log(level: string, ...args: (Error | DOMException | object | string)[]): void { // We don't know what locale the user may be running so use ISO strings const ts = new Date().toISOString(); // Convert objects and errors to helpful things args = args.map((arg) => { if (arg instanceof DOMException) { - return arg.message + ` (${arg.name} | ${arg.code}) ` + (arg.stack ? `\n${arg.stack}` : ''); + return arg.message + ` (${arg.name} | ${arg.code})`; } else if (arg instanceof Error) { return arg.message + (arg.stack ? `\n${arg.stack}` : ''); } else if (typeof (arg) === 'object') { @@ -118,7 +116,7 @@ class ConsoleLogger { * @param {boolean} keepLogs True to not delete logs after flushing. * @return {string} \n delimited log lines to flush. */ - flush(keepLogs) { + public flush(keepLogs?: boolean): string { // The ConsoleLogger doesn't care how these end up on disk, it just // flushes them to the caller. if (keepLogs) { @@ -131,27 +129,28 @@ class ConsoleLogger { } // A class which stores log lines in an IndexedDB instance. -class IndexedDBLogStore { - constructor(indexedDB, logger) { - this.indexedDB = indexedDB; - this.logger = logger; - this.id = "instance-" + Math.random() + Date.now(); - this.index = 0; - this.db = null; +export class IndexedDBLogStore { + private id: string; + private index = 0; + private db = null; + private flushPromise = null; + private flushAgainPromise = null; - // these promises are cleared as soon as fulfilled - this.flushPromise = null; - // set if flush() is called whilst one is ongoing - this.flushAgainPromise = null; + constructor( + private indexedDB: IDBFactory, + private logger: ConsoleLogger, + ) { + this.id = "instance-" + Math.random() + Date.now(); } /** * @return {Promise} Resolves when the store is ready. */ - connect() { + public connect(): Promise { const req = this.indexedDB.open("logs"); return new Promise((resolve, reject) => { - req.onsuccess = (event) => { + req.onsuccess = (event: Event) => { + // @ts-ignore this.db = event.target.result; // Periodically flush logs to local storage / indexeddb setInterval(this.flush.bind(this), FLUSH_RATE_MS); @@ -160,6 +159,7 @@ class IndexedDBLogStore { req.onerror = (event) => { const err = ( + // @ts-ignore "Failed to open log database: " + event.target.error.name ); console.error(err); @@ -168,6 +168,7 @@ class IndexedDBLogStore { // First time: Setup the object store req.onupgradeneeded = (event) => { + // @ts-ignore const db = event.target.result; const logObjStore = db.createObjectStore("logs", { keyPath: ["id", "index"], @@ -178,7 +179,7 @@ class IndexedDBLogStore { logObjStore.createIndex("id", "id", { unique: false }); logObjStore.add( - this._generateLogEntry( + this.generateLogEntry( new Date() + " ::: Log database was created.", ), ); @@ -186,7 +187,7 @@ class IndexedDBLogStore { const lastModifiedStore = db.createObjectStore("logslastmod", { keyPath: "id", }); - lastModifiedStore.add(this._generateLastModifiedTime()); + lastModifiedStore.add(this.generateLastModifiedTime()); }; }); } @@ -210,7 +211,7 @@ class IndexedDBLogStore { * * @return {Promise} Resolved when the logs have been flushed. */ - flush() { + public flush(): Promise { // check if a flush() operation is ongoing if (this.flushPromise) { if (this.flushAgainPromise) { @@ -227,7 +228,7 @@ class IndexedDBLogStore { } // there is no flush promise or there was but it has finished, so do // a brand new one, destroying the chain which may have been built up. - this.flushPromise = new Promise((resolve, reject) => { + this.flushPromise = new Promise((resolve, reject) => { if (!this.db) { // not connected yet or user rejected access for us to r/w to the db. reject(new Error("No connected database")); @@ -251,9 +252,9 @@ class IndexedDBLogStore { new Error("Failed to write logs: " + event.target.errorCode), ); }; - objStore.add(this._generateLogEntry(lines)); + objStore.add(this.generateLogEntry(lines)); const lastModStore = txn.objectStore("logslastmod"); - lastModStore.put(this._generateLastModifiedTime()); + lastModStore.put(this.generateLastModifiedTime()); }).then(() => { this.flushPromise = null; }); @@ -270,12 +271,12 @@ class IndexedDBLogStore { * log ID). The objects have said log ID in an "id" field and "lines" which * is a big string with all the new-line delimited logs. */ - async consume() { + public async consume(): Promise<{lines: string, id: string}[]> { const db = this.db; // Returns: a string representing the concatenated logs for this ID. // Stops adding log fragments when the size exceeds maxSize - function fetchLogs(id, maxSize) { + function fetchLogs(id: string, maxSize: number): Promise { const objectStore = db.transaction("logs", "readonly").objectStore("logs"); return new Promise((resolve, reject) => { @@ -301,7 +302,7 @@ class IndexedDBLogStore { } // Returns: A sorted array of log IDs. (newest first) - function fetchLogIds() { + function fetchLogIds(): Promise { // To gather all the log IDs, query for all records in logslastmod. const o = db.transaction("logslastmod", "readonly").objectStore( "logslastmod", @@ -319,8 +320,8 @@ class IndexedDBLogStore { }); } - function deleteLogs(id) { - return new Promise((resolve, reject) => { + function deleteLogs(id: number): Promise { + return new Promise((resolve, reject) => { const txn = db.transaction( ["logs", "logslastmod"], "readwrite", ); @@ -389,7 +390,7 @@ class IndexedDBLogStore { return logs; } - _generateLogEntry(lines) { + private generateLogEntry(lines: string): {id: string, lines: string, index: number} { return { id: this.id, lines: lines, @@ -397,7 +398,7 @@ class IndexedDBLogStore { }; } - _generateLastModifiedTime() { + private generateLastModifiedTime(): {id: string, ts: number} { return { id: this.id, ts: Date.now(), @@ -415,15 +416,19 @@ class IndexedDBLogStore { * @return {Promise} Resolves to an array of whatever you returned from * resultMapper. */ -function selectQuery(store, keyRange, resultMapper) { +function selectQuery( + store: IDBIndex, keyRange: IDBKeyRange, resultMapper: (cursor: IDBCursorWithValue) => T, +): Promise { const query = store.openCursor(keyRange); return new Promise((resolve, reject) => { const results = []; query.onerror = (event) => { + // @ts-ignore reject(new Error("Query failed: " + event.target.errorCode)); }; // collect results query.onsuccess = (event) => { + // @ts-ignore const cursor = event.target.result; if (!cursor) { resolve(results); @@ -442,7 +447,7 @@ function selectQuery(store, keyRange, resultMapper) { * be set up immediately for the logs. * @return {Promise} Resolves when set up. */ -export function init(setUpPersistence = true) { +export function init(setUpPersistence = true): Promise { if (global.mx_rage_initPromise) { return global.mx_rage_initPromise; } @@ -462,7 +467,7 @@ export function init(setUpPersistence = true) { * then this no-ops. * @return {Promise} Resolves when complete. */ -export function tryInitStorage() { +export function tryInitStorage(): Promise { if (global.mx_rage_initStoragePromise) { return global.mx_rage_initStoragePromise; } From af9429c8a067c248515d9d8d703e31460f6d1b16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Sun, 26 Sep 2021 19:57:02 +0200 Subject: [PATCH 37/65] Convert ActiveWidgetStore to TS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/@types/global.d.ts | 2 + src/Lifecycle.ts | 4 +- src/components/views/elements/AppTile.tsx | 8 +- .../views/elements/PersistentApp.tsx | 18 +-- src/stores/ActiveWidgetStore.js | 110 ----------------- src/stores/ActiveWidgetStore.ts | 112 ++++++++++++++++++ src/stores/WidgetStore.ts | 8 +- src/stores/widgets/StopGapWidget.ts | 8 +- 8 files changed, 137 insertions(+), 133 deletions(-) delete mode 100644 src/stores/ActiveWidgetStore.js create mode 100644 src/stores/ActiveWidgetStore.ts diff --git a/src/@types/global.d.ts b/src/@types/global.d.ts index 8ad93fa960..220a34c688 100644 --- a/src/@types/global.d.ts +++ b/src/@types/global.d.ts @@ -49,6 +49,7 @@ import PerformanceMonitor from "../performance"; import UIStore from "../stores/UIStore"; import { SetupEncryptionStore } from "../stores/SetupEncryptionStore"; import { RoomScrollStateStore } from "../stores/RoomScrollStateStore"; +import ActiveWidgetStore from "../stores/ActiveWidgetStore"; /* eslint-disable @typescript-eslint/naming-convention */ @@ -92,6 +93,7 @@ declare global { mxUIStore: UIStore; mxSetupEncryptionStore?: SetupEncryptionStore; mxRoomScrollStateStore?: RoomScrollStateStore; + mxActiveWidgetStore?: ActiveWidgetStore; mxOnRecaptchaLoaded?: () => void; electron?: Electron; } diff --git a/src/Lifecycle.ts b/src/Lifecycle.ts index 57c922aeb7..3685f7b938 100644 --- a/src/Lifecycle.ts +++ b/src/Lifecycle.ts @@ -786,7 +786,7 @@ async function startMatrixClient(startSyncing = true): Promise { UserActivity.sharedInstance().start(); DMRoomMap.makeShared().start(); IntegrationManagers.sharedInstance().startWatching(); - ActiveWidgetStore.start(); + ActiveWidgetStore.instance.start(); CallHandler.sharedInstance().start(); // Start Mjolnir even though we haven't checked the feature flag yet. Starting @@ -892,7 +892,7 @@ export function stopMatrixClient(unsetClient = true): void { UserActivity.sharedInstance().stop(); TypingStore.sharedInstance().reset(); Presence.stop(); - ActiveWidgetStore.stop(); + ActiveWidgetStore.instance.stop(); IntegrationManagers.sharedInstance().stopWatching(); Mjolnir.sharedInstance().stop(); DeviceListener.sharedInstance().stop(); diff --git a/src/components/views/elements/AppTile.tsx b/src/components/views/elements/AppTile.tsx index ea9ef71626..3f4d75df27 100644 --- a/src/components/views/elements/AppTile.tsx +++ b/src/components/views/elements/AppTile.tsx @@ -163,7 +163,7 @@ export default class AppTile extends React.Component { if (this.state.hasPermissionToLoad && !hasPermissionToLoad) { // Force the widget to be non-persistent (able to be deleted/forgotten) - ActiveWidgetStore.destroyPersistentWidget(this.props.app.id); + ActiveWidgetStore.instance.destroyPersistentWidget(this.props.app.id); PersistedElement.destroyElement(this.persistKey); if (this.sgWidget) this.sgWidget.stop(); } @@ -198,8 +198,8 @@ export default class AppTile extends React.Component { if (this.dispatcherRef) dis.unregister(this.dispatcherRef); // if it's not remaining on screen, get rid of the PersistedElement container - if (!ActiveWidgetStore.getWidgetPersistence(this.props.app.id)) { - ActiveWidgetStore.destroyPersistentWidget(this.props.app.id); + if (!ActiveWidgetStore.instance.getWidgetPersistence(this.props.app.id)) { + ActiveWidgetStore.instance.destroyPersistentWidget(this.props.app.id); PersistedElement.destroyElement(this.persistKey); } @@ -282,7 +282,7 @@ export default class AppTile extends React.Component { // Delete the widget from the persisted store for good measure. PersistedElement.destroyElement(this.persistKey); - ActiveWidgetStore.destroyPersistentWidget(this.props.app.id); + ActiveWidgetStore.instance.destroyPersistentWidget(this.props.app.id); if (this.sgWidget) this.sgWidget.stop({ forceDestroy: true }); } diff --git a/src/components/views/elements/PersistentApp.tsx b/src/components/views/elements/PersistentApp.tsx index 1f911659e2..8d0751cc1d 100644 --- a/src/components/views/elements/PersistentApp.tsx +++ b/src/components/views/elements/PersistentApp.tsx @@ -17,7 +17,7 @@ limitations under the License. import React from 'react'; import RoomViewStore from '../../../stores/RoomViewStore'; -import ActiveWidgetStore from '../../../stores/ActiveWidgetStore'; +import ActiveWidgetStore, { ActiveWidgetStoreEvent } from '../../../stores/ActiveWidgetStore'; import WidgetUtils from '../../../utils/WidgetUtils'; import { MatrixClientPeg } from '../../../MatrixClientPeg'; import { replaceableComponent } from "../../../utils/replaceableComponent"; @@ -39,13 +39,13 @@ export default class PersistentApp extends React.Component<{}, IState> { this.state = { roomId: RoomViewStore.getRoomId(), - persistentWidgetId: ActiveWidgetStore.getPersistentWidgetId(), + persistentWidgetId: ActiveWidgetStore.instance.getPersistentWidgetId(), }; } public componentDidMount(): void { this.roomStoreToken = RoomViewStore.addListener(this.onRoomViewStoreUpdate); - ActiveWidgetStore.on('update', this.onActiveWidgetStoreUpdate); + ActiveWidgetStore.instance.on(ActiveWidgetStoreEvent.Update, this.onActiveWidgetStoreUpdate); MatrixClientPeg.get().on("Room.myMembership", this.onMyMembership); } @@ -53,7 +53,7 @@ export default class PersistentApp extends React.Component<{}, IState> { if (this.roomStoreToken) { this.roomStoreToken.remove(); } - ActiveWidgetStore.removeListener('update', this.onActiveWidgetStoreUpdate); + ActiveWidgetStore.instance.removeListener(ActiveWidgetStoreEvent.Update, this.onActiveWidgetStoreUpdate); if (MatrixClientPeg.get()) { MatrixClientPeg.get().removeListener("Room.myMembership", this.onMyMembership); } @@ -68,23 +68,23 @@ export default class PersistentApp extends React.Component<{}, IState> { private onActiveWidgetStoreUpdate = (): void => { this.setState({ - persistentWidgetId: ActiveWidgetStore.getPersistentWidgetId(), + persistentWidgetId: ActiveWidgetStore.instance.getPersistentWidgetId(), }); }; private onMyMembership = async (room: Room, membership: string): Promise => { - const persistentWidgetInRoomId = ActiveWidgetStore.getRoomId(this.state.persistentWidgetId); + const persistentWidgetInRoomId = ActiveWidgetStore.instance.getRoomId(this.state.persistentWidgetId); if (membership !== "join") { // we're not in the room anymore - delete if (room .roomId === persistentWidgetInRoomId) { - ActiveWidgetStore.destroyPersistentWidget(this.state.persistentWidgetId); + ActiveWidgetStore.instance.destroyPersistentWidget(this.state.persistentWidgetId); } } }; public render(): JSX.Element { if (this.state.persistentWidgetId) { - const persistentWidgetInRoomId = ActiveWidgetStore.getRoomId(this.state.persistentWidgetId); + const persistentWidgetInRoomId = ActiveWidgetStore.instance.getRoomId(this.state.persistentWidgetId); const persistentWidgetInRoom = MatrixClientPeg.get().getRoom(persistentWidgetInRoomId); @@ -96,7 +96,7 @@ export default class PersistentApp extends React.Component<{}, IState> { if (this.state.roomId !== persistentWidgetInRoomId && myMembership === "join") { // get the widget data const appEvent = WidgetUtils.getRoomWidgets(persistentWidgetInRoom).find((ev) => { - return ev.getStateKey() === ActiveWidgetStore.getPersistentWidgetId(); + return ev.getStateKey() === ActiveWidgetStore.instance.getPersistentWidgetId(); }); const app = WidgetUtils.makeAppConfig( appEvent.getStateKey(), appEvent.getContent(), appEvent.getSender(), diff --git a/src/stores/ActiveWidgetStore.js b/src/stores/ActiveWidgetStore.js deleted file mode 100644 index b270d99693..0000000000 --- a/src/stores/ActiveWidgetStore.js +++ /dev/null @@ -1,110 +0,0 @@ -/* -Copyright 2018 New Vector Ltd - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -import EventEmitter from 'events'; - -import { MatrixClientPeg } from '../MatrixClientPeg'; -import { WidgetMessagingStore } from "./widgets/WidgetMessagingStore"; - -/** - * Stores information about the widgets active in the app right now: - * * What widget is set to remain always-on-screen, if any - * Only one widget may be 'always on screen' at any one time. - * * Negotiated capabilities for active apps - */ -class ActiveWidgetStore extends EventEmitter { - constructor() { - super(); - this._persistentWidgetId = null; - - // What room ID each widget is associated with (if it's a room widget) - this._roomIdByWidgetId = {}; - - this.onRoomStateEvents = this.onRoomStateEvents.bind(this); - - this.dispatcherRef = null; - } - - start() { - MatrixClientPeg.get().on('RoomState.events', this.onRoomStateEvents); - } - - stop() { - if (MatrixClientPeg.get()) { - MatrixClientPeg.get().removeListener('RoomState.events', this.onRoomStateEvents); - } - this._roomIdByWidgetId = {}; - } - - onRoomStateEvents(ev, state) { - // XXX: This listens for state events in order to remove the active widget. - // Everything else relies on views listening for events and calling setters - // on this class which is terrible. This store should just listen for events - // and keep itself up to date. - // TODO: Enable support for m.widget event type (https://github.com/vector-im/element-web/issues/13111) - if (ev.getType() !== 'im.vector.modular.widgets') return; - - if (ev.getStateKey() === this._persistentWidgetId) { - this.destroyPersistentWidget(this._persistentWidgetId); - } - } - - destroyPersistentWidget(id) { - if (id !== this._persistentWidgetId) return; - const toDeleteId = this._persistentWidgetId; - - WidgetMessagingStore.instance.stopMessagingById(id); - - this.setWidgetPersistence(toDeleteId, false); - this.delRoomId(toDeleteId); - } - - setWidgetPersistence(widgetId, val) { - if (this._persistentWidgetId === widgetId && !val) { - this._persistentWidgetId = null; - } else if (this._persistentWidgetId !== widgetId && val) { - this._persistentWidgetId = widgetId; - } - this.emit('update'); - } - - getWidgetPersistence(widgetId) { - return this._persistentWidgetId === widgetId; - } - - getPersistentWidgetId() { - return this._persistentWidgetId; - } - - getRoomId(widgetId) { - return this._roomIdByWidgetId[widgetId]; - } - - setRoomId(widgetId, roomId) { - this._roomIdByWidgetId[widgetId] = roomId; - this.emit('update'); - } - - delRoomId(widgetId) { - delete this._roomIdByWidgetId[widgetId]; - this.emit('update'); - } -} - -if (global.singletonActiveWidgetStore === undefined) { - global.singletonActiveWidgetStore = new ActiveWidgetStore(); -} -export default global.singletonActiveWidgetStore; diff --git a/src/stores/ActiveWidgetStore.ts b/src/stores/ActiveWidgetStore.ts new file mode 100644 index 0000000000..ca50689188 --- /dev/null +++ b/src/stores/ActiveWidgetStore.ts @@ -0,0 +1,112 @@ +/* +Copyright 2018 New Vector Ltd + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import EventEmitter from 'events'; +import { MatrixEvent } from "matrix-js-sdk"; + +import { MatrixClientPeg } from '../MatrixClientPeg'; +import { WidgetMessagingStore } from "./widgets/WidgetMessagingStore"; + +export enum ActiveWidgetStoreEvent { + Update = "update", +} + +/** + * Stores information about the widgets active in the app right now: + * * What widget is set to remain always-on-screen, if any + * Only one widget may be 'always on screen' at any one time. + * * Negotiated capabilities for active apps + */ +export default class ActiveWidgetStore extends EventEmitter { + private static internalInstance: ActiveWidgetStore; + private persistentWidgetId: string; + // What room ID each widget is associated with (if it's a room widget) + private roomIdByWidgetId = new Map(); + + public static get instance(): ActiveWidgetStore { + if (!ActiveWidgetStore.internalInstance) { + ActiveWidgetStore.internalInstance = new ActiveWidgetStore(); + } + return ActiveWidgetStore.internalInstance; + } + + public start(): void { + MatrixClientPeg.get().on('RoomState.events', this.onRoomStateEvents); + } + + public stop(): void { + if (MatrixClientPeg.get()) { + MatrixClientPeg.get().removeListener('RoomState.events', this.onRoomStateEvents); + } + this.roomIdByWidgetId.clear(); + } + + private onRoomStateEvents = (ev: MatrixEvent): void => { + // XXX: This listens for state events in order to remove the active widget. + // Everything else relies on views listening for events and calling setters + // on this class which is terrible. This store should just listen for events + // and keep itself up to date. + // TODO: Enable support for m.widget event type (https://github.com/vector-im/element-web/issues/13111) + if (ev.getType() !== 'im.vector.modular.widgets') return; + + if (ev.getStateKey() === this.persistentWidgetId) { + this.destroyPersistentWidget(this.persistentWidgetId); + } + }; + + public destroyPersistentWidget(id: string): void { + if (id !== this.persistentWidgetId) return; + const toDeleteId = this.persistentWidgetId; + + WidgetMessagingStore.instance.stopMessagingById(id); + + this.setWidgetPersistence(toDeleteId, false); + this.delRoomId(toDeleteId); + } + + public setWidgetPersistence(widgetId: string, val: boolean): void { + if (this.persistentWidgetId === widgetId && !val) { + this.persistentWidgetId = null; + } else if (this.persistentWidgetId !== widgetId && val) { + this.persistentWidgetId = widgetId; + } + this.emit(ActiveWidgetStoreEvent.Update); + } + + public getWidgetPersistence(widgetId: string): boolean { + return this.persistentWidgetId === widgetId; + } + + public getPersistentWidgetId(): string { + return this.persistentWidgetId; + } + + public getRoomId(widgetId: string): string { + return this.roomIdByWidgetId.get(widgetId); + } + + public setRoomId(widgetId: string, roomId: string): void { + this.roomIdByWidgetId.set(widgetId, roomId); + this.emit(ActiveWidgetStoreEvent.Update); + } + + public delRoomId(widgetId: string): void { + this.roomIdByWidgetId.delete(widgetId); + this.emit(ActiveWidgetStoreEvent.Update); + } +} + +window.mxActiveWidgetStore = ActiveWidgetStore.instance; diff --git a/src/stores/WidgetStore.ts b/src/stores/WidgetStore.ts index bd03e2065b..44c8327c04 100644 --- a/src/stores/WidgetStore.ts +++ b/src/stores/WidgetStore.ts @@ -142,14 +142,14 @@ export default class WidgetStore extends AsyncStoreWithClient { // If a persistent widget is active, check to see if it's just been removed. // If it has, it needs to destroyed otherwise unmounting the node won't kill it - const persistentWidgetId = ActiveWidgetStore.getPersistentWidgetId(); + const persistentWidgetId = ActiveWidgetStore.instance.getPersistentWidgetId(); if (persistentWidgetId) { if ( - ActiveWidgetStore.getRoomId(persistentWidgetId) === room.roomId && + ActiveWidgetStore.instance.getRoomId(persistentWidgetId) === room.roomId && !roomInfo.widgets.some(w => w.id === persistentWidgetId) ) { logger.log(`Persistent widget ${persistentWidgetId} removed from room ${room.roomId}: destroying.`); - ActiveWidgetStore.destroyPersistentWidget(persistentWidgetId); + ActiveWidgetStore.instance.destroyPersistentWidget(persistentWidgetId); } } @@ -195,7 +195,7 @@ export default class WidgetStore extends AsyncStoreWithClient { // A persistent conference widget indicates that we're participating const widgets = roomInfo.widgets.filter(w => WidgetType.JITSI.matches(w.type)); - return widgets.some(w => ActiveWidgetStore.getWidgetPersistence(w.id)); + return widgets.some(w => ActiveWidgetStore.instance.getWidgetPersistence(w.id)); } } diff --git a/src/stores/widgets/StopGapWidget.ts b/src/stores/widgets/StopGapWidget.ts index 361b02bc3e..e00c4c6c0b 100644 --- a/src/stores/widgets/StopGapWidget.ts +++ b/src/stores/widgets/StopGapWidget.ts @@ -266,7 +266,7 @@ export class StopGapWidget extends EventEmitter { WidgetMessagingStore.instance.storeMessaging(this.mockWidget, this.messaging); if (!this.appTileProps.userWidget && this.appTileProps.room) { - ActiveWidgetStore.setRoomId(this.mockWidget.id, this.appTileProps.room.roomId); + ActiveWidgetStore.instance.setRoomId(this.mockWidget.id, this.appTileProps.room.roomId); } // Always attach a handler for ViewRoom, but permission check it internally @@ -319,7 +319,7 @@ export class StopGapWidget extends EventEmitter { if (WidgetType.JITSI.matches(this.mockWidget.type)) { CountlyAnalytics.instance.trackJoinCall(this.appTileProps.room.roomId, true, true); } - ActiveWidgetStore.setWidgetPersistence(this.mockWidget.id, ev.detail.data.value); + ActiveWidgetStore.instance.setWidgetPersistence(this.mockWidget.id, ev.detail.data.value); ev.preventDefault(); this.messaging.transport.reply(ev.detail, {}); // ack } @@ -406,13 +406,13 @@ export class StopGapWidget extends EventEmitter { } public stop(opts = { forceDestroy: false }) { - if (!opts?.forceDestroy && ActiveWidgetStore.getPersistentWidgetId() === this.mockWidget.id) { + if (!opts?.forceDestroy && ActiveWidgetStore.instance.getPersistentWidgetId() === this.mockWidget.id) { logger.log("Skipping destroy - persistent widget"); return; } if (!this.started) return; WidgetMessagingStore.instance.stopMessaging(this.mockWidget); - ActiveWidgetStore.delRoomId(this.mockWidget.id); + ActiveWidgetStore.instance.delRoomId(this.mockWidget.id); if (MatrixClientPeg.get()) { MatrixClientPeg.get().off('event', this.onEvent); From 6aa5d98741b7642a0c9eb1dbc1bd73e59a582017 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Mon, 27 Sep 2021 10:12:45 +0200 Subject: [PATCH 38/65] Remove constructor Co-authored-by: Germain --- src/components/views/messages/MjolnirBody.tsx | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/components/views/messages/MjolnirBody.tsx b/src/components/views/messages/MjolnirBody.tsx index c21a9a353e..db806e3a15 100644 --- a/src/components/views/messages/MjolnirBody.tsx +++ b/src/components/views/messages/MjolnirBody.tsx @@ -26,9 +26,6 @@ interface IProps { @replaceableComponent("views.messages.MjolnirBody") export default class MjolnirBody extends React.Component { - constructor(props: IProps) { - super(props); - } private onAllowClick = (e: React.MouseEvent): void => { e.preventDefault(); From a1d5e7ba66c94e2679a6cd596c83f319a0d69167 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Mon, 27 Sep 2021 10:12:55 +0200 Subject: [PATCH 39/65] Add return type Co-authored-by: Germain --- src/components/views/messages/MKeyVerificationConclusion.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/messages/MKeyVerificationConclusion.tsx b/src/components/views/messages/MKeyVerificationConclusion.tsx index 3e2ae9fa9b..66ae3766d6 100644 --- a/src/components/views/messages/MKeyVerificationConclusion.tsx +++ b/src/components/views/messages/MKeyVerificationConclusion.tsx @@ -55,7 +55,7 @@ export default class MKeyVerificationConclusion extends React.Component } } - private onRequestChanged = () => { + private onRequestChanged = (): void => { this.forceUpdate(); }; From 1ed4a9eb4f994bbfca17a0768df97b8f850abdf2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Mon, 27 Sep 2021 10:19:53 +0200 Subject: [PATCH 40/65] Fix JSDoc type MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/utils/MegolmExportEncryption.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/utils/MegolmExportEncryption.ts b/src/utils/MegolmExportEncryption.ts index 4fab271015..47c395bfb7 100644 --- a/src/utils/MegolmExportEncryption.ts +++ b/src/utils/MegolmExportEncryption.ts @@ -28,7 +28,7 @@ const subtleCrypto = window.crypto.subtle || window.crypto.webkitSubtle; * * @param {string} message message for the exception * @param {string} friendlyText - * @returns {Error} + * @returns {{message: string, friendlyText: string}} */ function friendlyError( message: string, friendlyText: string, From dd92f8f1f9e013a379b0f280ec9050876de48a6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Mon, 27 Sep 2021 10:21:46 +0200 Subject: [PATCH 41/65] Move imports MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/views/messages/RedactedBody.tsx | 2 +- src/components/views/messages/RoomAvatarEvent.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/views/messages/RedactedBody.tsx b/src/components/views/messages/RedactedBody.tsx index 7202e3a2e5..66200036cd 100644 --- a/src/components/views/messages/RedactedBody.tsx +++ b/src/components/views/messages/RedactedBody.tsx @@ -16,12 +16,12 @@ limitations under the License. import React, { useContext } from "react"; import { MatrixClient } from "matrix-js-sdk/src/client"; +import { MatrixEvent } from "matrix-js-sdk/src/models/event"; import { _t } from "../../../languageHandler"; import MatrixClientContext from "../../../contexts/MatrixClientContext"; import { formatFullDate } from "../../../DateUtils"; import SettingsStore from "../../../settings/SettingsStore"; import { IBodyProps } from "./IBodyProps"; -import { MatrixEvent } from "matrix-js-sdk/src/models/event"; interface IProps { mxEvent: MatrixEvent; diff --git a/src/components/views/messages/RoomAvatarEvent.tsx b/src/components/views/messages/RoomAvatarEvent.tsx index c29460b002..12a8c88913 100644 --- a/src/components/views/messages/RoomAvatarEvent.tsx +++ b/src/components/views/messages/RoomAvatarEvent.tsx @@ -17,13 +17,13 @@ limitations under the License. */ import React from 'react'; +import { MatrixEvent } from "matrix-js-sdk/src/models/event"; import { MatrixClientPeg } from '../../../MatrixClientPeg'; import { _t } from '../../../languageHandler'; import Modal from '../../../Modal'; import AccessibleButton from '../elements/AccessibleButton'; import { replaceableComponent } from "../../../utils/replaceableComponent"; import { mediaFromMxc } from "../../../customisations/Media"; -import { MatrixEvent } from "matrix-js-sdk/src/models/event"; import RoomAvatar from "../avatars/RoomAvatar"; import ImageView from "../elements/ImageView"; From 2712dde581d1c2e0eeb1137677c745c56e5aa5a1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Mon, 27 Sep 2021 10:28:07 +0200 Subject: [PATCH 42/65] Remove empty line MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/views/messages/MjolnirBody.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/views/messages/MjolnirBody.tsx b/src/components/views/messages/MjolnirBody.tsx index db806e3a15..7e922a5715 100644 --- a/src/components/views/messages/MjolnirBody.tsx +++ b/src/components/views/messages/MjolnirBody.tsx @@ -26,7 +26,6 @@ interface IProps { @replaceableComponent("views.messages.MjolnirBody") export default class MjolnirBody extends React.Component { - private onAllowClick = (e: React.MouseEvent): void => { e.preventDefault(); e.stopPropagation(); From 0cba943f9648d675573dd990333569073a40021e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Mon, 27 Sep 2021 10:35:33 +0200 Subject: [PATCH 43/65] Fix types MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- .../views/messages/MKeyVerificationConclusion.tsx | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/components/views/messages/MKeyVerificationConclusion.tsx b/src/components/views/messages/MKeyVerificationConclusion.tsx index 66ae3766d6..1ce39e1157 100644 --- a/src/components/views/messages/MKeyVerificationConclusion.tsx +++ b/src/components/views/messages/MKeyVerificationConclusion.tsx @@ -109,15 +109,18 @@ export default class MKeyVerificationConclusion extends React.Component let title; if (request.done) { - title = _t("You verified %(name)s", { name: getNameForEventRoom(request.otherUserId, mxEvent) }); + title = _t( + "You verified %(name)s", + { name: getNameForEventRoom(request.otherUserId, mxEvent.getRoomId()) }, + ); } else if (request.cancelled) { const userId = request.cancellingUserId; if (userId === myUserId) { title = _t("You cancelled verifying %(name)s", - { name: getNameForEventRoom(request.otherUserId, mxEvent) }); + { name: getNameForEventRoom(request.otherUserId, mxEvent.getRoomId()) }); } else { title = _t("%(name)s cancelled verifying", - { name: getNameForEventRoom(userId, mxEvent) }); + { name: getNameForEventRoom(userId, mxEvent.getRoomId()) }); } } From 0cfa2a58c7867eec05d5611c342da3cfc8e0fc8e Mon Sep 17 00:00:00 2001 From: Dariusz Niemczyk <3636685+Palid@users.noreply.github.com> Date: Mon, 27 Sep 2021 12:20:37 +0200 Subject: [PATCH 44/65] Add ability to expand and collapse long quoted messages (#6701) In case where we had a very long message the experience of going between messages to see the full quote isn't very nice on desktop, therefore this commit adds a button with additional hotkey to normalize the experience a bit. Fixes https://github.com/vector-im/element-web/issues/18884 --- res/css/views/elements/_ReplyThread.scss | 11 +++ res/css/views/messages/_MessageActionBar.scss | 10 +++ res/img/element-icons/collapse-message.svg | 1 + res/img/element-icons/expand-message.svg | 1 + src/components/structures/ContextMenu.tsx | 8 +- .../context_menus/MessageContextMenu.tsx | 4 +- src/components/views/elements/ReplyThread.tsx | 78 ++++++++++++------- .../views/messages/MessageActionBar.tsx | 39 +++++++--- src/components/views/messages/TextualBody.tsx | 1 + src/components/views/rooms/EventTile.tsx | 44 ++++++----- src/components/views/rooms/ReplyTile.tsx | 18 +++-- src/i18n/strings/en_EN.json | 2 + 12 files changed, 153 insertions(+), 64 deletions(-) create mode 100644 res/img/element-icons/collapse-message.svg create mode 100644 res/img/element-icons/expand-message.svg diff --git a/res/css/views/elements/_ReplyThread.scss b/res/css/views/elements/_ReplyThread.scss index 032cb49359..e19be82e25 100644 --- a/res/css/views/elements/_ReplyThread.scss +++ b/res/css/views/elements/_ReplyThread.scss @@ -59,3 +59,14 @@ limitations under the License. border-left-color: $username-variant8-color; } } + +.mx_ReplyThread--expanded { + .mx_EventTile_body { + display: block; + overflow-y: scroll !important; + } + .mx_EventTile_collapsedCodeBlock { + // !important needed due to .mx_ReplyTile .mx_EventTile_content .mx_EventTile_pre_container > pre + display: block !important; + } +} diff --git a/res/css/views/messages/_MessageActionBar.scss b/res/css/views/messages/_MessageActionBar.scss index 6805036e3d..46fc11956f 100644 --- a/res/css/views/messages/_MessageActionBar.scss +++ b/res/css/views/messages/_MessageActionBar.scss @@ -117,6 +117,16 @@ limitations under the License. mask-image: url('$(res)/img/download.svg'); } +.mx_MessageActionBar_expandMessageButton::after { + mask-size: 12px; + mask-image: url('$(res)/img/element-icons/expand-message.svg'); +} + +.mx_MessageActionBar_collapseMessageButton::after { + mask-size: 12px; + mask-image: url('$(res)/img/element-icons/collapse-message.svg'); +} + .mx_MessageActionBar_downloadButton.mx_MessageActionBar_downloadSpinnerButton::after { background-color: transparent; // hide the download icon mask } diff --git a/res/img/element-icons/collapse-message.svg b/res/img/element-icons/collapse-message.svg new file mode 100644 index 0000000000..91b0713f43 --- /dev/null +++ b/res/img/element-icons/collapse-message.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/res/img/element-icons/expand-message.svg b/res/img/element-icons/expand-message.svg new file mode 100644 index 0000000000..a1c5149718 --- /dev/null +++ b/res/img/element-icons/expand-message.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/components/structures/ContextMenu.tsx b/src/components/structures/ContextMenu.tsx index d65f8e3a10..2173230627 100644 --- a/src/components/structures/ContextMenu.tsx +++ b/src/components/structures/ContextMenu.tsx @@ -45,7 +45,7 @@ function getOrCreateContainer(): HTMLDivElement { const ARIA_MENU_ITEM_ROLES = new Set(["menuitem", "menuitemcheckbox", "menuitemradio"]); -interface IPosition { +export interface IPosition { top?: number; bottom?: number; left?: number; @@ -430,7 +430,11 @@ export type AboveLeftOf = IPosition & { // Placement method for to position context menu right-aligned and flowing to the left of elementRect, // and either above or below: wherever there is more space (maybe this should be aboveOrBelowLeftOf?) -export const aboveLeftOf = (elementRect: DOMRect, chevronFace = ChevronFace.None, vPadding = 0): AboveLeftOf => { +export const aboveLeftOf = ( + elementRect: DOMRect, + chevronFace = ChevronFace.None, + vPadding = 0, +): AboveLeftOf => { const menuOptions: IPosition & { chevronFace: ChevronFace } = { chevronFace }; const buttonRight = elementRect.right + window.pageXOffset; diff --git a/src/components/views/context_menus/MessageContextMenu.tsx b/src/components/views/context_menus/MessageContextMenu.tsx index 8f5d3baa17..c7fcf32260 100644 --- a/src/components/views/context_menus/MessageContextMenu.tsx +++ b/src/components/views/context_menus/MessageContextMenu.tsx @@ -38,6 +38,7 @@ import ConfirmRedactDialog from '../dialogs/ConfirmRedactDialog'; import ErrorDialog from '../dialogs/ErrorDialog'; import ShareDialog from '../dialogs/ShareDialog'; import { RoomPermalinkCreator } from "../../../utils/permalinks/Permalinks"; +import { IPosition, ChevronFace } from '../../structures/ContextMenu'; export function canCancel(eventStatus: EventStatus): boolean { return eventStatus === EventStatus.QUEUED || eventStatus === EventStatus.NOT_SENT; @@ -52,7 +53,8 @@ export interface IOperableEventTile { getEventTileOps(): IEventTileOps; } -interface IProps { +interface IProps extends IPosition { + chevronFace: ChevronFace; /* the MatrixEvent associated with the context menu */ mxEvent: MatrixEvent; /* an optional EventTileOps implementation that can be used to unhide preview widgets */ diff --git a/src/components/views/elements/ReplyThread.tsx b/src/components/views/elements/ReplyThread.tsx index 59c827d5d8..bd81218623 100644 --- a/src/components/views/elements/ReplyThread.tsx +++ b/src/components/views/elements/ReplyThread.tsx @@ -16,6 +16,8 @@ limitations under the License. */ import React from 'react'; +import classNames from 'classnames'; + import { _t } from '../../../languageHandler'; import dis from '../../../dispatcher/dispatcher'; import { MatrixEvent } from 'matrix-js-sdk/src/models/event'; @@ -35,6 +37,12 @@ import ReplyTile from "../rooms/ReplyTile"; import Pill from './Pill'; import { Room } from 'matrix-js-sdk/src/models/room'; +/** + * This number is based on the previous behavior - if we have message of height + * over 60px then we want to show button that will allow to expand it. + */ +const SHOW_EXPAND_QUOTE_PIXELS = 60; + interface IProps { // the latest event in this chain of replies parentEv?: MatrixEvent; @@ -45,6 +53,8 @@ interface IProps { layout?: Layout; // Whether to always show a timestamp alwaysShowTimestamps?: boolean; + isQuoteExpanded?: boolean; + setQuoteExpanded: (isExpanded: boolean) => void; } interface IState { @@ -66,6 +76,7 @@ export default class ReplyThread extends React.Component { static contextType = MatrixClientContext; private unmounted = false; private room: Room; + private blockquoteRef = React.createRef(); constructor(props, context) { super(props, context); @@ -80,7 +91,7 @@ export default class ReplyThread extends React.Component { this.room = this.context.getRoom(this.props.parentEv.getRoomId()); } - public static getParentEventId(ev: MatrixEvent): string { + public static getParentEventId(ev: MatrixEvent): string | undefined { if (!ev || ev.isRedacted()) return; // XXX: For newer relations (annotations, replacements, etc.), we now @@ -137,7 +148,7 @@ export default class ReplyThread extends React.Component { public static getNestedReplyText( ev: MatrixEvent, permalinkCreator: RoomPermalinkCreator, - ): { body: string, html: string } { + ): { body: string, html: string } | null { if (!ev) return null; let { body, formatted_body: html } = ev.getContent(); @@ -237,37 +248,38 @@ export default class ReplyThread extends React.Component { return replyMixin; } - public static makeThread( - parentEv: MatrixEvent, - onHeightChanged: () => void, - permalinkCreator: RoomPermalinkCreator, - ref: React.RefObject, - layout: Layout, - alwaysShowTimestamps: boolean, - ): JSX.Element { - if (!ReplyThread.getParentEventId(parentEv)) return null; - return ; + public static hasThreadReply(event: MatrixEvent) { + return Boolean(ReplyThread.getParentEventId(event)); } componentDidMount() { this.initialize(); + this.trySetExpandableQuotes(); } componentDidUpdate() { this.props.onHeightChanged(); + this.trySetExpandableQuotes(); } componentWillUnmount() { this.unmounted = true; } + private trySetExpandableQuotes() { + if (this.props.isQuoteExpanded === undefined && this.blockquoteRef.current) { + const el: HTMLElement | null = this.blockquoteRef.current.querySelector('.mx_EventTile_body'); + if (el) { + const code: HTMLElement | null = el.querySelector('code'); + const isCodeEllipsisShown = code ? code.offsetHeight >= SHOW_EXPAND_QUOTE_PIXELS : false; + const isElipsisShown = el.offsetHeight >= SHOW_EXPAND_QUOTE_PIXELS || isCodeEllipsisShown; + if (isElipsisShown) { + this.props.setQuoteExpanded(false); + } + } + } + } + private async initialize(): Promise { const { parentEv } = this.props; // at time of making this component we checked that props.parentEv has a parentEventId @@ -321,7 +333,7 @@ export default class ReplyThread extends React.Component { this.initialize(); }; - private onQuoteClick = async (): Promise => { + private onQuoteClick = async (event: React.MouseEvent): Promise => { const events = [this.state.loadedEv, ...this.state.events]; let loadedEv = null; @@ -373,14 +385,26 @@ export default class ReplyThread extends React.Component { header = ; } + const { isQuoteExpanded } = this.props; const evTiles = this.state.events.map((ev) => { - return
- -
; + const classname = classNames({ + 'mx_ReplyThread': true, + [this.getReplyThreadColorClass(ev)]: true, + // We don't want to add the class if it's undefined, it should only be expanded/collapsed when it's true/false + 'mx_ReplyThread--expanded': isQuoteExpanded === true, + // We don't want to add the class if it's undefined, it should only be expanded/collapsed when it's true/false + 'mx_ReplyThread--collapsed': isQuoteExpanded === false, + }); + return ( +
+ this.props.setQuoteExpanded(!this.props.isQuoteExpanded)} + /> +
+ ); }); return
diff --git a/src/components/views/messages/MessageActionBar.tsx b/src/components/views/messages/MessageActionBar.tsx index f76fa32ddc..e835584387 100644 --- a/src/components/views/messages/MessageActionBar.tsx +++ b/src/components/views/messages/MessageActionBar.tsx @@ -17,7 +17,8 @@ limitations under the License. */ import React, { useEffect } from 'react'; -import { MatrixEvent, EventStatus } from 'matrix-js-sdk/src/models/event'; +import { EventStatus, MatrixEvent } from 'matrix-js-sdk/src/models/event'; +import type { Relations } from 'matrix-js-sdk/src/models/relations'; import { _t } from '../../../languageHandler'; import * as sdk from '../../../index'; @@ -35,13 +36,17 @@ import Resend from "../../../Resend"; import { MatrixClientPeg } from "../../../MatrixClientPeg"; import { MediaEventHelper } from "../../../utils/MediaEventHelper"; import DownloadActionButton from "./DownloadActionButton"; +import MessageContextMenu from "../context_menus/MessageContextMenu"; +import classNames from 'classnames'; + import SettingsStore from '../../../settings/SettingsStore'; import { RoomPermalinkCreator } from '../../../utils/permalinks/Permalinks'; import ReplyThread from '../elements/ReplyThread'; interface IOptionsButtonProps { mxEvent: MatrixEvent; - getTile: () => any; // TODO: FIXME, haven't figured out what the return type is here + // TODO: Types + getTile: () => any | null; getReplyThread: () => ReplyThread; permalinkCreator: RoomPermalinkCreator; onFocusChange: (menuDisplayed: boolean) => void; @@ -57,8 +62,6 @@ const OptionsButton: React.FC = let contextMenu; if (menuDisplayed) { - const MessageContextMenu = sdk.getComponent('context_menus.MessageContextMenu'); - const tile = getTile && getTile(); const replyThread = getReplyThread && getReplyThread(); @@ -90,7 +93,7 @@ const OptionsButton: React.FC = interface IReactButtonProps { mxEvent: MatrixEvent; - reactions: any; // TODO: types + reactions: Relations; onFocusChange: (menuDisplayed: boolean) => void; } @@ -127,12 +130,14 @@ const ReactButton: React.FC = ({ mxEvent, reactions, onFocusC interface IMessageActionBarProps { mxEvent: MatrixEvent; - // The Relations model from the JS SDK for reactions to `mxEvent` - reactions?: any; // TODO: types + reactions?: Relations; + // TODO: Types + getTile: () => any | null; + getReplyThread: () => ReplyThread | undefined; permalinkCreator?: RoomPermalinkCreator; - getTile: () => any; // TODO: FIXME, haven't figured out what the return type is here - getReplyThread?: () => ReplyThread; - onFocusChange?: (menuDisplayed: boolean) => void; + onFocusChange: (menuDisplayed: boolean) => void; + isQuoteExpanded?: boolean; + toggleThreadExpanded: () => void; } @replaceableComponent("views.messages.MessageActionBar") @@ -324,6 +329,20 @@ export default class MessageActionBar extends React.PureComponent); + } + // The menu button should be last, so dump it there. toolbarOpts.push( { // If it's less than 30% we don't add the expansion button. // We also round the number as it sometimes can be 29.99... const percentageOfViewport = Math.round(pre.offsetHeight / UIStore.instance.windowHeight * 100); + // TODO: additionally show the button if it's an expanded quoted message if (percentageOfViewport < 30) return; const button = document.createElement("span"); diff --git a/src/components/views/rooms/EventTile.tsx b/src/components/views/rooms/EventTile.tsx index d1ac06b199..592827eaf5 100644 --- a/src/components/views/rooms/EventTile.tsx +++ b/src/components/views/rooms/EventTile.tsx @@ -322,7 +322,7 @@ interface IState { reactions: Relations; hover: boolean; - + isQuoteExpanded?: boolean; thread?: Thread; } @@ -330,7 +330,8 @@ interface IState { export default class EventTile extends React.Component { private suppressReadReceiptAnimation: boolean; private isListeningForReceipts: boolean; - private tile = React.createRef(); + // TODO: Types + private tile = React.createRef(); private replyThread = React.createRef(); public readonly ref = createRef(); @@ -888,8 +889,8 @@ export default class EventTile extends React.Component { actionBarFocused: focused, }); }; - - getTile = () => this.tile.current; + // TODO: Types + getTile: () => any | null = () => this.tile.current; getReplyThread = () => this.replyThread.current; @@ -914,6 +915,11 @@ export default class EventTile extends React.Component { }); }; + private setQuoteExpanded = (expanded: boolean) => { + this.setState({ + isQuoteExpanded: expanded, + }); + }; render() { const msgtype = this.props.mxEvent.getContent().msgtype; const eventType = this.props.mxEvent.getType() as EventType; @@ -923,6 +929,7 @@ export default class EventTile extends React.Component { isInfoMessage, isLeftAlignedBubbleMessage, } = getEventDisplayInfo(this.props.mxEvent); + const { isQuoteExpanded } = this.state; // This shouldn't happen: the caller should check we support this type // before trying to instantiate us @@ -935,6 +942,7 @@ export default class EventTile extends React.Component {
; } + const EventTileType = sdk.getComponent(tileHandler); const isSending = (['sending', 'queued', 'encrypting'].indexOf(this.props.eventSendStatus) !== -1); @@ -1054,6 +1062,8 @@ export default class EventTile extends React.Component { getTile={this.getTile} getReplyThread={this.getReplyThread} onFocusChange={this.onActionBarFocusChange} + isQuoteExpanded={isQuoteExpanded} + toggleThreadExpanded={() => this.setQuoteExpanded(!isQuoteExpanded)} /> : undefined; const showTimestamp = this.props.mxEvent.getTs() @@ -1192,20 +1202,18 @@ export default class EventTile extends React.Component { } default: { - let thread; - // When the "showHiddenEventsInTimeline" lab is enabled, - // avoid showing replies for hidden events (events without tiles) - if (haveTileForEvent(this.props.mxEvent)) { - thread = ReplyThread.makeThread( - this.props.mxEvent, - this.props.onHeightChanged, - this.props.permalinkCreator, - this.replyThread, - this.props.layout, - this.props.alwaysShowTimestamps || this.state.hover, - ); - } - + const thread = haveTileForEvent(this.props.mxEvent) && + ReplyThread.hasThreadReply(this.props.mxEvent) ? ( + ) : null; const isOwnEvent = this.props.mxEvent?.sender?.userId === MatrixClientPeg.get().getUserId(); // tab-index=-1 to allow it to be focusable but do not add tab stop for it, primarily for screen readers diff --git a/src/components/views/rooms/ReplyTile.tsx b/src/components/views/rooms/ReplyTile.tsx index cf7d1ce945..01a9e2f18b 100644 --- a/src/components/views/rooms/ReplyTile.tsx +++ b/src/components/views/rooms/ReplyTile.tsx @@ -35,6 +35,7 @@ interface IProps { highlights?: string[]; highlightLink?: string; onHeightChanged?(): void; + toggleExpandedQuote?: () => void; } @replaceableComponent("views.rooms.ReplyTile") @@ -82,12 +83,17 @@ export default class ReplyTile extends React.PureComponent { // This allows the permalink to be opened in a new tab/window or copied as // matrix.to, but also for it to enable routing within Riot when clicked. e.preventDefault(); - dis.dispatch({ - action: 'view_room', - event_id: this.props.mxEvent.getId(), - highlighted: true, - room_id: this.props.mxEvent.getRoomId(), - }); + // Expand thread on shift key + if (this.props.toggleExpandedQuote && e.shiftKey) { + this.props.toggleExpandedQuote(); + } else { + dis.dispatch({ + action: 'view_room', + event_id: this.props.mxEvent.getId(), + highlighted: true, + room_id: this.props.mxEvent.getRoomId(), + }); + } } }; diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index b5d90f6671..bc45caedb5 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1944,6 +1944,8 @@ "Edit": "Edit", "Reply": "Reply", "Thread": "Thread", + "Collapse quotes │ ⇧+click": "Collapse quotes │ ⇧+click", + "Expand quotes │ ⇧+click": "Expand quotes │ ⇧+click", "Message Actions": "Message Actions", "Download %(text)s": "Download %(text)s", "Error decrypting attachment": "Error decrypting attachment", From ccfc57657ab6d59921a088e24f32880959ccdcbe Mon Sep 17 00:00:00 2001 From: RiotRobot Date: Mon, 27 Sep 2021 14:27:26 +0100 Subject: [PATCH 45/65] Upgrade matrix-js-sdk to 13.0.0 --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 9593f74aae..7b6aa14bf9 100644 --- a/package.json +++ b/package.json @@ -82,7 +82,7 @@ "katex": "^0.12.0", "linkifyjs": "^2.1.9", "lodash": "^4.17.20", - "matrix-js-sdk": "13.0.0-rc.1", + "matrix-js-sdk": "13.0.0", "matrix-widget-api": "^0.1.0-beta.16", "minimist": "^1.2.5", "opus-recorder": "^8.0.3", diff --git a/yarn.lock b/yarn.lock index f5931c8c6e..56b4f74f4a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5806,10 +5806,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@13.0.0-rc.1: - version "13.0.0-rc.1" - resolved "https://registry.yarnpkg.com/matrix-js-sdk/-/matrix-js-sdk-13.0.0-rc.1.tgz#12deab353862852acae8342108d30ce080d364da" - integrity sha512-dfqJwXmG1+Ky2geaNADWYb7mwB2IfLFTE+T4q16gCoh2HM0W5yTMvi+kiJs0QspWFXICTps7eBSSq0827QNU8A== +matrix-js-sdk@13.0.0: + version "13.0.0" + resolved "https://registry.yarnpkg.com/matrix-js-sdk/-/matrix-js-sdk-13.0.0.tgz#a57a4b73892e98fcd5eb0081e4745fac567211dd" + integrity sha512-VkZhQBd6jlRNjsqjLIoHV3fnjOSljPDuJklKsF4l6yffkra7llxQctyMsrCnoiosdAQWigbLZsKSb+HbSFmcnw== dependencies: "@babel/runtime" "^7.12.5" another-json "^0.2.0" From d5dddadc3b54782959204f3bf646963754b9015d Mon Sep 17 00:00:00 2001 From: RiotRobot Date: Mon, 27 Sep 2021 14:31:25 +0100 Subject: [PATCH 46/65] Prepare changelog for v3.31.0 --- CHANGELOG.md | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 71ddb1c5fe..c28d72a3eb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,51 @@ +Changes in [3.31.0](https://github.com/vector-im/element-desktop/releases/tag/v3.31.0) (2021-09-27) +=================================================================================================== + +## ✨ Features + * Say Joining space instead of Joining room where we know its a space ([\#6818](https://github.com/matrix-org/matrix-react-sdk/pull/6818)). Fixes vector-im/element-web#19064 and vector-im/element-web#19064. + * Add warning that some spaces may not be relinked to the newly upgraded room ([\#6805](https://github.com/matrix-org/matrix-react-sdk/pull/6805)). Fixes vector-im/element-web#18858 and vector-im/element-web#18858. + * Delabs Spaces, iterate some copy and move communities/space toggle to preferences ([\#6594](https://github.com/matrix-org/matrix-react-sdk/pull/6594)). Fixes vector-im/element-web#18088, vector-im/element-web#18524 vector-im/element-web#18088 and vector-im/element-web#18088. + * Show "Message" in the user info panel instead of "Start chat" ([\#6319](https://github.com/matrix-org/matrix-react-sdk/pull/6319)). Fixes vector-im/element-web#17877 and vector-im/element-web#17877. Contributed by [SimonBrandner](https://github.com/SimonBrandner). + * Fix space keyboard shortcuts conflicting with native zoom shortcuts ([\#6804](https://github.com/matrix-org/matrix-react-sdk/pull/6804)). + * Replace plain text emoji at the end of a line ([\#6784](https://github.com/matrix-org/matrix-react-sdk/pull/6784)). Fixes vector-im/element-web#18833 and vector-im/element-web#18833. Contributed by [SimonBrandner](https://github.com/SimonBrandner). + * Simplify Space Panel layout and fix some edge cases ([\#6800](https://github.com/matrix-org/matrix-react-sdk/pull/6800)). Fixes vector-im/element-web#18694 and vector-im/element-web#18694. + * Show unsent message warning on Space Panel buttons ([\#6778](https://github.com/matrix-org/matrix-react-sdk/pull/6778)). Fixes vector-im/element-web#18891 and vector-im/element-web#18891. + * Hide mute/unmute button in UserInfo for Spaces as it makes no sense ([\#6790](https://github.com/matrix-org/matrix-react-sdk/pull/6790)). Fixes vector-im/element-web#19007 and vector-im/element-web#19007. + * Fix automatic field population in space create menu not validating ([\#6792](https://github.com/matrix-org/matrix-react-sdk/pull/6792)). Fixes vector-im/element-web#19005 and vector-im/element-web#19005. + * Optimize input label transition on focus ([\#6783](https://github.com/matrix-org/matrix-react-sdk/pull/6783)). Fixes vector-im/element-web#12876 and vector-im/element-web#12876. Contributed by [MadLittleMods](https://github.com/MadLittleMods). + * Adapt and re-use the RolesRoomSettingsTab for Spaces ([\#6779](https://github.com/matrix-org/matrix-react-sdk/pull/6779)). Fixes vector-im/element-web#18908 vector-im/element-web#18909 and vector-im/element-web#18908. + * Deduplicate join rule management between rooms and spaces ([\#6724](https://github.com/matrix-org/matrix-react-sdk/pull/6724)). Fixes vector-im/element-web#18798 and vector-im/element-web#18798. + * Add config option to turn on in-room event sending timing metrics ([\#6766](https://github.com/matrix-org/matrix-react-sdk/pull/6766)). + * Improve the upgrade for restricted user experience ([\#6764](https://github.com/matrix-org/matrix-react-sdk/pull/6764)). Fixes vector-im/element-web#18677 and vector-im/element-web#18677. + * Improve tooltips on space quick actions and explore button ([\#6760](https://github.com/matrix-org/matrix-react-sdk/pull/6760)). Fixes vector-im/element-web#18528 and vector-im/element-web#18528. + * Make space members and user info behave more expectedly ([\#6765](https://github.com/matrix-org/matrix-react-sdk/pull/6765)). Fixes vector-im/element-web#17018 and vector-im/element-web#17018. + * hide no-op m.room.encryption events and better word param changes ([\#6747](https://github.com/matrix-org/matrix-react-sdk/pull/6747)). Fixes vector-im/element-web#18597 and vector-im/element-web#18597. + * Respect m.space.parent relations if they hold valid permissions ([\#6746](https://github.com/matrix-org/matrix-react-sdk/pull/6746)). Fixes vector-im/element-web#10935 and vector-im/element-web#10935. + * Space panel accessibility improvements ([\#6744](https://github.com/matrix-org/matrix-react-sdk/pull/6744)). Fixes vector-im/element-web#18892 and vector-im/element-web#18892. + +## 🐛 Bug Fixes + * Fix spacing for message composer buttons ([\#6854](https://github.com/matrix-org/matrix-react-sdk/pull/6854)). + * Fix accessing field on oobData which may be undefined ([\#6830](https://github.com/matrix-org/matrix-react-sdk/pull/6830)). Fixes vector-im/element-web#19085 and vector-im/element-web#19085. + * Fix reactions aria-label not being a string and thus being read as [Object object] ([\#6828](https://github.com/matrix-org/matrix-react-sdk/pull/6828)). + * Fix missing null guard in space hierarchy pagination ([\#6821](https://github.com/matrix-org/matrix-react-sdk/pull/6821)). Fixes matrix-org/element-web-rageshakes#6299 and matrix-org/element-web-rageshakes#6299. + * Fix checks to show prompt to start new chats ([\#6812](https://github.com/matrix-org/matrix-react-sdk/pull/6812)). + * Fix room list scroll jumps ([\#6777](https://github.com/matrix-org/matrix-react-sdk/pull/6777)). Fixes vector-im/element-web#17460 vector-im/element-web#18440 and vector-im/element-web#17460. Contributed by [robintown](https://github.com/robintown). + * Fix various message bubble alignment issues ([\#6785](https://github.com/matrix-org/matrix-react-sdk/pull/6785)). Fixes vector-im/element-web#18293, vector-im/element-web#18294 vector-im/element-web#18305 and vector-im/element-web#18293. Contributed by [robintown](https://github.com/robintown). + * Make message bubble font size consistent ([\#6795](https://github.com/matrix-org/matrix-react-sdk/pull/6795)). Contributed by [robintown](https://github.com/robintown). + * Fix edge cases around joining new room which does not belong to active space ([\#6797](https://github.com/matrix-org/matrix-react-sdk/pull/6797)). Fixes vector-im/element-web#19025 and vector-im/element-web#19025. + * Fix edge case space issues around creation and initial view ([\#6798](https://github.com/matrix-org/matrix-react-sdk/pull/6798)). Fixes vector-im/element-web#19023 and vector-im/element-web#19023. + * Stop spinner on space preview if the join fails ([\#6803](https://github.com/matrix-org/matrix-react-sdk/pull/6803)). Fixes vector-im/element-web#19034 and vector-im/element-web#19034. + * Fix emoji picker and stickerpicker not appearing correctly when opened ([\#6793](https://github.com/matrix-org/matrix-react-sdk/pull/6793)). Fixes vector-im/element-web#19012 and vector-im/element-web#19012. Contributed by [Palid](https://github.com/Palid). + * Fix autocomplete not having y-scroll ([\#6794](https://github.com/matrix-org/matrix-react-sdk/pull/6794)). Fixes vector-im/element-web#18997 and vector-im/element-web#18997. Contributed by [Palid](https://github.com/Palid). + * Fix broken edge case with public space creation with no alias ([\#6791](https://github.com/matrix-org/matrix-react-sdk/pull/6791)). Fixes vector-im/element-web#19003 and vector-im/element-web#19003. + * Redirect from /#/welcome to /#/home if already logged in ([\#6786](https://github.com/matrix-org/matrix-react-sdk/pull/6786)). Fixes vector-im/element-web#18990 and vector-im/element-web#18990. Contributed by [aaronraimist](https://github.com/aaronraimist). + * Fix build issues from two conflicting PRs landing without merge conflict ([\#6780](https://github.com/matrix-org/matrix-react-sdk/pull/6780)). + * Render guest settings only in public rooms/spaces ([\#6693](https://github.com/matrix-org/matrix-react-sdk/pull/6693)). Fixes vector-im/element-web#18776 and vector-im/element-web#18776. Contributed by [SimonBrandner](https://github.com/SimonBrandner). + * Fix message bubble corners being wrong in the presence of hidden events ([\#6776](https://github.com/matrix-org/matrix-react-sdk/pull/6776)). Fixes vector-im/element-web#18124 and vector-im/element-web#18124. Contributed by [robintown](https://github.com/robintown). + * Debounce read marker update on scroll ([\#6771](https://github.com/matrix-org/matrix-react-sdk/pull/6771)). Fixes vector-im/element-web#18961 and vector-im/element-web#18961. + * Use cursor:pointer on space panel buttons ([\#6770](https://github.com/matrix-org/matrix-react-sdk/pull/6770)). Fixes vector-im/element-web#18951 and vector-im/element-web#18951. + * Fix regressed tab view buttons in space update toast ([\#6761](https://github.com/matrix-org/matrix-react-sdk/pull/6761)). Fixes vector-im/element-web#18781 and vector-im/element-web#18781. + Changes in [3.31.0-rc.2](https://github.com/vector-im/element-desktop/releases/tag/v3.31.0-rc.2) (2021-09-22) ============================================================================================================= From dad60804ba336c5d3ad8844c4b07733d46e0b30a Mon Sep 17 00:00:00 2001 From: RiotRobot Date: Mon, 27 Sep 2021 14:31:26 +0100 Subject: [PATCH 47/65] v3.31.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 7b6aa14bf9..c8a7d0f74e 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "matrix-react-sdk", - "version": "3.31.0-rc.2", + "version": "3.31.0", "description": "SDK for matrix.org using React", "author": "matrix.org", "repository": { From 3e2e3fcd2014d6f7ac7f62b53c99f2e79bc1289f Mon Sep 17 00:00:00 2001 From: RiotRobot Date: Mon, 27 Sep 2021 14:33:46 +0100 Subject: [PATCH 48/65] Resetting package fields for development --- package.json | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index c8a7d0f74e..9e740fa8a7 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,7 @@ "bin": { "reskindex": "scripts/reskindex.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", @@ -210,6 +210,5 @@ "coverageReporters": [ "text" ] - }, - "typings": "./lib/index.d.ts" + } } From b635d017799e198fa0391e51af0b12e9d5843992 Mon Sep 17 00:00:00 2001 From: RiotRobot Date: Mon, 27 Sep 2021 14:33:56 +0100 Subject: [PATCH 49/65] 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 9e740fa8a7..89084acd68 100644 --- a/package.json +++ b/package.json @@ -82,7 +82,7 @@ "katex": "^0.12.0", "linkifyjs": "^2.1.9", "lodash": "^4.17.20", - "matrix-js-sdk": "13.0.0", + "matrix-js-sdk": "github:matrix-org/matrix-js-sdk#develop", "matrix-widget-api": "^0.1.0-beta.16", "minimist": "^1.2.5", "opus-recorder": "^8.0.3", diff --git a/yarn.lock b/yarn.lock index 107caf1730..39c50464d5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5806,10 +5806,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@13.0.0: +"matrix-js-sdk@github:matrix-org/matrix-js-sdk#develop": version "13.0.0" - resolved "https://registry.yarnpkg.com/matrix-js-sdk/-/matrix-js-sdk-13.0.0.tgz#a57a4b73892e98fcd5eb0081e4745fac567211dd" - integrity sha512-VkZhQBd6jlRNjsqjLIoHV3fnjOSljPDuJklKsF4l6yffkra7llxQctyMsrCnoiosdAQWigbLZsKSb+HbSFmcnw== + resolved "https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/2515d07c8fc3bf5e1afc8352e3e330cca30dde85" dependencies: "@babel/runtime" "^7.12.5" another-json "^0.2.0" From dabc13c98fbcad659a89a84dfc22e5bd4e1b01b1 Mon Sep 17 00:00:00 2001 From: Paulo Pinto Date: Mon, 27 Sep 2021 16:32:01 +0100 Subject: [PATCH 50/65] Extract logic to a function For better readability of the call to useAsyncMemo() Signed-off-by: Paulo Pinto --- .../views/rooms/LinkPreviewGroup.tsx | 21 ++++++++++++------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/src/components/views/rooms/LinkPreviewGroup.tsx b/src/components/views/rooms/LinkPreviewGroup.tsx index c9842bdd33..f9d0c76e99 100644 --- a/src/components/views/rooms/LinkPreviewGroup.tsx +++ b/src/components/views/rooms/LinkPreviewGroup.tsx @@ -16,7 +16,7 @@ limitations under the License. import React, { useContext, useEffect } from "react"; import { MatrixEvent } from "matrix-js-sdk/src/models/event"; -import { IPreviewUrlResponse } from "matrix-js-sdk/src/client"; +import { IPreviewUrlResponse, MatrixClient } from "matrix-js-sdk/src/client"; import { useStateToggle } from "../../../hooks/useStateToggle"; import LinkPreviewWidget from "./LinkPreviewWidget"; @@ -40,13 +40,7 @@ const LinkPreviewGroup: React.FC = ({ links, mxEvent, onCancelClick, onH const ts = mxEvent.getTs(); const previews = useAsyncMemo<[string, IPreviewUrlResponse][]>(async () => { - return Promise.all<[string, IPreviewUrlResponse] | void>(links.map(async link => { - try { - return [link, await cli.getUrlPreview(link, ts)]; - } catch (error) { - console.error("Failed to get URL preview: " + error); - } - })).then(a => a.filter(Boolean)) as Promise<[string, IPreviewUrlResponse][]>; + return fetchPreviews(cli, links, ts); }, [links, ts], []); useEffect(() => { @@ -89,4 +83,15 @@ const LinkPreviewGroup: React.FC = ({ links, mxEvent, onCancelClick, onH ; }; +const fetchPreviews = (cli: MatrixClient, links: string[], ts: number): + Promise<[string, IPreviewUrlResponse][]> => { + return Promise.all<[string, IPreviewUrlResponse] | void>(links.map(async link => { + try { + return [link, await cli.getUrlPreview(link, ts)]; + } catch (error) { + console.error("Failed to get URL preview: " + error); + } + })).then(a => a.filter(Boolean)) as Promise<[string, IPreviewUrlResponse][]>; +}; + export default LinkPreviewGroup; From 432dd994bdf70add062235c3c6aa5e2cc158fe4c Mon Sep 17 00:00:00 2001 From: Paulo Pinto Date: Mon, 27 Sep 2021 18:03:15 +0100 Subject: [PATCH 51/65] Filter out invalid previews The call to cli.getUrlPreview() might return an empty object ({}), which means there is in fact no preview for that URL. Signed-off-by: Paulo Pinto --- src/components/views/rooms/LinkPreviewGroup.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/components/views/rooms/LinkPreviewGroup.tsx b/src/components/views/rooms/LinkPreviewGroup.tsx index f9d0c76e99..eed13aff0f 100644 --- a/src/components/views/rooms/LinkPreviewGroup.tsx +++ b/src/components/views/rooms/LinkPreviewGroup.tsx @@ -87,7 +87,10 @@ const fetchPreviews = (cli: MatrixClient, links: string[], ts: number): Promise<[string, IPreviewUrlResponse][]> => { return Promise.all<[string, IPreviewUrlResponse] | void>(links.map(async link => { try { - return [link, await cli.getUrlPreview(link, ts)]; + const preview = await cli.getUrlPreview(link, ts); + if (preview && Object.keys(preview).length > 0) { + return [link, preview]; + } } catch (error) { console.error("Failed to get URL preview: " + error); } From d71f2345fb01f0cfca1fdb9d546633e71908d9b6 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 28 Sep 2021 10:34:32 +0100 Subject: [PATCH 52/65] Null guard around the matrixClient in SpaceStore --- src/stores/SpaceStore.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stores/SpaceStore.tsx b/src/stores/SpaceStore.tsx index f28d279d00..d440c33c83 100644 --- a/src/stores/SpaceStore.tsx +++ b/src/stores/SpaceStore.tsx @@ -818,7 +818,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient { } protected async onAction(payload: ActionPayload) { - if (!spacesEnabled) return; + if (!spacesEnabled || !this.matrixClient) return; switch (payload.action) { case "view_room": { // Don't auto-switch rooms when reacting to a context-switch From ba8ec1763b3f309ae68a32eae8dda5ca5b680cc3 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 28 Sep 2021 11:05:03 +0100 Subject: [PATCH 53/65] Fix space panel width mismatch for creation modal shade --- res/css/views/spaces/_SpaceCreateMenu.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/css/views/spaces/_SpaceCreateMenu.scss b/res/css/views/spaces/_SpaceCreateMenu.scss index 3f526a6bba..7084c2f20e 100644 --- a/res/css/views/spaces/_SpaceCreateMenu.scss +++ b/res/css/views/spaces/_SpaceCreateMenu.scss @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -$spacePanelWidth: 71px; +$spacePanelWidth: 68px; .mx_SpaceCreateMenu_wrapper { // background blur everything except SpacePanel From 160bf8e21f1737a369e5a91f46dbc31ea358c4e3 Mon Sep 17 00:00:00 2001 From: Germain Souquet Date: Tue, 28 Sep 2021 11:06:06 +0100 Subject: [PATCH 54/65] Fix missing prop --- res/img/element-icons/message/view-in-timeline.svg | 1 + src/components/views/messages/MessageActionBar.tsx | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) create mode 100644 res/img/element-icons/message/view-in-timeline.svg diff --git a/res/img/element-icons/message/view-in-timeline.svg b/res/img/element-icons/message/view-in-timeline.svg new file mode 100644 index 0000000000..9f05950ce0 --- /dev/null +++ b/res/img/element-icons/message/view-in-timeline.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/components/views/messages/MessageActionBar.tsx b/src/components/views/messages/MessageActionBar.tsx index 3be24d47ab..d3d4d5a693 100644 --- a/src/components/views/messages/MessageActionBar.tsx +++ b/src/components/views/messages/MessageActionBar.tsx @@ -135,11 +135,10 @@ interface IMessageActionBarProps { getTile: () => any | null; getReplyThread: () => ReplyThread | undefined; permalinkCreator?: RoomPermalinkCreator; - getTile: () => any; // TODO: FIXME, haven't figured out what the return type is here - getReplyThread?: () => ReplyThread; onFocusChange?: (menuDisplayed: boolean) => void; toggleThreadExpanded: () => void; isInThreadTimeline?: boolean; + isQuoteExpanded?: boolean; } @replaceableComponent("views.messages.MessageActionBar") From c1165830edff18015caf808c28f020c36eaf7d73 Mon Sep 17 00:00:00 2001 From: Germain Souquet Date: Tue, 28 Sep 2021 14:55:00 +0100 Subject: [PATCH 55/65] Create rendering context enum for MessageActionBar --- src/components/views/messages/MessageActionBar.tsx | 11 ++++++++--- src/components/views/rooms/EventTile.tsx | 9 ++++++--- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/components/views/messages/MessageActionBar.tsx b/src/components/views/messages/MessageActionBar.tsx index d3d4d5a693..06817b910a 100644 --- a/src/components/views/messages/MessageActionBar.tsx +++ b/src/components/views/messages/MessageActionBar.tsx @@ -128,6 +128,11 @@ const ReactButton: React.FC = ({ mxEvent, reactions, onFocusC ; }; +export enum ActionBarRenderingContext { + Room, + Thread +} + interface IMessageActionBarProps { mxEvent: MatrixEvent; reactions?: Relations; @@ -137,7 +142,7 @@ interface IMessageActionBarProps { permalinkCreator?: RoomPermalinkCreator; onFocusChange?: (menuDisplayed: boolean) => void; toggleThreadExpanded: () => void; - isInThreadTimeline?: boolean; + renderingContext?: ActionBarRenderingContext; isQuoteExpanded?: boolean; } @@ -146,7 +151,7 @@ export default class MessageActionBar extends React.PureComponent { } } - const isInThreadTimeline = this.props.tileShape === TileShape.Thread; + const renderingContext = this.props.tileShape === TileShape.Thread + ? ActionBarRenderingContext.Thread + : ActionBarRenderingContext.Room; const actionBar = !isEditing ? { getTile={this.getTile} getReplyThread={this.getReplyThread} onFocusChange={this.onActionBarFocusChange} - isInThreadTimeline={isInThreadTimeline} + renderingContext={renderingContext} + isQuoteExpanded={isQuoteExpanded} toggleThreadExpanded={() => this.setQuoteExpanded(!isQuoteExpanded)} /> : undefined; From 8331d4c7b725c4312b6e86a4d3037f09796f871f Mon Sep 17 00:00:00 2001 From: Dariusz Niemczyk <3636685+Palid@users.noreply.github.com> Date: Tue, 28 Sep 2021 16:04:25 +0200 Subject: [PATCH 56/65] Happily handle empty text messages (#6825) This commig refactors the message-deletion modal and reuses it for case of removing the entire message and trying to send it, which should trigger removal flow instead. Fix vector-im/element-web#18873 --- .../context_menus/MessageContextMenu.tsx | 36 +++------------- .../views/dialogs/ConfirmRedactDialog.tsx | 41 +++++++++++++++++++ .../views/rooms/EditMessageComposer.tsx | 9 ++++ src/editor/serialize.ts | 2 +- 4 files changed, 57 insertions(+), 31 deletions(-) diff --git a/src/components/views/context_menus/MessageContextMenu.tsx b/src/components/views/context_menus/MessageContextMenu.tsx index c7fcf32260..22dd3ac438 100644 --- a/src/components/views/context_menus/MessageContextMenu.tsx +++ b/src/components/views/context_menus/MessageContextMenu.tsx @@ -34,8 +34,7 @@ import ForwardDialog from "../dialogs/ForwardDialog"; import { Action } from "../../../dispatcher/actions"; import ReportEventDialog from '../dialogs/ReportEventDialog'; import ViewSource from '../../structures/ViewSource'; -import ConfirmRedactDialog from '../dialogs/ConfirmRedactDialog'; -import ErrorDialog from '../dialogs/ErrorDialog'; +import { createRedactEventDialog } from '../dialogs/ConfirmRedactDialog'; import ShareDialog from '../dialogs/ShareDialog'; import { RoomPermalinkCreator } from "../../../utils/permalinks/Permalinks"; import { IPosition, ChevronFace } from '../../structures/ContextMenu'; @@ -140,34 +139,11 @@ export default class MessageContextMenu extends React.Component }; private onRedactClick = (): void => { - Modal.createTrackedDialog('Confirm Redact Dialog', '', ConfirmRedactDialog, { - onFinished: async (proceed: boolean, reason?: string) => { - if (!proceed) return; - - const cli = MatrixClientPeg.get(); - try { - this.props.onCloseDialog?.(); - await cli.redactEvent( - this.props.mxEvent.getRoomId(), - this.props.mxEvent.getId(), - undefined, - reason ? { reason } : {}, - ); - } catch (e) { - const code = e.errcode || e.statusCode; - // only show the dialog if failing for something other than a network error - // (e.g. no errcode or statusCode) as in that case the redactions end up in the - // detached queue and we show the room status bar to allow retry - if (typeof code !== "undefined") { - // display error message stating you couldn't delete this. - Modal.createTrackedDialog('You cannot delete this message', '', ErrorDialog, { - title: _t('Error'), - description: _t('You cannot delete this message. (%(code)s)', { code }), - }); - } - } - }, - }, 'mx_Dialog_confirmredact'); + const { mxEvent, onCloseDialog } = this.props; + createRedactEventDialog({ + mxEvent, + onCloseDialog, + }); this.closeMenu(); }; diff --git a/src/components/views/dialogs/ConfirmRedactDialog.tsx b/src/components/views/dialogs/ConfirmRedactDialog.tsx index b346d2d44c..74b3320fdf 100644 --- a/src/components/views/dialogs/ConfirmRedactDialog.tsx +++ b/src/components/views/dialogs/ConfirmRedactDialog.tsx @@ -14,9 +14,13 @@ See the License for the specific language governing permissions and limitations under the License. */ +import { MatrixEvent } from 'matrix-js-sdk/src/models/event'; import React from 'react'; import { _t } from '../../../languageHandler'; +import { MatrixClientPeg } from '../../../MatrixClientPeg'; +import Modal from '../../../Modal'; import { replaceableComponent } from "../../../utils/replaceableComponent"; +import ErrorDialog from './ErrorDialog'; import TextInputDialog from "./TextInputDialog"; interface IProps { @@ -42,3 +46,40 @@ export default class ConfirmRedactDialog extends React.Component { ); } } + +export function createRedactEventDialog({ + mxEvent, + onCloseDialog = () => {}, +}: { + mxEvent: MatrixEvent; + onCloseDialog?: () => void; +}) { + Modal.createTrackedDialog('Confirm Redact Dialog', '', ConfirmRedactDialog, { + onFinished: async (proceed: boolean, reason?: string) => { + if (!proceed) return; + + const cli = MatrixClientPeg.get(); + try { + onCloseDialog?.(); + await cli.redactEvent( + mxEvent.getRoomId(), + mxEvent.getId(), + undefined, + reason ? { reason } : {}, + ); + } catch (e) { + const code = e.errcode || e.statusCode; + // only show the dialog if failing for something other than a network error + // (e.g. no errcode or statusCode) as in that case the redactions end up in the + // detached queue and we show the room status bar to allow retry + if (typeof code !== "undefined") { + // display error message stating you couldn't delete this. + Modal.createTrackedDialog('You cannot delete this message', '', ErrorDialog, { + title: _t('Error'), + description: _t('You cannot delete this message. (%(code)s)', { code }), + }); + } + } + }, + }, 'mx_Dialog_confirmredact'); +} diff --git a/src/components/views/rooms/EditMessageComposer.tsx b/src/components/views/rooms/EditMessageComposer.tsx index f2f80b7670..33273f1f95 100644 --- a/src/components/views/rooms/EditMessageComposer.tsx +++ b/src/components/views/rooms/EditMessageComposer.tsx @@ -42,6 +42,7 @@ import ErrorDialog from "../dialogs/ErrorDialog"; import QuestionDialog from "../dialogs/QuestionDialog"; import { ActionPayload } from "../../../dispatcher/payloads"; import AccessibleButton from '../elements/AccessibleButton'; +import { createRedactEventDialog } from '../dialogs/ConfirmRedactDialog'; import SettingsStore from "../../../settings/SettingsStore"; import { logger } from "matrix-js-sdk/src/logger"; @@ -331,6 +332,14 @@ export default class EditMessageComposer extends React.Component let shouldSend = true; + if (newContent?.body === '') { + this.cancelPreviousPendingEdit(); + createRedactEventDialog({ + mxEvent: editedEvent, + }); + return; + } + // If content is modified then send an updated event into the room if (this.isContentModified(newContent)) { const roomId = editedEvent.getRoomId(); diff --git a/src/editor/serialize.ts b/src/editor/serialize.ts index 38a73cc945..9822046a0d 100644 --- a/src/editor/serialize.ts +++ b/src/editor/serialize.ts @@ -185,7 +185,7 @@ export function startsWith(model: EditorModel, prefix: string, caseSensitive = t const firstPart = model.parts[0]; // part type will be "plain" while editing, // and "command" while composing a message. - let text = firstPart && firstPart.text; + let text = firstPart?.text || ''; if (!caseSensitive) { prefix = prefix.toLowerCase(); text = text.toLowerCase(); From 4335cd03c5f5d5c9ff5f5da60b3c5738b7b15062 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 28 Sep 2021 16:30:17 +0100 Subject: [PATCH 57/65] Pluck out JoinRuleSettings styles so they apply in space settings too --- res/css/_components.scss | 4 +- res/css/views/settings/_JoinRuleSettings.scss | 56 +++++++++++++++++++ .../tabs/room/_SecurityRoomSettingsTab.scss | 41 -------------- .../views/settings/JoinRuleSettings.tsx | 4 +- 4 files changed, 61 insertions(+), 44 deletions(-) create mode 100644 res/css/views/settings/_JoinRuleSettings.scss diff --git a/res/css/_components.scss b/res/css/_components.scss index ffaec43b68..4ef95e8cd8 100644 --- a/res/css/_components.scss +++ b/res/css/_components.scss @@ -1,8 +1,10 @@ // autogenerated by rethemendex.sh +@import "./_animations.scss"; @import "./_common.scss"; @import "./_font-sizes.scss"; @import "./_font-weights.scss"; @import "./structures/_AutoHideScrollbar.scss"; +@import "./structures/_BackdropPanel.scss"; @import "./structures/_CompatibilityPage.scss"; @import "./structures/_ContextualMenu.scss"; @import "./structures/_CreateRoom.scss"; @@ -17,7 +19,6 @@ @import "./structures/_LeftPanelWidget.scss"; @import "./structures/_MainSplit.scss"; @import "./structures/_MatrixChat.scss"; -@import "./structures/_BackdropPanel.scss"; @import "./structures/_MyGroups.scss"; @import "./structures/_NonUrgentToastContainer.scss"; @import "./structures/_NotificationPanel.scss"; @@ -243,6 +244,7 @@ @import "./views/settings/_E2eAdvancedPanel.scss"; @import "./views/settings/_EmailAddresses.scss"; @import "./views/settings/_IntegrationManager.scss"; +@import "./views/settings/_JoinRuleSettings.scss"; @import "./views/settings/_LayoutSwitcher.scss"; @import "./views/settings/_Notifications.scss"; @import "./views/settings/_PhoneNumbers.scss"; diff --git a/res/css/views/settings/_JoinRuleSettings.scss b/res/css/views/settings/_JoinRuleSettings.scss new file mode 100644 index 0000000000..35c58c92a1 --- /dev/null +++ b/res/css/views/settings/_JoinRuleSettings.scss @@ -0,0 +1,56 @@ +/* +Copyright 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. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +.mx_JoinRuleSettings_upgradeRequired { + margin-left: 16px; + padding: 4px 16px; + border: 1px solid $accent-color; + border-radius: 8px; + color: $accent-color; + font-size: $font-12px; + line-height: $font-15px; +} + +.mx_JoinRuleSettings_spacesWithAccess { + > h4 { + color: $secondary-content; + font-weight: $font-semi-bold; + font-size: $font-12px; + line-height: $font-15px; + text-transform: uppercase; + } + + > span { + font-weight: 500; + font-size: $font-14px; + line-height: 32px; // matches height of avatar for v-align + color: $secondary-content; + display: inline-block; + + img.mx_RoomAvatar_isSpaceRoom, + .mx_RoomAvatar_isSpaceRoom img { + border-radius: 8px; + } + + .mx_BaseAvatar { + margin-right: 8px; + } + + & + span { + margin-left: 16px; + } + } +} diff --git a/res/css/views/settings/tabs/room/_SecurityRoomSettingsTab.scss b/res/css/views/settings/tabs/room/_SecurityRoomSettingsTab.scss index 8fd0f14418..c43c2aa1d7 100644 --- a/res/css/views/settings/tabs/room/_SecurityRoomSettingsTab.scss +++ b/res/css/views/settings/tabs/room/_SecurityRoomSettingsTab.scss @@ -19,37 +19,6 @@ limitations under the License. padding: 0; margin-bottom: 16px; } - - .mx_SecurityRoomSettingsTab_spacesWithAccess { - > h4 { - color: $secondary-content; - font-weight: $font-semi-bold; - font-size: $font-12px; - line-height: $font-15px; - text-transform: uppercase; - } - - > span { - font-weight: 500; - font-size: $font-14px; - line-height: 32px; // matches height of avatar for v-align - color: $secondary-content; - display: inline-block; - - img.mx_RoomAvatar_isSpaceRoom, - .mx_RoomAvatar_isSpaceRoom img { - border-radius: 8px; - } - - .mx_BaseAvatar { - margin-right: 8px; - } - - & + span { - margin-left: 16px; - } - } - } } .mx_SecurityRoomSettingsTab_warning { @@ -69,16 +38,6 @@ limitations under the License. margin-bottom: 32px; } -.mx_SecurityRoomSettingsTab_upgradeRequired { - margin-left: 16px; - padding: 4px 16px; - border: 1px solid $accent-color; - border-radius: 8px; - color: $accent-color; - font-size: $font-12px; - line-height: $font-15px; -} - .mx_SecurityRoomSettingsTab_joinRule { .mx_RadioButton { padding-top: 16px; diff --git a/src/components/views/settings/JoinRuleSettings.tsx b/src/components/views/settings/JoinRuleSettings.tsx index a32d147d3a..2dec4e601a 100644 --- a/src/components/views/settings/JoinRuleSettings.tsx +++ b/src/components/views/settings/JoinRuleSettings.tsx @@ -97,7 +97,7 @@ const JoinRuleSettings = ({ room, promptUpgrade, onError, beforeChange, closeSet if (roomSupportsRestricted || preferredRestrictionVersion || joinRule === JoinRule.Restricted) { let upgradeRequiredPill; if (preferredRestrictionVersion) { - upgradeRequiredPill = + upgradeRequiredPill = { _t("Upgrade required") } ; } @@ -165,7 +165,7 @@ const JoinRuleSettings = ({ room, promptUpgrade, onError, beforeChange, closeSet }) } -
+

{ _t("Spaces with access") }

{ shownSpaces.map(room => { return From 02c5b7e28d10f8a361b353999ab0fdd222dab6ee Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 28 Sep 2021 16:36:25 +0100 Subject: [PATCH 58/65] Pluck out some more applicable styles --- res/css/views/settings/_JoinRuleSettings.scss | 32 +++++++++++++++++ .../tabs/room/_SecurityRoomSettingsTab.scss | 34 ------------------- .../views/settings/JoinRuleSettings.tsx | 2 ++ 3 files changed, 34 insertions(+), 34 deletions(-) diff --git a/res/css/views/settings/_JoinRuleSettings.scss b/res/css/views/settings/_JoinRuleSettings.scss index 35c58c92a1..8b520b2ab1 100644 --- a/res/css/views/settings/_JoinRuleSettings.scss +++ b/res/css/views/settings/_JoinRuleSettings.scss @@ -54,3 +54,35 @@ limitations under the License. } } } + +.mx_JoinRuleSettings_radioButton { + padding-top: 16px; + margin-bottom: 8px; + + .mx_RadioButton_content { + margin-left: 14px; + font-weight: $font-semi-bold; + font-size: $font-15px; + line-height: $font-24px; + color: $primary-content; + display: block; + } + + & + span { + display: inline-block; + margin-left: 34px; + margin-bottom: 16px; + font-size: $font-15px; + line-height: $font-24px; + color: $secondary-content; + + & + .mx_RadioButton { + border-top: 1px solid $menu-border-color; + } + } +} + +.mx_JoinRuleSettings_linkButton { + padding: 0; + font-size: inherit; +} diff --git a/res/css/views/settings/tabs/room/_SecurityRoomSettingsTab.scss b/res/css/views/settings/tabs/room/_SecurityRoomSettingsTab.scss index c43c2aa1d7..a3b3b17899 100644 --- a/res/css/views/settings/tabs/room/_SecurityRoomSettingsTab.scss +++ b/res/css/views/settings/tabs/room/_SecurityRoomSettingsTab.scss @@ -37,37 +37,3 @@ limitations under the License. border-bottom: 1px solid $menu-border-color; margin-bottom: 32px; } - -.mx_SecurityRoomSettingsTab_joinRule { - .mx_RadioButton { - padding-top: 16px; - margin-bottom: 8px; - - .mx_RadioButton_content { - margin-left: 14px; - font-weight: $font-semi-bold; - font-size: $font-15px; - line-height: $font-24px; - color: $primary-content; - display: block; - } - } - - > span { - display: inline-block; - margin-left: 34px; - margin-bottom: 16px; - font-size: $font-15px; - line-height: $font-24px; - color: $secondary-content; - - & + .mx_RadioButton { - border-top: 1px solid $menu-border-color; - } - } - - .mx_AccessibleButton_kind_link { - padding: 0; - font-size: inherit; - } -} diff --git a/src/components/views/settings/JoinRuleSettings.tsx b/src/components/views/settings/JoinRuleSettings.tsx index 2dec4e601a..76596103f5 100644 --- a/src/components/views/settings/JoinRuleSettings.tsx +++ b/src/components/views/settings/JoinRuleSettings.tsx @@ -159,6 +159,7 @@ const JoinRuleSettings = ({ room, promptUpgrade, onError, beforeChange, closeSet disabled={disabled} onClick={onEditRestrictedClick} kind="link" + className="mx_JoinRuleSettings_linkButton" > { sub } , @@ -286,6 +287,7 @@ const JoinRuleSettings = ({ room, promptUpgrade, onError, beforeChange, closeSet onChange={onChange} definitions={definitions} disabled={disabled} + className="mx_JoinRuleSettings_radioButton" /> ); }; From b285315cb8a5566e444754784cd6c8df3f812b83 Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Wed, 29 Sep 2021 11:16:41 +0100 Subject: [PATCH 59/65] Fix `ScrollPanel.isAtBottom` for non-standard DPI settings --- src/components/structures/ScrollPanel.tsx | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/components/structures/ScrollPanel.tsx b/src/components/structures/ScrollPanel.tsx index 2eae585f4f..563686cee4 100644 --- a/src/components/structures/ScrollPanel.tsx +++ b/src/components/structures/ScrollPanel.tsx @@ -277,8 +277,15 @@ export default class ScrollPanel extends React.Component { // fractional values (both too big and too small) // for scrollTop happen on certain browsers/platforms // when scrolled all the way down. E.g. Chrome 72 on debian. - // so check difference <= 1; - return Math.abs(sn.scrollHeight - (sn.scrollTop + sn.clientHeight)) <= 1; + // + // We therefore leave a bit of wiggle-room and assume we're at the + // bottom if the unscrolled area is less than one pixel high. + // + // non-standard DPI settings also seem to have effect here and can + // actually lead to scrollTop+clientHeight being *larger* than + // scrollHeight. (observed in element-desktop on Ubuntu 20.04) + // + return sn.scrollHeight - (sn.scrollTop + sn.clientHeight) <= 1; }; // returns the vertical height in the given direction that can be removed from From c99216814f141202a766b041e467c9d124d938ef Mon Sep 17 00:00:00 2001 From: Richard van der Hoff Date: Wed, 29 Sep 2021 11:20:51 +0100 Subject: [PATCH 60/65] fix lint --- src/components/structures/ScrollPanel.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/structures/ScrollPanel.tsx b/src/components/structures/ScrollPanel.tsx index 563686cee4..0ea070627a 100644 --- a/src/components/structures/ScrollPanel.tsx +++ b/src/components/structures/ScrollPanel.tsx @@ -284,7 +284,7 @@ export default class ScrollPanel extends React.Component { // non-standard DPI settings also seem to have effect here and can // actually lead to scrollTop+clientHeight being *larger* than // scrollHeight. (observed in element-desktop on Ubuntu 20.04) - // + // return sn.scrollHeight - (sn.scrollTop + sn.clientHeight) <= 1; }; From 842ab0af05f8f83e92b4bec80fd056fe8440b1e4 Mon Sep 17 00:00:00 2001 From: Andy Balaam Date: Wed, 29 Sep 2021 11:18:15 +0100 Subject: [PATCH 61/65] Remove extra 'are' in README Signed-off-by: Andy Balaam --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 67e5e12f59..4588a0586e 100644 --- a/README.md +++ b/README.md @@ -48,7 +48,7 @@ Code should be committed as follows: * CSS: https://github.com/matrix-org/matrix-react-sdk/tree/master/res/css * Theme specific CSS & resources: https://github.com/matrix-org/matrix-react-sdk/tree/master/res/themes -React components in matrix-react-sdk are come in two different flavours: +React components in matrix-react-sdk come in two different flavours: 'structures' and 'views'. Structures are stateful components which handle the more complicated business logic of the app, delegating their actual presentation rendering to stateless 'view' components. For instance, the RoomView component From af8c07a69b5e65e7f10cfd5943f2605a6d7496d6 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 29 Sep 2021 11:45:27 +0100 Subject: [PATCH 62/65] Ensure that sub-spaces aren't considered for notification badges --- src/stores/SpaceStore.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stores/SpaceStore.tsx b/src/stores/SpaceStore.tsx index d440c33c83..ea482ffb9b 100644 --- a/src/stores/SpaceStore.tsx +++ b/src/stores/SpaceStore.tsx @@ -595,7 +595,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient { // Update NotificationStates this.getNotificationState(s).setRooms(visibleRooms.filter(room => { - if (!roomIds.has(room.roomId)) return false; + if (!roomIds.has(room.roomId) || room.isSpaceRoom()) return false; if (DMRoomMap.shared().getUserIdForRoomId(room.roomId)) { return s === HOME_SPACE; From 0e79daee4bd25d139c630e6b512bf9fd774902b6 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 29 Sep 2021 11:53:13 +0100 Subject: [PATCH 63/65] Remove stale new in the spaces beta toast & modal --- src/stores/SpaceStore.tsx | 59 --------------------------------------- 1 file changed, 59 deletions(-) diff --git a/src/stores/SpaceStore.tsx b/src/stores/SpaceStore.tsx index d440c33c83..30df579fdf 100644 --- a/src/stores/SpaceStore.tsx +++ b/src/stores/SpaceStore.tsx @@ -233,65 +233,6 @@ export class SpaceStoreClass extends AsyncStoreWithClient { window.localStorage.removeItem(ACTIVE_SPACE_LS_KEY); } - // New in Spaces beta toast for Restricted Join Rule - const lsKey = "mx_SpaceBeta_restrictedJoinRuleToastSeen"; - if (contextSwitch && space?.getJoinRule() === JoinRule.Invite && shouldShowSpaceSettings(space) && - space.getJoinedMemberCount() > 1 && !localStorage.getItem(lsKey) - && this.restrictedJoinRuleSupport?.preferred - ) { - const toastKey = "restrictedjoinrule"; - ToastStore.sharedInstance().addOrReplaceToast({ - key: toastKey, - title: _t("New in the Spaces beta"), - props: { - description: _t("Help people in spaces to find and join private rooms"), - acceptLabel: _t("Learn more"), - onAccept: () => { - localStorage.setItem(lsKey, "true"); - ToastStore.sharedInstance().dismissToast(toastKey); - - Modal.createTrackedDialog("New in the Spaces beta", "restricted join rule", InfoDialog, { - title: _t("Help space members find private rooms"), - description: <> -

{ _t("To help space members find and join a private room, " + - "go to that room's Security & Privacy settings.") }

- - { /* Reuses classes from TabbedView for simplicity, non-interactive */ } -
-
- - { _t("General") } -
-
- - { _t("Security & Privacy") } -
-
- - { _t("Roles & Permissions") } -
-
- -

{ _t("This makes it easy for rooms to stay private to a space, " + - "while letting people in the space find and join them. " + - "All new rooms in a space will have this option available.") }

- , - button: _t("OK"), - hasCloseButton: false, - fixedWidth: true, - }); - }, - rejectLabel: _t("Skip"), - onReject: () => { - localStorage.setItem(lsKey, "true"); - ToastStore.sharedInstance().dismissToast(toastKey); - }, - }, - component: GenericToast, - priority: 35, - }); - } - if (space) { this.loadSuggestedRooms(space); } From 21e93febb6c5447ba1d23ed0e12aedcb7d59ec6f Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 29 Sep 2021 11:54:03 +0100 Subject: [PATCH 64/65] i18n --- src/i18n/strings/en_EN.json | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index bc45caedb5..62cd130110 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -773,16 +773,6 @@ "The person who invited you already left the room.": "The person who invited you already left the room.", "The person who invited you already left the room, or their server is offline.": "The person who invited you already left the room, or their server is offline.", "Failed to join room": "Failed to join room", - "New in the Spaces beta": "New in the Spaces beta", - "Help people in spaces to find and join private rooms": "Help people in spaces to find and join private rooms", - "Learn more": "Learn more", - "Help space members find private rooms": "Help space members find private rooms", - "To help space members find and join a private room, go to that room's Security & Privacy settings.": "To help space members find and join a private room, go to that room's Security & Privacy settings.", - "General": "General", - "Security & Privacy": "Security & Privacy", - "Roles & Permissions": "Roles & Permissions", - "This makes it easy for rooms to stay private to a space, while letting people in the space find and join them. All new rooms in a space will have this option available.": "This makes it easy for rooms to stay private to a space, while letting people in the space find and join them. All new rooms in a space will have this option available.", - "Skip": "Skip", "You joined the call": "You joined the call", "%(senderName)s joined the call": "%(senderName)s joined the call", "Call in progress": "Call in progress", @@ -1056,6 +1046,7 @@ "Invite people": "Invite people", "Invite with email or username": "Invite with email or username", "Failed to save space settings.": "Failed to save space settings.", + "General": "General", "Edit settings relating to your space.": "Edit settings relating to your space.", "Saving...": "Saving...", "Save Changes": "Save Changes", @@ -1465,6 +1456,7 @@ "Muted Users": "Muted Users", "Banned users": "Banned users", "Send %(eventType)s events": "Send %(eventType)s events", + "Roles & Permissions": "Roles & Permissions", "Permissions": "Permissions", "Select the roles required to change various parts of the space": "Select the roles required to change various parts of the space", "Select the roles required to change various parts of the room": "Select the roles required to change various parts of the room", @@ -1487,6 +1479,7 @@ "Changes to who can read history will only apply to future messages in this room. The visibility of existing history will be unchanged.": "Changes to who can read history will only apply to future messages in this room. The visibility of existing history will be unchanged.", "People with supported clients will be able to join the room without having a registered account.": "People with supported clients will be able to join the room without having a registered account.", "Who can read history?": "Who can read history?", + "Security & Privacy": "Security & Privacy", "Once enabled, encryption cannot be disabled.": "Once enabled, encryption cannot be disabled.", "Encrypted": "Encrypted", "Access": "Access", @@ -2211,6 +2204,7 @@ "People you know on %(brand)s": "People you know on %(brand)s", "Hide": "Hide", "Show": "Show", + "Skip": "Skip", "Send %(count)s invites|other": "Send %(count)s invites", "Send %(count)s invites|one": "Send %(count)s invite", "Invite people to join %(communityName)s": "Invite people to join %(communityName)s", @@ -2532,6 +2526,7 @@ "We call the places where you can host your account ‘homeservers’.": "We call the places where you can host your account ‘homeservers’.", "Other homeserver": "Other homeserver", "Use your preferred Matrix homeserver if you have one, or host your own.": "Use your preferred Matrix homeserver if you have one, or host your own.", + "Learn more": "Learn more", "About homeservers": "About homeservers", "Reset event store?": "Reset event store?", "You most likely do not want to reset your event index store": "You most likely do not want to reset your event index store", From d7aaeb37b946632d0197aed502254ac085108b35 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 29 Sep 2021 14:05:31 +0100 Subject: [PATCH 65/65] delint --- src/stores/{SpaceStore.tsx => SpaceStore.ts} | 8 -------- 1 file changed, 8 deletions(-) rename src/stores/{SpaceStore.tsx => SpaceStore.ts} (98%) diff --git a/src/stores/SpaceStore.tsx b/src/stores/SpaceStore.ts similarity index 98% rename from src/stores/SpaceStore.tsx rename to src/stores/SpaceStore.ts index 30df579fdf..6b2d06c369 100644 --- a/src/stores/SpaceStore.tsx +++ b/src/stores/SpaceStore.ts @@ -14,13 +14,11 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React from "react"; import { ListIteratee, Many, sortBy, throttle } from "lodash"; import { EventType, RoomType } from "matrix-js-sdk/src/@types/event"; import { Room } from "matrix-js-sdk/src/models/room"; import { MatrixEvent } from "matrix-js-sdk/src/models/event"; import { IHierarchyRoom } from "matrix-js-sdk/src/@types/spaces"; -import { JoinRule } from "matrix-js-sdk/src/@types/partials"; import { IRoomCapability } from "matrix-js-sdk/src/client"; import { AsyncStoreWithClient } from "./AsyncStoreWithClient"; @@ -41,12 +39,6 @@ import { arrayHasDiff, arrayHasOrderChange } from "../utils/arrays"; import { objectDiff } from "../utils/objects"; import { reorderLexicographically } from "../utils/stringOrderField"; import { TAG_ORDER } from "../components/views/rooms/RoomList"; -import { shouldShowSpaceSettings } from "../utils/space"; -import ToastStore from "./ToastStore"; -import { _t } from "../languageHandler"; -import GenericToast from "../components/views/toasts/GenericToast"; -import Modal from "../Modal"; -import InfoDialog from "../components/views/dialogs/InfoDialog"; import { SettingUpdatedPayload } from "../dispatcher/payloads/SettingUpdatedPayload"; type SpaceKey = string | symbol;