Improve thread partitioning for 2nd degree relations (#7748)

This commit is contained in:
Germain 2022-02-10 15:18:55 +00:00 committed by GitHub
parent 87ca70edb1
commit 15a9303d29
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 56 additions and 17 deletions

View file

@ -467,6 +467,16 @@ export default class MessagePanel extends React.Component<IProps, IState> {
// TODO: Implement granular (per-room) hide options // TODO: Implement granular (per-room) hide options
public shouldShowEvent(mxEv: MatrixEvent, forceHideEvents = false): boolean { public shouldShowEvent(mxEv: MatrixEvent, forceHideEvents = false): boolean {
if (this.props.hideThreadedMessages && SettingsStore.getValue("feature_thread")) {
if (mxEv.isThreadRelation) {
return false;
}
if (this.shouldLiveInThreadOnly(mxEv)) {
return false;
}
}
if (MatrixClientPeg.get().isUserIgnored(mxEv.getSender())) { if (MatrixClientPeg.get().isUserIgnored(mxEv.getSender())) {
return false; // ignored = no show (only happens if the ignore happens after an event was received) return false; // ignored = no show (only happens if the ignore happens after an event was received)
} }
@ -482,17 +492,25 @@ export default class MessagePanel extends React.Component<IProps, IState> {
// Always show highlighted event // Always show highlighted event
if (this.props.highlightedEventId === mxEv.getId()) return true; if (this.props.highlightedEventId === mxEv.getId()) return true;
// Checking if the message has a "parentEventId" as we do not return !shouldHideEvent(mxEv, this.context);
// want to hide the root event of the thread }
if (mxEv.isThreadRelation &&
!mxEv.isThreadRoot && private shouldLiveInThreadOnly(event: MatrixEvent): boolean {
this.props.hideThreadedMessages && const associatedId = event.getAssociatedId();
SettingsStore.getValue("feature_thread")
) { const targetsThreadRoot = event.threadRootId === associatedId;
if (event.isThreadRoot || targetsThreadRoot || !event.isThreadRelation) {
return false; return false;
} }
return !shouldHideEvent(mxEv, this.context); // If this is a reply, then we use the associated event to decide whether
// this should be thread only or not
const parentEvent = this.props.room.findEventById(associatedId);
if (parentEvent) {
return this.shouldLiveInThreadOnly(parentEvent);
} else {
return true;
}
} }
public readMarkerForEvent(eventId: string, isLastEvent: boolean): ReactNode { public readMarkerForEvent(eventId: string, isLastEvent: boolean): ReactNode {

View file

@ -209,6 +209,7 @@ export interface IRoomState {
wasContextSwitch?: boolean; wasContextSwitch?: boolean;
editState?: EditorStateTransfer; editState?: EditorStateTransfer;
timelineRenderingType: TimelineRenderingType; timelineRenderingType: TimelineRenderingType;
threadId?: string;
liveTimeline?: EventTimeline; liveTimeline?: EventTimeline;
} }

View file

@ -264,6 +264,7 @@ export default class ThreadView extends React.Component<IProps, IState> {
<RoomContext.Provider value={{ <RoomContext.Provider value={{
...this.context, ...this.context,
timelineRenderingType: TimelineRenderingType.Thread, timelineRenderingType: TimelineRenderingType.Thread,
threadId: this.state.thread?.id,
liveTimeline: this.state?.thread?.timelineSet?.getLiveTimeline(), liveTimeline: this.state?.thread?.timelineSet?.getLiveTimeline(),
}}> }}>

View file

@ -1285,11 +1285,30 @@ class TimelinePanel extends React.Component<IProps, IState> {
// should use this list, so that they don't advance into pending events. // should use this list, so that they don't advance into pending events.
const liveEvents = [...events]; const liveEvents = [...events];
const thread = events[0]?.getThread();
// if we're at the end of the live timeline, append the pending events // if we're at the end of the live timeline, append the pending events
if (!this.timelineWindow.canPaginate(EventTimeline.FORWARDS)) { if (!this.timelineWindow.canPaginate(EventTimeline.FORWARDS)) {
events.push(...this.props.timelineSet.getPendingEvents(thread)); const pendingEvents = this.props.timelineSet.getPendingEvents();
if (this.context.timelineRenderingType === TimelineRenderingType.Thread) {
events.push(...pendingEvents.filter(e => e.threadRootId === this.context.threadId));
} else {
events.push(...pendingEvents.filter(e => {
const hasNoRelation = !e.getRelation();
if (hasNoRelation) {
return true;
}
if (e.isThreadRelation) {
return false;
}
const parentEvent = this.props.timelineSet.findEventById(e.getAssociatedId());
if (!parentEvent) {
return false;
} else {
return !parentEvent.isThreadRelation;
}
}));
}
} }
return { return {

View file

@ -649,13 +649,13 @@ export default class EventTile extends React.Component<IProps, IState> {
} }
private renderThreadPanelSummary(): JSX.Element | null { private renderThreadPanelSummary(): JSX.Element | null {
if (!this.thread) { if (!this.state.thread) {
return null; return null;
} }
return <div className="mx_ThreadPanel_replies"> return <div className="mx_ThreadPanel_replies">
<span className="mx_ThreadPanel_repliesSummary"> <span className="mx_ThreadPanel_repliesSummary">
{ this.thread.length } { this.state.thread.length }
</span> </span>
{ this.renderThreadLastMessagePreview() } { this.renderThreadLastMessagePreview() }
</div>; </div>;
@ -665,7 +665,7 @@ export default class EventTile extends React.Component<IProps, IState> {
const { threadLastReply } = this.state; const { threadLastReply } = this.state;
const threadMessagePreview = MessagePreviewStore.instance.generatePreviewForEvent(threadLastReply); const threadMessagePreview = MessagePreviewStore.instance.generatePreviewForEvent(threadLastReply);
const sender = this.thread.roomState.getSentinelMember(threadLastReply.getSender()); const sender = this.state.thread?.roomState.getSentinelMember(threadLastReply.getSender());
return <> return <>
<MemberAvatar <MemberAvatar
member={sender} member={sender}
@ -689,7 +689,7 @@ export default class EventTile extends React.Component<IProps, IState> {
return ( return (
<p className="mx_ThreadSummaryIcon">{ _t("From a thread") }</p> <p className="mx_ThreadSummaryIcon">{ _t("From a thread") }</p>
); );
} else if (this.state.threadReplyCount) { } else if (this.state.threadReplyCount && this.props.mxEvent.isThreadRoot) {
return ( return (
<CardContext.Consumer> <CardContext.Consumer>
{ context => { context =>
@ -1092,11 +1092,10 @@ export default class EventTile extends React.Component<IProps, IState> {
return this.props.getRelationsForEvent(eventId, "m.annotation", "m.reaction"); return this.props.getRelationsForEvent(eventId, "m.annotation", "m.reaction");
}; };
private onReactionsCreated = (relationType: string, eventType: string) => { private onReactionsCreated = (relationType: string, eventType: string): void => {
if (relationType !== "m.annotation" || eventType !== "m.reaction") { if (relationType !== "m.annotation" || eventType !== "m.reaction") {
return; return;
} }
this.props.mxEvent.removeListener("Event.relationsCreated", this.onReactionsCreated);
this.setState({ this.setState({
reactions: this.getReactions(), reactions: this.getReactions(),
}); });

View file

@ -63,6 +63,7 @@ const RoomContext = createContext<IRoomState>({
matrixClientIsReady: false, matrixClientIsReady: false,
dragCounter: 0, dragCounter: 0,
timelineRenderingType: TimelineRenderingType.Room, timelineRenderingType: TimelineRenderingType.Room,
threadId: undefined,
liveTimeline: undefined, liveTimeline: undefined,
}); });
RoomContext.displayName = "RoomContext"; RoomContext.displayName = "RoomContext";