Adapt threading UI to new backend

pull/21833/head
Germain Souquet 2021-08-17 10:38:09 +01:00
parent d971802789
commit e5024c4b71
7 changed files with 150 additions and 8 deletions

View File

@ -46,17 +46,20 @@ import UserInfo from "../views/right_panel/UserInfo";
import ThirdPartyMemberInfo from "../views/rooms/ThirdPartyMemberInfo"; import ThirdPartyMemberInfo from "../views/rooms/ThirdPartyMemberInfo";
import FilePanel from "./FilePanel"; import FilePanel from "./FilePanel";
import ThreadView from "./ThreadView"; import ThreadView from "./ThreadView";
import ThreadPanel from "./ThreadPanel";
import NotificationPanel from "./NotificationPanel"; import NotificationPanel from "./NotificationPanel";
import ResizeNotifier from "../../utils/ResizeNotifier"; import ResizeNotifier from "../../utils/ResizeNotifier";
import PinnedMessagesCard from "../views/right_panel/PinnedMessagesCard"; import PinnedMessagesCard from "../views/right_panel/PinnedMessagesCard";
import { throttle } from 'lodash'; import { throttle } from 'lodash';
import SpaceStore from "../../stores/SpaceStore"; import SpaceStore from "../../stores/SpaceStore";
import { RoomPermalinkCreator } from '../../utils/permalinks/Permalinks';
interface IProps { interface IProps {
room?: Room; // if showing panels for a given room, this is set room?: Room; // if showing panels for a given room, this is set
groupId?: string; // if showing panels for a given group, 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 user?: User; // used if we know the user ahead of opening the panel
resizeNotifier: ResizeNotifier; resizeNotifier: ResizeNotifier;
permalinkCreator?: RoomPermalinkCreator;
} }
interface IState { interface IState {
@ -315,7 +318,15 @@ export default class RightPanel extends React.Component<IProps, IState> {
roomId={roomId} roomId={roomId}
resizeNotifier={this.props.resizeNotifier} resizeNotifier={this.props.resizeNotifier}
onClose={this.onClose} onClose={this.onClose}
mxEvent={this.state.event} />; mxEvent={this.state.event}
permalinkCreator={this.props.permalinkCreator} />;
break;
case RightPanelPhases.ThreadPanel:
panel = <ThreadPanel
roomId={roomId}
resizeNotifier={this.props.resizeNotifier}
onClose={this.onClose} />;
break; break;
case RightPanelPhases.RoomSummary: case RightPanelPhases.RoomSummary:

View File

@ -2052,7 +2052,10 @@ export default class RoomView extends React.Component<IProps, IState> {
const showRightPanel = this.state.room && this.state.showRightPanel; const showRightPanel = this.state.room && this.state.showRightPanel;
const rightPanel = 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; : null;
const timelineClasses = classNames("mx_RoomView_timeline", { const timelineClasses = classNames("mx_RoomView_timeline", {

View File

@ -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<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);
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 <EventTile
key={event.getId()}
mxEvent={event}
enableFlair={false}
showReadReceipts={false}
as="div"
/>;
}
public render() {
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>
);
}
}
export default ThreadView;

View File

@ -26,12 +26,14 @@ import { MatrixClientPeg } from '../../MatrixClientPeg';
import ResizeNotifier from '../../utils/ResizeNotifier'; import ResizeNotifier from '../../utils/ResizeNotifier';
import EventTile from '../views/rooms/EventTile'; import EventTile from '../views/rooms/EventTile';
import MessageComposer from '../views/rooms/MessageComposer'; import MessageComposer from '../views/rooms/MessageComposer';
import { RoomPermalinkCreator } from '../../utils/permalinks/Permalinks';
interface IProps { interface IProps {
roomId: string; roomId: string;
onClose: () => void; onClose: () => void;
resizeNotifier: ResizeNotifier; resizeNotifier: ResizeNotifier;
mxEvent: MatrixEvent; mxEvent: MatrixEvent;
permalinkCreator?: RoomPermalinkCreator;
} }
interface IState { interface IState {
@ -44,9 +46,18 @@ interface IState {
class ThreadView extends React.Component<IProps, IState> { class ThreadView extends React.Component<IProps, IState> {
state = {}; 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 { public renderEventTile(event: MatrixEvent): JSX.Element {
return <EventTile return <EventTile
@ -59,9 +70,8 @@ class ThreadView extends React.Component<IProps, IState> {
} }
public render() { public render() {
const client = MatrixClientPeg.get(); const thread = this.props.mxEvent.getThread();
const room = client.getRoom(this.props.roomId); const room = MatrixClientPeg.get().getRoom(this.props.roomId);
const thread = room.getThread(this.props.mxEvent.getId());
return ( return (
<BaseCard <BaseCard
className="mx_ThreadView" className="mx_ThreadView"
@ -80,7 +90,7 @@ class ThreadView extends React.Component<IProps, IState> {
room={room} room={room}
resizeNotifier={this.props.resizeNotifier} resizeNotifier={this.props.resizeNotifier}
replyToEvent={this.props.mxEvent} replyToEvent={this.props.mxEvent}
permalinkCreator={null} permalinkCreator={this.props.permalinkCreator}
/> />
</BaseCard> </BaseCard>
); );

View File

@ -220,6 +220,13 @@ const onRoomFilesClick = () => {
}); });
}; };
const onRoomThreadsClick = () => {
defaultDispatcher.dispatch<SetRightPanelPhasePayload>({
action: Action.SetRightPanelPhase,
phase: RightPanelPhases.ThreadPanel,
});
};
const onRoomSettingsClick = () => { const onRoomSettingsClick = () => {
defaultDispatcher.dispatch({ action: "open_room_settings" }); defaultDispatcher.dispatch({ action: "open_room_settings" });
}; };
@ -273,6 +280,9 @@ const RoomSummaryCard: React.FC<IProps> = ({ room, onClose }) => {
<Button className="mx_RoomSummaryCard_icon_files" onClick={onRoomFilesClick}> <Button className="mx_RoomSummaryCard_icon_files" onClick={onRoomFilesClick}>
{ _t("Show files") } { _t("Show files") }
</Button> </Button>
<Button className="mx_RoomSummaryCard_icon_files" onClick={onRoomThreadsClick}>
{ _t("Show threads") }
</Button>
<Button className="mx_RoomSummaryCard_icon_share" onClick={onShareRoomClick}> <Button className="mx_RoomSummaryCard_icon_share" onClick={onShareRoomClick}>
{ _t("Share room") } { _t("Share room") }
</Button> </Button>

View File

@ -1790,6 +1790,7 @@
"%(count)s people|other": "%(count)s people", "%(count)s people|other": "%(count)s people",
"%(count)s people|one": "%(count)s person", "%(count)s people|one": "%(count)s person",
"Show files": "Show files", "Show files": "Show files",
"Show threads": "Show threads",
"Share room": "Share room", "Share room": "Share room",
"Room settings": "Room settings", "Room settings": "Room settings",
"Trusted": "Trusted", "Trusted": "Trusted",

View File

@ -40,6 +40,7 @@ export enum RightPanelPhases {
// Thread stuff // Thread stuff
ThreadView = "ThreadView", ThreadView = "ThreadView",
ThreadPanel = "ThreadPanel",
} }
// These are the phases that are safe to persist (the ones that don't require additional // These are the phases that are safe to persist (the ones that don't require additional