diff --git a/src/components/views/messages/MessageActionBar.tsx b/src/components/views/messages/MessageActionBar.tsx index 80f9b6daf8..7bca48097c 100644 --- a/src/components/views/messages/MessageActionBar.tsx +++ b/src/components/views/messages/MessageActionBar.tsx @@ -20,6 +20,7 @@ import React, { ReactElement, useContext, useEffect } from 'react'; import { EventStatus, MatrixEvent, MatrixEventEvent } from 'matrix-js-sdk/src/models/event'; import classNames from 'classnames'; import { MsgType, RelationType } from 'matrix-js-sdk/src/@types/event'; +import { Thread } from 'matrix-js-sdk/src/models/thread'; import type { Relations } from 'matrix-js-sdk/src/models/relations'; import { _t } from '../../../languageHandler'; @@ -164,11 +165,16 @@ const ReplyInThreadButton = ({ mxEvent }: IReplyInThreadButton) => { const relationType = mxEvent?.getRelation()?.rel_type; const hasARelation = !!relationType && relationType !== RelationType.Thread; - const firstTimeSeeingThreads = localStorage.getItem("mx_seen_feature_thread") === null && - !SettingsStore.getValue("feature_thread"); + const firstTimeSeeingThreads = !localStorage.getItem("mx_seen_feature_thread"); + const threadsEnabled = SettingsStore.getValue("feature_thread"); + + if (!threadsEnabled && !Thread.hasServerSideSupport) { + // hide the prompt if the user would only have degraded mode + return null; + } const onClick = (): void => { - if (localStorage.getItem("mx_seen_feature_thread") === null) { + if (firstTimeSeeingThreads) { localStorage.setItem("mx_seen_feature_thread", "true"); } @@ -219,7 +225,7 @@ const ReplyInThreadButton = ({ mxEvent }: IReplyInThreadButton) => { onClick={onClick} > - { firstTimeSeeingThreads && ( + { firstTimeSeeingThreads && !threadsEnabled && (
) } ; diff --git a/src/i18n/strings/en_EN.json b/src/i18n/strings/en_EN.json index a3ab40d344..bc238477f9 100644 --- a/src/i18n/strings/en_EN.json +++ b/src/i18n/strings/en_EN.json @@ -960,6 +960,10 @@ "Automatically send debug logs on any error": "Automatically send debug logs on any error", "Automatically send debug logs on decryption errors": "Automatically send debug logs on decryption errors", "Automatically send debug logs when key backup is not functioning": "Automatically send debug logs when key backup is not functioning", + "Partial Support for Threads": "Partial Support for Threads", + "Your homeserver does not currently support threads, so this feature may be unreliable. Some threaded messages may not be reliably available. Learn more.": "Your homeserver does not currently support threads, so this feature may be unreliable. Some threaded messages may not be reliably available. Learn more.", + "Do you want to enable threads anyway?": "Do you want to enable threads anyway?", + "Yes, enable": "Yes, enable", "Collecting app version information": "Collecting app version information", "Collecting logs": "Collecting logs", "Uploading logs": "Uploading logs", diff --git a/src/settings/Settings.tsx b/src/settings/Settings.tsx index 8cd842b74e..bd34297161 100644 --- a/src/settings/Settings.tsx +++ b/src/settings/Settings.tsx @@ -42,6 +42,7 @@ import IncompatibleController from "./controllers/IncompatibleController"; import { ImageSize } from "./enums/ImageSize"; import { MetaSpace } from "../stores/spaces"; import SdkConfig from "../SdkConfig"; +import ThreadBetaController from './controllers/ThreadBetaController'; // These are just a bunch of helper arrays to avoid copy/pasting a bunch of times const LEVELS_ROOM_SETTINGS = [ @@ -222,9 +223,7 @@ export const SETTINGS: {[setting: string]: ISetting} = { "feature_thread": { isFeature: true, labsGroup: LabGroup.Messaging, - // Requires a reload as we change an option flag on the `js-sdk` - // And the entire sync history needs to be parsed again - controller: new ReloadOnChangeController(), + controller: new ThreadBetaController(), displayName: _td("Threaded messaging"), supportedLevels: LEVELS_FEATURE, default: false, diff --git a/src/settings/SettingsStore.ts b/src/settings/SettingsStore.ts index ca9bfe3703..95ea0e6993 100644 --- a/src/settings/SettingsStore.ts +++ b/src/settings/SettingsStore.ts @@ -451,12 +451,13 @@ export default class SettingsStore { throw new Error("User cannot set " + settingName + " at " + level + " in " + roomId); } + if (setting.controller && !(await setting.controller.beforeChange(level, roomId, value))) { + return; // controller says no + } + await handler.setValue(settingName, roomId, value); - const controller = setting.controller; - if (controller) { - controller.onChange(level, roomId, value); - } + setting.controller?.onChange(level, roomId, value); } /** diff --git a/src/settings/controllers/SettingController.ts b/src/settings/controllers/SettingController.ts index 292b2d63e5..a274bcff2c 100644 --- a/src/settings/controllers/SettingController.ts +++ b/src/settings/controllers/SettingController.ts @@ -46,6 +46,17 @@ export default abstract class SettingController { return null; // no override } + /** + * Called before the setting value has been changed, can abort the change. + * @param {string} level The level at which the setting has been modified. + * @param {String} roomId The room ID, may be null. + * @param {*} newValue The new value for the setting, may be null. + * @return {boolean} Whether the settings change should be accepted. + */ + public async beforeChange(level: SettingLevel, roomId: string, newValue: any): Promise{ _t("Your homeserver does not currently support threads, so this feature may be unreliable. " + + "Some threaded messages may not be reliably available. Learn more.", {}, { + a: sub => ( + { sub } + ), + }) }
+{ _t("Do you want to enable threads anyway?") }
+ >, + button: _t("Yes, enable"), + }); + const [enable] = await finished; + return enable; + } + + public onChange(level: SettingLevel, roomId: string, newValue: any) { + // Requires a reload as we change an option flag on the `js-sdk` + // And the entire sync history needs to be parsed again + PlatformPeg.get().reload(); + } +}