From 7a1a2c41d2b1d8fb4e925dd428459e1e397848a1 Mon Sep 17 00:00:00 2001 From: Michael Telatynski <7t3chguy@gmail.com> Date: Thu, 14 Apr 2022 11:50:55 +0100 Subject: [PATCH] Improve Threads beta around degraded mode (#8318) * Hide MAB Threads prompt if user would have degraded mode * Confirm user wants to enable Threads beta if in degraded mode * fix * Fix copy --- .../views/messages/MessageActionBar.tsx | 14 +++-- src/i18n/strings/en_EN.json | 4 ++ src/settings/Settings.tsx | 5 +- src/settings/SettingsStore.ts | 9 ++-- src/settings/controllers/SettingController.ts | 11 ++++ .../controllers/ThreadBetaController.tsx | 53 +++++++++++++++++++ 6 files changed, 85 insertions(+), 11 deletions(-) create mode 100644 src/settings/controllers/ThreadBetaController.tsx 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 && ( <div className="mx_Indicator" /> ) } </RovingAccessibleTooltipButton>; 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. <a>Learn more</a>.": "Your homeserver does not currently support threads, so this feature may be unreliable. Some threaded messages may not be reliably available. <a>Learn more</a>.", + "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<boolean> { + 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 0000000000..487d2013b1 --- /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<boolean> { + 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: <> + <p>{ _t("Your homeserver does not currently support threads, so this feature may be unreliable. " + + "Some threaded messages may not be reliably available. <a>Learn more</a>.", {}, { + a: sub => ( + <a href="https://element.io/help#threads" target="_blank" rel="noreferrer noopener">{ sub }</a> + ), + }) }</p> + <p>{ _t("Do you want to enable threads anyway?") }</p> + </>, + 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(); + } +}