From 6868478044e8eb4eccfa15b279d9193499907891 Mon Sep 17 00:00:00 2001 From: Germain Souquet Date: Thu, 8 Jul 2021 11:30:56 +0200 Subject: [PATCH 01/93] Add threaded messaging feature flag --- src/i18n/strings/en_EN.json | 1 + src/settings/Settings.tsx | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index bbf6954435..eb82c1b533 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -808,6 +808,7 @@ "Render LaTeX maths in messages": "Render LaTeX maths in messages", "Communities v2 prototypes. Requires compatible homeserver. Highly experimental - use with caution.": "Communities v2 prototypes. Requires compatible homeserver. Highly experimental - use with caution.", "Message Pinning": "Message Pinning", + "Threaded messaging": "Threaded messaging", "Custom user status messages": "Custom user status messages", "Group & filter rooms by custom tags (refresh to apply changes)": "Group & filter rooms by custom tags (refresh to apply changes)", "Render simple counters in room header": "Render simple counters in room header", diff --git a/src/settings/Settings.tsx b/src/settings/Settings.tsx index 1751eddb2c..2224d1bbd5 100644 --- a/src/settings/Settings.tsx +++ b/src/settings/Settings.tsx @@ -239,6 +239,12 @@ export const SETTINGS: {[setting: string]: ISetting} = { supportedLevels: LEVELS_FEATURE, default: false, }, + "feature_threading": { + isFeature: true, + displayName: _td("Threaded messaging"), + supportedLevels: LEVELS_FEATURE, + default: false, + }, "feature_custom_status": { isFeature: true, displayName: _td("Custom user status messages"), From cf3117bd57ee298fa84cc0b0dea6eba7804abc4a Mon Sep 17 00:00:00 2001 From: Germain Souquet Date: Thu, 8 Jul 2021 11:55:31 +0200 Subject: [PATCH 02/93] Migrate ViewSourceEvent to TypeScript --- ...ViewSourceEvent.js => ViewSourceEvent.tsx} | 28 ++++++++++--------- 1 file changed, 15 insertions(+), 13 deletions(-) rename src/components/views/messages/{ViewSourceEvent.js => ViewSourceEvent.tsx} (85%) diff --git a/src/components/views/messages/ViewSourceEvent.js b/src/components/views/messages/ViewSourceEvent.tsx similarity index 85% rename from src/components/views/messages/ViewSourceEvent.js rename to src/components/views/messages/ViewSourceEvent.tsx index 62454fef1a..d8b558c4a1 100644 --- a/src/components/views/messages/ViewSourceEvent.js +++ b/src/components/views/messages/ViewSourceEvent.tsx @@ -15,27 +15,29 @@ limitations under the License. */ import React from 'react'; -import PropTypes from 'prop-types'; import classNames from 'classnames'; import { replaceableComponent } from "../../../utils/replaceableComponent"; import { MatrixClientPeg } from "../../../MatrixClientPeg"; +import { MatrixEvent } from '../../../../../matrix-js-sdk/src'; + +interface IProps { + mxEvent: MatrixEvent; +} + +interface IState { + expanded: boolean; +} @replaceableComponent("views.messages.ViewSourceEvent") -export default class ViewSourceEvent extends React.PureComponent { - static propTypes = { - /* the MatrixEvent to show */ - mxEvent: PropTypes.object.isRequired, - }; - - constructor(props) { +export default class ViewSourceEvent extends React.PureComponent { + constructor(props: IProps) { super(props); - this.state = { expanded: false, }; } - componentDidMount() { + public componentDidMount(): void { const { mxEvent } = this.props; const client = MatrixClientPeg.get(); @@ -46,15 +48,15 @@ export default class ViewSourceEvent extends React.PureComponent { } } - onToggle = (ev) => { + private onToggle = (ev: React.MouseEvent): void => { ev.preventDefault(); const { expanded } = this.state; this.setState({ expanded: !expanded, }); - } + }; - render() { + public render(): React.ReactNode { const { mxEvent } = this.props; const { expanded } = this.state; From f57bffad3c5bf3ecfdc17c93a75ce814ab2e35ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Fri, 6 Aug 2021 09:05:33 +0200 Subject: [PATCH 03/93] Soften border color MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/css/views/rooms/_EventTile.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/css/views/rooms/_EventTile.scss b/res/css/views/rooms/_EventTile.scss index 1c9d8e87d9..47512c3094 100644 --- a/res/css/views/rooms/_EventTile.scss +++ b/res/css/views/rooms/_EventTile.scss @@ -510,7 +510,7 @@ $hover-select-border: 4px; .mx_EventTile:hover .mx_EventTile_body pre, .mx_EventTile.focus-visible:focus-within .mx_EventTile_body pre { - border: 1px solid #e5e5e5; // deliberate constant as we're behind an invert filter + border: 1px solid $tertiary-fg-color; // deliberate constant as we're behind an invert filter } .mx_EventTile_pre_container { From 3c0ac487da0a0f2f9c2d5d0ae4e2d074003c701e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Fri, 6 Aug 2021 09:06:24 +0200 Subject: [PATCH 04/93] Remove comment MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/css/views/rooms/_EventTile.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/css/views/rooms/_EventTile.scss b/res/css/views/rooms/_EventTile.scss index 47512c3094..6192fc4df9 100644 --- a/res/css/views/rooms/_EventTile.scss +++ b/res/css/views/rooms/_EventTile.scss @@ -510,7 +510,7 @@ $hover-select-border: 4px; .mx_EventTile:hover .mx_EventTile_body pre, .mx_EventTile.focus-visible:focus-within .mx_EventTile_body pre { - border: 1px solid $tertiary-fg-color; // deliberate constant as we're behind an invert filter + border: 1px solid $tertiary-fg-color; } .mx_EventTile_pre_container { From d9718027892e0932ebf84ab10652c5d616e79f7b Mon Sep 17 00:00:00 2001 From: Germain Souquet Date: Tue, 10 Aug 2021 14:30:12 +0200 Subject: [PATCH 05/93] Create ThreadView phase in RightPanel --- res/css/views/messages/_MessageActionBar.scss | 4 + src/components/structures/RightPanel.tsx | 9 ++ src/components/structures/ThreadView.tsx | 90 +++++++++++++++++++ .../views/messages/MessageActionBar.js | 33 +++++-- src/i18n/strings/en_EN.json | 1 + src/stores/RightPanelStorePhases.ts | 3 + 6 files changed, 134 insertions(+), 6 deletions(-) create mode 100644 src/components/structures/ThreadView.tsx diff --git a/res/css/views/messages/_MessageActionBar.scss b/res/css/views/messages/_MessageActionBar.scss index 69f3c672b7..509ded8ee8 100644 --- a/res/css/views/messages/_MessageActionBar.scss +++ b/res/css/views/messages/_MessageActionBar.scss @@ -92,6 +92,10 @@ limitations under the License. mask-image: url('$(res)/img/element-icons/room/message-bar/reply.svg'); } +.mx_MessageActionBar_threadButton::after { + mask-image: url('$(res)/img/element-icons/room/files.svg'); +} + .mx_MessageActionBar_editButton::after { mask-image: url('$(res)/img/element-icons/room/message-bar/edit.svg'); } diff --git a/src/components/structures/RightPanel.tsx b/src/components/structures/RightPanel.tsx index 95d70e913a..f2bd7db0f3 100644 --- a/src/components/structures/RightPanel.tsx +++ b/src/components/structures/RightPanel.tsx @@ -45,6 +45,7 @@ import GroupRoomInfo from "../views/groups/GroupRoomInfo"; import UserInfo from "../views/right_panel/UserInfo"; import ThirdPartyMemberInfo from "../views/rooms/ThirdPartyMemberInfo"; import FilePanel from "./FilePanel"; +import ThreadView from "./ThreadView"; import NotificationPanel from "./NotificationPanel"; import ResizeNotifier from "../../utils/ResizeNotifier"; import PinnedMessagesCard from "../views/right_panel/PinnedMessagesCard"; @@ -309,6 +310,14 @@ export default class RightPanel extends React.Component { panel = ; break; + case RightPanelPhases.ThreadView: + panel = ; + break; + case RightPanelPhases.RoomSummary: panel = ; break; diff --git a/src/components/structures/ThreadView.tsx b/src/components/structures/ThreadView.tsx new file mode 100644 index 0000000000..8b196cb4f0 --- /dev/null +++ b/src/components/structures/ThreadView.tsx @@ -0,0 +1,90 @@ +/* +Copyright 2016 OpenMarket Ltd +Copyright 2019 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import React from 'react'; +import { MatrixEvent } from 'matrix-js-sdk/src'; + +import BaseCard from "../views/right_panel/BaseCard"; +import { RightPanelPhases } from "../../stores/RightPanelStorePhases"; +import { replaceableComponent } from "../../utils/replaceableComponent"; +import { MatrixClientPeg } from '../../MatrixClientPeg'; + +import ResizeNotifier from '../../utils/ResizeNotifier'; +import EventTile from '../views/rooms/EventTile'; +import MessageComposer from '../views/rooms/MessageComposer'; + +interface IProps { + roomId: string; + onClose: () => void; + resizeNotifier: ResizeNotifier; + mxEvent: MatrixEvent; +} + +interface IState { +} + +/* + * Component which shows the filtered file using a TimelinePanel + */ +@replaceableComponent("structures.ThreadView") +class ThreadView extends React.Component { + state = {}; + + public componentDidMount(): void {} + + public componentWillUnmount(): void {} + + public renderEventTile(event: MatrixEvent): JSX.Element { + return ; + } + + public render() { + const client = MatrixClientPeg.get(); + const room = client.getRoom(this.props.roomId); + const thread = room.getThread(this.props.mxEvent.getId()); + return ( + + { this.renderEventTile(this.props.mxEvent) } + + { thread && ( + thread.eventTimeline.map((event: MatrixEvent) => { + return this.renderEventTile(event); + }) + ) } + + + + ); + } +} + +export default ThreadView; diff --git a/src/components/views/messages/MessageActionBar.js b/src/components/views/messages/MessageActionBar.js index 7fe0eca697..25f3dc740f 100644 --- a/src/components/views/messages/MessageActionBar.js +++ b/src/components/views/messages/MessageActionBar.js @@ -23,6 +23,8 @@ import { EventStatus } from 'matrix-js-sdk/src/models/event'; import { _t } from '../../../languageHandler'; import * as sdk from '../../../index'; import dis from '../../../dispatcher/dispatcher'; +import { Action } from '../../../dispatcher/actions'; +import { RightPanelPhases } from '../../../stores/RightPanelStorePhases'; import { aboveLeftOf, ContextMenu, ContextMenuTooltipButton, useContextMenu } from '../../structures/ContextMenu'; import { isContentActionable, canEditContent } from '../../../utils/EventUtils'; import RoomContext from "../../../contexts/RoomContext"; @@ -170,6 +172,17 @@ export default class MessageActionBar extends React.PureComponent { }); }; + onThreadClick = () => { + dis.dispatch({ + action: Action.SetRightPanelPhase, + phase: RightPanelPhases.ThreadView, + allowClose: false, + refireParams: { + event: this.props.mxEvent, + } + }); + } + onEditClick = (ev) => { dis.dispatch({ action: 'edit_event', @@ -254,12 +267,20 @@ export default class MessageActionBar extends React.PureComponent { // The only catch is we do the reply button first so that we can make sure the react // button is the very first button without having to do length checks for `splice()`. if (this.context.canReply) { - toolbarOpts.splice(0, 0, ); + toolbarOpts.splice(0, 0, <> + + + ); } if (this.context.canReact) { toolbarOpts.splice(0, 0, Date: Wed, 11 Aug 2021 17:54:18 +0200 Subject: [PATCH 06/93] Add ignoreSelfEvent MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- .../algorithms/tag-sorting/RecentAlgorithm.ts | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/stores/room-list/algorithms/tag-sorting/RecentAlgorithm.ts b/src/stores/room-list/algorithms/tag-sorting/RecentAlgorithm.ts index f47458d1b1..23fff70eef 100644 --- a/src/stores/room-list/algorithms/tag-sorting/RecentAlgorithm.ts +++ b/src/stores/room-list/algorithms/tag-sorting/RecentAlgorithm.ts @@ -20,6 +20,25 @@ import { IAlgorithm } from "./IAlgorithm"; import { MatrixClientPeg } from "../../../../MatrixClientPeg"; import * as Unread from "../../../../Unread"; import { EffectiveMembership, getEffectiveMembership } from "../../../../utils/membership"; +import { EventType } from "matrix-js-sdk/src/@types/event"; +import { MatrixEvent } from "matrix-js-sdk/src/models/event"; + +const ignoreSelfEvent = (event: MatrixEvent): boolean => { + const type = event.getType(); + const content = event.getContent(); + const prevContent = event.getPrevContent(); + + // Never ignore membership changes + if (type === EventType.RoomMember && prevContent.membership !== content.membership) return false; + + // Ignore status changes + // XXX: This should be an enum + if (type === "im.vector.user_status") return true; + // Ignore display name changes + if (type === EventType.RoomMember && prevContent.displayname !== content.displayname) return true; + + return false; +}; export const sortRooms = (rooms: Room[]): Room[] => { // We cache the timestamp lookup to avoid iterating forever on the timeline From 1f98ea4562844a64157e9629b184903bc017a068 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Wed, 11 Aug 2021 17:54:27 +0200 Subject: [PATCH 07/93] Use ignoreSelfEvent MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- .../room-list/algorithms/tag-sorting/RecentAlgorithm.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/stores/room-list/algorithms/tag-sorting/RecentAlgorithm.ts b/src/stores/room-list/algorithms/tag-sorting/RecentAlgorithm.ts index 23fff70eef..539cfde726 100644 --- a/src/stores/room-list/algorithms/tag-sorting/RecentAlgorithm.ts +++ b/src/stores/room-list/algorithms/tag-sorting/RecentAlgorithm.ts @@ -87,7 +87,10 @@ export const sortRooms = (rooms: Room[]): Room[] => { const ev = r.timeline[i]; if (!ev.getTs()) continue; // skip events that don't have timestamps (tests only?) - if (ev.getSender() === myUserId || Unread.eventTriggersUnreadCount(ev)) { + if ( + (ev.getSender() === myUserId && !ignoreSelfEvent(ev)) || + Unread.eventTriggersUnreadCount(ev) + ) { return ev.getTs(); } } From f2dccbc9f7bc58ee46609c31650d3f18af6cde28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Thu, 12 Aug 2021 09:38:57 +0200 Subject: [PATCH 08/93] Use function syntax MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- .../room-list/algorithms/tag-sorting/RecentAlgorithm.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/stores/room-list/algorithms/tag-sorting/RecentAlgorithm.ts b/src/stores/room-list/algorithms/tag-sorting/RecentAlgorithm.ts index 539cfde726..5ddfd96c8a 100644 --- a/src/stores/room-list/algorithms/tag-sorting/RecentAlgorithm.ts +++ b/src/stores/room-list/algorithms/tag-sorting/RecentAlgorithm.ts @@ -23,7 +23,7 @@ import { EffectiveMembership, getEffectiveMembership } from "../../../../utils/m import { EventType } from "matrix-js-sdk/src/@types/event"; import { MatrixEvent } from "matrix-js-sdk/src/models/event"; -const ignoreSelfEvent = (event: MatrixEvent): boolean => { +export function ignoreSelfEvent(event: MatrixEvent): boolean { const type = event.getType(); const content = event.getContent(); const prevContent = event.getPrevContent(); @@ -38,7 +38,7 @@ const ignoreSelfEvent = (event: MatrixEvent): boolean => { if (type === EventType.RoomMember && prevContent.displayname !== content.displayname) return true; return false; -}; +} export const sortRooms = (rooms: Room[]): Room[] => { // We cache the timestamp lookup to avoid iterating forever on the timeline From 0a4a1506d412cdec610196af73fca6401fead15b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Thu, 12 Aug 2021 09:40:26 +0200 Subject: [PATCH 09/93] Use ignoreSelfEvent in the RoomListStore (which doesn't work!) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/stores/room-list/RoomListStore.ts | 4 ++++ .../room-list/algorithms/tag-sorting/RecentAlgorithm.ts | 5 +---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/stores/room-list/RoomListStore.ts b/src/stores/room-list/RoomListStore.ts index 1a5ef0484e..6a80bcd1e5 100644 --- a/src/stores/room-list/RoomListStore.ts +++ b/src/stores/room-list/RoomListStore.ts @@ -38,6 +38,7 @@ import { SpaceWatcher } from "./SpaceWatcher"; import SpaceStore from "../SpaceStore"; import { Action } from "../../dispatcher/actions"; import { SettingUpdatedPayload } from "../../dispatcher/payloads/SettingUpdatedPayload"; +import { ignoreSelfEvent } from "./algorithms/tag-sorting/RecentAlgorithm"; interface IState { tagsEnabled?: boolean; @@ -270,6 +271,9 @@ export class RoomListStoreClass extends AsyncStoreWithClient { // Ignore non-live events (backfill) if (!eventPayload.isLiveEvent || !payload.isLiveUnfilteredRoomTimelineEvent) return; + // Avoid re-ordering on status, profile etc. changes + if (ignoreSelfEvent(eventPayload.event)) return; + const roomId = eventPayload.event.getRoomId(); const room = this.matrixClient.getRoom(roomId); const tryUpdate = async (updatedRoom: Room) => { diff --git a/src/stores/room-list/algorithms/tag-sorting/RecentAlgorithm.ts b/src/stores/room-list/algorithms/tag-sorting/RecentAlgorithm.ts index 5ddfd96c8a..bbfac876bc 100644 --- a/src/stores/room-list/algorithms/tag-sorting/RecentAlgorithm.ts +++ b/src/stores/room-list/algorithms/tag-sorting/RecentAlgorithm.ts @@ -87,10 +87,7 @@ export const sortRooms = (rooms: Room[]): Room[] => { const ev = r.timeline[i]; if (!ev.getTs()) continue; // skip events that don't have timestamps (tests only?) - if ( - (ev.getSender() === myUserId && !ignoreSelfEvent(ev)) || - Unread.eventTriggersUnreadCount(ev) - ) { + if (ev.getSender() === myUserId || Unread.eventTriggersUnreadCount(ev)) { return ev.getTs(); } } From e5024c4b71b2552750edf337e212618795b53a7e Mon Sep 17 00:00:00 2001 From: Germain Souquet Date: Tue, 17 Aug 2021 10:38:09 +0100 Subject: [PATCH 10/93] Adapt threading UI to new backend --- src/components/structures/RightPanel.tsx | 13 ++- src/components/structures/RoomView.tsx | 5 +- src/components/structures/ThreadPanel.tsx | 106 ++++++++++++++++++ src/components/structures/ThreadView.tsx | 22 +++- .../views/right_panel/RoomSummaryCard.tsx | 10 ++ src/i18n/strings/en_EN.json | 1 + src/stores/RightPanelStorePhases.ts | 1 + 7 files changed, 150 insertions(+), 8 deletions(-) create mode 100644 src/components/structures/ThreadPanel.tsx diff --git a/src/components/structures/RightPanel.tsx b/src/components/structures/RightPanel.tsx index f2bd7db0f3..85d3013a58 100644 --- a/src/components/structures/RightPanel.tsx +++ b/src/components/structures/RightPanel.tsx @@ -46,17 +46,20 @@ import UserInfo from "../views/right_panel/UserInfo"; import ThirdPartyMemberInfo from "../views/rooms/ThirdPartyMemberInfo"; import FilePanel from "./FilePanel"; import ThreadView from "./ThreadView"; +import ThreadPanel from "./ThreadPanel"; import NotificationPanel from "./NotificationPanel"; import ResizeNotifier from "../../utils/ResizeNotifier"; import PinnedMessagesCard from "../views/right_panel/PinnedMessagesCard"; import { throttle } from 'lodash'; import SpaceStore from "../../stores/SpaceStore"; +import { RoomPermalinkCreator } from '../../utils/permalinks/Permalinks'; 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 resizeNotifier: ResizeNotifier; + permalinkCreator?: RoomPermalinkCreator; } interface IState { @@ -315,7 +318,15 @@ export default class RightPanel extends React.Component { roomId={roomId} resizeNotifier={this.props.resizeNotifier} onClose={this.onClose} - mxEvent={this.state.event} />; + mxEvent={this.state.event} + permalinkCreator={this.props.permalinkCreator} />; + break; + + case RightPanelPhases.ThreadPanel: + panel = ; break; case RightPanelPhases.RoomSummary: diff --git a/src/components/structures/RoomView.tsx b/src/components/structures/RoomView.tsx index 474b99262d..baf557ee18 100644 --- a/src/components/structures/RoomView.tsx +++ b/src/components/structures/RoomView.tsx @@ -2052,7 +2052,10 @@ export default class RoomView extends React.Component { const showRightPanel = this.state.room && this.state.showRightPanel; const rightPanel = showRightPanel - ? + ? : null; const timelineClasses = classNames("mx_RoomView_timeline", { diff --git a/src/components/structures/ThreadPanel.tsx b/src/components/structures/ThreadPanel.tsx new file mode 100644 index 0000000000..c99246ccda --- /dev/null +++ b/src/components/structures/ThreadPanel.tsx @@ -0,0 +1,106 @@ +/* +Copyright 2016 OpenMarket Ltd +Copyright 2019 The Matrix.org Foundation C.I.C. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import React from 'react'; +import { MatrixEvent, Room } from 'matrix-js-sdk/src'; + +import BaseCard from "../views/right_panel/BaseCard"; +import { RightPanelPhases } from "../../stores/RightPanelStorePhases"; +import { replaceableComponent } from "../../utils/replaceableComponent"; +import { MatrixClientPeg } from '../../MatrixClientPeg'; + +import ResizeNotifier from '../../utils/ResizeNotifier'; +import EventTile from '../views/rooms/EventTile'; +import { Thread } from '../../../../matrix-js-sdk/src/models/thread'; + +interface IProps { + roomId: string; + onClose: () => void; + resizeNotifier: ResizeNotifier; +} + +interface IState { + threads?: Thread[]; +} + +/* + * Component which shows the filtered file using a TimelinePanel + */ +@replaceableComponent("structures.ThreadView") +class ThreadView extends React.Component { + private room: Room; + + constructor(props: IProps) { + super(props); + this.room = MatrixClientPeg.get().getRoom(this.props.roomId); + } + + public componentDidMount(): void { + this.room.on("Thread.update", this.onThreadEventReceived); + this.room.on("Thread.ready", this.onThreadEventReceived); + this.updateThreads(() => { + this.state.threads.forEach(thread => { + if (!thread.ready) { + thread.fetchReplyChain(); + } + }); + }); + } + + public componentWillUnmount(): void { + this.room.removeListener("Thread.update", this.onThreadEventReceived); + this.room.removeListener("Thread.ready", this.onThreadEventReceived); + } + + public onThreadEventReceived = () => this.updateThreads(); + + public updateThreads = (callback?: () => void): void => { + this.setState({ + threads: this.room.getThreads(), + }, callback); + }; + + public renderEventTile(event: MatrixEvent): JSX.Element { + return ; + } + + public render() { + return ( + + { + this.state?.threads.map((thread: Thread) => { + if (thread.ready) { + return this.renderEventTile(thread.rootEvent); + } + }) + } + + ); + } +} + +export default ThreadView; diff --git a/src/components/structures/ThreadView.tsx b/src/components/structures/ThreadView.tsx index 8b196cb4f0..0f9d499884 100644 --- a/src/components/structures/ThreadView.tsx +++ b/src/components/structures/ThreadView.tsx @@ -26,12 +26,14 @@ import { MatrixClientPeg } from '../../MatrixClientPeg'; import ResizeNotifier from '../../utils/ResizeNotifier'; import EventTile from '../views/rooms/EventTile'; import MessageComposer from '../views/rooms/MessageComposer'; +import { RoomPermalinkCreator } from '../../utils/permalinks/Permalinks'; interface IProps { roomId: string; onClose: () => void; resizeNotifier: ResizeNotifier; mxEvent: MatrixEvent; + permalinkCreator?: RoomPermalinkCreator; } interface IState { @@ -44,9 +46,18 @@ interface IState { class ThreadView extends React.Component { state = {}; - public componentDidMount(): void {} + public componentDidMount(): void { + // this.props.mxEvent.getThread().on("Thread.update", this.updateThread); + this.props.mxEvent.getThread().once("Thread.ready", this.updateThread); + } - public componentWillUnmount(): void {} + public componentWillUnmount(): void { + this.props.mxEvent.getThread().removeListener("Thread.update", this.updateThread); + } + + updateThread = () => { + this.forceUpdate(); + }; public renderEventTile(event: MatrixEvent): JSX.Element { return { } public render() { - const client = MatrixClientPeg.get(); - const room = client.getRoom(this.props.roomId); - const thread = room.getThread(this.props.mxEvent.getId()); + const thread = this.props.mxEvent.getThread(); + const room = MatrixClientPeg.get().getRoom(this.props.roomId); return ( { room={room} resizeNotifier={this.props.resizeNotifier} replyToEvent={this.props.mxEvent} - permalinkCreator={null} + permalinkCreator={this.props.permalinkCreator} /> ); diff --git a/src/components/views/right_panel/RoomSummaryCard.tsx b/src/components/views/right_panel/RoomSummaryCard.tsx index 047448d925..eb3d7499f4 100644 --- a/src/components/views/right_panel/RoomSummaryCard.tsx +++ b/src/components/views/right_panel/RoomSummaryCard.tsx @@ -220,6 +220,13 @@ const onRoomFilesClick = () => { }); }; +const onRoomThreadsClick = () => { + defaultDispatcher.dispatch({ + action: Action.SetRightPanelPhase, + phase: RightPanelPhases.ThreadPanel, + }); +}; + const onRoomSettingsClick = () => { defaultDispatcher.dispatch({ action: "open_room_settings" }); }; @@ -273,6 +280,9 @@ const RoomSummaryCard: React.FC = ({ room, onClose }) => { + diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index ca55f45619..ec28be664b 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -1790,6 +1790,7 @@ "%(count)s people|other": "%(count)s people", "%(count)s people|one": "%(count)s person", "Show files": "Show files", + "Show threads": "Show threads", "Share room": "Share room", "Room settings": "Room settings", "Trusted": "Trusted", diff --git a/src/stores/RightPanelStorePhases.ts b/src/stores/RightPanelStorePhases.ts index 8f41b17e47..96a585b676 100644 --- a/src/stores/RightPanelStorePhases.ts +++ b/src/stores/RightPanelStorePhases.ts @@ -40,6 +40,7 @@ export enum RightPanelPhases { // Thread stuff ThreadView = "ThreadView", + ThreadPanel = "ThreadPanel", } // These are the phases that are safe to persist (the ones that don't require additional From d1dbfbd014094554a6d3e25e37a5ff33708708f1 Mon Sep 17 00:00:00 2001 From: Germain Souquet Date: Tue, 17 Aug 2021 11:10:02 +0100 Subject: [PATCH 11/93] hide thread events from the timeline --- res/css/views/rooms/_EventTile.scss | 30 +++++++++++++++ res/img/element-icons/message/thread.svg | 4 ++ src/components/structures/MessagePanel.tsx | 7 ++++ src/components/structures/ThreadView.tsx | 29 +++++++++----- src/components/structures/TimelinePanel.tsx | 4 ++ src/components/views/rooms/EventTile.tsx | 38 +++++++++++++++++++ .../views/rooms/MessageComposer.tsx | 9 ++++- 7 files changed, 110 insertions(+), 11 deletions(-) create mode 100644 res/img/element-icons/message/thread.svg diff --git a/res/css/views/rooms/_EventTile.scss b/res/css/views/rooms/_EventTile.scss index 1c9d8e87d9..1adfc0cde3 100644 --- a/res/css/views/rooms/_EventTile.scss +++ b/res/css/views/rooms/_EventTile.scss @@ -670,3 +670,33 @@ $hover-select-border: 4px; margin-right: 0; } } + + +.mx_ThreadView { + + display: flex; + flex-direction: column; + + .mx_ThreadView_List { + flex: 1; + overflow: scroll; + } + + .mx_EventTile_roomName { + display: none; + } + + .mx_EventTile_line { + padding-left: 0 !important; + order: 10 !important; + } + .mx_EventTile { + width: 100%; + display: flex; + flex-direction: column; + border-bottom: 1px solid #888; + margin-top: 0; + padding-bottom: 5px; + margin-bottom: 5px; + } +} diff --git a/res/img/element-icons/message/thread.svg b/res/img/element-icons/message/thread.svg new file mode 100644 index 0000000000..b4a7cc0066 --- /dev/null +++ b/res/img/element-icons/message/thread.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/components/structures/MessagePanel.tsx b/src/components/structures/MessagePanel.tsx index 1691d90651..15275328a6 100644 --- a/src/components/structures/MessagePanel.tsx +++ b/src/components/structures/MessagePanel.tsx @@ -173,6 +173,8 @@ interface IProps { onUnfillRequest?(backwards: boolean, scrollToken: string): void; getRelationsForEvent?(eventId: string, relationType: string, eventType: string): Relations; + + hideThreadedMessages?: boolean; } interface IState { @@ -443,6 +445,11 @@ export default class MessagePanel extends React.Component { // Always show highlighted event if (this.props.highlightedEventId === mxEv.getId()) return true; + const threadingEnabled = SettingsStore.getValue("feature_threading"); + if (threadingEnabled && mxEv.replyEventId && this.props.hideThreadedMessages === true) { + return false; + } + return !shouldHideEvent(mxEv, this.context); } diff --git a/src/components/structures/ThreadView.tsx b/src/components/structures/ThreadView.tsx index 0f9d499884..f34be87c95 100644 --- a/src/components/structures/ThreadView.tsx +++ b/src/components/structures/ThreadView.tsx @@ -24,9 +24,11 @@ import { replaceableComponent } from "../../utils/replaceableComponent"; import { MatrixClientPeg } from '../../MatrixClientPeg'; import ResizeNotifier from '../../utils/ResizeNotifier'; -import EventTile from '../views/rooms/EventTile'; +import EventTile, { TileShape } from '../views/rooms/EventTile'; import MessageComposer from '../views/rooms/MessageComposer'; import { RoomPermalinkCreator } from '../../utils/permalinks/Permalinks'; +import { Layout } from '../../settings/Layout'; +import TimelinePanel from './TimelinePanel'; interface IProps { roomId: string; @@ -37,6 +39,7 @@ interface IProps { } interface IState { + replyToEvent?: MatrixEvent; } /* @@ -65,6 +68,7 @@ class ThreadView extends React.Component { mxEvent={event} enableFlair={false} showReadReceipts={false} + tileShape={TileShape.FileGrid} as="div" />; } @@ -77,19 +81,24 @@ class ThreadView extends React.Component { className="mx_ThreadView" onClose={this.props.onClose} previousPhase={RightPanelPhases.RoomSummary} + withoutScrollContainer={true} > - { this.renderEventTile(this.props.mxEvent) } - - { thread && ( - thread.eventTimeline.map((event: MatrixEvent) => { - return this.renderEventTile(event); - }) - ) } - + empty} + alwaysShowTimestamps={true} + layout={Layout.Group} + hideThreadedMessages={false} + /> diff --git a/src/components/structures/TimelinePanel.tsx b/src/components/structures/TimelinePanel.tsx index f62676a4fc..e5fa6967dc 100644 --- a/src/components/structures/TimelinePanel.tsx +++ b/src/components/structures/TimelinePanel.tsx @@ -126,6 +126,8 @@ interface IProps { // callback which is called when we wish to paginate the timeline window. onPaginationRequest?(timelineWindow: TimelineWindow, direction: string, size: number): Promise; + + hideThreadedMessages?: boolean; } interface IState { @@ -214,6 +216,7 @@ class TimelinePanel extends React.Component { timelineCap: Number.MAX_VALUE, className: 'mx_RoomView_messagePanel', sendReadReceiptOnLoad: true, + hideThreadedMessages: true, }; private lastRRSentEventId: string = undefined; @@ -1511,6 +1514,7 @@ class TimelinePanel extends React.Component { showReactions={this.props.showReactions} layout={this.props.layout} enableFlair={SettingsStore.getValue(UIFeature.Flair)} + hideThreadedMessages={this.props.hideThreadedMessages} /> ); } diff --git a/src/components/views/rooms/EventTile.tsx b/src/components/views/rooms/EventTile.tsx index 884d004551..30355e55bf 100644 --- a/src/components/views/rooms/EventTile.tsx +++ b/src/components/views/rooms/EventTile.tsx @@ -55,6 +55,7 @@ import ReadReceiptMarker from "./ReadReceiptMarker"; import MessageActionBar from "../messages/MessageActionBar"; import ReactionsRow from '../messages/ReactionsRow'; import { getEventDisplayInfo } from '../../../utils/EventUtils'; +import { RightPanelPhases } from "../../../stores/RightPanelStorePhases"; const eventTileTypes = { [EventType.RoomMessage]: 'messages.MessageEvent', @@ -299,6 +300,9 @@ interface IProps { // whether or not to display the sender hideSender?: boolean; + + // whether or not to display thread info + showThreadInfo?: boolean; } interface IState { @@ -451,6 +455,7 @@ export default class EventTile extends React.Component { client.on("Room.receipt", this.onRoomReceipt); this.isListeningForReceipts = true; } + this.props.mxEvent.on("Thread.update", this.forceUpdate); } // TODO: [REACT-WARNING] Replace with appropriate lifecycle event @@ -491,6 +496,38 @@ export default class EventTile extends React.Component { } } + private renderThreadInfo(): React.ReactNode { + const room = MatrixClientPeg.get().getRoom(this.props.mxEvent.getRoomId()); + const thread = room.getThread(this.props.mxEvent.getId()); + if (!thread || this.props.showThreadInfo === false) { + return null; + } + + const avatars = Array.from(thread.participants).map((mxId: string) => { + const member = room.getMember(mxId); + return ; + }); + + return ( +
{ + dis.dispatch({ + action: Action.SetRightPanelPhase, + phase: RightPanelPhases.ThreadView, + refireParams: { + event: this.props.mxEvent, + }, + }); + }} + > + + { avatars } + + { thread.length } { thread.length === 1 ? 'reply' : 'replies' } +
+ ); + } + private onRoomReceipt = (ev, room) => { // ignore events for other rooms const tileRoom = MatrixClientPeg.get().getRoom(this.props.mxEvent.getRoomId()); @@ -1167,6 +1204,7 @@ export default class EventTile extends React.Component { { keyRequestInfo } { actionBar } { this.props.layout === Layout.IRC && (reactionsRow) } + { this.renderThreadInfo() } { this.props.layout !== Layout.IRC && (reactionsRow) } { msgOption } diff --git a/src/components/views/rooms/MessageComposer.tsx b/src/components/views/rooms/MessageComposer.tsx index 8455e9aa11..bd53c9566a 100644 --- a/src/components/views/rooms/MessageComposer.tsx +++ b/src/components/views/rooms/MessageComposer.tsx @@ -183,6 +183,7 @@ interface IProps { resizeNotifier: ResizeNotifier; permalinkCreator: RoomPermalinkCreator; replyToEvent?: MatrixEvent; + showReplyPreview?: boolean; e2eStatus?: E2EStatus; } @@ -201,6 +202,10 @@ export default class MessageComposer extends React.Component { private messageComposerInput: SendMessageComposer; private voiceRecordingButton: VoiceRecordComposerTile; + static defaultProps = { + showReplyPreview: true, + }; + constructor(props) { super(props); VoiceRecordingStore.instance.on(UPDATE_EVENT, this.onVoiceStoreUpdate); @@ -454,7 +459,9 @@ export default class MessageComposer extends React.Component {
{ recordingTooltip }
- + { this.props.showReplyPreview && ( + + ) }
{ controls }
From 95f4513bd21506665646dd5bfca230add77398f6 Mon Sep 17 00:00:00 2001 From: Germain Souquet Date: Tue, 17 Aug 2021 17:42:47 +0100 Subject: [PATCH 12/93] Make UI respond to thread events --- res/css/views/messages/_MessageActionBar.scss | 4 ++-- .../views/right_panel/_RoomSummaryCard.scss | 4 ++++ src/components/structures/MessagePanel.tsx | 1 + src/components/structures/ThreadPanel.tsx | 7 ------ src/components/structures/ThreadView.tsx | 23 +++++++++++++++---- .../views/right_panel/RoomSummaryCard.tsx | 2 +- src/components/views/rooms/EventTile.tsx | 20 +++++++++++++--- 7 files changed, 44 insertions(+), 17 deletions(-) diff --git a/res/css/views/messages/_MessageActionBar.scss b/res/css/views/messages/_MessageActionBar.scss index 509ded8ee8..3e5ab87ca9 100644 --- a/res/css/views/messages/_MessageActionBar.scss +++ b/res/css/views/messages/_MessageActionBar.scss @@ -89,11 +89,11 @@ limitations under the License. } .mx_MessageActionBar_replyButton::after { - mask-image: url('$(res)/img/element-icons/room/message-bar/reply.svg'); + mask-image: url('$(res)/img/element-icons/room/files.svg'); } .mx_MessageActionBar_threadButton::after { - mask-image: url('$(res)/img/element-icons/room/files.svg'); + mask-image: url('$(res)/img/element-icons/message/thread.svg'); } .mx_MessageActionBar_editButton::after { diff --git a/res/css/views/right_panel/_RoomSummaryCard.scss b/res/css/views/right_panel/_RoomSummaryCard.scss index dc7804d072..d93e593679 100644 --- a/res/css/views/right_panel/_RoomSummaryCard.scss +++ b/res/css/views/right_panel/_RoomSummaryCard.scss @@ -232,6 +232,10 @@ limitations under the License. mask-image: url('$(res)/img/element-icons/room/files.svg'); } +.mx_RoomSummaryCard_icon_threads::before { + mask-image: url('$(res)/img/element-icons/message/thread.svg'); +} + .mx_RoomSummaryCard_icon_share::before { mask-image: url('$(res)/img/element-icons/room/share.svg'); } diff --git a/src/components/structures/MessagePanel.tsx b/src/components/structures/MessagePanel.tsx index 15275328a6..fd716041c2 100644 --- a/src/components/structures/MessagePanel.tsx +++ b/src/components/structures/MessagePanel.tsx @@ -267,6 +267,7 @@ export default class MessagePanel extends React.Component { componentDidMount() { this.calculateRoomMembersCount(); this.props.room?.on("RoomState.members", this.calculateRoomMembersCount); + this.props.room?.getThreads().forEach(thread => thread.fetchReplyChain()); this.isMounted = true; } diff --git a/src/components/structures/ThreadPanel.tsx b/src/components/structures/ThreadPanel.tsx index c99246ccda..f0b85e9723 100644 --- a/src/components/structures/ThreadPanel.tsx +++ b/src/components/structures/ThreadPanel.tsx @@ -52,13 +52,6 @@ class ThreadView extends React.Component { public componentDidMount(): void { this.room.on("Thread.update", this.onThreadEventReceived); this.room.on("Thread.ready", this.onThreadEventReceived); - this.updateThreads(() => { - this.state.threads.forEach(thread => { - if (!thread.ready) { - thread.fetchReplyChain(); - } - }); - }); } public componentWillUnmount(): void { diff --git a/src/components/structures/ThreadView.tsx b/src/components/structures/ThreadView.tsx index f34be87c95..7767e5929e 100644 --- a/src/components/structures/ThreadView.tsx +++ b/src/components/structures/ThreadView.tsx @@ -29,6 +29,7 @@ import MessageComposer from '../views/rooms/MessageComposer'; import { RoomPermalinkCreator } from '../../utils/permalinks/Permalinks'; import { Layout } from '../../settings/Layout'; import TimelinePanel from './TimelinePanel'; +import { Thread } from '../../../../matrix-js-sdk/src/models/thread'; interface IProps { roomId: string; @@ -40,6 +41,7 @@ interface IProps { interface IState { replyToEvent?: MatrixEvent; + thread?: Thread; } /* @@ -50,16 +52,29 @@ class ThreadView extends React.Component { state = {}; public componentDidMount(): void { - // this.props.mxEvent.getThread().on("Thread.update", this.updateThread); - this.props.mxEvent.getThread().once("Thread.ready", this.updateThread); + this.setupThread(); } public componentWillUnmount(): void { - this.props.mxEvent.getThread().removeListener("Thread.update", this.updateThread); + if (this.state.thread) { + this.state.thread.removeListener("Thread.update", this.updateThread); + this.state.thread.removeListener("Thread.ready", this.updateThread); + } } + setupThread = () => { + const thread = this.props.mxEvent.getThread(); + if (thread) { + thread.on("Thread.update", this.updateThread); + thread.once("Thread.ready", this.updateThread); + this.updateThread(); + } + }; + updateThread = () => { - this.forceUpdate(); + this.setState({ + thread: this.props.mxEvent.getThread(), + }); }; public renderEventTile(event: MatrixEvent): JSX.Element { diff --git a/src/components/views/right_panel/RoomSummaryCard.tsx b/src/components/views/right_panel/RoomSummaryCard.tsx index eb3d7499f4..a09fabc06d 100644 --- a/src/components/views/right_panel/RoomSummaryCard.tsx +++ b/src/components/views/right_panel/RoomSummaryCard.tsx @@ -280,7 +280,7 @@ const RoomSummaryCard: React.FC = ({ room, onClose }) => { - - + { SettingsStore.getValue("experimentalThreadSupport") && ( + + ) } diff --git a/src/components/views/rooms/EventTile.tsx b/src/components/views/rooms/EventTile.tsx index 6f807f66d6..74d9fc5ca6 100644 --- a/src/components/views/rooms/EventTile.tsx +++ b/src/components/views/rooms/EventTile.tsx @@ -57,6 +57,7 @@ import ReactionsRow from '../messages/ReactionsRow'; import { getEventDisplayInfo } from '../../../utils/EventUtils'; import { RightPanelPhases } from "../../../stores/RightPanelStorePhases"; import { Thread } from '../../../../../matrix-js-sdk/src/models/thread'; +import SettingsStore from "../../../settings/SettingsStore"; const eventTileTypes = { [EventType.RoomMessage]: 'messages.MessageEvent', @@ -461,8 +462,10 @@ export default class EventTile extends React.Component { this.isListeningForReceipts = true; } - this.props.mxEvent.once("Thread.ready", this.updateThread); - this.props.mxEvent.on("Thread.update", this.updateThread); + if (SettingsStore.getValue("experimentalThreadSupport")) { + this.props.mxEvent.once("Thread.ready", this.updateThread); + this.props.mxEvent.on("Thread.update", this.updateThread); + } } private updateThread = (thread) => { @@ -511,6 +514,10 @@ export default class EventTile extends React.Component { } private renderThreadInfo(): React.ReactNode { + if (!SettingsStore.getValue("experimentalThreadSupport")) { + return null; + } + const thread = this.state.thread; const room = MatrixClientPeg.get().getRoom(this.props.mxEvent.getRoomId()); if (!thread || this.props.showThreadInfo === false) { From 30a762944af85a7134e3a4829d2f95ab53001d6c Mon Sep 17 00:00:00 2001 From: Germain Souquet Date: Fri, 20 Aug 2021 12:11:04 +0100 Subject: [PATCH 15/93] Implement a very low fidelity UI for threads --- res/css/views/rooms/_EventTile.scss | 25 ++++++++++++++++++- res/css/views/rooms/_MessageComposer.scss | 14 +++++++++++ src/components/structures/ThreadView.tsx | 1 + .../views/rooms/MessageComposer.tsx | 12 +++++++-- 4 files changed, 49 insertions(+), 3 deletions(-) diff --git a/res/css/views/rooms/_EventTile.scss b/res/css/views/rooms/_EventTile.scss index 4dd91eb7f2..4b48ec971b 100644 --- a/res/css/views/rooms/_EventTile.scss +++ b/res/css/views/rooms/_EventTile.scss @@ -681,6 +681,30 @@ $hover-select-border: 4px; display: flex; flex-direction: column; + .mx_ScrollPanel { + margin-top: 20px; + .mx_RoomView_MessageList { + padding: 0; + } + } + + .mx_EventTile_senderDetails { + display: flex; + align-items: center; + gap: 6px; + margin-bottom: 6px; + a { + flex: 1; + min-width: none; + max-width: 100%; + display: flex; + align-items: center; + .mx_SenderProfile { + flex: 1; + } + } + } + .mx_ThreadView_List { flex: 1; overflow: scroll; @@ -698,7 +722,6 @@ $hover-select-border: 4px; width: 100%; display: flex; flex-direction: column; - border-bottom: 1px solid #888; margin-top: 0; padding-bottom: 5px; margin-bottom: 5px; diff --git a/res/css/views/rooms/_MessageComposer.scss b/res/css/views/rooms/_MessageComposer.scss index 5e2eff4047..b2747f7a9b 100644 --- a/res/css/views/rooms/_MessageComposer.scss +++ b/res/css/views/rooms/_MessageComposer.scss @@ -340,3 +340,17 @@ limitations under the License. height: 50px; } } + +/** + * Unstable compact mode + */ + +.mx_MessageComposer.mx_MessageComposer--compact { + margin-right: 0; + .mx_MessageComposer_wrapper { + padding: 0; + } + .mx_MessageComposer_button:last-child { + margin-right: 0; + } +} diff --git a/src/components/structures/ThreadView.tsx b/src/components/structures/ThreadView.tsx index 7767e5929e..5d07b7feaa 100644 --- a/src/components/structures/ThreadView.tsx +++ b/src/components/structures/ThreadView.tsx @@ -115,6 +115,7 @@ class ThreadView extends React.Component { replyToEvent={thread?.replyToEvent} showReplyPreview={false} permalinkCreator={this.props.permalinkCreator} + compact={true} /> ); diff --git a/src/components/views/rooms/MessageComposer.tsx b/src/components/views/rooms/MessageComposer.tsx index bd53c9566a..fbf3b58570 100644 --- a/src/components/views/rooms/MessageComposer.tsx +++ b/src/components/views/rooms/MessageComposer.tsx @@ -185,6 +185,7 @@ interface IProps { replyToEvent?: MatrixEvent; showReplyPreview?: boolean; e2eStatus?: E2EStatus; + compact?: boolean; } interface IState { @@ -204,6 +205,7 @@ export default class MessageComposer extends React.Component { static defaultProps = { showReplyPreview: true, + compact: false, }; constructor(props) { @@ -367,7 +369,7 @@ export default class MessageComposer extends React.Component { render() { const controls = [ - this.state.me ? : null, + this.state.me && !this.props.compact ? : null, this.props.e2eStatus ? : null, @@ -455,8 +457,14 @@ export default class MessageComposer extends React.Component { />; } + const classes = classNames({ + "mx_MessageComposer": true, + "mx_GroupLayout": true, + "mx_MessageComposer--compact": this.props.compact, + }); + return ( -
+
{ recordingTooltip }
{ this.props.showReplyPreview && ( From 9facb0d963afa4932f5b48ff3f20aa7128bef30a Mon Sep 17 00:00:00 2001 From: Germain Souquet Date: Mon, 23 Aug 2021 14:44:44 +0100 Subject: [PATCH 16/93] Polish UI --- res/css/views/messages/_MessageActionBar.scss | 2 +- res/css/views/rooms/_EventTile.scss | 4 + src/components/structures/ThreadView.tsx | 88 +++++++++++++------ src/components/views/rooms/EventTile.tsx | 3 +- 4 files changed, 67 insertions(+), 30 deletions(-) diff --git a/res/css/views/messages/_MessageActionBar.scss b/res/css/views/messages/_MessageActionBar.scss index 3e5ab87ca9..c2f3c2ebca 100644 --- a/res/css/views/messages/_MessageActionBar.scss +++ b/res/css/views/messages/_MessageActionBar.scss @@ -89,7 +89,7 @@ limitations under the License. } .mx_MessageActionBar_replyButton::after { - mask-image: url('$(res)/img/element-icons/room/files.svg'); + mask-image: url('$(res)/img/element-icons/room/message-bar/reply.svg'); } .mx_MessageActionBar_threadButton::after { diff --git a/res/css/views/rooms/_EventTile.scss b/res/css/views/rooms/_EventTile.scss index 4b48ec971b..1bf62d3fb1 100644 --- a/res/css/views/rooms/_EventTile.scss +++ b/res/css/views/rooms/_EventTile.scss @@ -676,6 +676,10 @@ $hover-select-border: 4px; } +.mx_ThreadInfo:hover { + cursor: pointer; +} + .mx_ThreadView { display: flex; diff --git a/src/components/structures/ThreadView.tsx b/src/components/structures/ThreadView.tsx index 5d07b7feaa..cac4054dfb 100644 --- a/src/components/structures/ThreadView.tsx +++ b/src/components/structures/ThreadView.tsx @@ -30,6 +30,8 @@ import { RoomPermalinkCreator } from '../../utils/permalinks/Permalinks'; import { Layout } from '../../settings/Layout'; import TimelinePanel from './TimelinePanel'; import { Thread } from '../../../../matrix-js-sdk/src/models/thread'; +import dis from "../../dispatcher/dispatcher"; +import { ActionPayload } from '../../dispatcher/payloads'; interface IProps { roomId: string; @@ -49,32 +51,61 @@ interface IState { */ @replaceableComponent("structures.ThreadView") class ThreadView extends React.Component { - state = {}; + private dispatcherRef: string; + + constructor(props: IProps) { + super(props); + this.state = {}; + } public componentDidMount(): void { - this.setupThread(); + this.setupThread(this.props.mxEvent); + this.dispatcherRef = dis.register(this.onAction); } public componentWillUnmount(): void { + this.teardownThread(); + dis.unregister(this.dispatcherRef); + } + + public componentDidUpdate(prevProps) { + if (prevProps.mxEvent !== this.props.mxEvent) { + this.teardownThread(); + this.setupThread(this.props.mxEvent); + } + } + + private onAction = (payload: ActionPayload): void => { + if (payload.phase == RightPanelPhases.ThreadView && payload.event) { + if (payload.event !== this.props.mxEvent) { + this.teardownThread(); + this.setupThread(payload.event); + } + } + }; + + setupThread = (mxEv: MatrixEvent) => { + const thread = mxEv.getThread(); + if (thread) { + thread.on("Thread.update", this.updateThread); + thread.once("Thread.ready", this.updateThread); + this.updateThread(thread); + } + }; + + teardownThread = () => { if (this.state.thread) { this.state.thread.removeListener("Thread.update", this.updateThread); this.state.thread.removeListener("Thread.ready", this.updateThread); } - } - - setupThread = () => { - const thread = this.props.mxEvent.getThread(); - if (thread) { - thread.on("Thread.update", this.updateThread); - thread.once("Thread.ready", this.updateThread); - this.updateThread(); - } }; - updateThread = () => { - this.setState({ - thread: this.props.mxEvent.getThread(), - }); + updateThread = (thread?: Thread) => { + if (thread) { + this.setState({ thread }); + } else { + this.forceUpdate(); + } }; public renderEventTile(event: MatrixEvent): JSX.Element { @@ -89,7 +120,6 @@ class ThreadView extends React.Component { } public render() { - const thread = this.props.mxEvent.getThread(); const room = MatrixClientPeg.get().getRoom(this.props.roomId); return ( { previousPhase={RightPanelPhases.RoomSummary} withoutScrollContainer={true} > - empty
} - alwaysShowTimestamps={true} - layout={Layout.Group} - hideThreadedMessages={false} - /> + { this.state.thread && ( + empty
} + alwaysShowTimestamps={true} + layout={Layout.Group} + hideThreadedMessages={false} + /> + ) } { return (
{ dis.dispatch({ action: Action.SetRightPanelPhase, @@ -544,7 +545,7 @@ export default class EventTile extends React.Component { { avatars } - { thread.length } { thread.length === 1 ? 'reply' : 'replies' } + { thread.length - 1 } { thread.length === 2 ? 'reply' : 'replies' }
); } From ef51a46d247b1ed673532632ac1cfaf5445c46ec Mon Sep 17 00:00:00 2001 From: Germain Souquet Date: Mon, 23 Aug 2021 14:55:14 +0100 Subject: [PATCH 17/93] Fix linting --- src/components/views/messages/MessageActionBar.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/views/messages/MessageActionBar.js b/src/components/views/messages/MessageActionBar.js index 29212449d1..83e063ab8c 100644 --- a/src/components/views/messages/MessageActionBar.js +++ b/src/components/views/messages/MessageActionBar.js @@ -180,7 +180,7 @@ export default class MessageActionBar extends React.PureComponent { allowClose: false, refireParams: { event: this.props.mxEvent, - } + }, }); } @@ -282,7 +282,7 @@ export default class MessageActionBar extends React.PureComponent { onClick={this.onThreadClick} key="thread" /> - )} + ) } ); } if (this.context.canReact) { From 34da07f1f9f314b21441e5cf4f2568a00d38f283 Mon Sep 17 00:00:00 2001 From: Germain Souquet Date: Mon, 23 Aug 2021 17:31:23 +0100 Subject: [PATCH 18/93] Pass room to ThreadView over roomId --- src/components/structures/RightPanel.tsx | 2 +- src/components/structures/ThreadPanel.tsx | 2 +- src/components/structures/ThreadView.tsx | 18 +++++++++++++----- src/components/views/rooms/EventTile.tsx | 2 +- 4 files changed, 16 insertions(+), 8 deletions(-) diff --git a/src/components/structures/RightPanel.tsx b/src/components/structures/RightPanel.tsx index 85d3013a58..67634c63d2 100644 --- a/src/components/structures/RightPanel.tsx +++ b/src/components/structures/RightPanel.tsx @@ -315,7 +315,7 @@ export default class RightPanel extends React.Component { case RightPanelPhases.ThreadView: panel = void; resizeNotifier: ResizeNotifier; mxEvent: MatrixEvent; @@ -73,6 +75,13 @@ class ThreadView extends React.Component { this.teardownThread(); this.setupThread(this.props.mxEvent); } + + if (prevProps.room !== this.props.room) { + dis.dispatch({ + action: Action.SetRightPanelPhase, + phase: RightPanelPhases.RoomSummary, + }); + } } private onAction = (payload: ActionPayload): void => { @@ -120,7 +129,6 @@ class ThreadView extends React.Component { } public render() { - const room = MatrixClientPeg.get().getRoom(this.props.roomId); return ( { /> ) } Date: Tue, 24 Aug 2021 09:09:28 +0100 Subject: [PATCH 19/93] PR feedback --- .stylelintrc.js | 1 + res/css/views/rooms/_EventTile.scss | 6 ++++-- res/css/views/rooms/_MessageComposer.scss | 2 ++ src/MatrixClientPeg.ts | 2 +- src/components/structures/MessagePanel.tsx | 7 ++++--- src/components/structures/ThreadPanel.tsx | 18 ++++++------------ src/components/structures/ThreadView.tsx | 19 ++++++------------- .../views/messages/MessageActionBar.js | 2 +- .../views/right_panel/RoomSummaryCard.tsx | 2 +- src/components/views/rooms/EventTile.tsx | 4 ++-- src/settings/Settings.tsx | 2 +- 11 files changed, 29 insertions(+), 36 deletions(-) diff --git a/.stylelintrc.js b/.stylelintrc.js index 0e6de7000f..c044b19a63 100644 --- a/.stylelintrc.js +++ b/.stylelintrc.js @@ -17,6 +17,7 @@ module.exports = { "selector-list-comma-newline-after": null, "at-rule-no-unknown": null, "no-descending-specificity": null, + "no-empty-first-line": true, "scss/at-rule-no-unknown": [true, { // https://github.com/vector-im/element-web/issues/10544 "ignoreAtRules": ["define-mixin"], diff --git a/res/css/views/rooms/_EventTile.scss b/res/css/views/rooms/_EventTile.scss index 1bf62d3fb1..0e77f20c49 100644 --- a/res/css/views/rooms/_EventTile.scss +++ b/res/css/views/rooms/_EventTile.scss @@ -643,6 +643,7 @@ $hover-select-border: 4px; // Remove some of the default tile padding so that the error is centered margin-right: 0; + .mx_EventTile_line { padding-left: 0; margin-right: 0; @@ -675,18 +676,17 @@ $hover-select-border: 4px; } } - .mx_ThreadInfo:hover { cursor: pointer; } .mx_ThreadView { - display: flex; flex-direction: column; .mx_ScrollPanel { margin-top: 20px; + .mx_RoomView_MessageList { padding: 0; } @@ -703,6 +703,7 @@ $hover-select-border: 4px; max-width: 100%; display: flex; align-items: center; + .mx_SenderProfile { flex: 1; } @@ -722,6 +723,7 @@ $hover-select-border: 4px; padding-left: 0 !important; order: 10 !important; } + .mx_EventTile { width: 100%; display: flex; diff --git a/res/css/views/rooms/_MessageComposer.scss b/res/css/views/rooms/_MessageComposer.scss index b2747f7a9b..54c250fc2e 100644 --- a/res/css/views/rooms/_MessageComposer.scss +++ b/res/css/views/rooms/_MessageComposer.scss @@ -347,9 +347,11 @@ limitations under the License. .mx_MessageComposer.mx_MessageComposer--compact { margin-right: 0; + .mx_MessageComposer_wrapper { padding: 0; } + .mx_MessageComposer_button:last-child { margin-right: 0; } diff --git a/src/MatrixClientPeg.ts b/src/MatrixClientPeg.ts index d3382a2b5e..7d0ff560b7 100644 --- a/src/MatrixClientPeg.ts +++ b/src/MatrixClientPeg.ts @@ -213,7 +213,7 @@ class MatrixClientPegClass implements IMatrixClientPeg { opts.pendingEventOrdering = PendingEventOrdering.Detached; opts.lazyLoadMembers = true; opts.clientWellKnownPollPeriod = 2 * 60 * 60; // 2 hours - opts.experimentalThreadSupport = SettingsStore.getValue("experimentalThreadSupport"); + opts.experimentalThreadSupport = SettingsStore.getValue("feature_thread"); // Connect the matrix client to the dispatcher and setting handlers MatrixActionCreators.start(this.matrixClient); diff --git a/src/components/structures/MessagePanel.tsx b/src/components/structures/MessagePanel.tsx index 49fb50814c..8bf1f5bd5f 100644 --- a/src/components/structures/MessagePanel.tsx +++ b/src/components/structures/MessagePanel.tsx @@ -267,7 +267,7 @@ export default class MessagePanel extends React.Component { componentDidMount() { this.calculateRoomMembersCount(); this.props.room?.on("RoomState.members", this.calculateRoomMembersCount); - if (SettingsStore.getValue("experimentalThreadSupport")) { + if (SettingsStore.getValue("feature_thread")) { this.props.room?.getThreads().forEach(thread => thread.fetchReplyChain()); } this.isMounted = true; @@ -448,8 +448,9 @@ export default class MessagePanel extends React.Component { // Always show highlighted event if (this.props.highlightedEventId === mxEv.getId()) return true; - const threadingEnabled = SettingsStore.getValue("experimentalThreadSupport"); - if (threadingEnabled && mxEv.replyEventId && this.props.hideThreadedMessages === true) { + if (mxEv.replyEventId + && this.props.hideThreadedMessages + && SettingsStore.getValue("feature_thread")) { return false; } diff --git a/src/components/structures/ThreadPanel.tsx b/src/components/structures/ThreadPanel.tsx index 9259dbe13b..047e527f34 100644 --- a/src/components/structures/ThreadPanel.tsx +++ b/src/components/structures/ThreadPanel.tsx @@ -1,6 +1,5 @@ /* -Copyright 2016 OpenMarket Ltd -Copyright 2019 The Matrix.org Foundation C.I.C. +Copyright 2021 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. @@ -37,11 +36,8 @@ interface IState { threads?: Thread[]; } -/* - * Component which shows the filtered file using a TimelinePanel - */ @replaceableComponent("structures.ThreadView") -class ThreadView extends React.Component { +export default class ThreadPanel extends React.Component { private room: Room; constructor(props: IProps) { @@ -59,15 +55,15 @@ class ThreadView extends React.Component { this.room.removeListener("Thread.ready", this.onThreadEventReceived); } - public onThreadEventReceived = () => this.updateThreads(); + private onThreadEventReceived = () => this.updateThreads(); - public updateThreads = (callback?: () => void): void => { + private updateThreads = (callback?: () => void): void => { this.setState({ threads: this.room.getThreads(), }, callback); }; - public renderEventTile(event: MatrixEvent): JSX.Element { + private renderEventTile(event: MatrixEvent): JSX.Element { return { />; } - public render() { + public render(): JSX.Element { return ( { ); } } - -export default ThreadView; diff --git a/src/components/structures/ThreadView.tsx b/src/components/structures/ThreadView.tsx index 38b1c8dc08..03609c66d0 100644 --- a/src/components/structures/ThreadView.tsx +++ b/src/components/structures/ThreadView.tsx @@ -1,6 +1,5 @@ /* -Copyright 2016 OpenMarket Ltd -Copyright 2019 The Matrix.org Foundation C.I.C. +Copyright 2021 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. @@ -22,7 +21,6 @@ import { Thread } from 'matrix-js-sdk/src/models/thread'; import BaseCard from "../views/right_panel/BaseCard"; import { RightPanelPhases } from "../../stores/RightPanelStorePhases"; import { replaceableComponent } from "../../utils/replaceableComponent"; -import { MatrixClientPeg } from '../../MatrixClientPeg'; import ResizeNotifier from '../../utils/ResizeNotifier'; import EventTile, { TileShape } from '../views/rooms/EventTile'; @@ -48,11 +46,8 @@ interface IState { thread?: Thread; } -/* - * Component which shows the filtered file using a TimelinePanel - */ @replaceableComponent("structures.ThreadView") -class ThreadView extends React.Component { +export default class ThreadView extends React.Component { private dispatcherRef: string; constructor(props: IProps) { @@ -93,7 +88,7 @@ class ThreadView extends React.Component { } }; - setupThread = (mxEv: MatrixEvent) => { + private setupThread = (mxEv: MatrixEvent) => { const thread = mxEv.getThread(); if (thread) { thread.on("Thread.update", this.updateThread); @@ -102,14 +97,14 @@ class ThreadView extends React.Component { } }; - teardownThread = () => { + private teardownThread = () => { if (this.state.thread) { this.state.thread.removeListener("Thread.update", this.updateThread); this.state.thread.removeListener("Thread.ready", this.updateThread); } }; - updateThread = (thread?: Thread) => { + private updateThread = (thread?: Thread) => { if (thread) { this.setState({ thread }); } else { @@ -128,7 +123,7 @@ class ThreadView extends React.Component { />; } - public render() { + public render(): JSX.Element { return ( { ); } } - -export default ThreadView; diff --git a/src/components/views/messages/MessageActionBar.js b/src/components/views/messages/MessageActionBar.js index 83e063ab8c..cb8ea7a50d 100644 --- a/src/components/views/messages/MessageActionBar.js +++ b/src/components/views/messages/MessageActionBar.js @@ -275,7 +275,7 @@ export default class MessageActionBar extends React.PureComponent { onClick={this.onReplyClick} key="reply" /> - { SettingsStore.getValue("experimentalThreadSupport") && ( + { SettingsStore.getValue("feature_thread") && ( = ({ room, onClose }) => { - { SettingsStore.getValue("experimentalThreadSupport") && ( + { SettingsStore.getValue("feature_thread") && ( diff --git a/src/components/views/rooms/EventTile.tsx b/src/components/views/rooms/EventTile.tsx index 638bef5061..935a349b10 100644 --- a/src/components/views/rooms/EventTile.tsx +++ b/src/components/views/rooms/EventTile.tsx @@ -462,7 +462,7 @@ export default class EventTile extends React.Component { this.isListeningForReceipts = true; } - if (SettingsStore.getValue("experimentalThreadSupport")) { + if (SettingsStore.getValue("feature_thread")) { this.props.mxEvent.once("Thread.ready", this.updateThread); this.props.mxEvent.on("Thread.update", this.updateThread); } @@ -514,7 +514,7 @@ export default class EventTile extends React.Component { } private renderThreadInfo(): React.ReactNode { - if (!SettingsStore.getValue("experimentalThreadSupport")) { + if (!SettingsStore.getValue("feature_thread")) { return null; } diff --git a/src/settings/Settings.tsx b/src/settings/Settings.tsx index 6496231679..2dba4cf55d 100644 --- a/src/settings/Settings.tsx +++ b/src/settings/Settings.tsx @@ -211,7 +211,7 @@ export const SETTINGS: {[setting: string]: ISetting} = { supportedLevels: LEVELS_FEATURE, default: false, }, - "experimentalThreadSupport": { + "feature_thread": { isFeature: true, // Requires a reload as we change an option flag on the `js-sdk` // And the entire sync history needs to be parsed again From d4a4ac56801b46b56132fcc776e7ebf5c95b58ad Mon Sep 17 00:00:00 2001 From: RiotRobot Date: Tue, 24 Aug 2021 17:09:26 +0100 Subject: [PATCH 20/93] Upgrade matrix-js-sdk to 12.4.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 e0883f5556..92953212e1 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": "12.4.0-rc.1", "matrix-widget-api": "^0.1.0-beta.15", "minimist": "^1.2.5", "opus-recorder": "^8.0.3", diff --git a/yarn.lock b/yarn.lock index 256fee5277..74883d7059 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5801,9 +5801,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.3.1" - resolved "https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/3216d7e5a7a333212b00d4d7578e29a9f0e247d8" +matrix-js-sdk@12.4.0-rc.1: + version "12.4.0-rc.1" + resolved "https://registry.yarnpkg.com/matrix-js-sdk/-/matrix-js-sdk-12.4.0-rc.1.tgz#6eee9f159bcc5dd968346a9ce5c7b7427636f57c" + integrity sha512-6AGIEJvgWlNYyzxPj/V13s9JUg8zsiJ9Hyj6yTpDahufkAUT5yhpFzdPNNRaLlVxHox5HlwJJBdyNQtML727Og== dependencies: "@babel/runtime" "^7.12.5" another-json "^0.2.0" From 920bd82b66a38050557664bb84c8c571f2256a60 Mon Sep 17 00:00:00 2001 From: Dariusz Niemczyk Date: Tue, 24 Aug 2021 18:22:06 +0200 Subject: [PATCH 21/93] Fix resizer for detaching --- src/resizer/resizer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/resizer/resizer.ts b/src/resizer/resizer.ts index 19af425b8b..0db13e1af5 100644 --- a/src/resizer/resizer.ts +++ b/src/resizer/resizer.ts @@ -84,7 +84,7 @@ export default class Resizer { } public detach() { - const attachment = this?.config?.handler.parentElement ?? this.container; + const attachment = this?.config?.handler?.parentElement ?? this.container; attachment.removeEventListener("mousedown", this.onMouseDown, false); window.removeEventListener("resize", this.onResize); } From ea081d49257c7b98789a21c2e171339f1b3a222f Mon Sep 17 00:00:00 2001 From: RiotRobot Date: Tue, 24 Aug 2021 17:30:43 +0100 Subject: [PATCH 22/93] Prepare changelog for v3.29.0-rc.1 --- CHANGELOG.md | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6f71c1414c..f2ef4e9433 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,45 @@ +Changes in [3.29.0-rc.1](https://github.com/vector-im/element-desktop/releases/tag/v3.29.0-rc.1) (2021-08-24) +============================================================================================================= + +## ✨ Features + * Add a warning on E2EE rooms if you try to make them public ([\#5698](https://github.com/matrix-org/matrix-react-sdk/pull/5698)). Contributed by [SimonBrandner](https://github.com/SimonBrandner). + * Allow pagination of the space hierarchy and use new APIs ([\#6507](https://github.com/matrix-org/matrix-react-sdk/pull/6507)). Fixes vector-im/element-web#18089 and vector-im/element-web#18427. + * Improve emoji in composer ([\#6650](https://github.com/matrix-org/matrix-react-sdk/pull/6650)). Fixes vector-im/element-web#18593 and vector-im/element-web#18593. Contributed by [SimonBrandner](https://github.com/SimonBrandner). + * Allow playback of replied-to voice message ([\#6629](https://github.com/matrix-org/matrix-react-sdk/pull/6629)). Fixes vector-im/element-web#18599 and vector-im/element-web#18599. Contributed by [SimonBrandner](https://github.com/SimonBrandner). + * Format autocomplete suggestions vertically ([\#6620](https://github.com/matrix-org/matrix-react-sdk/pull/6620)). Fixes vector-im/element-web#17574 and vector-im/element-web#17574. Contributed by [SimonBrandner](https://github.com/SimonBrandner). + * Remember last `MemberList` search query per-room ([\#6640](https://github.com/matrix-org/matrix-react-sdk/pull/6640)). Fixes vector-im/element-web#18613 and vector-im/element-web#18613. Contributed by [SimonBrandner](https://github.com/SimonBrandner). + * Sentry rageshakes ([\#6597](https://github.com/matrix-org/matrix-react-sdk/pull/6597)). Fixes vector-im/element-web#11111 and vector-im/element-web#11111. Contributed by [novocaine](https://github.com/novocaine). + * Autocomplete has been updated to match modern accessibility standards. Navigate via up/down arrows rather than Tab. Enter or Tab to confirm a suggestion. This should be familiar to Slack & Discord users. You can now use Tab to navigate around the application and do more without touching your mouse. No more accidentally sending half of people's names because the completion didn't fire on Enter! ([\#5659](https://github.com/matrix-org/matrix-react-sdk/pull/5659)). Fixes vector-im/element-web#4872, vector-im/element-web#11071, vector-im/element-web#17171, vector-im/element-web#15646 vector-im/element-web#4872 and vector-im/element-web#4872. + * Add new call tile states ([\#6610](https://github.com/matrix-org/matrix-react-sdk/pull/6610)). Fixes vector-im/element-web#18521 and vector-im/element-web#18521. Contributed by [SimonBrandner](https://github.com/SimonBrandner). + * Left align call tiles ([\#6609](https://github.com/matrix-org/matrix-react-sdk/pull/6609)). Contributed by [SimonBrandner](https://github.com/SimonBrandner). + * Make loading encrypted images look snappier ([\#6590](https://github.com/matrix-org/matrix-react-sdk/pull/6590)). Fixes vector-im/element-web#17878 and vector-im/element-web#17862. Contributed by [Palid](https://github.com/Palid). + * Offer a way to create a space based on existing community ([\#6543](https://github.com/matrix-org/matrix-react-sdk/pull/6543)). Fixes vector-im/element-web#18092. + * Accessibility improvements in and around Spaces ([\#6569](https://github.com/matrix-org/matrix-react-sdk/pull/6569)). Fixes vector-im/element-web#18094 and vector-im/element-web#18094. + +## 🐛 Bug Fixes + * Fix images not rendering when sent from other clients. ([\#6661](https://github.com/matrix-org/matrix-react-sdk/pull/6661)). Fixes vector-im/element-web#18702 and vector-im/element-web#18702. + * Fix autocomplete scrollbar and make the autocomplete a little smaller ([\#6655](https://github.com/matrix-org/matrix-react-sdk/pull/6655)). Fixes vector-im/element-web#18682 and vector-im/element-web#18682. Contributed by [SimonBrandner](https://github.com/SimonBrandner). + * Fix replies on the bubble layout ([\#6451](https://github.com/matrix-org/matrix-react-sdk/pull/6451)). Fixes vector-im/element-web#18184. Contributed by [SimonBrandner](https://github.com/SimonBrandner). + * Show "Enable encryption in settings" only when the user can do that ([\#6646](https://github.com/matrix-org/matrix-react-sdk/pull/6646)). Fixes vector-im/element-web#18646 and vector-im/element-web#18646. Contributed by [SimonBrandner](https://github.com/SimonBrandner). + * Fix cross signing setup from settings screen ([\#6633](https://github.com/matrix-org/matrix-react-sdk/pull/6633)). Fixes vector-im/element-web#17761 and vector-im/element-web#17761. + * Fix call tiles on the bubble layout ([\#6647](https://github.com/matrix-org/matrix-react-sdk/pull/6647)). Fixes vector-im/element-web#18648 and vector-im/element-web#18648. Contributed by [SimonBrandner](https://github.com/SimonBrandner). + * Fix error on accessing encrypted media without encryption keys ([\#6625](https://github.com/matrix-org/matrix-react-sdk/pull/6625)). Contributed by [Palid](https://github.com/Palid). + * Fix jitsi widget sometimes being permanently stuck in the bottom-right corner ([\#6632](https://github.com/matrix-org/matrix-react-sdk/pull/6632)). Fixes vector-im/element-web#17226 and vector-im/element-web#17226. Contributed by [Palid](https://github.com/Palid). + * Fix FilePanel pagination in E2EE rooms ([\#6630](https://github.com/matrix-org/matrix-react-sdk/pull/6630)). Fixes vector-im/element-web#18415 and vector-im/element-web#18415. Contributed by [SimonBrandner](https://github.com/SimonBrandner). + * Fix call tile buttons ([\#6624](https://github.com/matrix-org/matrix-react-sdk/pull/6624)). Fixes vector-im/element-web#18565 and vector-im/element-web#18565. Contributed by [SimonBrandner](https://github.com/SimonBrandner). + * Fix vertical call tile spacing issues ([\#6621](https://github.com/matrix-org/matrix-react-sdk/pull/6621)). Fixes vector-im/element-web#18558 and vector-im/element-web#18558. Contributed by [SimonBrandner](https://github.com/SimonBrandner). + * Fix long display names in call tiles ([\#6618](https://github.com/matrix-org/matrix-react-sdk/pull/6618)). Fixes vector-im/element-web#18562 and vector-im/element-web#18562. Contributed by [SimonBrandner](https://github.com/SimonBrandner). + * Avoid access token overflow ([\#6616](https://github.com/matrix-org/matrix-react-sdk/pull/6616)). Contributed by [SimonBrandner](https://github.com/SimonBrandner). + * Properly handle media errors ([\#6615](https://github.com/matrix-org/matrix-react-sdk/pull/6615)). Contributed by [SimonBrandner](https://github.com/SimonBrandner). + * Fix glare related regressions ([\#6614](https://github.com/matrix-org/matrix-react-sdk/pull/6614)). Fixes vector-im/element-web#18538 and vector-im/element-web#18538. Contributed by [SimonBrandner](https://github.com/SimonBrandner). + * Fix long display names in call toasts ([\#6617](https://github.com/matrix-org/matrix-react-sdk/pull/6617)). Fixes vector-im/element-web#18557 and vector-im/element-web#18557. Contributed by [SimonBrandner](https://github.com/SimonBrandner). + * Fix PiP of held calls ([\#6611](https://github.com/matrix-org/matrix-react-sdk/pull/6611)). Fixes vector-im/element-web#18539 and vector-im/element-web#18539. Contributed by [SimonBrandner](https://github.com/SimonBrandner). + * Fix call tile behaviour on narrow layouts ([\#6556](https://github.com/matrix-org/matrix-react-sdk/pull/6556)). Fixes vector-im/element-web#18398. Contributed by [SimonBrandner](https://github.com/SimonBrandner). + * Fix video call persisting when widget removed ([\#6608](https://github.com/matrix-org/matrix-react-sdk/pull/6608)). Fixes vector-im/element-web#15703 and vector-im/element-web#15703. + * Fix toast colors ([\#6606](https://github.com/matrix-org/matrix-react-sdk/pull/6606)). Contributed by [SimonBrandner](https://github.com/SimonBrandner). + * Remove tiny scrollbar dot from code blocks ([\#6596](https://github.com/matrix-org/matrix-react-sdk/pull/6596)). Fixes vector-im/element-web#18474. Contributed by [SimonBrandner](https://github.com/SimonBrandner). + * Improve handling of pills in the composer ([\#6353](https://github.com/matrix-org/matrix-react-sdk/pull/6353)). Fixes vector-im/element-web#10134 vector-im/element-web#10896 and vector-im/element-web#15037. Contributed by [SimonBrandner](https://github.com/SimonBrandner). + Changes in [3.28.1](https://github.com/vector-im/element-desktop/releases/tag/v3.28.1) (2021-08-17) =================================================================================================== From b58f7fea5745deac0e6c206bd0045f8fe7930b28 Mon Sep 17 00:00:00 2001 From: RiotRobot Date: Tue, 24 Aug 2021 17:30:44 +0100 Subject: [PATCH 23/93] v3.29.0-rc.1 --- package.json | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 92953212e1..8ba3c6bce9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "matrix-react-sdk", - "version": "3.28.1", + "version": "3.29.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", @@ -207,5 +207,6 @@ "coverageReporters": [ "text" ] - } + }, + "typings": "./lib/index.d.ts" } From b5f377d5438771e14db524e9fc88c9cb508d8317 Mon Sep 17 00:00:00 2001 From: David Baker Date: Tue, 24 Aug 2021 17:43:23 +0100 Subject: [PATCH 24/93] Fix import breaking types in release mode --- src/sentry.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sentry.ts b/src/sentry.ts index 59152f66f2..206ff9811b 100644 --- a/src/sentry.ts +++ b/src/sentry.ts @@ -19,7 +19,7 @@ import PlatformPeg from "./PlatformPeg"; import SdkConfig from "./SdkConfig"; import { MatrixClientPeg } from "./MatrixClientPeg"; import SettingsStore from "./settings/SettingsStore"; -import { MatrixClient } from "matrix-js-sdk"; +import { MatrixClient } from "matrix-js-sdk/src/client"; /* eslint-disable camelcase */ From 798da0f5fac43b4bf5429230cfee5459eb4b3873 Mon Sep 17 00:00:00 2001 From: Dariusz Niemczyk Date: Thu, 19 Aug 2021 19:00:31 +0200 Subject: [PATCH 25/93] Improve layering for chat Currently we have a lot of unnecessary layers being created in the app, which is extremely problematic on Windows when using Chrome, as it deoptimises fonts rendering due to layers mismatch; chrome's algorithm can't figure out if the layers won't resize or overlap so it creates a lot of additional layers, which has a side effect of turning off sub-pixel antialiasing on Windows. This is a first step to improve the layering in the entire app. --- res/css/structures/_LeftPanel.scss | 2 ++ res/css/structures/_RoomView.scss | 3 ++- res/css/structures/_ScrollPanel.scss | 1 + 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/res/css/structures/_LeftPanel.scss b/res/css/structures/_LeftPanel.scss index da17e5f1f9..fced77bac8 100644 --- a/res/css/structures/_LeftPanel.scss +++ b/res/css/structures/_LeftPanel.scss @@ -26,6 +26,8 @@ $roomListCollapsedWidth: 68px; .mx_LeftPanel_wrapper { display: flex; max-width: 50%; + // Contain the amount of layers rendered by constraining what actually needs re-layering via css + contain: layout paint; .mx_LeftPanel_wrapper--user { background-color: $roomlist-bg-color; diff --git a/res/css/structures/_RoomView.scss b/res/css/structures/_RoomView.scss index 81ea75c442..67529878c9 100644 --- a/res/css/structures/_RoomView.scss +++ b/res/css/structures/_RoomView.scss @@ -20,6 +20,8 @@ limitations under the License. flex: 1; position: relative; justify-content: center; + // Contain the amount of layers rendered by constraining what actually needs re-layering via css + contain: strict; } .mx_RoomView { @@ -163,7 +165,6 @@ limitations under the License. flex: 1; display: flex; flex-direction: column; - contain: content; } .mx_RoomView_statusArea { diff --git a/res/css/structures/_ScrollPanel.scss b/res/css/structures/_ScrollPanel.scss index 7b75c69e86..82caeae29d 100644 --- a/res/css/structures/_ScrollPanel.scss +++ b/res/css/structures/_ScrollPanel.scss @@ -15,6 +15,7 @@ limitations under the License. */ .mx_ScrollPanel { + contain: strict; .mx_RoomView_MessageList { position: relative; From 972682139e6e14bf152e37666b44cb0743e29e4c Mon Sep 17 00:00:00 2001 From: Dariusz Niemczyk Date: Tue, 24 Aug 2021 19:23:12 +0200 Subject: [PATCH 26/93] Force a new layer for backdrop panel --- res/css/structures/_BackdropPanel.scss | 2 ++ res/css/structures/_LeftPanel.scss | 2 ++ 2 files changed, 4 insertions(+) diff --git a/res/css/structures/_BackdropPanel.scss b/res/css/structures/_BackdropPanel.scss index 6c41ed7a09..482507cb15 100644 --- a/res/css/structures/_BackdropPanel.scss +++ b/res/css/structures/_BackdropPanel.scss @@ -22,6 +22,8 @@ limitations under the License. width: 100%; overflow: hidden; filter: blur(var(--lp-background-blur)); + // Force a new layer for the backdropPanel so it's better hardware supported + transform: translateZ(0); } .mx_BackdropPanel--image { diff --git a/res/css/structures/_LeftPanel.scss b/res/css/structures/_LeftPanel.scss index fced77bac8..32d50b9a80 100644 --- a/res/css/structures/_LeftPanel.scss +++ b/res/css/structures/_LeftPanel.scss @@ -26,6 +26,8 @@ $roomListCollapsedWidth: 68px; .mx_LeftPanel_wrapper { display: flex; max-width: 50%; + position: relative; + // Contain the amount of layers rendered by constraining what actually needs re-layering via css contain: layout paint; From b7df1127bd7782ba143f71bffc2f19a6691f3eb2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Wed, 25 Aug 2021 08:38:47 +0200 Subject: [PATCH 27/93] Revert "Use ignoreSelfEvent in the RoomListStore (which doesn't work!)" This reverts commit 0a4a1506d412cdec610196af73fca6401fead15b. --- src/stores/room-list/RoomListStore.ts | 4 ---- .../room-list/algorithms/tag-sorting/RecentAlgorithm.ts | 5 ++++- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/stores/room-list/RoomListStore.ts b/src/stores/room-list/RoomListStore.ts index 6a80bcd1e5..1a5ef0484e 100644 --- a/src/stores/room-list/RoomListStore.ts +++ b/src/stores/room-list/RoomListStore.ts @@ -38,7 +38,6 @@ import { SpaceWatcher } from "./SpaceWatcher"; import SpaceStore from "../SpaceStore"; import { Action } from "../../dispatcher/actions"; import { SettingUpdatedPayload } from "../../dispatcher/payloads/SettingUpdatedPayload"; -import { ignoreSelfEvent } from "./algorithms/tag-sorting/RecentAlgorithm"; interface IState { tagsEnabled?: boolean; @@ -271,9 +270,6 @@ export class RoomListStoreClass extends AsyncStoreWithClient { // Ignore non-live events (backfill) if (!eventPayload.isLiveEvent || !payload.isLiveUnfilteredRoomTimelineEvent) return; - // Avoid re-ordering on status, profile etc. changes - if (ignoreSelfEvent(eventPayload.event)) return; - const roomId = eventPayload.event.getRoomId(); const room = this.matrixClient.getRoom(roomId); const tryUpdate = async (updatedRoom: Room) => { diff --git a/src/stores/room-list/algorithms/tag-sorting/RecentAlgorithm.ts b/src/stores/room-list/algorithms/tag-sorting/RecentAlgorithm.ts index bbfac876bc..5ddfd96c8a 100644 --- a/src/stores/room-list/algorithms/tag-sorting/RecentAlgorithm.ts +++ b/src/stores/room-list/algorithms/tag-sorting/RecentAlgorithm.ts @@ -87,7 +87,10 @@ export const sortRooms = (rooms: Room[]): Room[] => { const ev = r.timeline[i]; if (!ev.getTs()) continue; // skip events that don't have timestamps (tests only?) - if (ev.getSender() === myUserId || Unread.eventTriggersUnreadCount(ev)) { + if ( + (ev.getSender() === myUserId && !ignoreSelfEvent(ev)) || + Unread.eventTriggersUnreadCount(ev) + ) { return ev.getTs(); } } From 0465815486fd1afdcbf6bae3536d33b20d7c00a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Wed, 25 Aug 2021 08:43:13 +0200 Subject: [PATCH 28/93] ignoreSelfEvent -> shouldCauseReorder MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- .../algorithms/tag-sorting/RecentAlgorithm.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/stores/room-list/algorithms/tag-sorting/RecentAlgorithm.ts b/src/stores/room-list/algorithms/tag-sorting/RecentAlgorithm.ts index 5ddfd96c8a..08f9f6efd5 100644 --- a/src/stores/room-list/algorithms/tag-sorting/RecentAlgorithm.ts +++ b/src/stores/room-list/algorithms/tag-sorting/RecentAlgorithm.ts @@ -23,21 +23,21 @@ import { EffectiveMembership, getEffectiveMembership } from "../../../../utils/m import { EventType } from "matrix-js-sdk/src/@types/event"; import { MatrixEvent } from "matrix-js-sdk/src/models/event"; -export function ignoreSelfEvent(event: MatrixEvent): boolean { +export function shouldCauseReorder(event: MatrixEvent): boolean { const type = event.getType(); const content = event.getContent(); const prevContent = event.getPrevContent(); // Never ignore membership changes - if (type === EventType.RoomMember && prevContent.membership !== content.membership) return false; + if (type === EventType.RoomMember && prevContent.membership !== content.membership) return true; // Ignore status changes // XXX: This should be an enum - if (type === "im.vector.user_status") return true; + if (type === "im.vector.user_status") return false; // Ignore display name changes - if (type === EventType.RoomMember && prevContent.displayname !== content.displayname) return true; + if (type === EventType.RoomMember && prevContent.displayname !== content.displayname) return false; - return false; + return true; } export const sortRooms = (rooms: Room[]): Room[] => { @@ -88,7 +88,7 @@ export const sortRooms = (rooms: Room[]): Room[] => { if (!ev.getTs()) continue; // skip events that don't have timestamps (tests only?) if ( - (ev.getSender() === myUserId && !ignoreSelfEvent(ev)) || + (ev.getSender() === myUserId && shouldCauseReorder(ev)) || Unread.eventTriggersUnreadCount(ev) ) { return ev.getTs(); From 78562685bb7e8739b380eca0ae487a431d9d2a9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Wed, 25 Aug 2021 08:59:42 +0200 Subject: [PATCH 29/93] Ignore avatar changes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/stores/room-list/algorithms/tag-sorting/RecentAlgorithm.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/stores/room-list/algorithms/tag-sorting/RecentAlgorithm.ts b/src/stores/room-list/algorithms/tag-sorting/RecentAlgorithm.ts index 08f9f6efd5..0da2c69eb8 100644 --- a/src/stores/room-list/algorithms/tag-sorting/RecentAlgorithm.ts +++ b/src/stores/room-list/algorithms/tag-sorting/RecentAlgorithm.ts @@ -36,6 +36,8 @@ export function shouldCauseReorder(event: MatrixEvent): boolean { if (type === "im.vector.user_status") return false; // Ignore display name changes if (type === EventType.RoomMember && prevContent.displayname !== content.displayname) return false; + // Ignore avatar changes + if (type === EventType.RoomMember && prevContent.avatar_url !== content.avatar_url) return false; return true; } From 237212b08292b526d88e1da302bb31a4987a5621 Mon Sep 17 00:00:00 2001 From: Germain Souquet Date: Wed, 25 Aug 2021 09:36:35 +0100 Subject: [PATCH 30/93] Add ESLint shared settings to detect React version --- .eslintrc.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.eslintrc.js b/.eslintrc.js index 827b373949..9d68942228 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -63,6 +63,11 @@ module.exports = { "@typescript-eslint/ban-ts-comment": "off", }, }], + settings: { + react: { + version: "detect", + } + } }; function buildRestrictedPropertiesOptions(properties, message) { From e080b0d66c725518dd290d44ab357d59f30a3d1f Mon Sep 17 00:00:00 2001 From: RiotRobot Date: Wed, 25 Aug 2021 11:45:42 +0100 Subject: [PATCH 31/93] changelog --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f2ef4e9433..1fdcdfa96a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +Changes in [3.29.0-rc.2](https://github.com/vector-im/element-desktop/releases/tag/v3.29.0-rc.2) (2021-08-25) +============================================================================================================= + +## ✨ Features + * [Release]Increase general app performance by optimizing layers ([\#6672](https://github.com/matrix-org/matrix-react-sdk/pull/6672)). Fixes vector-im/element-web#18730 and vector-im/element-web#18730. Contributed by [Palid](https://github.com/Palid). + Changes in [3.29.0-rc.1](https://github.com/vector-im/element-desktop/releases/tag/v3.29.0-rc.1) (2021-08-24) ============================================================================================================= From ccafcd0d3975a43ebf9692f27f8e6f8e7f9644f8 Mon Sep 17 00:00:00 2001 From: RiotRobot Date: Wed, 25 Aug 2021 11:46:07 +0100 Subject: [PATCH 32/93] v3.29.0-rc.2 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 8ba3c6bce9..bb2f3432d4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "matrix-react-sdk", - "version": "3.29.0-rc.1", + "version": "3.29.0-rc.2", "description": "SDK for matrix.org using React", "author": "matrix.org", "repository": { From 59833d1306d7dd43a2abe385de9b6b94747736c9 Mon Sep 17 00:00:00 2001 From: David Baker Date: Wed, 25 Aug 2021 17:21:48 +0100 Subject: [PATCH 33/93] Update changelog generator --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index e0883f5556..d6845e0981 100644 --- a/package.json +++ b/package.json @@ -151,7 +151,7 @@ "@typescript-eslint/eslint-plugin": "^4.17.0", "@typescript-eslint/parser": "^4.17.0", "@wojtekmaj/enzyme-adapter-react-17": "^0.6.1", - "allchange": "^1.0.0", + "allchange": "^1.0.2", "babel-jest": "^26.6.3", "chokidar": "^3.5.1", "concurrently": "^5.3.0", diff --git a/yarn.lock b/yarn.lock index 80c6ac5155..0c9514ae30 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2036,10 +2036,10 @@ ajv@^8.0.1: require-from-string "^2.0.2" uri-js "^4.2.2" -allchange@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/allchange/-/allchange-1.0.1.tgz#f32a75f65ab182d044d18e8baa43bd1c9be982f6" - integrity sha512-lj8HZcvQ04RsNqwLWjCYSDvchrW4nnjlOZ3z+VGhA78M7KootV0eRwlvTlYJec73jsz/Ts59kVArgooEsACOog== +allchange@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/allchange/-/allchange-1.0.2.tgz#86b9190e12b7ede4f230ae763cbd504c48fd907b" + integrity sha512-qJv1t2yvBThkes8g/dPMt8CGu+04U+q5QjCJn2Ngp92edZU8DJBfKGyGXo7w1iV48LVuQKQDfMsdIWhP7zHdlQ== dependencies: "@actions/core" "^1.4.0" "@actions/github" "^5.0.0" From 6b6fefa73dcb5a6f18757e5e13e81c2c410fce28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Wed, 25 Aug 2021 18:38:20 +0200 Subject: [PATCH 34/93] Fix class MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/autocomplete/CommandProvider.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/autocomplete/CommandProvider.tsx b/src/autocomplete/CommandProvider.tsx index d56adc026c..d0fb9919f6 100644 --- a/src/autocomplete/CommandProvider.tsx +++ b/src/autocomplete/CommandProvider.tsx @@ -95,7 +95,7 @@ export default class CommandProvider extends AutocompleteProvider { renderCompletions(completions: React.ReactNode[]): React.ReactNode { return (
From ecd45f7a81080ae9d826cfa279f8935447665639 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Wed, 25 Aug 2021 18:38:36 +0200 Subject: [PATCH 35/93] Use min-height instead of height MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/css/views/rooms/_Autocomplete.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/css/views/rooms/_Autocomplete.scss b/res/css/views/rooms/_Autocomplete.scss index 529936b2b2..bebe0b47c9 100644 --- a/res/css/views/rooms/_Autocomplete.scss +++ b/res/css/views/rooms/_Autocomplete.scss @@ -20,7 +20,7 @@ /* a "block" completion takes up a whole line */ .mx_Autocomplete_Completion_block { - height: 34px; + min-height: 34px; display: flex; padding: 0 12px; user-select: none; From 9f791031a8136daa74dd2e459da674e5072eb83e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Wed, 25 Aug 2021 18:54:52 +0200 Subject: [PATCH 36/93] Fix codeblock formatting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/css/views/rooms/_EventTile.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/css/views/rooms/_EventTile.scss b/res/css/views/rooms/_EventTile.scss index dcfb308ac4..1c58dfb95b 100644 --- a/res/css/views/rooms/_EventTile.scss +++ b/res/css/views/rooms/_EventTile.scss @@ -480,7 +480,7 @@ $hover-select-border: 4px; } pre code > * { - display: inline-block; + display: inline; } pre { From 289ac347645c9b3e5e370279f671e1af22b1ca1f Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Wed, 25 Aug 2021 18:16:40 -0600 Subject: [PATCH 37/93] Add support for MSC2762's timeline functionality See https://github.com/matrix-org/matrix-widget-api/pull/41 --- .../WidgetCapabilitiesPromptDialog.tsx | 19 +++- src/i18n/strings/en_EN.json | 2 + src/stores/widgets/StopGapWidget.ts | 6 +- src/stores/widgets/StopGapWidgetDriver.ts | 98 ++++++++++++------- src/widgets/CapabilityText.tsx | 42 +++++++- 5 files changed, 119 insertions(+), 48 deletions(-) diff --git a/src/components/views/dialogs/WidgetCapabilitiesPromptDialog.tsx b/src/components/views/dialogs/WidgetCapabilitiesPromptDialog.tsx index ebeab191b1..556dc057f9 100644 --- a/src/components/views/dialogs/WidgetCapabilitiesPromptDialog.tsx +++ b/src/components/views/dialogs/WidgetCapabilitiesPromptDialog.tsx @@ -1,5 +1,5 @@ /* -Copyright 2020 The Matrix.org Foundation C.I.C. +Copyright 2020 - 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. @@ -20,6 +20,7 @@ import { _t } from "../../../languageHandler"; import { IDialogProps } from "./IDialogProps"; import { Capability, + isTimelineCapability, Widget, WidgetEventCapability, WidgetKind, @@ -30,6 +31,7 @@ import DialogButtons from "../elements/DialogButtons"; import LabelledToggleSwitch from "../elements/LabelledToggleSwitch"; import { CapabilityText } from "../../../widgets/CapabilityText"; import { replaceableComponent } from "../../../utils/replaceableComponent"; +import { lexicographicCompare } from "matrix-js-sdk/src/utils"; export function getRememberedCapabilitiesForWidget(widget: Widget): Capability[] { return JSON.parse(localStorage.getItem(`widget_${widget.id}_approved_caps`) || "[]"); @@ -102,7 +104,20 @@ export default class WidgetCapabilitiesPromptDialog extends React.PureComponent< } public render() { - const checkboxRows = Object.entries(this.state.booleanStates).map(([cap, isChecked], i) => { + // We specifically order the timeline capabilities down to the bottom. The capability text + // generation cares strongly about this. + const orderedCapabilities = Object.entries(this.state.booleanStates).sort(([capA], [capB]) => { + const isTimelineA = isTimelineCapability(capA); + const isTimelineB = isTimelineCapability(capB); + + if (!isTimelineA && !isTimelineB) return lexicographicCompare(capA, capB); + if (isTimelineA && !isTimelineB) return 1; + if (!isTimelineA && isTimelineB) return -1; + if (isTimelineA && isTimelineB) return lexicographicCompare(capA, capB); + + return 0; + }); + const checkboxRows = orderedCapabilities.map(([cap, isChecked], i) => { const text = CapabilityText.for(cap, this.props.widgetKind); const byline = text.byline ? { text.byline } diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 21859fb1aa..3b67db374c 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -604,6 +604,8 @@ "See when anyone posts a sticker to your active room": "See when anyone posts a sticker to your active room", "with an empty state key": "with an empty state key", "with state key %(stateKey)s": "with state key %(stateKey)s", + "The above, but in any room you are joined or invited to as well": "The above, but in any room you are joined or invited to as well", + "The above, but in as well": "The above, but in as well", "Send %(eventType)s events as you in this room": "Send %(eventType)s events as you in this room", "See %(eventType)s events posted to this room": "See %(eventType)s events posted to this room", "Send %(eventType)s events as you in your active room": "Send %(eventType)s events as you in your active room", diff --git a/src/stores/widgets/StopGapWidget.ts b/src/stores/widgets/StopGapWidget.ts index daa1e0e787..49653626c1 100644 --- a/src/stores/widgets/StopGapWidget.ts +++ b/src/stores/widgets/StopGapWidget.ts @@ -1,5 +1,5 @@ /* - * Copyright 2020, 2021 The Matrix.org Foundation C.I.C. + * Copyright 2020 - 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. @@ -408,13 +408,11 @@ export class StopGapWidget extends EventEmitter { private onEvent = (ev: MatrixEvent) => { MatrixClientPeg.get().decryptEventIfNeeded(ev); if (ev.isBeingDecrypted() || ev.isDecryptionFailure()) return; - if (ev.getRoomId() !== this.eventListenerRoomId) return; this.feedEvent(ev); }; private onEventDecrypted = (ev: MatrixEvent) => { if (ev.isDecryptionFailure()) return; - if (ev.getRoomId() !== this.eventListenerRoomId) return; this.feedEvent(ev); }; @@ -422,7 +420,7 @@ export class StopGapWidget extends EventEmitter { if (!this.messaging) return; const raw = ev.getEffectiveEvent(); - this.messaging.feedEvent(raw).catch(e => { + this.messaging.feedEvent(raw, this.eventListenerRoomId).catch(e => { console.error("Error sending event to widget: ", e); }); } diff --git a/src/stores/widgets/StopGapWidgetDriver.ts b/src/stores/widgets/StopGapWidgetDriver.ts index 13cd260ef0..78d7c9ede0 100644 --- a/src/stores/widgets/StopGapWidgetDriver.ts +++ b/src/stores/widgets/StopGapWidgetDriver.ts @@ -1,5 +1,5 @@ /* - * Copyright 2020 The Matrix.org Foundation C.I.C. + * Copyright 2020 - 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. @@ -23,6 +23,7 @@ import { MatrixCapabilities, OpenIDRequestState, SimpleObservable, + Symbols, Widget, WidgetDriver, WidgetEventCapability, @@ -44,7 +45,8 @@ import { CHAT_EFFECTS } from "../../effects"; import { containsEmoji } from "../../effects/utils"; import dis from "../../dispatcher/dispatcher"; import { tryTransformPermalinkToLocalHref } from "../../utils/permalinks/Permalinks"; -import { MatrixEvent } from "matrix-js-sdk/src/models/event"; +import { IEvent, MatrixEvent } from "matrix-js-sdk/src/models/event"; +import { Room } from "matrix-js-sdk"; // TODO: Purge this from the universe @@ -119,9 +121,9 @@ export class StopGapWidgetDriver extends WidgetDriver { return new Set(iterableUnion(allowedSoFar, requested)); } - public async sendEvent(eventType: string, content: any, stateKey: string = null): Promise { + public async sendEvent(eventType: string, content: any, stateKey: string = null, targetRoomId: string = null): Promise { const client = MatrixClientPeg.get(); - const roomId = ActiveRoomObserver.activeRoomId; + const roomId = targetRoomId || ActiveRoomObserver.activeRoomId; if (!client || !roomId) throw new Error("Not in a room or not attached to a client"); @@ -145,48 +147,68 @@ export class StopGapWidgetDriver extends WidgetDriver { return { roomId, eventId: r.event_id }; } - public async readRoomEvents(eventType: string, msgtype: string | undefined, limit: number): Promise { - limit = limit > 0 ? Math.min(limit, 25) : 25; // arbitrary choice - + private pickRooms(roomIds: (string | Symbols.AnyRoom)[] = null): Room[] { const client = MatrixClientPeg.get(); - const roomId = ActiveRoomObserver.activeRoomId; - const room = client.getRoom(roomId); - if (!client || !roomId || !room) throw new Error("Not in a room or not attached to a client"); + if (!client) throw new Error("Not attached to a client"); - const results: MatrixEvent[] = []; - const events = room.getLiveTimeline().getEvents(); // timelines are most recent last - for (let i = events.length - 1; i > 0; i--) { - if (results.length >= limit) break; - - const ev = events[i]; - if (ev.getType() !== eventType || ev.isState()) continue; - if (eventType === EventType.RoomMessage && msgtype && msgtype !== ev.getContent()['msgtype']) continue; - results.push(ev); - } - - return results.map(e => e.getEffectiveEvent()); + const targetRooms = roomIds + ? (roomIds.includes(Symbols.AnyRoom) ? client.getVisibleRooms() : roomIds.map(r => client.getRoom(r))) + : [client.getRoom(ActiveRoomObserver.activeRoomId)]; + return targetRooms.filter(r => !!r); } - public async readStateEvents(eventType: string, stateKey: string | undefined, limit: number): Promise { - limit = limit > 0 ? Math.min(limit, 100) : 100; // arbitrary choice + public async readRoomEvents( + eventType: string, + msgtype: string | undefined, + limitPerRoom: number, + roomIds: (string | Symbols.AnyRoom)[] = null, + ): Promise { + limitPerRoom = limitPerRoom > 0 ? Math.min(limitPerRoom, 25) : 25; // arbitrary choice - const client = MatrixClientPeg.get(); - const roomId = ActiveRoomObserver.activeRoomId; - const room = client.getRoom(roomId); - if (!client || !roomId || !room) throw new Error("Not in a room or not attached to a client"); + const rooms = this.pickRooms(roomIds); + const allResults: IEvent[] = []; + for (const room of rooms) { + const results: MatrixEvent[] = []; + const events = room.getLiveTimeline().getEvents(); // timelines are most recent last + for (let i = events.length - 1; i > 0; i--) { + if (results.length >= limitPerRoom) break; - const results: MatrixEvent[] = []; - const state: Map = room.currentState.events.get(eventType); - if (state) { - if (stateKey === "" || !!stateKey) { - const forKey = state.get(stateKey); - if (forKey) results.push(forKey); - } else { - results.push(...Array.from(state.values())); + const ev = events[i]; + if (ev.getType() !== eventType || ev.isState()) continue; + if (eventType === EventType.RoomMessage && msgtype && msgtype !== ev.getContent()['msgtype']) continue; + results.push(ev); } - } - return results.slice(0, limit).map(e => e.event); + results.forEach(e => allResults.push(e.getEffectiveEvent())); + } + return allResults; + } + + public async readStateEvents( + eventType: string, + stateKey: string | undefined, + limitPerRoom: number, + roomIds: (string | Symbols.AnyRoom)[] = null, + ): Promise { + limitPerRoom = limitPerRoom > 0 ? Math.min(limitPerRoom, 100) : 100; // arbitrary choice + + const rooms = this.pickRooms(roomIds); + const allResults: IEvent[] = []; + for (const room of rooms) { + const results: MatrixEvent[] = []; + const state: Map = room.currentState.events.get(eventType); + if (state) { + if (stateKey === "" || !!stateKey) { + const forKey = state.get(stateKey); + if (forKey) results.push(forKey); + } else { + results.push(...Array.from(state.values())); + } + } + + results.slice(0, limitPerRoom).forEach(e => allResults.push(e.getEffectiveEvent())); + } + return allResults; } public async askOpenID(observer: SimpleObservable) { diff --git a/src/widgets/CapabilityText.tsx b/src/widgets/CapabilityText.tsx index 63e34eea7a..30349fe0f6 100644 --- a/src/widgets/CapabilityText.tsx +++ b/src/widgets/CapabilityText.tsx @@ -1,5 +1,5 @@ /* -Copyright 2020 The Matrix.org Foundation C.I.C. +Copyright 2020 - 2021 The Matrix.org Foundation C.I.C. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -14,11 +14,22 @@ See the License for the specific language governing permissions and limitations under the License. */ -import { Capability, EventDirection, MatrixCapabilities, WidgetEventCapability, WidgetKind } from "matrix-widget-api"; +import { + Capability, + EventDirection, + getTimelineRoomIDFromCapability, + isTimelineCapability, + isTimelineCapabilityFor, + MatrixCapabilities, Symbols, + WidgetEventCapability, + WidgetKind +} from "matrix-widget-api"; import { _t, _td, TranslatedString } from "../languageHandler"; import { EventType, MsgType } from "matrix-js-sdk/src/@types/event"; import { ElementWidgetCapabilities } from "../stores/widgets/ElementWidgetCapabilities"; import React from "react"; +import { MatrixClientPeg } from "../MatrixClientPeg"; +import TextWithTooltip from "../components/views/elements/TextWithTooltip"; type GENERIC_WIDGET_KIND = "generic"; // eslint-disable-line @typescript-eslint/naming-convention const GENERIC_WIDGET_KIND: GENERIC_WIDGET_KIND = "generic"; @@ -138,8 +149,31 @@ export class CapabilityText { if (textForKind[GENERIC_WIDGET_KIND]) return { primary: _t(textForKind[GENERIC_WIDGET_KIND]) }; // ... we'll fall through to the generic capability processing at the end of this - // function if we fail to locate a simple string and the capability isn't for an - // event. + // function if we fail to generate a string for the capability. + } + + // Try to handle timeline capabilities. The text here implies that the caller has sorted + // the timeline caps to the end for UI purposes. + if (isTimelineCapability(capability)) { + if (isTimelineCapabilityFor(capability, Symbols.AnyRoom)) { + return { primary: _t("The above, but in any room you are joined or invited to as well") }; + } else { + const roomId = getTimelineRoomIDFromCapability(capability); + const room = MatrixClientPeg.get().getRoom(roomId); + return { + primary: _t("The above, but in as well", {}, { + Room: () => { + if (room) { + return + { room.name } + ; + } else { + return { roomId }; + } + }, + }), + }; + } } // We didn't have a super simple line of text, so try processing the capability as the From f912d9d1b40308f55e0db008abc0f1288f0a255e Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Wed, 25 Aug 2021 18:25:20 -0600 Subject: [PATCH 38/93] Appease the linter --- src/stores/widgets/StopGapWidgetDriver.ts | 7 ++++++- src/widgets/CapabilityText.tsx | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/stores/widgets/StopGapWidgetDriver.ts b/src/stores/widgets/StopGapWidgetDriver.ts index 78d7c9ede0..45c7d6bd2e 100644 --- a/src/stores/widgets/StopGapWidgetDriver.ts +++ b/src/stores/widgets/StopGapWidgetDriver.ts @@ -121,7 +121,12 @@ export class StopGapWidgetDriver extends WidgetDriver { return new Set(iterableUnion(allowedSoFar, requested)); } - public async sendEvent(eventType: string, content: any, stateKey: string = null, targetRoomId: string = null): Promise { + public async sendEvent( + eventType: string, + content: any, + stateKey: string = null, + targetRoomId: string = null, + ): Promise { const client = MatrixClientPeg.get(); const roomId = targetRoomId || ActiveRoomObserver.activeRoomId; diff --git a/src/widgets/CapabilityText.tsx b/src/widgets/CapabilityText.tsx index 30349fe0f6..8c13a4b2fc 100644 --- a/src/widgets/CapabilityText.tsx +++ b/src/widgets/CapabilityText.tsx @@ -22,7 +22,7 @@ import { isTimelineCapabilityFor, MatrixCapabilities, Symbols, WidgetEventCapability, - WidgetKind + WidgetKind, } from "matrix-widget-api"; import { _t, _td, TranslatedString } from "../languageHandler"; import { EventType, MsgType } from "matrix-js-sdk/src/@types/event"; From 76cc5bfb7db7a3b4b66e0e8d2656db6670986123 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Wed, 25 Aug 2021 22:30:57 -0600 Subject: [PATCH 39/93] Remember all capabilities given to a widget Fixes https://github.com/vector-im/element-web/issues/18786 Instead of replacing the remembered set, we now add to it. This should be safe as the user expectation was previously to remember the capabilities, so we can keep appending to that list. This commit also moves the utility functions closer to the widget driver as they aren't needed in the dialog anymore. --- .../WidgetCapabilitiesPromptDialog.tsx | 13 +---------- src/stores/widgets/StopGapWidgetDriver.ts | 22 +++++++++++++++---- 2 files changed, 19 insertions(+), 16 deletions(-) diff --git a/src/components/views/dialogs/WidgetCapabilitiesPromptDialog.tsx b/src/components/views/dialogs/WidgetCapabilitiesPromptDialog.tsx index ebeab191b1..c184664df3 100644 --- a/src/components/views/dialogs/WidgetCapabilitiesPromptDialog.tsx +++ b/src/components/views/dialogs/WidgetCapabilitiesPromptDialog.tsx @@ -31,14 +31,6 @@ import LabelledToggleSwitch from "../elements/LabelledToggleSwitch"; import { CapabilityText } from "../../../widgets/CapabilityText"; import { replaceableComponent } from "../../../utils/replaceableComponent"; -export function getRememberedCapabilitiesForWidget(widget: Widget): Capability[] { - return JSON.parse(localStorage.getItem(`widget_${widget.id}_approved_caps`) || "[]"); -} - -function setRememberedCapabilitiesForWidget(widget: Widget, caps: Capability[]) { - localStorage.setItem(`widget_${widget.id}_approved_caps`, JSON.stringify(caps)); -} - interface IProps extends IDialogProps { requestedCapabilities: Set; widget: Widget; @@ -95,10 +87,7 @@ export default class WidgetCapabilitiesPromptDialog extends React.PureComponent< }; private closeAndTryRemember(approved: Capability[]) { - if (this.state.rememberSelection) { - setRememberedCapabilitiesForWidget(this.props.widget, approved); - } - this.props.onFinished({ approved }); + this.props.onFinished({ approved, remember: this.state.rememberSelection }); } public render() { diff --git a/src/stores/widgets/StopGapWidgetDriver.ts b/src/stores/widgets/StopGapWidgetDriver.ts index 13cd260ef0..af43c645a9 100644 --- a/src/stores/widgets/StopGapWidgetDriver.ts +++ b/src/stores/widgets/StopGapWidgetDriver.ts @@ -33,9 +33,7 @@ import { MatrixClientPeg } from "../../MatrixClientPeg"; import ActiveRoomObserver from "../../ActiveRoomObserver"; import Modal from "../../Modal"; import WidgetOpenIDPermissionsDialog from "../../components/views/dialogs/WidgetOpenIDPermissionsDialog"; -import WidgetCapabilitiesPromptDialog, { - getRememberedCapabilitiesForWidget, -} from "../../components/views/dialogs/WidgetCapabilitiesPromptDialog"; +import WidgetCapabilitiesPromptDialog from "../../components/views/dialogs/WidgetCapabilitiesPromptDialog"; import { WidgetPermissionCustomisations } from "../../customisations/WidgetPermissions"; import { OIDCState, WidgetPermissionStore } from "./WidgetPermissionStore"; import { WidgetType } from "../../widgets/WidgetType"; @@ -48,6 +46,14 @@ import { MatrixEvent } from "matrix-js-sdk/src/models/event"; // TODO: Purge this from the universe +function getRememberedCapabilitiesForWidget(widget: Widget): Capability[] { + return JSON.parse(localStorage.getItem(`widget_${widget.id}_approved_caps`) || "[]"); +} + +function setRememberedCapabilitiesForWidget(widget: Widget, caps: Capability[]) { + localStorage.setItem(`widget_${widget.id}_approved_caps`, JSON.stringify(caps)); +} + export class StopGapWidgetDriver extends WidgetDriver { private allowedCapabilities: Set; @@ -100,6 +106,7 @@ export class StopGapWidgetDriver extends WidgetDriver { } } // TODO: Do something when the widget requests new capabilities not yet asked for + let rememberApproved = false; if (missing.size > 0) { try { const [result] = await Modal.createTrackedDialog( @@ -111,12 +118,19 @@ export class StopGapWidgetDriver extends WidgetDriver { widgetKind: this.forWidgetKind, }).finished; (result.approved || []).forEach(cap => allowedSoFar.add(cap)); + rememberApproved = result.remember; } catch (e) { console.error("Non-fatal error getting capabilities: ", e); } } - return new Set(iterableUnion(allowedSoFar, requested)); + const allAllowed = new Set(iterableUnion(allowedSoFar, requested)); + + if (rememberApproved) { + setRememberedCapabilitiesForWidget(this.forWidget, Array.from(allAllowed)); + } + + return allAllowed; } public async sendEvent(eventType: string, content: any, stateKey: string = null): Promise { From cafa52c466e5e7831236e7d17b730edc84b864cc Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Wed, 25 Aug 2021 22:40:51 -0600 Subject: [PATCH 40/93] Special case redaction event sending from widgets per MSC2762 Fixes https://github.com/vector-im/element-web/issues/18573 See https://github.com/matrix-org/matrix-doc/pull/2762/commits/853bed215743f3e04af8683ea5e1ea2b47b7845b --- src/stores/widgets/StopGapWidgetDriver.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/stores/widgets/StopGapWidgetDriver.ts b/src/stores/widgets/StopGapWidgetDriver.ts index 13cd260ef0..57cf27764c 100644 --- a/src/stores/widgets/StopGapWidgetDriver.ts +++ b/src/stores/widgets/StopGapWidgetDriver.ts @@ -129,6 +129,9 @@ export class StopGapWidgetDriver extends WidgetDriver { if (stateKey !== null) { // state event r = await client.sendStateEvent(roomId, eventType, content, stateKey); + } else if (eventType === EventType.RoomRedaction) { + // special case: extract the `redacts` property and call redact + r = await client.redactEvent(roomId, content['redacts']); } else { // message event r = await client.sendEvent(roomId, eventType, content); From 4415a7037c87421ac00c73d6bf68de799ba37419 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Wed, 25 Aug 2021 23:10:19 -0600 Subject: [PATCH 41/93] Fix left panel widgets not remembering collapsed state Fixes https://github.com/vector-im/element-web/issues/17803 The `setExpanded` function takes a boolean, not a function. --- src/components/structures/LeftPanelWidget.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/structures/LeftPanelWidget.tsx b/src/components/structures/LeftPanelWidget.tsx index 144c0e3051..331e428355 100644 --- a/src/components/structures/LeftPanelWidget.tsx +++ b/src/components/structures/LeftPanelWidget.tsx @@ -115,7 +115,7 @@ const LeftPanelWidget: React.FC = () => { aria-expanded={expanded} aria-level={1} onClick={() => { - setExpanded(e => !e); + setExpanded(!expanded); }} > Date: Thu, 26 Aug 2021 08:19:44 +0100 Subject: [PATCH 42/93] Update copyright and method accessors --- res/css/views/rooms/_EventTile.scss | 1 + src/components/structures/ThreadPanel.tsx | 2 +- src/components/structures/ThreadView.tsx | 4 ++-- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/res/css/views/rooms/_EventTile.scss b/res/css/views/rooms/_EventTile.scss index 5648a164e4..494f14c9bd 100644 --- a/res/css/views/rooms/_EventTile.scss +++ b/res/css/views/rooms/_EventTile.scss @@ -697,6 +697,7 @@ $hover-select-border: 4px; align-items: center; gap: 6px; margin-bottom: 6px; + a { flex: 1; min-width: none; diff --git a/src/components/structures/ThreadPanel.tsx b/src/components/structures/ThreadPanel.tsx index 047e527f34..a0bccfdce9 100644 --- a/src/components/structures/ThreadPanel.tsx +++ b/src/components/structures/ThreadPanel.tsx @@ -1,5 +1,5 @@ /* -Copyright 2021 New Vector Ltd. +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. diff --git a/src/components/structures/ThreadView.tsx b/src/components/structures/ThreadView.tsx index 03609c66d0..2e69d7b0f4 100644 --- a/src/components/structures/ThreadView.tsx +++ b/src/components/structures/ThreadView.tsx @@ -1,5 +1,5 @@ /* -Copyright 2021 New Vector Ltd. +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. @@ -112,7 +112,7 @@ export default class ThreadView extends React.Component { } }; - public renderEventTile(event: MatrixEvent): JSX.Element { + private renderEventTile(event: MatrixEvent): JSX.Element { return Date: Thu, 26 Aug 2021 08:45:00 +0100 Subject: [PATCH 43/93] Remove unused renderEventTile method --- src/components/structures/ThreadView.tsx | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/components/structures/ThreadView.tsx b/src/components/structures/ThreadView.tsx index 2e69d7b0f4..951c821d5c 100644 --- a/src/components/structures/ThreadView.tsx +++ b/src/components/structures/ThreadView.tsx @@ -112,17 +112,6 @@ export default class ThreadView extends React.Component { } }; - private renderEventTile(event: MatrixEvent): JSX.Element { - return ; - } - public render(): JSX.Element { return ( Date: Thu, 26 Aug 2021 14:59:06 +0200 Subject: [PATCH 44/93] Fix areAnyCallsUnsilenced() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/CallHandler.tsx | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/CallHandler.tsx b/src/CallHandler.tsx index e11e828864..5b730acaec 100644 --- a/src/CallHandler.tsx +++ b/src/CallHandler.tsx @@ -250,7 +250,15 @@ export default class CallHandler extends EventEmitter { * @returns {boolean} */ private areAnyCallsUnsilenced(): boolean { - return this.calls.size > this.silencedCalls.size; + for (const call of this.calls.values()) { + if ( + call.state === CallState.Ringing && + !this.isCallSilenced(call.callId) + ) { + return true; + } + } + return false; } private async checkProtocols(maxTries) { From d013ae79b701664c197bdacde1f01cc46b60aa2f Mon Sep 17 00:00:00 2001 From: "J. Ryan Stinnett" Date: Thu, 26 Aug 2021 13:59:12 +0100 Subject: [PATCH 45/93] Fix changelog link colour back to blue https://github.com/matrix-org/matrix-react-sdk/pull/5698 changed all dialog links to green, which is not expected. In general, we use blue links for information and green links for actions. This resolves the regression by removing the general change and adjusting the new links added by the above PR to be green as desired there. Fixes https://github.com/vector-im/element-web/issues/18726 --- res/css/_common.scss | 5 ----- .../tabs/room/SecurityRoomSettingsTab.tsx | 20 +++++++++++-------- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/res/css/_common.scss b/res/css/_common.scss index 5fcc10add0..a16e7d4d8f 100644 --- a/res/css/_common.scss +++ b/res/css/_common.scss @@ -381,11 +381,6 @@ input[type=text]:focus, input[type=password]:focus, textarea:focus { font-size: $font-14px; color: $primary-content; word-wrap: break-word; - - a { - color: $accent-color; - cursor: pointer; - } } .mx_Dialog_buttons { diff --git a/src/components/views/settings/tabs/room/SecurityRoomSettingsTab.tsx b/src/components/views/settings/tabs/room/SecurityRoomSettingsTab.tsx index d9e97d570b..081b1a8698 100644 --- a/src/components/views/settings/tabs/room/SecurityRoomSettingsTab.tsx +++ b/src/components/views/settings/tabs/room/SecurityRoomSettingsTab.tsx @@ -149,10 +149,12 @@ export default class SecurityRoomSettingsTab extends React.Componentnew encrypted room for " + "the conversation you plan to have.", null, - { "a": (sub) => { - dialog.close(); - this.createNewRoom(false, true); - }}> { sub } }, + { "a": (sub) => { + dialog.close(); + this.createNewRoom(false, true); + }}> { sub } }, ) }

, @@ -248,10 +250,12 @@ export default class SecurityRoomSettingsTab extends React.Component { - dialog.close(); - this.createNewRoom(true, false); - }}> { sub } , + "a": (sub) => { + dialog.close(); + this.createNewRoom(true, false); + }}> { sub } , }, ) }

, From e4f8c795bc3af11ef5fa40c4489e2fb1049ffdd6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Thu, 26 Aug 2021 15:00:36 +0200 Subject: [PATCH 46/93] Add stopRingingIfPossible() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/CallHandler.tsx | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/CallHandler.tsx b/src/CallHandler.tsx index 5b730acaec..ad7209052d 100644 --- a/src/CallHandler.tsx +++ b/src/CallHandler.tsx @@ -937,6 +937,12 @@ export default class CallHandler extends EventEmitter { } }; + private stopRingingIfPossible(callId: string): void { + this.silencedCalls.delete(callId); + if (this.areAnyCallsUnsilenced()) return; + this.pause(AudioID.Ring); + } + private async dialNumber(number: string) { const results = await this.pstnLookup(number); if (!results || results.length === 0 || !results[0].userid) { From 242b4e73b9803e41774ad911c76ab7c060df47e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Thu, 26 Aug 2021 15:00:56 +0200 Subject: [PATCH 47/93] Pause ringing more aggressively MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/CallHandler.tsx | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/CallHandler.tsx b/src/CallHandler.tsx index ad7209052d..8fd45c49ae 100644 --- a/src/CallHandler.tsx +++ b/src/CallHandler.tsx @@ -886,6 +886,8 @@ export default class CallHandler extends EventEmitter { break; case 'hangup': case 'reject': + this.stopRingingIfPossible(this.calls.get(payload.room_id).callId); + if (!this.calls.get(payload.room_id)) { return; // no call to hangup } @@ -898,11 +900,15 @@ export default class CallHandler extends EventEmitter { // the hangup event away) break; case 'hangup_all': + this.stopRingingIfPossible(this.calls.get(payload.room_id).callId); + for (const call of this.calls.values()) { call.hangup(CallErrorCode.UserHangup, false); } break; case 'answer': { + this.stopRingingIfPossible(this.calls.get(payload.room_id).callId); + if (!this.calls.has(payload.room_id)) { return; // no call to answer } From 9299c5b1a51166f4f4553bcbc4f288f2d59177cc Mon Sep 17 00:00:00 2001 From: Dariusz Niemczyk Date: Thu, 26 Aug 2021 14:55:37 +0200 Subject: [PATCH 48/93] Fix commit edit history Fix https://github.com/vector-im/element-web/issues/18742 --- res/css/structures/_ScrollPanel.scss | 2 -- 1 file changed, 2 deletions(-) diff --git a/res/css/structures/_ScrollPanel.scss b/res/css/structures/_ScrollPanel.scss index 82caeae29d..a668594bba 100644 --- a/res/css/structures/_ScrollPanel.scss +++ b/res/css/structures/_ScrollPanel.scss @@ -15,8 +15,6 @@ limitations under the License. */ .mx_ScrollPanel { - contain: strict; - .mx_RoomView_MessageList { position: relative; display: flex; From fc1dd510463a6e0309c31fe57f9aa5ea9b1edf79 Mon Sep 17 00:00:00 2001 From: Dariusz Niemczyk Date: Thu, 26 Aug 2021 14:55:37 +0200 Subject: [PATCH 49/93] Fix commit edit history Fix https://github.com/vector-im/element-web/issues/18742 --- res/css/structures/_ScrollPanel.scss | 2 -- 1 file changed, 2 deletions(-) diff --git a/res/css/structures/_ScrollPanel.scss b/res/css/structures/_ScrollPanel.scss index 82caeae29d..a668594bba 100644 --- a/res/css/structures/_ScrollPanel.scss +++ b/res/css/structures/_ScrollPanel.scss @@ -15,8 +15,6 @@ limitations under the License. */ .mx_ScrollPanel { - contain: strict; - .mx_RoomView_MessageList { position: relative; display: flex; From 6003e12e486d9278867407fa55deafb4e949dc43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Thu, 26 Aug 2021 15:12:26 +0200 Subject: [PATCH 50/93] Fix color name MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/css/views/rooms/_EventTile.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/res/css/views/rooms/_EventTile.scss b/res/css/views/rooms/_EventTile.scss index 64008346ca..447c878f14 100644 --- a/res/css/views/rooms/_EventTile.scss +++ b/res/css/views/rooms/_EventTile.scss @@ -514,7 +514,7 @@ $hover-select-border: 4px; .mx_EventTile:hover .mx_EventTile_body pre, .mx_EventTile.focus-visible:focus-within .mx_EventTile_body pre { - border: 1px solid $tertiary-fg-color; + border: 1px solid $tertiary-content; } .mx_EventTile_pre_container { From 54b93f44d0c7250436e4006c45dcd04c8cd2f9a0 Mon Sep 17 00:00:00 2001 From: RiotRobot Date: Thu, 26 Aug 2021 14:19:19 +0100 Subject: [PATCH 51/93] v3.29.0-rc.3 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index bb2f3432d4..26a88bd0d9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "matrix-react-sdk", - "version": "3.29.0-rc.2", + "version": "3.29.0-rc.3", "description": "SDK for matrix.org using React", "author": "matrix.org", "repository": { From a8b86da0584594606a9f8be673d7cb1aae4c9d2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Thu, 26 Aug 2021 18:07:37 +0200 Subject: [PATCH 52/93] Set the new layout setting when reading it MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/settings/handlers/DeviceSettingsHandler.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/settings/handlers/DeviceSettingsHandler.ts b/src/settings/handlers/DeviceSettingsHandler.ts index e4ad4cb51e..e57862a824 100644 --- a/src/settings/handlers/DeviceSettingsHandler.ts +++ b/src/settings/handlers/DeviceSettingsHandler.ts @@ -71,7 +71,13 @@ export default class DeviceSettingsHandler extends SettingsHandler { // Special case for old useIRCLayout setting if (settingName === "layout") { const settings = this.getSettings() || {}; - if (settings["useIRCLayout"]) return Layout.IRC; + if (settings["useIRCLayout"]) { + // Set the new layout setting and delete the old one so that we + // can delete this block of code after some time + settings["layout"] = Layout.IRC; + delete settings["useIRCLayout"]; + localStorage.setItem("mx_local_settings", JSON.stringify(settings)); + } return settings[settingName]; } From 20b62191219f07545a7ba23067d2932a2dab7a02 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 26 Aug 2021 13:28:48 -0600 Subject: [PATCH 53/93] Don't send prehistorical events to widgets during decryption at startup Fixes https://github.com/vector-im/element-web/issues/18060 Tracking a localized read receipt of sorts appears to be the fastest and least complex approach, though not the greatest. --- src/stores/widgets/StopGapWidget.ts | 47 +++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/src/stores/widgets/StopGapWidget.ts b/src/stores/widgets/StopGapWidget.ts index daa1e0e787..f0c64b1d4a 100644 --- a/src/stores/widgets/StopGapWidget.ts +++ b/src/stores/widgets/StopGapWidget.ts @@ -55,6 +55,7 @@ import { MatrixEvent } from "matrix-js-sdk/src/models/event"; import { ELEMENT_CLIENT_ID } from "../../identifiers"; import { getUserLanguage } from "../../languageHandler"; import { WidgetVariableCustomisations } from "../../customisations/WidgetVariables"; +import { arrayFastClone } from "../../utils/arrays"; // TODO: Destroy all of this code @@ -146,6 +147,7 @@ export class StopGapWidget extends EventEmitter { private scalarToken: string; private roomId?: string; private kind: WidgetKind; + private readUpToMap: {[roomId: string]: string} = {}; // room ID to event ID constructor(private appTileProps: IAppTileProps) { super(); @@ -294,6 +296,14 @@ export class StopGapWidget extends EventEmitter { this.messaging.transport.reply(ev.detail, {}); }); + // Populate the map of "read up to" events for this widget with the current event in every room. + // This is a bit inefficient, but should be okay. We do this for all rooms in case the widget + // requests timeline capabilities in other rooms down the road. It's just easier to manage here. + for (const room of MatrixClientPeg.get().getRooms()) { + // Timelines are most recent last + this.readUpToMap[room.roomId] = arrayFastClone(room.getLiveTimeline().getEvents()).reverse()[0].getId(); + } + // Attach listeners for feeding events - the underlying widget classes handle permissions for us MatrixClientPeg.get().on('event', this.onEvent); MatrixClientPeg.get().on('Event.decrypted', this.onEventDecrypted); @@ -421,6 +431,43 @@ export class StopGapWidget extends EventEmitter { private feedEvent(ev: MatrixEvent) { if (!this.messaging) return; + // Check to see if this event would be before or after our "read up to" marker. If it's + // before, or we can't decide, then we assume the widget will have already seen the event. + // If the event is after, or we don't have a marker for the room, then we'll send it through. + // + // This approach of "read up to" prevents widgets receiving decryption spam from startup or + // receiving out-of-order events from backfill and such. + const upToEventId = this.readUpToMap[ev.getRoomId()]; + if (upToEventId) { + // Small optimization for exact match (prevent search) + if (upToEventId === ev.getId()) { + return; + } + + let isBeforeMark = true; + + // Timelines are most recent last, so reverse the order and limit ourselves to 100 events + // to avoid overusing the CPU. + const timeline = MatrixClientPeg.get().getRoom(ev.getRoomId()).getLiveTimeline(); + const events = arrayFastClone(timeline.getEvents()).reverse().slice(0, 100); + + for (const timelineEvent of events) { + if (timelineEvent.getId() === upToEventId) { + break; + } else if (timelineEvent.getId() === ev.getId()) { + isBeforeMark = false; + break; + } + } + + if (isBeforeMark) { + // Ignore the event: it is before our interest. + return; + } + } + + this.readUpToMap[ev.getRoomId()] = ev.getId(); + const raw = ev.getEffectiveEvent(); this.messaging.feedEvent(raw).catch(e => { console.error("Error sending event to widget: ", e); From 289b1c5ce008d8407082caf7e74a0d2f4be0b49e Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Thu, 26 Aug 2021 15:47:49 -0600 Subject: [PATCH 54/93] Always trigger the first amplitude capture from the worklet Fixes https://github.com/vector-im/element-web/issues/18587 See diff --- src/audio/RecorderWorklet.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/audio/RecorderWorklet.ts b/src/audio/RecorderWorklet.ts index 2d1bb0bcd2..73b053db93 100644 --- a/src/audio/RecorderWorklet.ts +++ b/src/audio/RecorderWorklet.ts @@ -45,7 +45,13 @@ class MxVoiceWorklet extends AudioWorkletProcessor { process(inputs, outputs, parameters) { const currentSecond = roundTimeToTargetFreq(currentTime); - if (currentSecond === this.nextAmplitudeSecond) { + // We special case the first ping because there's a fairly good chance that we'll miss the zeroth + // update. Firefox for instance takes 0.06 seconds (roughly) to call this function for the first + // time. Edge and Chrome occasionally lag behind too, but for the most part are on time. + // + // When this doesn't work properly we end up producing a waveform of nulls and no live preview + // of the recorded message. + if (currentSecond === this.nextAmplitudeSecond || this.nextAmplitudeSecond === 0) { // We're expecting exactly one mono input source, so just grab the very first frame of // samples for the analysis. const monoChan = inputs[0][0]; From 464d2b47b08393bca08da954f970e1c525607030 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Fri, 27 Aug 2021 09:16:39 +0200 Subject: [PATCH 55/93] Default to Don't leave any MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/views/dialogs/LeaveSpaceDialog.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/views/dialogs/LeaveSpaceDialog.tsx b/src/components/views/dialogs/LeaveSpaceDialog.tsx index 6e1e798e9d..3a8cd53945 100644 --- a/src/components/views/dialogs/LeaveSpaceDialog.tsx +++ b/src/components/views/dialogs/LeaveSpaceDialog.tsx @@ -80,7 +80,7 @@ const SpaceChildPicker = ({ filterPlaceholder, rooms, selected, onChange }) => { const LeaveRoomsPicker = ({ space, spaceChildren, roomsToLeave, setRoomsToLeave }) => { const selected = useMemo(() => new Set(roomsToLeave), [roomsToLeave]); - const [state, setState] = useState(RoomsToLeave.All); + const [state, setState] = useState(RoomsToLeave.None); useEffect(() => { if (state === RoomsToLeave.All) { @@ -97,11 +97,11 @@ const LeaveRoomsPicker = ({ space, spaceChildren, roomsToLeave, setRoomsToLeave onChange={setState} definitions={[ { - value: RoomsToLeave.All, - label: _t("Leave all rooms and spaces"), - }, { value: RoomsToLeave.None, label: _t("Don't leave any"), + }, { + value: RoomsToLeave.All, + label: _t("Leave all rooms and spaces"), }, { value: RoomsToLeave.Specific, label: _t("Leave specific rooms and spaces"), From 0fae3ed23324e1ccb53fec16121be3b00e8dd405 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Fri, 27 Aug 2021 09:22:30 +0200 Subject: [PATCH 56/93] i18n MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/i18n/strings/en_EN.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index 21859fb1aa..a3d9fd5203 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -2411,8 +2411,8 @@ "Clear cache and resync": "Clear cache and resync", "%(brand)s now uses 3-5x less memory, by only loading information about other users when needed. Please wait whilst we resynchronise with the server!": "%(brand)s now uses 3-5x less memory, by only loading information about other users when needed. Please wait whilst we resynchronise with the server!", "Updating %(brand)s": "Updating %(brand)s", - "Leave all rooms and spaces": "Leave all rooms and spaces", "Don't leave any": "Don't leave any", + "Leave all rooms and spaces": "Leave all rooms and spaces", "Leave specific rooms and spaces": "Leave specific rooms and spaces", "Search %(spaceName)s": "Search %(spaceName)s", "You won't be able to rejoin unless you are re-invited.": "You won't be able to rejoin unless you are re-invited.", From 816f0f5e9027b91442b844f28a32cdcb0346f9eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Fri, 27 Aug 2021 09:56:53 +0200 Subject: [PATCH 57/93] Avoid stacked dialogs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/views/spaces/SpacePublicShare.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/spaces/SpacePublicShare.tsx b/src/components/views/spaces/SpacePublicShare.tsx index 39e5115e55..cb282d8d1f 100644 --- a/src/components/views/spaces/SpacePublicShare.tsx +++ b/src/components/views/spaces/SpacePublicShare.tsx @@ -54,8 +54,8 @@ const SpacePublicShare = ({ space, onFinished }: IProps) => { { space.canInvite(MatrixClientPeg.get()?.getUserId()) ? { - showRoomInviteDialog(space.roomId); if (onFinished) onFinished(); + showRoomInviteDialog(space.roomId); }} >

{ _t("Invite people") }

From bf3c8e56646f0c394327984339e72cd4e016f6a2 Mon Sep 17 00:00:00 2001 From: Germain Souquet Date: Fri, 27 Aug 2021 10:38:01 +0100 Subject: [PATCH 58/93] Fix imports --- src/components/structures/ThreadView.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/structures/ThreadView.tsx b/src/components/structures/ThreadView.tsx index 951c821d5c..a2595debc8 100644 --- a/src/components/structures/ThreadView.tsx +++ b/src/components/structures/ThreadView.tsx @@ -23,7 +23,7 @@ import { RightPanelPhases } from "../../stores/RightPanelStorePhases"; import { replaceableComponent } from "../../utils/replaceableComponent"; import ResizeNotifier from '../../utils/ResizeNotifier'; -import EventTile, { TileShape } from '../views/rooms/EventTile'; +import { TileShape } from '../views/rooms/EventTile'; import MessageComposer from '../views/rooms/MessageComposer'; import { RoomPermalinkCreator } from '../../utils/permalinks/Permalinks'; import { Layout } from '../../settings/Layout'; From 6e30673164b41ecd2597e7d288930b2e17801364 Mon Sep 17 00:00:00 2001 From: Steffen Kolmer Date: Fri, 27 Aug 2021 19:23:26 +0200 Subject: [PATCH 59/93] Show spinner if user id is missing in event tile preview --- src/components/views/elements/EventTilePreview.tsx | 3 +++ .../views/settings/tabs/user/AppearanceUserSettingsTab.tsx | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/components/views/elements/EventTilePreview.tsx b/src/components/views/elements/EventTilePreview.tsx index 68a70133e6..d1386b63f4 100644 --- a/src/components/views/elements/EventTilePreview.tsx +++ b/src/components/views/elements/EventTilePreview.tsx @@ -25,6 +25,7 @@ import SettingsStore from "../../../settings/SettingsStore"; import { Layout } from "../../../settings/Layout"; import { UIFeature } from "../../../settings/UIFeature"; import { replaceableComponent } from "../../../utils/replaceableComponent"; +import InlineSpinner from './InlineSpinner'; interface IProps { /** @@ -118,6 +119,8 @@ export default class EventTilePreview extends React.Component { } public render() { + if (!this.props.userId) return
; + const event = this.fakeEvent(this.state); const className = classnames(this.props.className, { diff --git a/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.tsx b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.tsx index cbf0b7916c..47c0f712bd 100644 --- a/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.tsx +++ b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.tsx @@ -92,8 +92,8 @@ export default class AppearanceUserSettingsTab extends React.Component Date: Fri, 27 Aug 2021 19:32:38 +0200 Subject: [PATCH 60/93] Update props and state interfaces --- src/components/views/elements/EventTilePreview.tsx | 2 +- src/components/views/settings/LayoutSwitcher.tsx | 2 +- .../views/settings/tabs/user/AppearanceUserSettingsTab.tsx | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/views/elements/EventTilePreview.tsx b/src/components/views/elements/EventTilePreview.tsx index d1386b63f4..4e03508305 100644 --- a/src/components/views/elements/EventTilePreview.tsx +++ b/src/components/views/elements/EventTilePreview.tsx @@ -46,7 +46,7 @@ interface IProps { /** * The ID of the displayed user */ - userId: string; + userId?: string; /** * The display name of the displayed user diff --git a/src/components/views/settings/LayoutSwitcher.tsx b/src/components/views/settings/LayoutSwitcher.tsx index dd7accf9a8..ad8abd0033 100644 --- a/src/components/views/settings/LayoutSwitcher.tsx +++ b/src/components/views/settings/LayoutSwitcher.tsx @@ -26,7 +26,7 @@ import { Layout } from "../../../settings/Layout"; import { SettingLevel } from "../../../settings/SettingLevel"; interface IProps { - userId: string; + userId?: string; displayName: string; avatarUrl: string; messagePreviewText: string; diff --git a/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.tsx b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.tsx index 47c0f712bd..bc54a8155c 100644 --- a/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.tsx +++ b/src/components/views/settings/tabs/user/AppearanceUserSettingsTab.tsx @@ -67,7 +67,7 @@ interface IState extends IThemeState { showAdvanced: boolean; layout: Layout; // User profile data for the message preview - userId: string; + userId?: string; displayName: string; avatarUrl: string; } From 9b495eeec32b22f26129bcc29ab411804150bf39 Mon Sep 17 00:00:00 2001 From: Steffen Kolmer Date: Fri, 27 Aug 2021 20:10:01 +0200 Subject: [PATCH 61/93] Optimized style --- src/components/views/elements/EventTilePreview.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/views/elements/EventTilePreview.tsx b/src/components/views/elements/EventTilePreview.tsx index 4e03508305..3b4351f5e8 100644 --- a/src/components/views/elements/EventTilePreview.tsx +++ b/src/components/views/elements/EventTilePreview.tsx @@ -119,15 +119,15 @@ export default class EventTilePreview extends React.Component { } public render() { - if (!this.props.userId) return
; - - const event = this.fakeEvent(this.state); - const className = classnames(this.props.className, { "mx_IRCLayout": this.props.layout == Layout.IRC, "mx_GroupLayout": this.props.layout == Layout.Group, }); + if (!this.props.userId) return
; + + const event = this.fakeEvent(this.state); + return
Date: Fri, 27 Aug 2021 20:11:34 +0200 Subject: [PATCH 62/93] Fixed padding --- src/components/views/elements/EventTilePreview.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/elements/EventTilePreview.tsx b/src/components/views/elements/EventTilePreview.tsx index 3b4351f5e8..a433bd4b53 100644 --- a/src/components/views/elements/EventTilePreview.tsx +++ b/src/components/views/elements/EventTilePreview.tsx @@ -124,7 +124,7 @@ export default class EventTilePreview extends React.Component { "mx_GroupLayout": this.props.layout == Layout.Group, }); - if (!this.props.userId) return
; + if (!this.props.userId) return
; const event = this.fakeEvent(this.state); From f65eff103e34966012c13dbe61a214c36628a64a Mon Sep 17 00:00:00 2001 From: Steffen Kolmer Date: Fri, 27 Aug 2021 20:48:29 +0200 Subject: [PATCH 63/93] Switch from inline-spinner to spinner --- src/components/views/elements/EventTilePreview.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/views/elements/EventTilePreview.tsx b/src/components/views/elements/EventTilePreview.tsx index a433bd4b53..5bea8a0706 100644 --- a/src/components/views/elements/EventTilePreview.tsx +++ b/src/components/views/elements/EventTilePreview.tsx @@ -25,7 +25,7 @@ import SettingsStore from "../../../settings/SettingsStore"; import { Layout } from "../../../settings/Layout"; import { UIFeature } from "../../../settings/UIFeature"; import { replaceableComponent } from "../../../utils/replaceableComponent"; -import InlineSpinner from './InlineSpinner'; +import Spinner from './Spinner'; interface IProps { /** @@ -124,7 +124,7 @@ export default class EventTilePreview extends React.Component { "mx_GroupLayout": this.props.layout == Layout.Group, }); - if (!this.props.userId) return
; + if (!this.props.userId) return
; const event = this.fakeEvent(this.state); From 6da11f375dfb4c057a87ca50ca9acb04e6c60a59 Mon Sep 17 00:00:00 2001 From: Steffen Kolmer Date: Fri, 27 Aug 2021 21:44:34 +0200 Subject: [PATCH 64/93] Allow to use basic html formatting in invite reasons --- src/components/views/elements/InviteReason.tsx | 4 +++- src/components/views/rooms/RoomPreviewBar.js | 7 ++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/components/views/elements/InviteReason.tsx b/src/components/views/elements/InviteReason.tsx index dff5c7d6bd..865a5be747 100644 --- a/src/components/views/elements/InviteReason.tsx +++ b/src/components/views/elements/InviteReason.tsx @@ -16,11 +16,13 @@ limitations under the License. import classNames from "classnames"; import React from "react"; +import { sanitizedHtmlNode } from "../../../HtmlUtils"; import { _t } from "../../../languageHandler"; import { replaceableComponent } from "../../../utils/replaceableComponent"; interface IProps { reason: string; + htmlReason?: string; } interface IState { @@ -51,7 +53,7 @@ export default class InviteReason extends React.PureComponent { }); return
-
{ this.props.reason }
+
{ this.props.htmlReason ? sanitizedHtmlNode(this.props.htmlReason) : this.props.reason }
diff --git a/src/components/views/rooms/RoomPreviewBar.js b/src/components/views/rooms/RoomPreviewBar.js index b8a4315e2d..4358a2351f 100644 --- a/src/components/views/rooms/RoomPreviewBar.js +++ b/src/components/views/rooms/RoomPreviewBar.js @@ -492,9 +492,10 @@ export default class RoomPreviewBar extends React.Component { } const myUserId = MatrixClientPeg.get().getUserId(); - const reason = this.props.room.currentState.getMember(myUserId).events.member.event.content.reason; - if (reason) { - reasonElement = ; + const memberEventContent = this.props.room.currentState.getMember(myUserId).events.member.event.content; + + if (memberEventContent.reason) { + reasonElement = ; } primaryActionHandler = this.props.onJoinClick; From 70b4308bab3173a3ed3ee0b164c158dbfd0beba6 Mon Sep 17 00:00:00 2001 From: Steffen Kolmer Date: Fri, 27 Aug 2021 21:59:22 +0200 Subject: [PATCH 65/93] Fix eslint error --- src/components/views/rooms/RoomPreviewBar.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/components/views/rooms/RoomPreviewBar.js b/src/components/views/rooms/RoomPreviewBar.js index 4358a2351f..c73c066338 100644 --- a/src/components/views/rooms/RoomPreviewBar.js +++ b/src/components/views/rooms/RoomPreviewBar.js @@ -495,7 +495,10 @@ export default class RoomPreviewBar extends React.Component { const memberEventContent = this.props.room.currentState.getMember(myUserId).events.member.event.content; if (memberEventContent.reason) { - reasonElement = ; + reasonElement = ; } primaryActionHandler = this.props.onJoinClick; From d76adde1aba4fc6709dec5bb37a8db135a6d482e Mon Sep 17 00:00:00 2001 From: Steffen Kolmer Date: Sat, 28 Aug 2021 13:41:24 +0200 Subject: [PATCH 66/93] Use scss instead of inline styles --- res/css/_components.scss | 1 + res/css/views/elements/_EventTilePreview.scss | 22 +++++++++++++++++++ .../views/elements/EventTilePreview.tsx | 2 +- 3 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 res/css/views/elements/_EventTilePreview.scss diff --git a/res/css/_components.scss b/res/css/_components.scss index 566b84a7c8..ffaec43b68 100644 --- a/res/css/_components.scss +++ b/res/css/_components.scss @@ -132,6 +132,7 @@ @import "./views/elements/_EditableItemList.scss"; @import "./views/elements/_ErrorBoundary.scss"; @import "./views/elements/_EventListSummary.scss"; +@import "./views/elements/_EventTilePreview.scss"; @import "./views/elements/_FacePile.scss"; @import "./views/elements/_Field.scss"; @import "./views/elements/_ImageView.scss"; diff --git a/res/css/views/elements/_EventTilePreview.scss b/res/css/views/elements/_EventTilePreview.scss new file mode 100644 index 0000000000..a8c0deabbe --- /dev/null +++ b/res/css/views/elements/_EventTilePreview.scss @@ -0,0 +1,22 @@ +/* +Copyright 2016 OpenMarket 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. +*/ + +.mx_EventTilePreview_loader { + &.mx_IRCLayout, + &.mx_GroupLayout { + padding: 9px 0; + } +} diff --git a/src/components/views/elements/EventTilePreview.tsx b/src/components/views/elements/EventTilePreview.tsx index 5bea8a0706..183ac09268 100644 --- a/src/components/views/elements/EventTilePreview.tsx +++ b/src/components/views/elements/EventTilePreview.tsx @@ -124,7 +124,7 @@ export default class EventTilePreview extends React.Component { "mx_GroupLayout": this.props.layout == Layout.Group, }); - if (!this.props.userId) return
; + if (!this.props.userId) return
; const event = this.fakeEvent(this.state); From e661c017e3e5498af5f70119db70d0fa00c57f21 Mon Sep 17 00:00:00 2001 From: Steffen Kolmer Date: Sat, 28 Aug 2021 13:48:33 +0200 Subject: [PATCH 67/93] Moved custom field name to a const --- src/components/views/rooms/RoomPreviewBar.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/components/views/rooms/RoomPreviewBar.js b/src/components/views/rooms/RoomPreviewBar.js index c73c066338..f3201d2a3d 100644 --- a/src/components/views/rooms/RoomPreviewBar.js +++ b/src/components/views/rooms/RoomPreviewBar.js @@ -28,6 +28,8 @@ import { UPDATE_EVENT } from "../../../stores/AsyncStore"; import { replaceableComponent } from "../../../utils/replaceableComponent"; import InviteReason from "../elements/InviteReason"; +const MemberEventHtmlReasonField = "io.element.html_reason"; + const MessageCase = Object.freeze({ NotLoggedIn: "NotLoggedIn", Joining: "Joining", @@ -497,7 +499,7 @@ export default class RoomPreviewBar extends React.Component { if (memberEventContent.reason) { reasonElement = ; } From 9fdc5659b7df4bbb8703f8144cc08aeb043d1c09 Mon Sep 17 00:00:00 2001 From: Steffen Kolmer Date: Sat, 28 Aug 2021 13:56:08 +0200 Subject: [PATCH 68/93] Fixed eslint error and copyright header --- res/css/views/elements/_EventTilePreview.scss | 2 +- src/components/views/elements/EventTilePreview.tsx | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/res/css/views/elements/_EventTilePreview.scss b/res/css/views/elements/_EventTilePreview.scss index a8c0deabbe..6bb726168f 100644 --- a/res/css/views/elements/_EventTilePreview.scss +++ b/res/css/views/elements/_EventTilePreview.scss @@ -1,5 +1,5 @@ /* -Copyright 2016 OpenMarket Ltd +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. diff --git a/src/components/views/elements/EventTilePreview.tsx b/src/components/views/elements/EventTilePreview.tsx index 183ac09268..8928c45fc8 100644 --- a/src/components/views/elements/EventTilePreview.tsx +++ b/src/components/views/elements/EventTilePreview.tsx @@ -124,7 +124,11 @@ export default class EventTilePreview extends React.Component { "mx_GroupLayout": this.props.layout == Layout.Group, }); - if (!this.props.userId) return
; + if (!this.props.userId) { + return
+ +
; + } const event = this.fakeEvent(this.state); From 3ef9584f688d0df87b40810b626213a4a2b1078e Mon Sep 17 00:00:00 2001 From: Steffen Kolmer Date: Sat, 28 Aug 2021 14:00:18 +0200 Subject: [PATCH 69/93] Simplify things --- src/components/views/elements/EventTilePreview.tsx | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/components/views/elements/EventTilePreview.tsx b/src/components/views/elements/EventTilePreview.tsx index 8928c45fc8..bea060fcca 100644 --- a/src/components/views/elements/EventTilePreview.tsx +++ b/src/components/views/elements/EventTilePreview.tsx @@ -122,13 +122,10 @@ export default class EventTilePreview extends React.Component { const className = classnames(this.props.className, { "mx_IRCLayout": this.props.layout == Layout.IRC, "mx_GroupLayout": this.props.layout == Layout.Group, + "mx_EventTilePreview_loader": !this.props.userId }); - if (!this.props.userId) { - return
- -
; - } + if (!this.props.userId) return
; const event = this.fakeEvent(this.state); From 6a550f2f9c01035fc01e2e0fe2e191285d986285 Mon Sep 17 00:00:00 2001 From: Steffen Kolmer Date: Sat, 28 Aug 2021 14:04:00 +0200 Subject: [PATCH 70/93] Added trailing comma to make eslint happy --- src/components/views/elements/EventTilePreview.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/elements/EventTilePreview.tsx b/src/components/views/elements/EventTilePreview.tsx index bea060fcca..a7ebf40c3a 100644 --- a/src/components/views/elements/EventTilePreview.tsx +++ b/src/components/views/elements/EventTilePreview.tsx @@ -122,7 +122,7 @@ export default class EventTilePreview extends React.Component { const className = classnames(this.props.className, { "mx_IRCLayout": this.props.layout == Layout.IRC, "mx_GroupLayout": this.props.layout == Layout.Group, - "mx_EventTilePreview_loader": !this.props.userId + "mx_EventTilePreview_loader": !this.props.userId, }); if (!this.props.userId) return
; From 5b65528038b97c6c70b06e1a7a1474fd54f2075d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Sun, 29 Aug 2021 13:42:12 +0200 Subject: [PATCH 71/93] Make GH actions clone from the correct fork and cleanup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- .github/workflows/develop.yml | 2 ++ scripts/fetchdep.sh | 60 ++++++++++++++++------------------- 2 files changed, 29 insertions(+), 33 deletions(-) diff --git a/.github/workflows/develop.yml b/.github/workflows/develop.yml index 0ae59da09a..4f9826391a 100644 --- a/.github/workflows/develop.yml +++ b/.github/workflows/develop.yml @@ -10,6 +10,8 @@ on: jobs: end-to-end: runs-on: ubuntu-latest + env: + PR_NUMBER: ${{github.event.number}} container: vectorim/element-web-ci-e2etests-env:latest steps: - name: Checkout code diff --git a/scripts/fetchdep.sh b/scripts/fetchdep.sh index 0990af70ce..97be9c2414 100755 --- a/scripts/fetchdep.sh +++ b/scripts/fetchdep.sh @@ -10,6 +10,7 @@ defbranch="$3" rm -r "$defrepo" || true +# A fnction that clones a branch of a repo based on the org, repo and branch clone() { org=$1 repo=$2 @@ -21,45 +22,38 @@ clone() { fi } -# Try the PR author's branch in case it exists on the deps as well. -# First we check if GITHUB_HEAD_REF is defined, -# Then we check if BUILDKITE_BRANCH is defined, -# if they aren't we can assume this is a Netlify build -if [ -n "$GITHUB_HEAD_REF" ]; then - head=$GITHUB_HEAD_REF -elif [ -n "$BUILDKITE_BRANCH" ]; then - head=$BUILDKITE_BRANCH -else - # Netlify doesn't give us info about the fork so we have to get it from GitHub API - apiEndpoint="https://api.github.com/repos/matrix-org/matrix-react-sdk/pulls/" - apiEndpoint+=$REVIEW_ID - head=$(curl $apiEndpoint | jq -r '.head.label') -fi +# A function that gets info about a PR from the GitHub API based on its number +getPRInfo() { + number=$1 + if [ -n "$number" ]; then + echo "Getting info about a PR with number $number" -# If head is set, it will contain on Buildkite either: -# * "branch" when the author's branch and target branch are in the same repo -# * "fork:branch" when the author's branch is in their fork or if this is a Netlify build -# We can split on `:` into an array to check. -# For GitHub Actions we need to inspect GITHUB_REPOSITORY and GITHUB_ACTOR -# to determine whether the branch is from a fork or not -BRANCH_ARRAY=(${head//:/ }) -if [[ "${#BRANCH_ARRAY[@]}" == "1" ]]; then + apiEndpoint="https://api.github.com/repos/matrix-org/matrix-react-sdk/pulls/" + apiEndpoint+=$number - if [ -n "$GITHUB_HEAD_REF" ]; then - if [[ "$GITHUB_REPOSITORY" == "$deforg"* ]]; then - clone $deforg $defrepo $GITHUB_HEAD_REF - else - REPO_ARRAY=(${GITHUB_REPOSITORY//\// }) - clone $REPO_ARRAY[0] $defrepo $GITHUB_HEAD_REF - fi - else - clone $deforg $defrepo $BUILDKITE_BRANCH + head=$(curl $apiEndpoint | jq -r '.head.label') fi +} -elif [[ "${#BRANCH_ARRAY[@]}" == "2" ]]; then - clone ${BRANCH_ARRAY[0]} $defrepo ${BRANCH_ARRAY[1]} +# Some CIs don't give us enough info, so we just get the PR number and ask the +# GH API for more info - "fork:branch". Some give us this directly. +if [ -n "$BUILDKITE_BRANCH" ]; then + # BuildKite + head=$BUILDKITE_BRANCH +elif [ -n "$PR_NUMBER" ]; then + # GitHub + getPRInfo $PR_NUMBER +elif [ -n "$REVIEW_ID" ]; then + # Netlify + getPRInfo $REVIEW_ID fi +# $head will always be in the format "fork:branch", so we split it by ":" into +# an array. The first element will then be the fork and the second the branch. +# Based on that we clone +BRANCH_ARRAY=(${head//:/ }) +clone ${BRANCH_ARRAY[0]} $defrepo ${BRANCH_ARRAY[1]} + # Try the target branch of the push or PR. if [ -n $GITHUB_BASE_REF ]; then clone $deforg $defrepo $GITHUB_BASE_REF From 9684d7263b1018d4e60ce2fa18408b98bba309dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Mon, 30 Aug 2021 10:09:57 +0200 Subject: [PATCH 72/93] Filter members on load MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/views/rooms/MemberList.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/views/rooms/MemberList.tsx b/src/components/views/rooms/MemberList.tsx index 415d7e942b..df4f2d21fa 100644 --- a/src/components/views/rooms/MemberList.tsx +++ b/src/components/views/rooms/MemberList.tsx @@ -185,8 +185,8 @@ export default class MemberList extends React.Component { return { loading: false, members: members, - filteredJoinedMembers: this.filterMembers(members, 'join'), - filteredInvitedMembers: this.filterMembers(members, 'invite'), + filteredJoinedMembers: this.filterMembers(members, 'join', searchQuery), + filteredInvitedMembers: this.filterMembers(members, 'invite', searchQuery), canInvite: this.canInvite, // ideally we'd size this to the page height, but From ef30db41255b26d882bb1a5560a0c66eead0e9e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Mon, 30 Aug 2021 10:09:57 +0200 Subject: [PATCH 73/93] Filter members on load MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/views/rooms/MemberList.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/views/rooms/MemberList.tsx b/src/components/views/rooms/MemberList.tsx index 415d7e942b..df4f2d21fa 100644 --- a/src/components/views/rooms/MemberList.tsx +++ b/src/components/views/rooms/MemberList.tsx @@ -185,8 +185,8 @@ export default class MemberList extends React.Component { return { loading: false, members: members, - filteredJoinedMembers: this.filterMembers(members, 'join'), - filteredInvitedMembers: this.filterMembers(members, 'invite'), + filteredJoinedMembers: this.filterMembers(members, 'join', searchQuery), + filteredInvitedMembers: this.filterMembers(members, 'invite', searchQuery), canInvite: this.canInvite, // ideally we'd size this to the page height, but From 09b40870d4f9b455b91704cca18724c743de5385 Mon Sep 17 00:00:00 2001 From: RiotRobot Date: Tue, 31 Aug 2021 13:51:17 +0100 Subject: [PATCH 74/93] Upgrade matrix-js-sdk to 12.4.0 --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 26a88bd0d9..8fe928f162 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": "12.4.0-rc.1", + "matrix-js-sdk": "12.4.0", "matrix-widget-api": "^0.1.0-beta.15", "minimist": "^1.2.5", "opus-recorder": "^8.0.3", diff --git a/yarn.lock b/yarn.lock index 74883d7059..7fe5e3c143 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5801,10 +5801,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@12.4.0-rc.1: - version "12.4.0-rc.1" - resolved "https://registry.yarnpkg.com/matrix-js-sdk/-/matrix-js-sdk-12.4.0-rc.1.tgz#6eee9f159bcc5dd968346a9ce5c7b7427636f57c" - integrity sha512-6AGIEJvgWlNYyzxPj/V13s9JUg8zsiJ9Hyj6yTpDahufkAUT5yhpFzdPNNRaLlVxHox5HlwJJBdyNQtML727Og== +matrix-js-sdk@12.4.0: + version "12.4.0" + resolved "https://registry.yarnpkg.com/matrix-js-sdk/-/matrix-js-sdk-12.4.0.tgz#ff60306f9a9e39fd1ae6c7e501001f80eb779dd7" + integrity sha512-KamHmvNle4mkdErmNgVsGIL3n8/zgPe60DLVaEA2t4aSNwQLEmRS+oVpIgsO3ZrUivBvn4oc9sBqxP0OIl6kUg== dependencies: "@babel/runtime" "^7.12.5" another-json "^0.2.0" From 99fdd57bd37bc3d703ce83c5bca0d00580cc4087 Mon Sep 17 00:00:00 2001 From: RiotRobot Date: Tue, 31 Aug 2021 13:52:40 +0100 Subject: [PATCH 75/93] Prepare changelog for v3.29.0 --- CHANGELOG.md | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1fdcdfa96a..42e186220f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,13 +1,8 @@ -Changes in [3.29.0-rc.2](https://github.com/vector-im/element-desktop/releases/tag/v3.29.0-rc.2) (2021-08-25) -============================================================================================================= +Changes in [3.29.0](https://github.com/vector-im/element-desktop/releases/tag/v3.29.0) (2021-08-31) +=================================================================================================== ## ✨ Features * [Release]Increase general app performance by optimizing layers ([\#6672](https://github.com/matrix-org/matrix-react-sdk/pull/6672)). Fixes vector-im/element-web#18730 and vector-im/element-web#18730. Contributed by [Palid](https://github.com/Palid). - -Changes in [3.29.0-rc.1](https://github.com/vector-im/element-desktop/releases/tag/v3.29.0-rc.1) (2021-08-24) -============================================================================================================= - -## ✨ Features * Add a warning on E2EE rooms if you try to make them public ([\#5698](https://github.com/matrix-org/matrix-react-sdk/pull/5698)). Contributed by [SimonBrandner](https://github.com/SimonBrandner). * Allow pagination of the space hierarchy and use new APIs ([\#6507](https://github.com/matrix-org/matrix-react-sdk/pull/6507)). Fixes vector-im/element-web#18089 and vector-im/element-web#18427. * Improve emoji in composer ([\#6650](https://github.com/matrix-org/matrix-react-sdk/pull/6650)). Fixes vector-im/element-web#18593 and vector-im/element-web#18593. Contributed by [SimonBrandner](https://github.com/SimonBrandner). @@ -23,6 +18,7 @@ Changes in [3.29.0-rc.1](https://github.com/vector-im/element-desktop/releases/t * Accessibility improvements in and around Spaces ([\#6569](https://github.com/matrix-org/matrix-react-sdk/pull/6569)). Fixes vector-im/element-web#18094 and vector-im/element-web#18094. ## 🐛 Bug Fixes + * [Release] Fix commit edit history ([\#6690](https://github.com/matrix-org/matrix-react-sdk/pull/6690)). Fixes vector-im/element-web#18742 and vector-im/element-web#18742. Contributed by [Palid](https://github.com/Palid). * Fix images not rendering when sent from other clients. ([\#6661](https://github.com/matrix-org/matrix-react-sdk/pull/6661)). Fixes vector-im/element-web#18702 and vector-im/element-web#18702. * Fix autocomplete scrollbar and make the autocomplete a little smaller ([\#6655](https://github.com/matrix-org/matrix-react-sdk/pull/6655)). Fixes vector-im/element-web#18682 and vector-im/element-web#18682. Contributed by [SimonBrandner](https://github.com/SimonBrandner). * Fix replies on the bubble layout ([\#6451](https://github.com/matrix-org/matrix-react-sdk/pull/6451)). Fixes vector-im/element-web#18184. Contributed by [SimonBrandner](https://github.com/SimonBrandner). From e4f27f04b9bb160483bace13d0180f43d676601d Mon Sep 17 00:00:00 2001 From: RiotRobot Date: Tue, 31 Aug 2021 13:52:40 +0100 Subject: [PATCH 76/93] v3.29.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 8fe928f162..08c057b35d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "matrix-react-sdk", - "version": "3.29.0-rc.3", + "version": "3.29.0", "description": "SDK for matrix.org using React", "author": "matrix.org", "repository": { From 0a09682982c8d7d9da90f2b783ddc0b537bf6dfb Mon Sep 17 00:00:00 2001 From: RiotRobot Date: Tue, 31 Aug 2021 13:57:16 +0100 Subject: [PATCH 77/93] Resetting package fields for development --- package.json | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index e784fc1654..2f59ba4504 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", @@ -207,6 +207,5 @@ "coverageReporters": [ "text" ] - }, - "typings": "./lib/index.d.ts" + } } From 0cd00ae64f80184bab0a407d7949b5405675fc93 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 31 Aug 2021 16:05:16 +0100 Subject: [PATCH 78/93] Remove non-functional DuckDuckGo Autocomplete Provider --- src/SlashCommands.tsx | 15 ---- src/autocomplete/Autocompleter.ts | 2 - src/autocomplete/CommandProvider.tsx | 2 +- src/autocomplete/DuckDuckGoProvider.tsx | 115 ------------------------ src/i18n/strings/en_EN.json | 5 -- 5 files changed, 1 insertion(+), 138 deletions(-) delete mode 100644 src/autocomplete/DuckDuckGoProvider.tsx diff --git a/src/SlashCommands.tsx b/src/SlashCommands.tsx index b4deb6d8c4..db05ffc5ce 100644 --- a/src/SlashCommands.tsx +++ b/src/SlashCommands.tsx @@ -245,21 +245,6 @@ export const Commands = [ }, category: CommandCategories.messages, }), - new Command({ - command: 'ddg', - args: '', - description: _td('Searches DuckDuckGo for results'), - runFn: function() { - // TODO Don't explain this away, actually show a search UI here. - Modal.createTrackedDialog('Slash Commands', '/ddg is not a command', ErrorDialog, { - title: _t('/ddg is not a command'), - description: _t('To use it, just wait for autocomplete results to load and tab through them.'), - }); - return success(); - }, - category: CommandCategories.actions, - hideCompletionAfterSpace: true, - }), new Command({ command: 'upgraderoom', args: '', diff --git a/src/autocomplete/Autocompleter.ts b/src/autocomplete/Autocompleter.ts index acc7846510..4c9e82f290 100644 --- a/src/autocomplete/Autocompleter.ts +++ b/src/autocomplete/Autocompleter.ts @@ -20,7 +20,6 @@ import { Room } from 'matrix-js-sdk/src/models/room'; import CommandProvider from './CommandProvider'; import CommunityProvider from './CommunityProvider'; -import DuckDuckGoProvider from './DuckDuckGoProvider'; import RoomProvider from './RoomProvider'; import UserProvider from './UserProvider'; import EmojiProvider from './EmojiProvider'; @@ -55,7 +54,6 @@ const PROVIDERS = [ EmojiProvider, NotifProvider, CommandProvider, - DuckDuckGoProvider, ]; if (SpaceStore.spacesEnabled) { diff --git a/src/autocomplete/CommandProvider.tsx b/src/autocomplete/CommandProvider.tsx index d0fb9919f6..143b7e4cdc 100644 --- a/src/autocomplete/CommandProvider.tsx +++ b/src/autocomplete/CommandProvider.tsx @@ -53,7 +53,7 @@ export default class CommandProvider extends AutocompleteProvider { // The input looks like a command with arguments, perform exact match const name = command[1].substr(1); // strip leading `/` if (CommandMap.has(name) && CommandMap.get(name).isEnabled()) { - // some commands, namely `me` and `ddg` don't suit having the usage shown whilst typing their arguments + // some commands, namely `me` don't suit having the usage shown whilst typing their arguments if (CommandMap.get(name).hideCompletionAfterSpace) return []; matches = [CommandMap.get(name)]; } diff --git a/src/autocomplete/DuckDuckGoProvider.tsx b/src/autocomplete/DuckDuckGoProvider.tsx deleted file mode 100644 index c41a91b97f..0000000000 --- a/src/autocomplete/DuckDuckGoProvider.tsx +++ /dev/null @@ -1,115 +0,0 @@ -/* -Copyright 2016 Aviral Dasgupta -Copyright 2017 Vector Creations Ltd -Copyright 2017, 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 React from 'react'; -import { _t } from '../languageHandler'; -import AutocompleteProvider from './AutocompleteProvider'; - -import { TextualCompletion } from './Components'; -import { ICompletion, ISelectionRange } from "./Autocompleter"; - -const DDG_REGEX = /\/ddg\s+(.+)$/g; -const REFERRER = 'vector'; - -export default class DuckDuckGoProvider extends AutocompleteProvider { - constructor() { - super(DDG_REGEX); - } - - static getQueryUri(query: string) { - return `https://api.duckduckgo.com/?q=${encodeURIComponent(query)}` - + `&format=json&no_redirect=1&no_html=1&t=${encodeURIComponent(REFERRER)}`; - } - - async getCompletions( - query: string, - selection: ISelectionRange, - force = false, - limit = -1, - ): Promise { - const { command, range } = this.getCurrentCommand(query, selection); - if (!query || !command) { - return []; - } - - const response = await fetch(DuckDuckGoProvider.getQueryUri(command[1]), { - method: 'GET', - }); - const json = await response.json(); - const maxLength = limit > -1 ? limit : json.Results.length; - const results = json.Results.slice(0, maxLength).map((result) => { - return { - completion: result.Text, - component: ( - - ), - range, - }; - }); - if (json.Answer) { - results.unshift({ - completion: json.Answer, - component: ( - - ), - range, - }); - } - if (json.RelatedTopics && json.RelatedTopics.length > 0) { - results.unshift({ - completion: json.RelatedTopics[0].Text, - component: ( - - ), - range, - }); - } - if (json.AbstractText) { - results.unshift({ - completion: json.AbstractText, - component: ( - - ), - range, - }); - } - return results; - } - - getName() { - return '🔍 ' + _t('Results from DuckDuckGo'); - } - - renderCompletions(completions: React.ReactNode[]): React.ReactNode { - return ( -
- { completions } -
- ); - } -} diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index a3d9fd5203..ede8694545 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -426,9 +426,6 @@ "Prepends ( ͡° ͜ʖ ͡°) to a plain-text message": "Prepends ( ͡° ͜ʖ ͡°) to a plain-text message", "Sends a message as plain text, without interpreting it as markdown": "Sends a message as plain text, without interpreting it as markdown", "Sends a message as html, without interpreting it as markdown": "Sends a message as html, without interpreting it as markdown", - "Searches DuckDuckGo for results": "Searches DuckDuckGo for results", - "/ddg is not a command": "/ddg is not a command", - "To use it, just wait for autocomplete results to load and tab through them.": "To use it, just wait for autocomplete results to load and tab through them.", "Upgrades a room to a new version": "Upgrades a room to a new version", "You do not have the required permissions to use this command.": "You do not have the required permissions to use this command.", "Changes your display nickname": "Changes your display nickname", @@ -3002,8 +2999,6 @@ "Commands": "Commands", "Command Autocomplete": "Command Autocomplete", "Community Autocomplete": "Community Autocomplete", - "Results from DuckDuckGo": "Results from DuckDuckGo", - "DuckDuckGo Results": "DuckDuckGo Results", "Emoji": "Emoji", "Emoji Autocomplete": "Emoji Autocomplete", "Notify the whole room": "Notify the whole room", From c2a5cfb11e1cac808e208326741f56f0c6bc1cbb Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 31 Aug 2021 16:11:22 +0100 Subject: [PATCH 79/93] remove stale unused import --- src/SlashCommands.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/SlashCommands.tsx b/src/SlashCommands.tsx index db05ffc5ce..902c82fff8 100644 --- a/src/SlashCommands.tsx +++ b/src/SlashCommands.tsx @@ -50,7 +50,6 @@ import CallHandler from "./CallHandler"; import { guessAndSetDMRoom } from "./Rooms"; import { upgradeRoom } from './utils/RoomUpgrade'; import UploadConfirmDialog from './components/views/dialogs/UploadConfirmDialog'; -import ErrorDialog from './components/views/dialogs/ErrorDialog'; import DevtoolsDialog from './components/views/dialogs/DevtoolsDialog'; import RoomUpgradeWarningDialog from "./components/views/dialogs/RoomUpgradeWarningDialog"; import InfoDialog from "./components/views/dialogs/InfoDialog"; From b814100b98c342af64c7aef3f153388e813ec0cc Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 31 Aug 2021 16:31:10 +0100 Subject: [PATCH 80/93] Fix membership updates to Spaces not applying in real-time --- src/stores/SpaceStore.tsx | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/stores/SpaceStore.tsx b/src/stores/SpaceStore.tsx index c08c66714b..c1e86504f4 100644 --- a/src/stores/SpaceStore.tsx +++ b/src/stores/SpaceStore.tsx @@ -678,12 +678,14 @@ export class SpaceStoreClass extends AsyncStoreWithClient { } this.emit(room.roomId); break; + } + }; - case EventType.RoomMember: - if (room.isSpaceRoom()) { - this.onSpaceMembersChange(ev); - } - break; + // listening for m.room.member events in onRoomState above doesn't work as the Member object isn't updated by then + private onRoomStateMembers = (ev: MatrixEvent) => { + const room = this.matrixClient.getRoom(ev.getRoomId()); + if (room?.isSpaceRoom()) { + this.onSpaceMembersChange(ev); } }; @@ -743,6 +745,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient { this.matrixClient.removeListener("Room.myMembership", this.onRoom); this.matrixClient.removeListener("Room.accountData", this.onRoomAccountData); this.matrixClient.removeListener("RoomState.events", this.onRoomState); + this.matrixClient.removeListener("RoomState.members", this.onRoomStateMembers); this.matrixClient.removeListener("accountData", this.onAccountData); } await this.reset(); @@ -754,6 +757,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient { this.matrixClient.on("Room.myMembership", this.onRoom); this.matrixClient.on("Room.accountData", this.onRoomAccountData); this.matrixClient.on("RoomState.events", this.onRoomState); + this.matrixClient.on("RoomState.members", this.onRoomStateMembers); this.matrixClient.on("accountData", this.onAccountData); this.matrixClient.getCapabilities().then(capabilities => { From bf4ffa965c5cf2bb720c6a8001b30333f3cd829b Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 31 Aug 2021 16:56:47 +0100 Subject: [PATCH 81/93] Reload suggested rooms if we see the state change down /sync --- src/stores/SpaceStore.tsx | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/src/stores/SpaceStore.tsx b/src/stores/SpaceStore.tsx index c08c66714b..471e10d9d6 100644 --- a/src/stores/SpaceStore.tsx +++ b/src/stores/SpaceStore.tsx @@ -190,7 +190,7 @@ export class SpaceStoreClass extends AsyncStoreWithClient { * @param contextSwitch whether to switch the user's context, * should not be done when the space switch is done implicitly due to another event like switching room. */ - public async setActiveSpace(space: Room | null, contextSwitch = true) { + public setActiveSpace(space: Room | null, contextSwitch = true) { if (space === this.activeSpace || (space && !space.isSpaceRoom())) return; this._activeSpace = space; @@ -293,11 +293,15 @@ export class SpaceStoreClass extends AsyncStoreWithClient { } if (space) { - const suggestedRooms = await this.fetchSuggestedRooms(space); - if (this._activeSpace === space) { - this._suggestedRooms = suggestedRooms; - this.emit(SUGGESTED_ROOMS, this._suggestedRooms); - } + this.loadSuggestedRooms(space); + } + } + + private async loadSuggestedRooms(space) { + const suggestedRooms = await this.fetchSuggestedRooms(space); + if (this._activeSpace === space) { + this._suggestedRooms = suggestedRooms; + this.emit(SUGGESTED_ROOMS, this._suggestedRooms); } } @@ -666,6 +670,14 @@ export class SpaceStoreClass extends AsyncStoreWithClient { this.onSpaceUpdate(); this.emit(room.roomId); } + + if (room === this.activeSpace && // current space + this.matrixClient.getRoom(ev.getStateKey())?.getMyMembership() !== "join" && // target not joined + ev.getPrevContent().suggested !== ev.getContent().suggested // suggested flag changed + ) { + this.loadSuggestedRooms(room); + } + break; case EventType.SpaceParent: From bbd420096b8807cdf2119c908f51b3f27dea10df Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 31 Aug 2021 17:03:43 +0100 Subject: [PATCH 82/93] don't bother awaiting SpaceStore::setActiveSpace as it is no longer async --- src/stores/SpaceStore.tsx | 4 +- test/stores/SpaceStore-test.ts | 58 +++++++++++----------- test/stores/room-list/SpaceWatcher-test.ts | 22 ++++---- 3 files changed, 42 insertions(+), 42 deletions(-) diff --git a/src/stores/SpaceStore.tsx b/src/stores/SpaceStore.tsx index 471e10d9d6..5c921d6ad7 100644 --- a/src/stores/SpaceStore.tsx +++ b/src/stores/SpaceStore.tsx @@ -145,9 +145,9 @@ export class SpaceStoreClass extends AsyncStoreWithClient { return this._allRoomsInHome; } - public async setActiveRoomInSpace(space: Room | null): Promise { + public setActiveRoomInSpace(space: Room | null): void { if (space && !space.isSpaceRoom()) return; - if (space !== this.activeSpace) await this.setActiveSpace(space); + if (space !== this.activeSpace) this.setActiveSpace(space); if (space) { const roomId = this.getNotificationState(space.roomId).getFirstRoomWithNotifications(); diff --git a/test/stores/SpaceStore-test.ts b/test/stores/SpaceStore-test.ts index 2e823aa72b..7cfd97b234 100644 --- a/test/stores/SpaceStore-test.ts +++ b/test/stores/SpaceStore-test.ts @@ -562,7 +562,7 @@ describe("SpaceStore", () => { ]); mkSpace(space3).getMyMembership.mockReturnValue("invite"); await run(); - await store.setActiveSpace(null); + store.setActiveSpace(null); expect(store.activeSpace).toBe(null); }); afterEach(() => { @@ -570,31 +570,31 @@ describe("SpaceStore", () => { }); it("switch to home space", async () => { - await store.setActiveSpace(client.getRoom(space1)); + store.setActiveSpace(client.getRoom(space1)); fn.mockClear(); - await store.setActiveSpace(null); + store.setActiveSpace(null); expect(fn).toHaveBeenCalledWith(UPDATE_SELECTED_SPACE, null); expect(store.activeSpace).toBe(null); }); it("switch to invited space", async () => { const space = client.getRoom(space3); - await store.setActiveSpace(space); + store.setActiveSpace(space); expect(fn).toHaveBeenCalledWith(UPDATE_SELECTED_SPACE, space); expect(store.activeSpace).toBe(space); }); it("switch to top level space", async () => { const space = client.getRoom(space1); - await store.setActiveSpace(space); + store.setActiveSpace(space); expect(fn).toHaveBeenCalledWith(UPDATE_SELECTED_SPACE, space); expect(store.activeSpace).toBe(space); }); it("switch to subspace", async () => { const space = client.getRoom(space2); - await store.setActiveSpace(space); + store.setActiveSpace(space); expect(fn).toHaveBeenCalledWith(UPDATE_SELECTED_SPACE, space); expect(store.activeSpace).toBe(space); }); @@ -602,7 +602,7 @@ describe("SpaceStore", () => { it("switch to unknown space is a nop", async () => { expect(store.activeSpace).toBe(null); const space = client.getRoom(room1); // not a space - await store.setActiveSpace(space); + store.setActiveSpace(space); expect(fn).not.toHaveBeenCalledWith(UPDATE_SELECTED_SPACE, space); expect(store.activeSpace).toBe(null); }); @@ -635,59 +635,59 @@ describe("SpaceStore", () => { }; it("last viewed room in target space is the current viewed and in both spaces", async () => { - await store.setActiveSpace(client.getRoom(space1)); + store.setActiveSpace(client.getRoom(space1)); viewRoom(room2); - await store.setActiveSpace(client.getRoom(space2)); + store.setActiveSpace(client.getRoom(space2)); viewRoom(room2); - await store.setActiveSpace(client.getRoom(space1)); + store.setActiveSpace(client.getRoom(space1)); expect(getCurrentRoom()).toBe(room2); }); it("last viewed room in target space is in the current space", async () => { - await store.setActiveSpace(client.getRoom(space1)); + store.setActiveSpace(client.getRoom(space1)); viewRoom(room2); - await store.setActiveSpace(client.getRoom(space2)); + store.setActiveSpace(client.getRoom(space2)); expect(getCurrentRoom()).toBe(space2); - await store.setActiveSpace(client.getRoom(space1)); + store.setActiveSpace(client.getRoom(space1)); expect(getCurrentRoom()).toBe(room2); }); it("last viewed room in target space is not in the current space", async () => { - await store.setActiveSpace(client.getRoom(space1)); + store.setActiveSpace(client.getRoom(space1)); viewRoom(room1); - await store.setActiveSpace(client.getRoom(space2)); + store.setActiveSpace(client.getRoom(space2)); viewRoom(room2); - await store.setActiveSpace(client.getRoom(space1)); + store.setActiveSpace(client.getRoom(space1)); expect(getCurrentRoom()).toBe(room1); }); it("last viewed room is target space is not known", async () => { - await store.setActiveSpace(client.getRoom(space1)); + store.setActiveSpace(client.getRoom(space1)); viewRoom(room1); localStorage.setItem(`mx_space_context_${space2}`, orphan2); - await store.setActiveSpace(client.getRoom(space2)); + store.setActiveSpace(client.getRoom(space2)); expect(getCurrentRoom()).toBe(space2); }); it("last viewed room is target space is no longer in that space", async () => { - await store.setActiveSpace(client.getRoom(space1)); + store.setActiveSpace(client.getRoom(space1)); viewRoom(room1); localStorage.setItem(`mx_space_context_${space2}`, room1); - await store.setActiveSpace(client.getRoom(space2)); + store.setActiveSpace(client.getRoom(space2)); expect(getCurrentRoom()).toBe(space2); // Space home instead of room1 }); it("no last viewed room in target space", async () => { - await store.setActiveSpace(client.getRoom(space1)); + store.setActiveSpace(client.getRoom(space1)); viewRoom(room1); - await store.setActiveSpace(client.getRoom(space2)); + store.setActiveSpace(client.getRoom(space2)); expect(getCurrentRoom()).toBe(space2); }); it("no last viewed room in home space", async () => { - await store.setActiveSpace(client.getRoom(space1)); + store.setActiveSpace(client.getRoom(space1)); viewRoom(room1); - await store.setActiveSpace(null); + store.setActiveSpace(null); expect(getCurrentRoom()).toBeNull(); // Home }); }); @@ -715,28 +715,28 @@ describe("SpaceStore", () => { it("no switch required, room is in current space", async () => { viewRoom(room1); - await store.setActiveSpace(client.getRoom(space1), false); + store.setActiveSpace(client.getRoom(space1), false); viewRoom(room2); expect(store.activeSpace).toBe(client.getRoom(space1)); }); it("switch to canonical parent space for room", async () => { viewRoom(room1); - await store.setActiveSpace(client.getRoom(space2), false); + store.setActiveSpace(client.getRoom(space2), false); viewRoom(room2); expect(store.activeSpace).toBe(client.getRoom(space2)); }); it("switch to first containing space for room", async () => { viewRoom(room2); - await store.setActiveSpace(client.getRoom(space2), false); + store.setActiveSpace(client.getRoom(space2), false); viewRoom(room3); expect(store.activeSpace).toBe(client.getRoom(space1)); }); it("switch to home for orphaned room", async () => { viewRoom(room1); - await store.setActiveSpace(client.getRoom(space1), false); + store.setActiveSpace(client.getRoom(space1), false); viewRoom(orphan1); expect(store.activeSpace).toBeNull(); }); @@ -744,7 +744,7 @@ describe("SpaceStore", () => { it("when switching rooms in the all rooms home space don't switch to related space", async () => { await setShowAllRooms(true); viewRoom(room2); - await store.setActiveSpace(null, false); + store.setActiveSpace(null, false); viewRoom(room1); expect(store.activeSpace).toBeNull(); }); diff --git a/test/stores/room-list/SpaceWatcher-test.ts b/test/stores/room-list/SpaceWatcher-test.ts index 85f79c75b6..474c279fdd 100644 --- a/test/stores/room-list/SpaceWatcher-test.ts +++ b/test/stores/room-list/SpaceWatcher-test.ts @@ -57,7 +57,7 @@ describe("SpaceWatcher", () => { beforeEach(async () => { filter = null; store.removeAllListeners(); - await store.setActiveSpace(null); + store.setActiveSpace(null); client.getVisibleRooms.mockReturnValue(rooms = []); space1 = mkSpace(space1Id); @@ -95,7 +95,7 @@ describe("SpaceWatcher", () => { await setShowAllRooms(true); new SpaceWatcher(mockRoomListStore); - await SpaceStore.instance.setActiveSpace(space1); + SpaceStore.instance.setActiveSpace(space1); expect(filter).toBeInstanceOf(SpaceFilterCondition); expect(filter["space"]).toBe(space1); @@ -114,7 +114,7 @@ describe("SpaceWatcher", () => { await setShowAllRooms(false); new SpaceWatcher(mockRoomListStore); - await SpaceStore.instance.setActiveSpace(space1); + SpaceStore.instance.setActiveSpace(space1); expect(filter).toBeInstanceOf(SpaceFilterCondition); expect(filter["space"]).toBe(space1); @@ -124,22 +124,22 @@ describe("SpaceWatcher", () => { await setShowAllRooms(true); new SpaceWatcher(mockRoomListStore); - await SpaceStore.instance.setActiveSpace(space1); + SpaceStore.instance.setActiveSpace(space1); expect(filter).toBeInstanceOf(SpaceFilterCondition); expect(filter["space"]).toBe(space1); - await SpaceStore.instance.setActiveSpace(null); + SpaceStore.instance.setActiveSpace(null); expect(filter).toBeNull(); }); it("updates filter correctly for space -> home transition", async () => { await setShowAllRooms(false); - await SpaceStore.instance.setActiveSpace(space1); + SpaceStore.instance.setActiveSpace(space1); new SpaceWatcher(mockRoomListStore); expect(filter).toBeInstanceOf(SpaceFilterCondition); expect(filter["space"]).toBe(space1); - await SpaceStore.instance.setActiveSpace(null); + SpaceStore.instance.setActiveSpace(null); expect(filter).toBeInstanceOf(SpaceFilterCondition); expect(filter["space"]).toBe(null); @@ -147,12 +147,12 @@ describe("SpaceWatcher", () => { it("updates filter correctly for space -> space transition", async () => { await setShowAllRooms(false); - await SpaceStore.instance.setActiveSpace(space1); + SpaceStore.instance.setActiveSpace(space1); new SpaceWatcher(mockRoomListStore); expect(filter).toBeInstanceOf(SpaceFilterCondition); expect(filter["space"]).toBe(space1); - await SpaceStore.instance.setActiveSpace(space2); + SpaceStore.instance.setActiveSpace(space2); expect(filter).toBeInstanceOf(SpaceFilterCondition); expect(filter["space"]).toBe(space2); @@ -160,7 +160,7 @@ describe("SpaceWatcher", () => { it("doesn't change filter when changing showAllRooms mode to true", async () => { await setShowAllRooms(false); - await SpaceStore.instance.setActiveSpace(space1); + SpaceStore.instance.setActiveSpace(space1); new SpaceWatcher(mockRoomListStore); expect(filter).toBeInstanceOf(SpaceFilterCondition); @@ -173,7 +173,7 @@ describe("SpaceWatcher", () => { it("doesn't change filter when changing showAllRooms mode to false", async () => { await setShowAllRooms(true); - await SpaceStore.instance.setActiveSpace(space1); + SpaceStore.instance.setActiveSpace(space1); new SpaceWatcher(mockRoomListStore); expect(filter).toBeInstanceOf(SpaceFilterCondition); From 4ce79148cf8708708972ec6ee9830038b99d9d47 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 31 Aug 2021 17:14:50 +0100 Subject: [PATCH 83/93] Clear currentRoomId when viewing home page, fixing document title --- src/components/structures/MatrixChat.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/structures/MatrixChat.tsx b/src/components/structures/MatrixChat.tsx index 902d2a0921..531dc9fbe9 100644 --- a/src/components/structures/MatrixChat.tsx +++ b/src/components/structures/MatrixChat.tsx @@ -1016,6 +1016,7 @@ export default class MatrixChat extends React.PureComponent { this.setStateForNewView({ view: Views.LOGGED_IN, justRegistered, + currentRoomId: null, }); this.setPage(PageTypes.HomePage); this.notifyNewScreen('home'); From e089c34db72f129e04281dcae132cf62bbbc3baa Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Tue, 31 Aug 2021 17:36:10 +0100 Subject: [PATCH 84/93] Fix EmojiPicker filtering to lower case emojibase data strings --- src/components/views/emojipicker/EmojiPicker.tsx | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/src/components/views/emojipicker/EmojiPicker.tsx b/src/components/views/emojipicker/EmojiPicker.tsx index 0884db7101..9cc995a140 100644 --- a/src/components/views/emojipicker/EmojiPicker.tsx +++ b/src/components/views/emojipicker/EmojiPicker.tsx @@ -173,16 +173,16 @@ class EmojiPicker extends React.Component { }; private onChangeFilter = (filter: string) => { - filter = filter.toLowerCase(); // filter is case insensitive stored lower-case + const lcFilter = filter.toLowerCase().trim(); // filter is case insensitive for (const cat of this.categories) { let emojis; // If the new filter string includes the old filter string, we don't have to re-filter the whole dataset. - if (filter.includes(this.state.filter)) { + if (lcFilter.includes(this.state.filter)) { emojis = this.memoizedDataByCategory[cat.id]; } else { emojis = cat.id === "recent" ? this.recentlyUsed : DATA_BY_CATEGORY[cat.id]; } - emojis = emojis.filter(emoji => this.emojiMatchesFilter(emoji, filter)); + emojis = emojis.filter(emoji => this.emojiMatchesFilter(emoji, lcFilter)); this.memoizedDataByCategory[cat.id] = emojis; cat.enabled = emojis.length > 0; // The setState below doesn't re-render the header and we already have the refs for updateVisibility, so... @@ -194,9 +194,12 @@ class EmojiPicker extends React.Component { setTimeout(this.updateVisibility, 0); }; - private emojiMatchesFilter = (emoji: IEmoji, filter: string): boolean => - [emoji.annotation, ...emoji.shortcodes, emoji.emoticon, ...emoji.unicode.split(ZERO_WIDTH_JOINER)] - .some(x => x?.includes(filter)); + private emojiMatchesFilter = (emoji: IEmoji, filter: string): boolean => { + return emoji.annotation.toLowerCase().includes(filter) || + emoji.emoticon?.toLowerCase().includes(filter) || + emoji.shortcodes.some(x => x.toLowerCase().includes(filter)) || + emoji.unicode.split(ZERO_WIDTH_JOINER).includes(filter); + }; private onEnterFilter = () => { const btn = this.bodyRef.current.querySelector(".mx_EmojiPicker_item"); From 18b7a649104656799554cd50a472dd78f9d6b2ad Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Tue, 31 Aug 2021 13:17:49 -0600 Subject: [PATCH 85/93] Remove arbitrary limits from send/receive events for widgets Fixes https://github.com/vector-im/element-web/issues/17994 As per MSC change: https://github.com/matrix-org/matrix-doc/pull/2762/commits/aeadae81e2d68f76523eb61ff0ebbbd5c3202deb --- src/stores/widgets/StopGapWidgetDriver.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/stores/widgets/StopGapWidgetDriver.ts b/src/stores/widgets/StopGapWidgetDriver.ts index d473ecf3b1..c0f627b9c0 100644 --- a/src/stores/widgets/StopGapWidgetDriver.ts +++ b/src/stores/widgets/StopGapWidgetDriver.ts @@ -163,7 +163,7 @@ export class StopGapWidgetDriver extends WidgetDriver { } public async readRoomEvents(eventType: string, msgtype: string | undefined, limit: number): Promise { - limit = limit > 0 ? Math.min(limit, 25) : 25; // arbitrary choice + limit = limit > 0 ? Math.min(limit, Number.MAX_SAFE_INTEGER) : Number.MAX_SAFE_INTEGER; // relatively arbitrary const client = MatrixClientPeg.get(); const roomId = ActiveRoomObserver.activeRoomId; @@ -185,7 +185,7 @@ export class StopGapWidgetDriver extends WidgetDriver { } public async readStateEvents(eventType: string, stateKey: string | undefined, limit: number): Promise { - limit = limit > 0 ? Math.min(limit, 100) : 100; // arbitrary choice + limit = limit > 0 ? Math.min(limit, Number.MAX_SAFE_INTEGER) : Number.MAX_SAFE_INTEGER; // relatively arbitrary const client = MatrixClientPeg.get(); const roomId = ActiveRoomObserver.activeRoomId; From 855c3819c1a24f83901398b21e676b364940d74b Mon Sep 17 00:00:00 2001 From: David Baker Date: Wed, 1 Sep 2021 09:52:32 +0100 Subject: [PATCH 86/93] 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 2f59ba4504..5c62100587 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": "12.4.0", + "matrix-js-sdk": "github:matrix-org/matrix-js-sdk#develop", "matrix-widget-api": "^0.1.0-beta.15", "minimist": "^1.2.5", "opus-recorder": "^8.0.3", diff --git a/yarn.lock b/yarn.lock index 6dd1d269c3..4e58909563 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5791,10 +5791,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@12.4.0: +"matrix-js-sdk@github:matrix-org/matrix-js-sdk#develop": version "12.4.0" - resolved "https://registry.yarnpkg.com/matrix-js-sdk/-/matrix-js-sdk-12.4.0.tgz#ff60306f9a9e39fd1ae6c7e501001f80eb779dd7" - integrity sha512-KamHmvNle4mkdErmNgVsGIL3n8/zgPe60DLVaEA2t4aSNwQLEmRS+oVpIgsO3ZrUivBvn4oc9sBqxP0OIl6kUg== + resolved "https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/2783d162b77d6629c574f35e88bea9ae29765c34" dependencies: "@babel/runtime" "^7.12.5" another-json "^0.2.0" From 030fa17a6679d44bb0c6ea086ee20e6612e70903 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Wed, 1 Sep 2021 12:47:43 +0100 Subject: [PATCH 87/93] When creating private spaces, make the initial rooms restricted if supported --- src/components/structures/SpaceRoomView.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/components/structures/SpaceRoomView.tsx b/src/components/structures/SpaceRoomView.tsx index 767e0999c3..1ce5a53c3e 100644 --- a/src/components/structures/SpaceRoomView.tsx +++ b/src/components/structures/SpaceRoomView.tsx @@ -16,7 +16,7 @@ limitations under the License. import React, { RefObject, useContext, useRef, useState } from "react"; import { EventType } from "matrix-js-sdk/src/@types/event"; -import { Preset, JoinRule } from "matrix-js-sdk/src/@types/partials"; +import { JoinRule, Preset } from "matrix-js-sdk/src/@types/partials"; import { Room } from "matrix-js-sdk/src/models/room"; import { EventSubscription } from "fbemitter"; @@ -505,11 +505,12 @@ const SpaceSetupFirstRooms = ({ space, title, description, onFinished }) => { setError(""); setBusy(true); try { + const isPublic = space.getJoinRule() === JoinRule.Public; const filteredRoomNames = roomNames.map(name => name.trim()).filter(Boolean); await Promise.all(filteredRoomNames.map(name => { return createRoom({ createOpts: { - preset: space.getJoinRule() === "public" ? Preset.PublicChat : Preset.PrivateChat, + preset: isPublic ? Preset.PublicChat : Preset.PrivateChat, name, }, spinner: false, @@ -517,6 +518,7 @@ const SpaceSetupFirstRooms = ({ space, title, description, onFinished }) => { andView: false, inlineErrors: true, parentSpace: space, + joinRule: !isPublic ? JoinRule.Restricted : undefined, }); })); onFinished(filteredRoomNames.length > 0); From 4328ee18f523404016ee9a89530a9d9c61954d9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Wed, 1 Sep 2021 15:37:59 +0200 Subject: [PATCH 88/93] Show autocomplete sections vertically MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- res/css/views/rooms/_Autocomplete.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/res/css/views/rooms/_Autocomplete.scss b/res/css/views/rooms/_Autocomplete.scss index bebe0b47c9..8d2b338d9d 100644 --- a/res/css/views/rooms/_Autocomplete.scss +++ b/res/css/views/rooms/_Autocomplete.scss @@ -10,6 +10,7 @@ max-height: 35vh; overflow: clip; display: flex; + flex-direction: column; box-shadow: 0px -16px 32px $composer-shadow-color; } From 387239864d0685cb18495db682247b594d213498 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Wed, 1 Sep 2021 16:50:13 +0200 Subject: [PATCH 89/93] Add fallbackUserId MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/components/views/elements/ImageView.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/views/elements/ImageView.tsx b/src/components/views/elements/ImageView.tsx index 87f3dab718..7a1efb7a62 100644 --- a/src/components/views/elements/ImageView.tsx +++ b/src/components/views/elements/ImageView.tsx @@ -419,6 +419,7 @@ export default class ImageView extends React.Component { const avatar = ( Date: Wed, 1 Sep 2021 16:12:39 +0100 Subject: [PATCH 90/93] When creating subspaces properly set restricted join rule --- src/components/views/dialogs/CreateSubspaceDialog.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/dialogs/CreateSubspaceDialog.tsx b/src/components/views/dialogs/CreateSubspaceDialog.tsx index 03927c7d62..d80245918f 100644 --- a/src/components/views/dialogs/CreateSubspaceDialog.tsx +++ b/src/components/views/dialogs/CreateSubspaceDialog.tsx @@ -79,7 +79,7 @@ const CreateSubspaceDialog: React.FC = ({ space, onAddExistingSpaceClick } try { - await createSpace(name, joinRule === JoinRule.Public, alias, topic, avatar, {}, { parentSpace }); + await createSpace(name, joinRule === JoinRule.Public, alias, topic, avatar, {}, { parentSpace, joinRule }); onFinished(true); } catch (e) { From 4b557fe0adcd9f72f144af6a5aa6a5f377a10cc7 Mon Sep 17 00:00:00 2001 From: Travis Ralston Date: Wed, 1 Sep 2021 13:22:09 -0600 Subject: [PATCH 91/93] Update widget-api --- package.json | 2 +- yarn.lock | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index 5c62100587..6245b2c34e 100644 --- a/package.json +++ b/package.json @@ -83,7 +83,7 @@ "linkifyjs": "^2.1.9", "lodash": "^4.17.20", "matrix-js-sdk": "github:matrix-org/matrix-js-sdk#develop", - "matrix-widget-api": "^0.1.0-beta.15", + "matrix-widget-api": "^0.1.0-beta.16", "minimist": "^1.2.5", "opus-recorder": "^8.0.3", "pako": "^2.0.3", diff --git a/yarn.lock b/yarn.lock index 4e58909563..f70f0e75c1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5827,10 +5827,10 @@ matrix-react-test-utils@^0.2.3: "@babel/traverse" "^7.13.17" walk "^2.3.14" -matrix-widget-api@^0.1.0-beta.15: - version "0.1.0-beta.15" - resolved "https://registry.yarnpkg.com/matrix-widget-api/-/matrix-widget-api-0.1.0-beta.15.tgz#b02511f93fe1a3634868b6e246d736107f182745" - integrity sha512-sWmtb8ZarSbHVbk5ni7IHBR9jOh7m1+5R4soky0fEO9VKl+MN7skT0+qNux3J9WuUAu2D80dZW9xPUT9cxfxbg== +matrix-widget-api@^0.1.0-beta.16: + version "0.1.0-beta.16" + resolved "https://registry.yarnpkg.com/matrix-widget-api/-/matrix-widget-api-0.1.0-beta.16.tgz#32655f05cab48239b97fe4111a1d0858f2aad61a" + integrity sha512-9zqaNLaM14YDHfFb7WGSUOivGOjYw+w5Su84ZfOl6A4IUy1xT9QPp0nsSA8wNfz0LpxOIPn3nuoF8Tn/40F5tg== dependencies: "@types/events" "^3.0.0" events "^3.2.0" From c32a77c3b2c8cff17b54f166b41ac78c46106f39 Mon Sep 17 00:00:00 2001 From: Steffen Kolmer Date: Wed, 1 Sep 2021 22:40:42 +0200 Subject: [PATCH 92/93] Use helper function to get event content --- src/components/views/rooms/RoomPreviewBar.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/views/rooms/RoomPreviewBar.js b/src/components/views/rooms/RoomPreviewBar.js index f3201d2a3d..89b493595f 100644 --- a/src/components/views/rooms/RoomPreviewBar.js +++ b/src/components/views/rooms/RoomPreviewBar.js @@ -494,7 +494,7 @@ export default class RoomPreviewBar extends React.Component { } const myUserId = MatrixClientPeg.get().getUserId(); - const memberEventContent = this.props.room.currentState.getMember(myUserId).events.member.event.content; + const memberEventContent = this.props.room.currentState.getMember(myUserId).events.member.getContent(); if (memberEventContent.reason) { reasonElement = Date: Wed, 1 Sep 2021 14:43:37 -0600 Subject: [PATCH 93/93] Update scripts/fetchdep.sh --- scripts/fetchdep.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/fetchdep.sh b/scripts/fetchdep.sh index 97be9c2414..ec021236d9 100755 --- a/scripts/fetchdep.sh +++ b/scripts/fetchdep.sh @@ -10,7 +10,7 @@ defbranch="$3" rm -r "$defrepo" || true -# A fnction that clones a branch of a repo based on the org, repo and branch +# A function that clones a branch of a repo based on the org, repo and branch clone() { org=$1 repo=$2