diff --git a/src/components/structures/ThreadPanel.tsx b/src/components/structures/ThreadPanel.tsx index 7d75f918589..bc2c53d2035 100644 --- a/src/components/structures/ThreadPanel.tsx +++ b/src/components/structures/ThreadPanel.tsx @@ -286,7 +286,7 @@ const ThreadPanel: React.FC = ({ /> { timelineSet && ( 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 8cd842b74e5..bd34297161b 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 ca9bfe3703e..95ea0e6993e 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 292b2d63e59..a274bcff2c3 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 { + return true; + } + /** * Called when the setting value has been changed. * @param {string} level The level at which the setting has been modified. diff --git a/src/settings/controllers/ThreadBetaController.tsx b/src/settings/controllers/ThreadBetaController.tsx new file mode 100644 index 00000000000..487d2013b17 --- /dev/null +++ b/src/settings/controllers/ThreadBetaController.tsx @@ -0,0 +1,53 @@ +/* +Copyright 2022 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. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import * as React from "react"; +import { Thread } from "matrix-js-sdk/src/models/thread"; + +import SettingController from "./SettingController"; +import PlatformPeg from "../../PlatformPeg"; +import { SettingLevel } from "../SettingLevel"; +import Modal from "../../Modal"; +import QuestionDialog from "../../components/views/dialogs/QuestionDialog"; +import { _t } from "../../languageHandler"; + +export default class ThreadBetaController extends SettingController { + public async beforeChange(level: SettingLevel, roomId: string, newValue: any): Promise { + if (Thread.hasServerSideSupport || !newValue) return true; // Full support or user is disabling + + const { finished } = Modal.createTrackedDialog<[boolean]>("Thread beta", "degraded mode", QuestionDialog, { + title: _t("Partial Support for Threads"), + description: <> +

{ _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(); + } +}