Skip to content
This repository has been archived by the owner on Sep 11, 2024. It is now read-only.

Threading exploration work #6658

Merged
merged 23 commits into from
Sep 1, 2021
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
6868478
Add threaded messaging feature flag
germain-gg Jul 8, 2021
cf3117b
Migrate ViewSourceEvent to TypeScript
germain-gg Jul 8, 2021
92394da
Merge branch 'develop' into gsouquet/threaded-messaging-2349
germain-gg Jul 27, 2021
7d4698d
Merge branch 'develop' into gsouquet/threaded-messaging-2349
germain-gg Aug 10, 2021
d971802
Create ThreadView phase in RightPanel
germain-gg Aug 10, 2021
e5024c4
Adapt threading UI to new backend
germain-gg Aug 17, 2021
d1dbfbd
hide thread events from the timeline
germain-gg Aug 17, 2021
458f860
Merge branch 'develop' into gsouquet/threaded-messaging-2349
germain-gg Aug 17, 2021
95f4513
Make UI respond to thread events
germain-gg Aug 17, 2021
ffc7326
Merge branch 'develop' into gsouquet/threaded-messaging-2349
germain-gg Aug 19, 2021
ac0412d
rename feature flag for Threads
germain-gg Aug 19, 2021
d535636
Hide thread UI behind experimentalThreadSupport flag
germain-gg Aug 19, 2021
30a7629
Implement a very low fidelity UI for threads
germain-gg Aug 20, 2021
393bd48
Merge branch 'develop' into gsouquet/threaded-messaging-2349
germain-gg Aug 23, 2021
9facb0d
Polish UI
germain-gg Aug 23, 2021
ef51a46
Fix linting
germain-gg Aug 23, 2021
34da07f
Pass room to ThreadView over roomId
Aug 23, 2021
edd4d42
Merge branch 'develop' into gsouquet/threaded-messaging-2349
Aug 23, 2021
54a0a86
PR feedback
Aug 24, 2021
77a463e
Merge branch 'develop' into gsouquet/threaded-messaging-2349
Aug 25, 2021
bd1aa01
Update copyright and method accessors
Aug 26, 2021
f0a4225
Remove unused renderEventTile method
Aug 26, 2021
bf3c8e5
Fix imports
Aug 27, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .stylelintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -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"],
Expand Down
4 changes: 4 additions & 0 deletions res/css/views/messages/_MessageActionBar.scss
Original file line number Diff line number Diff line change
Expand Up @@ -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/message/thread.svg');
}

.mx_MessageActionBar_editButton::after {
mask-image: url('$(res)/img/element-icons/room/message-bar/edit.svg');
}
Expand Down
4 changes: 4 additions & 0 deletions res/css/views/right_panel/_RoomSummaryCard.scss
Original file line number Diff line number Diff line change
Expand Up @@ -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');
}
Expand Down
59 changes: 59 additions & 0 deletions res/css/views/rooms/_EventTile.scss
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -674,3 +675,61 @@ $hover-select-border: 4px;
margin-right: 0;
}
}

.mx_ThreadInfo:hover {
cursor: pointer;
}

.mx_ThreadView {
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 {
germain-gg marked this conversation as resolved.
Show resolved Hide resolved
germain-gg marked this conversation as resolved.
Show resolved Hide resolved
flex: 1;
min-width: none;
max-width: 100%;
display: flex;
align-items: center;

.mx_SenderProfile {
flex: 1;
}
}
}

.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;
margin-top: 0;
padding-bottom: 5px;
margin-bottom: 5px;
}
}
16 changes: 16 additions & 0 deletions res/css/views/rooms/_MessageComposer.scss
Original file line number Diff line number Diff line change
Expand Up @@ -340,3 +340,19 @@ 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;
}
}
4 changes: 4 additions & 0 deletions res/img/element-icons/message/thread.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions src/MatrixClientPeg.ts
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,7 @@ class MatrixClientPegClass implements IMatrixClientPeg {
opts.pendingEventOrdering = PendingEventOrdering.Detached;
opts.lazyLoadMembers = true;
opts.clientWellKnownPollPeriod = 2 * 60 * 60; // 2 hours
opts.experimentalThreadSupport = SettingsStore.getValue("feature_thread");

// Connect the matrix client to the dispatcher and setting handlers
MatrixActionCreators.start(this.matrixClient);
Expand Down
11 changes: 11 additions & 0 deletions src/components/structures/MessagePanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,8 @@ interface IProps {
onUnfillRequest?(backwards: boolean, scrollToken: string): void;

getRelationsForEvent?(eventId: string, relationType: string, eventType: string): Relations;

hideThreadedMessages?: boolean;
}

interface IState {
Expand Down Expand Up @@ -265,6 +267,9 @@ export default class MessagePanel extends React.Component<IProps, IState> {
componentDidMount() {
this.calculateRoomMembersCount();
this.props.room?.on("RoomState.members", this.calculateRoomMembersCount);
if (SettingsStore.getValue("feature_thread")) {
this.props.room?.getThreads().forEach(thread => thread.fetchReplyChain());
}
this.isMounted = true;
}

Expand Down Expand Up @@ -443,6 +448,12 @@ export default class MessagePanel extends React.Component<IProps, IState> {
// Always show highlighted event
if (this.props.highlightedEventId === mxEv.getId()) return true;

if (mxEv.replyEventId
&& this.props.hideThreadedMessages
&& SettingsStore.getValue("feature_thread")) {
return false;
}

return !shouldHideEvent(mxEv, this.context);
}

Expand Down
20 changes: 20 additions & 0 deletions src/components/structures/RightPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,17 +45,21 @@ 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 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 {
Expand Down Expand Up @@ -309,6 +313,22 @@ export default class RightPanel extends React.Component<IProps, IState> {
panel = <FilePanel roomId={roomId} resizeNotifier={this.props.resizeNotifier} onClose={this.onClose} />;
break;

case RightPanelPhases.ThreadView:
panel = <ThreadView
room={this.props.room}
resizeNotifier={this.props.resizeNotifier}
onClose={this.onClose}
mxEvent={this.state.event}
permalinkCreator={this.props.permalinkCreator} />;
break;

case RightPanelPhases.ThreadPanel:
panel = <ThreadPanel
roomId={roomId}
resizeNotifier={this.props.resizeNotifier}
onClose={this.onClose} />;
break;

case RightPanelPhases.RoomSummary:
panel = <RoomSummaryCard room={this.props.room} onClose={this.onClose} />;
break;
Expand Down
5 changes: 4 additions & 1 deletion src/components/structures/RoomView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2052,7 +2052,10 @@ export default class RoomView extends React.Component<IProps, IState> {

const showRightPanel = this.state.room && this.state.showRightPanel;
const rightPanel = showRightPanel
? <RightPanel room={this.state.room} resizeNotifier={this.props.resizeNotifier} />
? <RightPanel
room={this.state.room}
resizeNotifier={this.props.resizeNotifier}
permalinkCreator={this.getPermalinkCreatorForRoom(this.state.room)} />
: null;

const timelineClasses = classNames("mx_RoomView_timeline", {
Expand Down
93 changes: 93 additions & 0 deletions src/components/structures/ThreadPanel.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/*
Copyright 2021 New Vector Ltd.
germain-gg marked this conversation as resolved.
Show resolved Hide resolved

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 { 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 from '../views/rooms/EventTile';

interface IProps {
roomId: string;
onClose: () => void;
resizeNotifier: ResizeNotifier;
}

interface IState {
threads?: Thread[];
}

@replaceableComponent("structures.ThreadView")
export default class ThreadPanel extends React.Component<IProps, IState> {
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);
}

public componentWillUnmount(): void {
this.room.removeListener("Thread.update", this.onThreadEventReceived);
this.room.removeListener("Thread.ready", this.onThreadEventReceived);
}

private onThreadEventReceived = () => this.updateThreads();

private updateThreads = (callback?: () => void): void => {
this.setState({
threads: this.room.getThreads(),
}, callback);
};

private renderEventTile(event: MatrixEvent): JSX.Element {
return <EventTile
key={event.getId()}
mxEvent={event}
enableFlair={false}
showReadReceipts={false}
as="div"
/>;
}

public render(): JSX.Element {
return (
<BaseCard
className="mx_ThreadPanel"
onClose={this.props.onClose}
previousPhase={RightPanelPhases.RoomSummary}
>
{
this.state?.threads.map((thread: Thread) => {
if (thread.ready) {
return this.renderEventTile(thread.rootEvent);
}
})
}
</BaseCard>
);
}
}
Loading