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

Commit 8c22584

Browse files
author
Germain
authored
Remove threads labs flag and the ability to disable threads (#9878)
1 parent a09e105 commit 8c22584

34 files changed

+197
-501
lines changed

cypress/e2e/polls/polls.spec.ts

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
Copyright 2022 The Matrix.org Foundation C.I.C.
2+
Copyright 2022 - 2023 The Matrix.org Foundation C.I.C.
33
44
Licensed under the Apache License, Version 2.0 (the "License");
55
you may not use this file except in compliance with the License.
@@ -83,7 +83,6 @@ describe("Polls", () => {
8383
};
8484

8585
beforeEach(() => {
86-
cy.enableLabsFeature("feature_threadenabled");
8786
cy.window().then((win) => {
8887
win.localStorage.setItem("mx_lhs_size", "0"); // Collapse left panel for these tests
8988
});

cypress/e2e/threads/threads.spec.ts

+1-37
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
Copyright 2022 The Matrix.org Foundation C.I.C.
2+
Copyright 2022 - 2023 The Matrix.org Foundation C.I.C.
33
44
Licensed under the Apache License, Version 2.0 (the "License");
55
you may not use this file except in compliance with the License.
@@ -19,17 +19,10 @@ limitations under the License.
1919
import { HomeserverInstance } from "../../plugins/utils/homeserver";
2020
import { MatrixClient } from "../../global";
2121

22-
function markWindowBeforeReload(): void {
23-
// mark our window object to "know" when it gets reloaded
24-
cy.window().then((w) => (w.beforeReload = true));
25-
}
26-
2722
describe("Threads", () => {
2823
let homeserver: HomeserverInstance;
2924

3025
beforeEach(() => {
31-
// Default threads to ON for this spec
32-
cy.enableLabsFeature("feature_threadenabled");
3326
cy.window().then((win) => {
3427
win.localStorage.setItem("mx_lhs_size", "0"); // Collapse left panel for these tests
3528
});
@@ -44,35 +37,6 @@ describe("Threads", () => {
4437
cy.stopHomeserver(homeserver);
4538
});
4639

47-
it("should reload when enabling threads beta", () => {
48-
markWindowBeforeReload();
49-
50-
// Turn off
51-
cy.openUserSettings("Labs").within(() => {
52-
// initially the new property is there
53-
cy.window().should("have.prop", "beforeReload", true);
54-
55-
cy.leaveBeta("Threaded messages");
56-
cy.wait(1000);
57-
// after reload the property should be gone
58-
cy.window().should("not.have.prop", "beforeReload");
59-
});
60-
61-
cy.get(".mx_MatrixChat", { timeout: 15000 }); // wait for the app
62-
markWindowBeforeReload();
63-
64-
// Turn on
65-
cy.openUserSettings("Labs").within(() => {
66-
// initially the new property is there
67-
cy.window().should("have.prop", "beforeReload", true);
68-
69-
cy.joinBeta("Threaded messages");
70-
cy.wait(1000);
71-
// after reload the property should be gone
72-
cy.window().should("not.have.prop", "beforeReload");
73-
});
74-
});
75-
7640
it("should be usable for a conversation", () => {
7741
let bot: MatrixClient;
7842
cy.getBot(homeserver, {

src/DateUtils.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,10 @@ function withinCurrentYear(prevDate: Date, nextDate: Date): boolean {
175175
return prevDate.getFullYear() === nextDate.getFullYear();
176176
}
177177

178-
export function wantsDateSeparator(prevEventDate: Date | undefined, nextEventDate: Date | undefined): boolean {
178+
export function wantsDateSeparator(
179+
prevEventDate: Date | null | undefined,
180+
nextEventDate: Date | null | undefined,
181+
): boolean {
179182
if (!nextEventDate || !prevEventDate) {
180183
return false;
181184
}

src/MatrixClientPeg.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
Copyright 2015, 2016 OpenMarket Ltd
33
Copyright 2017 Vector Creations Ltd.
44
Copyright 2017, 2018, 2019 New Vector Ltd
5-
Copyright 2019, 2020 The Matrix.org Foundation C.I.C.
5+
Copyright 2019 - 2023 The Matrix.org Foundation C.I.C.
66
77
Licensed under the Apache License, Version 2.0 (the "License");
88
you may not use this file except in compliance with the License.
@@ -218,7 +218,7 @@ class MatrixClientPegClass implements IMatrixClientPeg {
218218
opts.pendingEventOrdering = PendingEventOrdering.Detached;
219219
opts.lazyLoadMembers = true;
220220
opts.clientWellKnownPollPeriod = 2 * 60 * 60; // 2 hours
221-
opts.threadSupport = SettingsStore.getValue("feature_threadenabled");
221+
opts.threadSupport = true;
222222

223223
if (SettingsStore.getValue("feature_sliding_sync")) {
224224
const proxyUrl = SettingsStore.getValue("feature_sliding_sync_proxy_url");

src/Unread.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
Copyright 2015 - 2021 The Matrix.org Foundation C.I.C.
2+
Copyright 2015 - 2023 The Matrix.org Foundation C.I.C.
33
44
Licensed under the Apache License, Version 2.0 (the "License");
55
you may not use this file except in compliance with the License.

src/components/structures/MessagePanel.tsx

+4-20
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
Copyright 2016 - 2022 The Matrix.org Foundation C.I.C.
2+
Copyright 2016 - 2023 The Matrix.org Foundation C.I.C.
33
44
Licensed under the Apache License, Version 2.0 (the "License");
55
you may not use this file except in compliance with the License.
@@ -75,7 +75,6 @@ export function shouldFormContinuation(
7575
prevEvent: MatrixEvent | null,
7676
mxEvent: MatrixEvent,
7777
showHiddenEvents: boolean,
78-
threadsEnabled: boolean,
7978
timelineRenderingType?: TimelineRenderingType,
8079
): boolean {
8180
if (timelineRenderingType === TimelineRenderingType.ThreadsList) return false;
@@ -105,7 +104,6 @@ export function shouldFormContinuation(
105104

106105
// Thread summaries in the main timeline should break up a continuation on both sides
107106
if (
108-
threadsEnabled &&
109107
(hasThreadSummary(mxEvent) || hasThreadSummary(prevEvent)) &&
110108
timelineRenderingType !== TimelineRenderingType.Thread
111109
) {
@@ -259,7 +257,6 @@ export default class MessagePanel extends React.Component<IProps, IState> {
259257
private readReceiptsByUserId: Record<string, IReadReceiptForUser> = {};
260258

261259
private readonly _showHiddenEvents: boolean;
262-
private readonly threadsEnabled: boolean;
263260
private isMounted = false;
264261

265262
private readMarkerNode = createRef<HTMLLIElement>();
@@ -287,7 +284,6 @@ export default class MessagePanel extends React.Component<IProps, IState> {
287284
// and we check this in a hot code path. This is also cached in our
288285
// RoomContext, however we still need a fallback for roomless MessagePanels.
289286
this._showHiddenEvents = SettingsStore.getValue("showHiddenEventsInTimeline");
290-
this.threadsEnabled = SettingsStore.getValue("feature_threadenabled");
291287

292288
this.showTypingNotificationsWatcherRef = SettingsStore.watchSetting(
293289
"showTypingNotifications",
@@ -464,7 +460,7 @@ export default class MessagePanel extends React.Component<IProps, IState> {
464460

465461
// TODO: Implement granular (per-room) hide options
466462
public shouldShowEvent(mxEv: MatrixEvent, forceHideEvents = false): boolean {
467-
if (this.props.hideThreadedMessages && this.threadsEnabled && this.props.room) {
463+
if (this.props.hideThreadedMessages && this.props.room) {
468464
const { shouldLiveInRoom } = this.props.room.eventShouldLiveIn(mxEv, this.props.events);
469465
if (!shouldLiveInRoom) {
470466
return false;
@@ -720,25 +716,13 @@ export default class MessagePanel extends React.Component<IProps, IState> {
720716
willWantDateSeparator ||
721717
mxEv.getSender() !== nextEv.getSender() ||
722718
getEventDisplayInfo(nextEv, this.showHiddenEvents).isInfoMessage ||
723-
!shouldFormContinuation(
724-
mxEv,
725-
nextEv,
726-
this.showHiddenEvents,
727-
this.threadsEnabled,
728-
this.context.timelineRenderingType,
729-
);
719+
!shouldFormContinuation(mxEv, nextEv, this.showHiddenEvents, this.context.timelineRenderingType);
730720
}
731721

732722
// is this a continuation of the previous message?
733723
const continuation =
734724
!wantsDateSeparator &&
735-
shouldFormContinuation(
736-
prevEvent,
737-
mxEv,
738-
this.showHiddenEvents,
739-
this.threadsEnabled,
740-
this.context.timelineRenderingType,
741-
);
725+
shouldFormContinuation(prevEvent, mxEv, this.showHiddenEvents, this.context.timelineRenderingType);
742726

743727
const eventId = mxEv.getId();
744728
const highlight = eventId === this.props.highlightedEventId;

src/components/structures/RoomSearchView.tsx

+14-19
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
Copyright 2015 - 2022 The Matrix.org Foundation C.I.C.
2+
Copyright 2015 - 2023 The Matrix.org Foundation C.I.C.
33
44
Licensed under the Apache License, Version 2.0 (the "License");
55
you may not use this file except in compliance with the License.
@@ -34,7 +34,6 @@ import ResizeNotifier from "../../utils/ResizeNotifier";
3434
import MatrixClientContext from "../../contexts/MatrixClientContext";
3535
import { RoomPermalinkCreator } from "../../utils/permalinks/Permalinks";
3636
import RoomContext from "../../contexts/RoomContext";
37-
import SettingsStore from "../../settings/SettingsStore";
3837

3938
const DEBUG = false;
4039
let debuglog = function (msg: string): void {};
@@ -100,23 +99,19 @@ export const RoomSearchView = forwardRef<ScrollPanel, Props>(
10099
return b.length - a.length;
101100
});
102101

103-
if (SettingsStore.getValue("feature_threadenabled")) {
104-
// Process all thread roots returned in this batch of search results
105-
// XXX: This won't work for results coming from Seshat which won't include the bundled relationship
106-
for (const result of results.results) {
107-
for (const event of result.context.getTimeline()) {
108-
const bundledRelationship =
109-
event.getServerAggregatedRelation<IThreadBundledRelationship>(
110-
THREAD_RELATION_TYPE.name,
111-
);
112-
if (!bundledRelationship || event.getThread()) continue;
113-
const room = client.getRoom(event.getRoomId());
114-
const thread = room?.findThreadForEvent(event);
115-
if (thread) {
116-
event.setThread(thread);
117-
} else {
118-
room?.createThread(event.getId()!, event, [], true);
119-
}
102+
for (const result of results.results) {
103+
for (const event of result.context.getTimeline()) {
104+
const bundledRelationship =
105+
event.getServerAggregatedRelation<IThreadBundledRelationship>(
106+
THREAD_RELATION_TYPE.name,
107+
);
108+
if (!bundledRelationship || event.getThread()) continue;
109+
const room = client.getRoom(event.getRoomId());
110+
const thread = room?.findThreadForEvent(event);
111+
if (thread) {
112+
event.setThread(thread);
113+
} else {
114+
room?.createThread(event.getId()!, event, [], true);
120115
}
121116
}
122117
}

src/components/structures/RoomView.tsx

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
Copyright 2015, 2016 OpenMarket Ltd
33
Copyright 2017 Vector Creations Ltd
44
Copyright 2018, 2019 New Vector Ltd
5-
Copyright 2019 - 2022 The Matrix.org Foundation C.I.C.
5+
Copyright 2019 - 2023 The Matrix.org Foundation C.I.C.
66
77
Licensed under the Apache License, Version 2.0 (the "License");
88
you may not use this file except in compliance with the License.
@@ -1194,7 +1194,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
11941194
CHAT_EFFECTS.forEach((effect) => {
11951195
if (containsEmoji(ev.getContent(), effect.emojis) || ev.getContent().msgtype === effect.msgType) {
11961196
// For initial threads launch, chat effects are disabled see #19731
1197-
if (!SettingsStore.getValue("feature_threadenabled") || !ev.isRelation(THREAD_RELATION_TYPE.name)) {
1197+
if (!ev.isRelation(THREAD_RELATION_TYPE.name)) {
11981198
dis.dispatch({ action: `effects.${effect.command}` });
11991199
}
12001200
}

src/components/structures/TimelinePanel.tsx

+2-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
Copyright 2016 - 2022 The Matrix.org Foundation C.I.C.
2+
Copyright 2016 - 2023 The Matrix.org Foundation C.I.C.
33
44
Licensed under the Apache License, Version 2.0 (the "License");
55
you may not use this file except in compliance with the License.
@@ -1689,8 +1689,7 @@ class TimelinePanel extends React.Component<IProps, IState> {
16891689
/* Threads do not have server side support for read receipts and the concept
16901690
is very tied to the main room timeline, we are forcing the timeline to
16911691
send read receipts for threaded events */
1692-
const isThreadTimeline = this.context.timelineRenderingType === TimelineRenderingType.Thread;
1693-
if (SettingsStore.getValue("feature_threadenabled") && isThreadTimeline) {
1692+
if (this.context.timelineRenderingType === TimelineRenderingType.Thread) {
16941693
return 0;
16951694
}
16961695
const index = this.state.events.findIndex((ev) => ev.getId() === evId);

src/components/views/context_menus/MessageContextMenu.tsx

+2-9
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/*
22
Copyright 2019 Michael Telatynski <[email protected]>
3-
Copyright 2015 - 2022 The Matrix.org Foundation C.I.C.
3+
Copyright 2015 - 2023 The Matrix.org Foundation C.I.C.
44
Copyright 2021 - 2022 Šimon Brandner <[email protected]>
55
66
Licensed under the Apache License, Version 2.0 (the "License");
@@ -56,7 +56,6 @@ import { getForwardableEvent } from "../../../events/forward/getForwardableEvent
5656
import { getShareableLocationEvent } from "../../../events/location/getShareableLocationEvent";
5757
import { ShowThreadPayload } from "../../../dispatcher/payloads/ShowThreadPayload";
5858
import { CardContext } from "../right_panel/context";
59-
import { UserTab } from "../dialogs/UserTab";
6059

6160
interface IReplyInThreadButton {
6261
mxEvent: MatrixEvent;
@@ -71,12 +70,7 @@ const ReplyInThreadButton: React.FC<IReplyInThreadButton> = ({ mxEvent, closeMen
7170
if (Boolean(relationType) && relationType !== RelationType.Thread) return null;
7271

7372
const onClick = (): void => {
74-
if (!SettingsStore.getValue("feature_threadenabled")) {
75-
dis.dispatch({
76-
action: Action.ViewUserSettings,
77-
initialTabId: UserTab.Labs,
78-
});
79-
} else if (mxEvent.getThread() && !mxEvent.isThreadRoot) {
73+
if (mxEvent.getThread() && !mxEvent.isThreadRoot) {
8074
dis.dispatch<ShowThreadPayload>({
8175
action: Action.ShowThread,
8276
rootEvent: mxEvent.getThread().rootEvent,
@@ -639,7 +633,6 @@ export default class MessageContextMenu extends React.Component<IProps, IState>
639633
rightClick &&
640634
contentActionable &&
641635
canSendMessages &&
642-
SettingsStore.getValue("feature_threadenabled") &&
643636
Thread.hasServerSideSupport &&
644637
timelineRenderingType !== TimelineRenderingType.Thread
645638
) {

src/components/views/messages/MessageActionBar.tsx

+2-23
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/*
22
Copyright 2019 New Vector Ltd
33
Copyright 2019 Michael Telatynski <[email protected]>
4-
Copyright 2019, 2020 The Matrix.org Foundation C.I.C.
4+
Copyright 2019 - 2023 The Matrix.org Foundation C.I.C.
55
66
Licensed under the Apache License, Version 2.0 (the "License");
77
you may not use this file except in compliance with the License.
@@ -20,7 +20,6 @@ import React, { ReactElement, useCallback, useContext, useEffect } from "react";
2020
import { EventStatus, MatrixEvent, MatrixEventEvent } from "matrix-js-sdk/src/models/event";
2121
import classNames from "classnames";
2222
import { MsgType, RelationType } from "matrix-js-sdk/src/@types/event";
23-
import { Thread } from "matrix-js-sdk/src/models/thread";
2423
import { M_BEACON_INFO } from "matrix-js-sdk/src/@types/beacon";
2524

2625
import { Icon as ContextMenuIcon } from "../../../../res/img/element-icons/context-menu.svg";
@@ -54,7 +53,6 @@ import { CardContext } from "../right_panel/context";
5453
import { shouldDisplayReply } from "../../../utils/Reply";
5554
import { Key } from "../../../Keyboard";
5655
import { ALTERNATE_KEY_NAME } from "../../../accessibility/KeyboardShortcuts";
57-
import { UserTab } from "../dialogs/UserTab";
5856
import { Action } from "../../../dispatcher/actions";
5957
import { ShowThreadPayload } from "../../../dispatcher/payloads/ShowThreadPayload";
6058
import useFavouriteMessages from "../../../hooks/useFavouriteMessages";
@@ -204,24 +202,13 @@ const ReplyInThreadButton: React.FC<IReplyInThreadButton> = ({ mxEvent }) => {
204202

205203
const relationType = mxEvent?.getRelation()?.rel_type;
206204
const hasARelation = !!relationType && relationType !== RelationType.Thread;
207-
const threadsEnabled = SettingsStore.getValue("feature_threadenabled");
208-
209-
if (!threadsEnabled && !Thread.hasServerSideSupport) {
210-
// hide the prompt if the user would only have degraded mode
211-
return null;
212-
}
213205

214206
const onClick = (e: React.MouseEvent): void => {
215207
// Don't open the regular browser or our context menu on right-click
216208
e.preventDefault();
217209
e.stopPropagation();
218210

219-
if (!SettingsStore.getValue("feature_threadenabled")) {
220-
dis.dispatch({
221-
action: Action.ViewUserSettings,
222-
initialTabId: UserTab.Labs,
223-
});
224-
} else if (mxEvent.getThread() && !mxEvent.isThreadRoot) {
211+
if (mxEvent.getThread() && !mxEvent.isThreadRoot) {
225212
defaultDispatcher.dispatch<ShowThreadPayload>({
226213
action: Action.ShowThread,
227214
rootEvent: mxEvent.getThread().rootEvent,
@@ -250,13 +237,6 @@ const ReplyInThreadButton: React.FC<IReplyInThreadButton> = ({ mxEvent }) => {
250237
? _t("Reply in thread")
251238
: _t("Can't create a thread from an event with an existing relation")}
252239
</div>
253-
{!hasARelation && (
254-
<div className="mx_Tooltip_sub">
255-
{SettingsStore.getValue("feature_threadenabled")
256-
? _t("Beta feature")
257-
: _t("Beta feature. Click to learn more.")}
258-
</div>
259-
)}
260240
</>
261241
}
262242
title={
@@ -548,7 +528,6 @@ export default class MessageActionBar extends React.PureComponent<IMessageAction
548528
);
549529
}
550530
} else if (
551-
SettingsStore.getValue("feature_threadenabled") &&
552531
// Show thread icon even for deleted messages, but only within main timeline
553532
this.context.timelineRenderingType === TimelineRenderingType.Room &&
554533
this.props.mxEvent.getThread()

0 commit comments

Comments
 (0)