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

Remove threads labs flag and the ability to disable threads #9878

Merged
merged 12 commits into from
Feb 20, 2023
3 changes: 1 addition & 2 deletions cypress/e2e/polls/polls.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
Copyright 2022 The Matrix.org Foundation C.I.C.
Copyright 2022 - 2023 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.
Expand Down Expand Up @@ -77,7 +77,6 @@ describe("Polls", () => {
};

beforeEach(() => {
cy.enableLabsFeature("feature_threadstable");
cy.window().then((win) => {
win.localStorage.setItem("mx_lhs_size", "0"); // Collapse left panel for these tests
});
Expand Down
38 changes: 1 addition & 37 deletions cypress/e2e/threads/threads.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
Copyright 2022 The Matrix.org Foundation C.I.C.
Copyright 2022 - 2023 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.
Expand All @@ -19,17 +19,10 @@ limitations under the License.
import { SynapseInstance } from "../../plugins/synapsedocker";
import { MatrixClient } from "../../global";

function markWindowBeforeReload(): void {
// mark our window object to "know" when it gets reloaded
cy.window().then((w) => (w.beforeReload = true));
}

describe("Threads", () => {
let synapse: SynapseInstance;

beforeEach(() => {
// Default threads to ON for this spec
cy.enableLabsFeature("feature_threadstable");
cy.window().then((win) => {
win.localStorage.setItem("mx_lhs_size", "0"); // Collapse left panel for these tests
});
Expand All @@ -44,35 +37,6 @@ describe("Threads", () => {
cy.stopSynapse(synapse);
});

it("should reload when enabling threads beta", () => {
markWindowBeforeReload();

// Turn off
cy.openUserSettings("Labs").within(() => {
// initially the new property is there
cy.window().should("have.prop", "beforeReload", true);

cy.leaveBeta("Threaded messages");
cy.wait(1000);
// after reload the property should be gone
cy.window().should("not.have.prop", "beforeReload");
});

cy.get(".mx_MatrixChat", { timeout: 15000 }); // wait for the app
markWindowBeforeReload();

// Turn on
cy.openUserSettings("Labs").within(() => {
// initially the new property is there
cy.window().should("have.prop", "beforeReload", true);

cy.joinBeta("Threaded messages");
cy.wait(1000);
// after reload the property should be gone
cy.window().should("not.have.prop", "beforeReload");
});
});

it("should be usable for a conversation", () => {
let bot: MatrixClient;
cy.getBot(synapse, {
Expand Down
2 changes: 1 addition & 1 deletion src/DateUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ function withinCurrentYear(prevDate: Date, nextDate: Date): boolean {
return prevDate.getFullYear() === nextDate.getFullYear();
}

export function wantsDateSeparator(prevEventDate: Date, nextEventDate: Date): boolean {
export function wantsDateSeparator(prevEventDate: Date | null, nextEventDate: Date | null): boolean {
if (!nextEventDate || !prevEventDate) {
return false;
}
Expand Down
4 changes: 2 additions & 2 deletions src/MatrixClientPeg.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
Copyright 2015, 2016 OpenMarket Ltd
Copyright 2017 Vector Creations Ltd.
Copyright 2017, 2018, 2019 New Vector Ltd
Copyright 2019, 2020 The Matrix.org Foundation C.I.C.
Copyright 2019 - 2023 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.
Expand Down Expand Up @@ -218,7 +218,7 @@ class MatrixClientPegClass implements IMatrixClientPeg {
opts.pendingEventOrdering = PendingEventOrdering.Detached;
opts.lazyLoadMembers = true;
opts.clientWellKnownPollPeriod = 2 * 60 * 60; // 2 hours
opts.experimentalThreadSupport = SettingsStore.getValue("feature_threadstable");
opts.experimentalThreadSupport = true;
germain-gg marked this conversation as resolved.
Show resolved Hide resolved

if (SettingsStore.getValue("feature_sliding_sync")) {
const proxyUrl = SettingsStore.getValue("feature_sliding_sync_proxy_url");
Expand Down
14 changes: 1 addition & 13 deletions src/Unread.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
Copyright 2015 - 2021 The Matrix.org Foundation C.I.C.
Copyright 2015 - 2023 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.
Expand Down Expand Up @@ -66,18 +66,6 @@ export function doesRoomHaveUnreadMessages(room: Room): boolean {
// despite the name of the method :((
const readUpToId = room.getEventReadUpTo(myUserId!);

if (!SettingsStore.getValue("feature_threadstable")) {
// as we don't send RRs for our own messages, make sure we special case that
// if *we* sent the last message into the room, we consider it not unread!
// Should fix: https://github.com/vector-im/element-web/issues/3263
// https://github.com/vector-im/element-web/issues/2427
// ...and possibly some of the others at
// https://github.com/vector-im/element-web/issues/3363
if (room.timeline.length && room.timeline[room.timeline.length - 1].getSender() === myUserId) {
return false;
}
}

// if the read receipt relates to an event is that part of a thread
// we consider that there are no unread messages
// This might be a false negative, but probably the best we can do until
Expand Down
24 changes: 4 additions & 20 deletions src/components/structures/MessagePanel.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
Copyright 2016 - 2022 The Matrix.org Foundation C.I.C.
Copyright 2016 - 2023 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.
Expand Down Expand Up @@ -75,7 +75,6 @@ export function shouldFormContinuation(
prevEvent: MatrixEvent,
mxEvent: MatrixEvent,
showHiddenEvents: boolean,
threadsEnabled: boolean,
timelineRenderingType?: TimelineRenderingType,
): boolean {
if (timelineRenderingType === TimelineRenderingType.ThreadsList) return false;
Expand Down Expand Up @@ -105,7 +104,6 @@ export function shouldFormContinuation(

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

private readonly _showHiddenEvents: boolean;
private readonly threadsEnabled: boolean;
private isMounted = false;

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

this.showTypingNotificationsWatcherRef = SettingsStore.watchSetting(
"showTypingNotifications",
Expand Down Expand Up @@ -470,7 +466,7 @@ export default class MessagePanel extends React.Component<IProps, IState> {

// TODO: Implement granular (per-room) hide options
public shouldShowEvent(mxEv: MatrixEvent, forceHideEvents = false): boolean {
if (this.props.hideThreadedMessages && this.threadsEnabled && this.props.room) {
if (this.props.hideThreadedMessages && this.props.room) {
const { shouldLiveInRoom } = this.props.room.eventShouldLiveIn(mxEv, this.props.events);
if (!shouldLiveInRoom) {
return false;
Expand Down Expand Up @@ -726,25 +722,13 @@ export default class MessagePanel extends React.Component<IProps, IState> {
willWantDateSeparator ||
mxEv.getSender() !== nextEv.getSender() ||
getEventDisplayInfo(nextEv, this.showHiddenEvents).isInfoMessage ||
!shouldFormContinuation(
mxEv,
nextEv,
this.showHiddenEvents,
this.threadsEnabled,
this.context.timelineRenderingType,
);
!shouldFormContinuation(mxEv, nextEv, this.showHiddenEvents, this.context.timelineRenderingType);
}

// is this a continuation of the previous message?
const continuation =
!wantsDateSeparator &&
shouldFormContinuation(
prevEvent,
mxEv,
this.showHiddenEvents,
this.threadsEnabled,
this.context.timelineRenderingType,
);
shouldFormContinuation(prevEvent, mxEv, this.showHiddenEvents, this.context.timelineRenderingType);

const eventId = mxEv.getId();
const highlight = eventId === this.props.highlightedEventId;
Expand Down
35 changes: 16 additions & 19 deletions src/components/structures/RoomSearchView.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
Copyright 2015 - 2022 The Matrix.org Foundation C.I.C.
Copyright 2015 - 2023 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.
Expand Down Expand Up @@ -34,7 +34,6 @@ import ResizeNotifier from "../../utils/ResizeNotifier";
import MatrixClientContext from "../../contexts/MatrixClientContext";
import { RoomPermalinkCreator } from "../../utils/permalinks/Permalinks";
import RoomContext from "../../contexts/RoomContext";
import SettingsStore from "../../settings/SettingsStore";

const DEBUG = false;
let debuglog = function (msg: string) {};
Expand Down Expand Up @@ -100,23 +99,21 @@ export const RoomSearchView = forwardRef<ScrollPanel, Props>(
return b.length - a.length;
});

if (SettingsStore.getValue("feature_threadstable")) {
// Process all thread roots returned in this batch of search results
// XXX: This won't work for results coming from Seshat which won't include the bundled relationship
for (const result of results.results) {
for (const event of result.context.getTimeline()) {
const bundledRelationship =
event.getServerAggregatedRelation<IThreadBundledRelationship>(
THREAD_RELATION_TYPE.name,
);
if (!bundledRelationship || event.getThread()) continue;
const room = client.getRoom(event.getRoomId());
const thread = room.findThreadForEvent(event);
if (thread) {
event.setThread(thread);
} else {
room.createThread(event.getId(), event, [], true);
}
// Process all thread roots returned in this batch of search results
// XXX: This won't work for results coming from Seshat which won't include the bundled relationship
for (const result of results.results) {
for (const event of result.context.getTimeline()) {
const bundledRelationship =
event.getServerAggregatedRelation<IThreadBundledRelationship>(
THREAD_RELATION_TYPE.name,
);
if (!bundledRelationship || event.getThread()) continue;
const room = client.getRoom(event.getRoomId());
const thread = room.findThreadForEvent(event);
if (thread) {
event.setThread(thread);
} else {
room.createThread(event.getId(), event, [], true);
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/components/structures/RoomView.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
Copyright 2015, 2016 OpenMarket Ltd
Copyright 2017 Vector Creations Ltd
Copyright 2018, 2019 New Vector Ltd
Copyright 2019 - 2022 The Matrix.org Foundation C.I.C.
Copyright 2019 - 2023 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.
Expand Down Expand Up @@ -1182,7 +1182,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
CHAT_EFFECTS.forEach((effect) => {
if (containsEmoji(ev.getContent(), effect.emojis) || ev.getContent().msgtype === effect.msgType) {
// For initial threads launch, chat effects are disabled see #19731
if (!SettingsStore.getValue("feature_threadstable") || !ev.isRelation(THREAD_RELATION_TYPE.name)) {
if (!ev.isRelation(THREAD_RELATION_TYPE.name)) {
dis.dispatch({ action: `effects.${effect.command}` });
}
}
Expand Down
45 changes: 2 additions & 43 deletions src/components/structures/ThreadPanel.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
Copyright 2021 - 2022 The Matrix.org Foundation C.I.C.
Copyright 2021 - 2023 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.
Expand Down Expand Up @@ -31,16 +31,9 @@ import { Layout } from "../../settings/enums/Layout";
import { RoomPermalinkCreator } from "../../utils/permalinks/Permalinks";
import Measured from "../views/elements/Measured";
import PosthogTrackers from "../../PosthogTrackers";
import AccessibleButton, { ButtonEvent } from "../views/elements/AccessibleButton";
import { BetaPill } from "../views/beta/BetaCard";
import Modal from "../../Modal";
import BetaFeedbackDialog from "../views/dialogs/BetaFeedbackDialog";
import { Action } from "../../dispatcher/actions";
import { UserTab } from "../views/dialogs/UserTab";
import dis from "../../dispatcher/dispatcher";
import { ButtonEvent } from "../views/elements/AccessibleButton";
import Spinner from "../views/elements/Spinner";
import Heading from "../views/typography/Heading";
import { shouldShowFeedback } from "../../utils/Feedback";

interface IProps {
roomId: string;
Expand Down Expand Up @@ -246,14 +239,6 @@ const ThreadPanel: React.FC<IProps> = ({ roomId, onClose, permalinkCreator }) =>
}
}, [timelineSet, timelinePanel]);

const openFeedback = shouldShowFeedback()
? () => {
Modal.createDialog(BetaFeedbackDialog, {
featureId: "feature_threadstable",
});
}
: null;

return (
<RoomContext.Provider
value={{
Expand All @@ -271,32 +256,6 @@ const ThreadPanel: React.FC<IProps> = ({ roomId, onClose, permalinkCreator }) =>
empty={!timelineSet?.getLiveTimeline()?.getEvents().length}
/>
}
footer={
<>
<BetaPill
tooltipTitle={_t("Threads are a beta feature")}
tooltipCaption={_t("Click for more info")}
onClick={() => {
dis.dispatch({
action: Action.ViewUserSettings,
initialTabId: UserTab.Labs,
});
}}
/>
{openFeedback &&
_t(
"<a>Give feedback</a>",
{},
{
a: (sub) => (
<AccessibleButton kind="link_inline" onClick={openFeedback}>
{sub}
</AccessibleButton>
),
},
)}
</>
}
className="mx_ThreadPanel"
onClose={onClose}
withoutScrollContainer={true}
Expand Down
5 changes: 2 additions & 3 deletions src/components/structures/TimelinePanel.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
Copyright 2016 - 2022 The Matrix.org Foundation C.I.C.
Copyright 2016 - 2023 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.
Expand Down Expand Up @@ -1687,8 +1687,7 @@ class TimelinePanel extends React.Component<IProps, IState> {
/* Threads do not have server side support for read receipts and the concept
is very tied to the main room timeline, we are forcing the timeline to
send read receipts for threaded events */
const isThreadTimeline = this.context.timelineRenderingType === TimelineRenderingType.Thread;
if (SettingsStore.getValue("feature_threadstable") && isThreadTimeline) {
if (this.context.timelineRenderingType === TimelineRenderingType.Thread) {
return 0;
}
const index = this.state.events.findIndex((ev) => ev.getId() === evId);
Expand Down
Loading