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

Commit 2d2755d

Browse files
author
Germain
authored
🧵 Enable threads by default (#9736)
* Delabs threads * remove threads reload when labs is toggled * Fix ts strict * fix rebase mistake * remove .only * fix pr comments * re-introduce backwards compat * Fix export test * Fix SearchREsultTile test * strict ts
1 parent 9668a24 commit 2d2755d

32 files changed

+88
-170
lines changed

cypress/e2e/polls/polls.spec.ts

-1
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,6 @@ describe("Polls", () => {
7777
};
7878

7979
beforeEach(() => {
80-
cy.enableLabsFeature("feature_thread");
8180
cy.window().then((win) => {
8281
win.localStorage.setItem("mx_lhs_size", "0"); // Collapse left panel for these tests
8382
});

cypress/e2e/threads/threads.spec.ts

-36
Original file line numberDiff line numberDiff line change
@@ -19,17 +19,10 @@ limitations under the License.
1919
import { SynapseInstance } from "../../plugins/synapsedocker";
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 synapse: SynapseInstance;
2924

3025
beforeEach(() => {
31-
// Default threads to ON for this spec
32-
cy.enableLabsFeature("feature_thread");
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.stopSynapse(synapse);
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("Threads");
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("Threads");
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(synapse, {

res/css/views/messages/_MessageActionBar.pcss

-7
Original file line numberDiff line numberDiff line change
@@ -118,13 +118,6 @@ limitations under the License.
118118
color: $primary-content;
119119
}
120120

121-
&.mx_MessageActionBar_threadButton {
122-
.mx_Indicator {
123-
background: $links;
124-
animation-iteration-count: infinite;
125-
}
126-
}
127-
128121
&.mx_MessageActionBar_favouriteButton_fillstar {
129122
color: var(--MessageActionBar-star-button-color);
130123
}

res/img/betas/threads.png

-85 KB
Binary file not shown.

src/MatrixClientPeg.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -233,7 +233,7 @@ class MatrixClientPegClass implements IMatrixClientPeg {
233233
opts.pendingEventOrdering = PendingEventOrdering.Detached;
234234
opts.lazyLoadMembers = true;
235235
opts.clientWellKnownPollPeriod = 2 * 60 * 60; // 2 hours
236-
opts.experimentalThreadSupport = SettingsStore.getValue("feature_thread");
236+
opts.experimentalThreadSupport = SettingsStore.getValue("feature_threadstable");
237237

238238
if (SettingsStore.getValue("feature_sliding_sync")) {
239239
const proxyUrl = SettingsStore.getValue("feature_sliding_sync_proxy_url");

src/components/structures/MessagePanel.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -287,7 +287,7 @@ export default class MessagePanel extends React.Component<IProps, IState> {
287287
// and we check this in a hot code path. This is also cached in our
288288
// RoomContext, however we still need a fallback for roomless MessagePanels.
289289
this._showHiddenEvents = SettingsStore.getValue("showHiddenEventsInTimeline");
290-
this.threadsEnabled = SettingsStore.getValue("feature_thread");
290+
this.threadsEnabled = SettingsStore.getValue("feature_threadstable");
291291

292292
this.showTypingNotificationsWatcherRef = SettingsStore.watchSetting(
293293
"showTypingNotifications",

src/components/structures/RoomSearchView.tsx

+2-1
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import ResizeNotifier from "../../utils/ResizeNotifier";
3333
import MatrixClientContext from "../../contexts/MatrixClientContext";
3434
import { RoomPermalinkCreator } from "../../utils/permalinks/Permalinks";
3535
import RoomContext from "../../contexts/RoomContext";
36+
import SettingsStore from "../../settings/SettingsStore";
3637

3738
const DEBUG = false;
3839
let debuglog = function (msg: string) {};
@@ -98,7 +99,7 @@ export const RoomSearchView = forwardRef<ScrollPanel, Props>(
9899
return b.length - a.length;
99100
});
100101

101-
if (client.supportsExperimentalThreads()) {
102+
if (SettingsStore.getValue("feature_threadstable")) {
102103
// Process all thread roots returned in this batch of search results
103104
// XXX: This won't work for results coming from Seshat which won't include the bundled relationship
104105
for (const result of results.results) {

src/components/structures/RoomView.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -1177,7 +1177,7 @@ export class RoomView extends React.Component<IRoomProps, IRoomState> {
11771177
CHAT_EFFECTS.forEach((effect) => {
11781178
if (containsEmoji(ev.getContent(), effect.emojis) || ev.getContent().msgtype === effect.msgType) {
11791179
// For initial threads launch, chat effects are disabled see #19731
1180-
if (!SettingsStore.getValue("feature_thread") || !ev.isRelation(THREAD_RELATION_TYPE.name)) {
1180+
if (!SettingsStore.getValue("feature_threadstable") || !ev.isRelation(THREAD_RELATION_TYPE.name)) {
11811181
dis.dispatch({ action: `effects.${effect.command}` });
11821182
}
11831183
}

src/components/structures/ThreadPanel.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -249,7 +249,7 @@ const ThreadPanel: React.FC<IProps> = ({ roomId, onClose, permalinkCreator }) =>
249249
const openFeedback = shouldShowFeedback()
250250
? () => {
251251
Modal.createDialog(BetaFeedbackDialog, {
252-
featureId: "feature_thread",
252+
featureId: "feature_threadstable",
253253
});
254254
}
255255
: null;

src/components/structures/TimelinePanel.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -1683,7 +1683,7 @@ class TimelinePanel extends React.Component<IProps, IState> {
16831683
is very tied to the main room timeline, we are forcing the timeline to
16841684
send read receipts for threaded events */
16851685
const isThreadTimeline = this.context.timelineRenderingType === TimelineRenderingType.Thread;
1686-
if (SettingsStore.getValue("feature_thread") && isThreadTimeline) {
1686+
if (SettingsStore.getValue("feature_threadstable") && isThreadTimeline) {
16871687
return 0;
16881688
}
16891689
const index = this.state.events.findIndex((ev) => ev.getId() === evId);

src/components/views/context_menus/MessageContextMenu.tsx

+2-6
Original file line numberDiff line numberDiff line change
@@ -71,11 +71,7 @@ const ReplyInThreadButton = ({ mxEvent, closeMenu }: IReplyInThreadButton) => {
7171
if (Boolean(relationType) && relationType !== RelationType.Thread) return null;
7272

7373
const onClick = (): void => {
74-
if (!localStorage.getItem("mx_seen_feature_thread")) {
75-
localStorage.setItem("mx_seen_feature_thread", "true");
76-
}
77-
78-
if (!SettingsStore.getValue("feature_thread")) {
74+
if (!SettingsStore.getValue("feature_threadstable")) {
7975
dis.dispatch({
8076
action: Action.ViewUserSettings,
8177
initialTabId: UserTab.Labs,
@@ -644,7 +640,7 @@ export default class MessageContextMenu extends React.Component<IProps, IState>
644640
rightClick &&
645641
contentActionable &&
646642
canSendMessages &&
647-
SettingsStore.getValue("feature_thread") &&
643+
SettingsStore.getValue("feature_threadstable") &&
648644
Thread.hasServerSideSupport &&
649645
timelineRenderingType !== TimelineRenderingType.Thread
650646
) {

src/components/views/messages/MessageActionBar.tsx

+4-26
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,6 @@ import { Key } from "../../../Keyboard";
5656
import { ALTERNATE_KEY_NAME } from "../../../accessibility/KeyboardShortcuts";
5757
import { UserTab } from "../dialogs/UserTab";
5858
import { Action } from "../../../dispatcher/actions";
59-
import SdkConfig from "../../../SdkConfig";
6059
import { ShowThreadPayload } from "../../../dispatcher/payloads/ShowThreadPayload";
6160
import useFavouriteMessages from "../../../hooks/useFavouriteMessages";
6261
import { GetRelationsForEvent } from "../rooms/EventTile";
@@ -204,8 +203,7 @@ const ReplyInThreadButton = ({ mxEvent }: IReplyInThreadButton) => {
204203

205204
const relationType = mxEvent?.getRelation()?.rel_type;
206205
const hasARelation = !!relationType && relationType !== RelationType.Thread;
207-
const firstTimeSeeingThreads = !localStorage.getItem("mx_seen_feature_thread");
208-
const threadsEnabled = SettingsStore.getValue("feature_thread");
206+
const threadsEnabled = SettingsStore.getValue("feature_threadstable");
209207

210208
if (!threadsEnabled && !Thread.hasServerSideSupport) {
211209
// hide the prompt if the user would only have degraded mode
@@ -217,11 +215,7 @@ const ReplyInThreadButton = ({ mxEvent }: IReplyInThreadButton) => {
217215
e.preventDefault();
218216
e.stopPropagation();
219217

220-
if (firstTimeSeeingThreads) {
221-
localStorage.setItem("mx_seen_feature_thread", "true");
222-
}
223-
224-
if (!SettingsStore.getValue("feature_thread")) {
218+
if (!SettingsStore.getValue("feature_threadstable")) {
225219
dis.dispatch({
226220
action: Action.ViewUserSettings,
227221
initialTabId: UserTab.Labs,
@@ -257,7 +251,7 @@ const ReplyInThreadButton = ({ mxEvent }: IReplyInThreadButton) => {
257251
</div>
258252
{!hasARelation && (
259253
<div className="mx_Tooltip_sub">
260-
{SettingsStore.getValue("feature_thread")
254+
{SettingsStore.getValue("feature_threadstable")
261255
? _t("Beta feature")
262256
: _t("Beta feature. Click to learn more.")}
263257
</div>
@@ -273,7 +267,6 @@ const ReplyInThreadButton = ({ mxEvent }: IReplyInThreadButton) => {
273267
onContextMenu={onClick}
274268
>
275269
<ThreadIcon />
276-
{firstTimeSeeingThreads && !threadsEnabled && <div className="mx_Indicator" />}
277270
</RovingAccessibleTooltipButton>
278271
);
279272
};
@@ -393,21 +386,6 @@ export default class MessageActionBar extends React.PureComponent<IMessageAction
393386
private readonly forbiddenThreadHeadMsgType = [MsgType.KeyVerificationRequest];
394387

395388
private get showReplyInThreadAction(): boolean {
396-
if (!SettingsStore.getValue("feature_thread") && !Thread.hasServerSideSupport) {
397-
// hide the prompt if the user would only have degraded mode
398-
return null;
399-
}
400-
401-
if (
402-
!SettingsStore.getBetaInfo("feature_thread") &&
403-
!SettingsStore.getValue("feature_thread") &&
404-
!SdkConfig.get("show_labs_settings")
405-
) {
406-
// Hide the beta prompt if there is no UI to enable it,
407-
// e.g if config.json disables it and doesn't enable show labs flags
408-
return false;
409-
}
410-
411389
const inNotThreadTimeline = this.context.timelineRenderingType !== TimelineRenderingType.Thread;
412390

413391
const isAllowedMessageType =
@@ -568,7 +546,7 @@ export default class MessageActionBar extends React.PureComponent<IMessageAction
568546
);
569547
}
570548
} else if (
571-
SettingsStore.getValue("feature_thread") &&
549+
SettingsStore.getValue("feature_threadstable") &&
572550
// Show thread icon even for deleted messages, but only within main timeline
573551
this.context.timelineRenderingType === TimelineRenderingType.Room &&
574552
this.props.mxEvent.getThread()

src/components/views/right_panel/RoomHeaderButtons.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -297,7 +297,7 @@ export default class RoomHeaderButtons extends HeaderButtons<IProps> {
297297
);
298298
rightPanelPhaseButtons.set(
299299
RightPanelPhases.ThreadPanel,
300-
SettingsStore.getValue("feature_thread") ? (
300+
SettingsStore.getValue("feature_threadstable") ? (
301301
<HeaderButton
302302
key={RightPanelPhases.ThreadPanel}
303303
name="threadsButton"

src/components/views/rooms/EventTile.tsx

+3-3
Original file line numberDiff line numberDiff line change
@@ -387,7 +387,7 @@ export class UnwrappedEventTile extends React.Component<EventTileProps, IState>
387387
}
388388
}
389389

390-
if (SettingsStore.getValue("feature_thread")) {
390+
if (SettingsStore.getValue("feature_threadstable")) {
391391
this.props.mxEvent.on(ThreadEvent.Update, this.updateThread);
392392

393393
if (this.thread && !this.supportsThreadNotifications) {
@@ -469,7 +469,7 @@ export class UnwrappedEventTile extends React.Component<EventTileProps, IState>
469469
if (this.props.showReactions) {
470470
this.props.mxEvent.removeListener(MatrixEventEvent.RelationsCreated, this.onReactionsCreated);
471471
}
472-
if (SettingsStore.getValue("feature_thread")) {
472+
if (SettingsStore.getValue("feature_threadstable")) {
473473
this.props.mxEvent.off(ThreadEvent.Update, this.updateThread);
474474
}
475475
this.threadState?.off(NotificationStateEvents.Update, this.onThreadStateUpdate);
@@ -496,7 +496,7 @@ export class UnwrappedEventTile extends React.Component<EventTileProps, IState>
496496
};
497497

498498
private get thread(): Thread | null {
499-
if (!SettingsStore.getValue("feature_thread")) {
499+
if (!SettingsStore.getValue("feature_threadstable")) {
500500
return null;
501501
}
502502

src/components/views/rooms/SearchResultTile.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ export default class SearchResultTile extends React.Component<IProps> {
6767
const layout = SettingsStore.getValue("layout");
6868
const isTwelveHour = SettingsStore.getValue("showTwelveHourTimestamps");
6969
const alwaysShowTimestamps = SettingsStore.getValue("alwaysShowTimestamps");
70-
const threadsEnabled = SettingsStore.getValue("feature_thread");
70+
const threadsEnabled = SettingsStore.getValue("feature_threadstable");
7171

7272
const timeline = result.context.getTimeline();
7373
for (let j = 0; j < timeline.length; j++) {

src/components/views/rooms/SendMessageComposer.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -436,7 +436,7 @@ export class SendMessageComposer extends React.Component<ISendMessageComposerPro
436436
// For initial threads launch, chat effects are disabled
437437
// see #19731
438438
const isNotThread = this.props.relation?.rel_type !== THREAD_RELATION_TYPE.name;
439-
if (!SettingsStore.getValue("feature_thread") || isNotThread) {
439+
if (!SettingsStore.getValue("feature_threadstable") || isNotThread) {
440440
dis.dispatch({ action: `effects.${effect.command}` });
441441
}
442442
}

src/components/views/rooms/wysiwyg_composer/utils/message.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ export function sendMessage(message: string, isHTML: boolean, { roomContext, mxC
108108
// For initial threads launch, chat effects are disabled
109109
// see #19731
110110
const isNotThread = relation?.rel_type !== THREAD_RELATION_TYPE.name;
111-
if (!SettingsStore.getValue("feature_thread") || isNotThread) {
111+
if (!SettingsStore.getValue("feature_threadstable") || isNotThread) {
112112
dis.dispatch({ action: `effects.${effect.command}` });
113113
}
114114
}

src/i18n/strings/en_EN.json

+3-7
Original file line numberDiff line numberDiff line change
@@ -915,15 +915,9 @@
915915
"In rooms that support moderation, the “Report” button will let you report abuse to room moderators.": "In rooms that support moderation, the “Report” button will let you report abuse to room moderators.",
916916
"Render LaTeX maths in messages": "Render LaTeX maths in messages",
917917
"Message Pinning": "Message Pinning",
918-
"Threaded messaging": "Threaded messaging",
918+
"Threaded messages": "Threaded messages",
919919
"Keep discussions organised with threads.": "Keep discussions organised with threads.",
920920
"Threads help keep conversations on-topic and easy to track. <a>Learn more</a>.": "Threads help keep conversations on-topic and easy to track. <a>Learn more</a>.",
921-
"How can I start a thread?": "How can I start a thread?",
922-
"Use “%(replyInThread)s” when hovering over a message.": "Use “%(replyInThread)s” when hovering over a message.",
923-
"Reply in thread": "Reply in thread",
924-
"How can I leave the beta?": "How can I leave the beta?",
925-
"To leave, return to this page and use the “%(leaveTheBeta)s” button.": "To leave, return to this page and use the “%(leaveTheBeta)s” button.",
926-
"Leave the beta": "Leave the beta",
927921
"Rich text editor": "Rich text editor",
928922
"Use rich text instead of Markdown in the message composer. Plain text mode coming soon.": "Use rich text instead of Markdown in the message composer. Plain text mode coming soon.",
929923
"Render simple counters in room header": "Render simple counters in room header",
@@ -2322,6 +2316,7 @@
23222316
"Error processing audio message": "Error processing audio message",
23232317
"View live location": "View live location",
23242318
"React": "React",
2319+
"Reply in thread": "Reply in thread",
23252320
"Can't create a thread from an event with an existing relation": "Can't create a thread from an event with an existing relation",
23262321
"Beta feature": "Beta feature",
23272322
"Beta feature. Click to learn more.": "Beta feature. Click to learn more.",
@@ -3198,6 +3193,7 @@
31983193
"Beta": "Beta",
31993194
"Leaving the beta will reload %(brand)s.": "Leaving the beta will reload %(brand)s.",
32003195
"Joining the beta will reload %(brand)s.": "Joining the beta will reload %(brand)s.",
3196+
"Leave the beta": "Leave the beta",
32013197
"Join the beta": "Join the beta",
32023198
"Updated %(humanizedUpdateTime)s": "Updated %(humanizedUpdateTime)s",
32033199
"Live until %(expiryTime)s": "Live until %(expiryTime)s",

src/settings/Settings.tsx

+4-26
Original file line numberDiff line numberDiff line change
@@ -255,15 +255,15 @@ export const SETTINGS: { [setting: string]: ISetting } = {
255255
supportedLevels: LEVELS_FEATURE,
256256
default: false,
257257
},
258-
"feature_thread": {
258+
"feature_threadstable": {
259259
isFeature: true,
260260
labsGroup: LabGroup.Messaging,
261261
controller: new ThreadBetaController(),
262-
displayName: _td("Threaded messaging"),
262+
displayName: _td("Threaded messages"),
263263
supportedLevels: LEVELS_FEATURE,
264-
default: false,
264+
default: true,
265265
betaInfo: {
266-
title: _td("Threads"),
266+
title: _td("Threaded messages"),
267267
caption: () => (
268268
<>
269269
<p>{_t("Keep discussions organised with threads.")}</p>
@@ -282,28 +282,6 @@ export const SETTINGS: { [setting: string]: ISetting } = {
282282
</p>
283283
</>
284284
),
285-
faq: () =>
286-
SdkConfig.get().bug_report_endpoint_url && (
287-
<>
288-
<h4>{_t("How can I start a thread?")}</h4>
289-
<p>
290-
{_t("Use “%(replyInThread)s” when hovering over a message.", {
291-
replyInThread: _t("Reply in thread"),
292-
})}
293-
</p>
294-
<h4>{_t("How can I leave the beta?")}</h4>
295-
<p>
296-
{_t("To leave, return to this page and use the “%(leaveTheBeta)s” button.", {
297-
leaveTheBeta: _t("Leave the beta"),
298-
})}
299-
</p>
300-
</>
301-
),
302-
feedbackLabel: "thread-feedback",
303-
feedbackSubheading: _td(
304-
"Thank you for trying the beta, " + "please go into as much detail as you can so we can improve it.",
305-
),
306-
image: require("../../res/img/betas/threads.png"),
307285
requiresRefresh: true,
308286
},
309287
},

src/stores/TypingStore.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ export default class TypingStore {
6565
if (SettingsStore.getValue("lowBandwidth")) return;
6666
// Disable typing notification for threads for the initial launch
6767
// before we figure out a better user experience for them
68-
if (SettingsStore.getValue("feature_thread") && threadId) return;
68+
if (SettingsStore.getValue("feature_threadstable") && threadId) return;
6969

7070
let currentTyping = this.typingStates[roomId];
7171
if ((!isTyping && !currentTyping) || (currentTyping && currentTyping.isTyping === isTyping)) {

0 commit comments

Comments
 (0)