From bc60e59eda904fd35a0b7c3f5b6a041114e45034 Mon Sep 17 00:00:00 2001 From: Eric Eastwood Date: Fri, 17 Mar 2023 15:08:17 -0500 Subject: [PATCH] Add stable unstable version for jump to date before `v1.6` is fully supported on a homeserver (#10398) Add stable unstable version (`org.matrix.msc3030.stable`) for jump to date [before `v1.6` is fully supported on a homeserver](https://github.com/matrix-org/synapse/issues/15089). Related to https://github.com/vector-im/element-web/issues/24362 but does not solve immediately because Synapse does not supply `org.matrix.msc3030.stable` yet Also refactored `ServerSupportUnstableFeatureController` to support multiple feature groups where any one of them will enable the setting. All features in a feature group are required. This way having either `org.matrix.msc3030` or `org.matrix.msc3030.stable` will enable the jump to date feature flag with a config of `[["org.matrix.msc3030"], ["org.matrix.msc3030.stable"]]` --- src/settings/Settings.tsx | 6 +-- .../ServerSupportUnstableFeatureController.ts | 45 +++++++++++++----- ...erSupportUnstableFeatureController-test.ts | 47 +++++++++++++++---- 3 files changed, 74 insertions(+), 24 deletions(-) diff --git a/src/settings/Settings.tsx b/src/settings/Settings.tsx index f9de96dbf2..351df6d62a 100644 --- a/src/settings/Settings.tsx +++ b/src/settings/Settings.tsx @@ -230,7 +230,7 @@ export const SETTINGS: { [setting: string]: ISetting } = { controller: new ServerSupportUnstableFeatureController( "feature_exploring_public_spaces", defaultWatchManager, - ["org.matrix.msc3827.stable"], + [["org.matrix.msc3827.stable"]], undefined, _td("Requires your server to support the stable version of MSC3827"), ), @@ -372,7 +372,7 @@ export const SETTINGS: { [setting: string]: ISetting } = { controller: new ServerSupportUnstableFeatureController( "feature_jump_to_date", defaultWatchManager, - ["org.matrix.msc3030"], + [["org.matrix.msc3030"], ["org.matrix.msc3030.stable"]], "v1.6", _td("Requires your server to support MSC3030"), ), @@ -388,7 +388,7 @@ export const SETTINGS: { [setting: string]: ISetting } = { controller: new ServerSupportUnstableFeatureController( "sendReadReceipts", defaultWatchManager, - ["org.matrix.msc2285.stable"], + [["org.matrix.msc2285.stable"]], "v1.4", _td("Your server doesn't support disabling sending read receipts."), true, diff --git a/src/settings/controllers/ServerSupportUnstableFeatureController.ts b/src/settings/controllers/ServerSupportUnstableFeatureController.ts index 7dfe4bb69c..b6022afbb9 100644 --- a/src/settings/controllers/ServerSupportUnstableFeatureController.ts +++ b/src/settings/controllers/ServerSupportUnstableFeatureController.ts @@ -26,12 +26,21 @@ import SettingsStore from "../SettingsStore"; * When a setting gets disabled or enabled from this controller it notifies the given WatchManager */ export default class ServerSupportUnstableFeatureController extends MatrixClientBackedController { + // Starts off as `undefined` so when we first compare the `newDisabledValue`, it sees + // it as a change and updates the watchers. private enabled: boolean | undefined; + /** + * Construct a new ServerSupportUnstableFeatureController. + * + * @param unstableFeatureGroups - If any one of the feature groups is satisfied, + * then the setting is considered enabled. A feature group is satisfied if all of + * the features in the group are supported (all features in a group are required). + */ public constructor( private readonly settingName: string, private readonly watchers: WatchManager, - private readonly unstableFeatures: string[], + private readonly unstableFeatureGroups: string[][], private readonly stableVersion?: string, private readonly disabledMessage?: string, private readonly forcedValue: any = false, @@ -43,9 +52,9 @@ export default class ServerSupportUnstableFeatureController extends MatrixClient return !this.enabled; } - public set disabled(v: boolean) { - if (!v === this.enabled) return; - this.enabled = !v; + public set disabled(newDisabledValue: boolean) { + if (!newDisabledValue === this.enabled) return; + this.enabled = !newDisabledValue; const level = SettingsStore.firstSupportedLevel(this.settingName); if (!level) return; const settingValue = SettingsStore.getValue(this.settingName, null); @@ -53,19 +62,33 @@ export default class ServerSupportUnstableFeatureController extends MatrixClient } protected async initMatrixClient(oldClient: MatrixClient, newClient: MatrixClient): Promise { - this.disabled = true; - let supported = true; - + // Check for stable version support first if (this.stableVersion && (await this.client.isVersionSupported(this.stableVersion))) { this.disabled = false; return; } - for (const feature of this.unstableFeatures) { - supported = await this.client.doesServerSupportUnstableFeature(feature); - if (!supported) break; + + // Otherwise, only one of the unstable feature groups needs to be satisfied in + // order for this setting overall to be enabled + let isEnabled = false; + for (const featureGroup of this.unstableFeatureGroups) { + const featureSupportList = await Promise.all( + featureGroup.map(async (feature) => { + const isFeatureSupported = await this.client.doesServerSupportUnstableFeature(feature); + return isFeatureSupported; + }), + ); + + // Every feature in a feature group is required in order + // for this setting overall to be enabled. + const isFeatureGroupSatisfied = featureSupportList.every((isFeatureSupported) => isFeatureSupported); + if (isFeatureGroupSatisfied) { + isEnabled = true; + break; + } } - this.disabled = !supported; + this.disabled = !isEnabled; } public getValueOverride( diff --git a/test/settings/controllers/ServerSupportUnstableFeatureController-test.ts b/test/settings/controllers/ServerSupportUnstableFeatureController-test.ts index bff87d93b2..8fe676d2b0 100644 --- a/test/settings/controllers/ServerSupportUnstableFeatureController-test.ts +++ b/test/settings/controllers/ServerSupportUnstableFeatureController-test.ts @@ -55,7 +55,7 @@ describe("ServerSupportUnstableFeatureController", () => { const controller = new ServerSupportUnstableFeatureController( setting, watchers, - ["feature"], + [["feature"]], undefined, undefined, "other_value", @@ -74,7 +74,7 @@ describe("ServerSupportUnstableFeatureController", () => { const controller = new ServerSupportUnstableFeatureController( setting, watchers, - ["feature"], + [["feature"]], "other_value", ); await prepareSetting(cli, controller); @@ -84,38 +84,65 @@ describe("ServerSupportUnstableFeatureController", () => { }); describe("settingDisabled()", () => { - it("returns true if there is no matrix client", () => { - const controller = new ServerSupportUnstableFeatureController(setting, watchers, ["org.matrix.msc3030"]); + it("considered disabled if there is no matrix client", () => { + const controller = new ServerSupportUnstableFeatureController(setting, watchers, [["org.matrix.msc3030"]]); expect(controller.settingDisabled).toEqual(true); }); - it("returns true if not all required features are supported", async () => { + it("considered disabled if not all required features in the only feature group are supported", async () => { const cli = stubClient(); cli.doesServerSupportUnstableFeature = jest.fn(async (featureName) => { return featureName === "org.matrix.msc3827.stable"; }); const controller = new ServerSupportUnstableFeatureController(setting, watchers, [ - "org.matrix.msc3827.stable", - "org.matrix.msc3030", + ["org.matrix.msc3827.stable", "org.matrix.msc3030"], ]); await prepareSetting(cli, controller); expect(controller.settingDisabled).toEqual(true); }); - it("returns false if all required features are supported", async () => { + it("considered enabled if all required features in the only feature group are supported", async () => { const cli = stubClient(); cli.doesServerSupportUnstableFeature = jest.fn(async (featureName) => { return featureName === "org.matrix.msc3827.stable" || featureName === "org.matrix.msc3030"; }); const controller = new ServerSupportUnstableFeatureController(setting, watchers, [ - "org.matrix.msc3827.stable", - "org.matrix.msc3030", + ["org.matrix.msc3827.stable", "org.matrix.msc3030"], ]); await prepareSetting(cli, controller); expect(controller.settingDisabled).toEqual(false); }); + + it("considered enabled if all required features in one of the feature groups are supported", async () => { + const cli = stubClient(); + cli.doesServerSupportUnstableFeature = jest.fn(async (featureName) => { + return featureName === "org.matrix.msc3827.stable" || featureName === "org.matrix.msc3030"; + }); + const controller = new ServerSupportUnstableFeatureController(setting, watchers, [ + ["foo-unsupported", "bar-unsupported"], + ["org.matrix.msc3827.stable", "org.matrix.msc3030"], + ]); + await prepareSetting(cli, controller); + + expect(controller.settingDisabled).toEqual(false); + }); + + it("considered disabled if not all required features in one of the feature groups are supported", async () => { + const cli = stubClient(); + cli.doesServerSupportUnstableFeature = jest.fn(async (featureName) => { + return featureName === "org.matrix.msc3827.stable"; + }); + + const controller = new ServerSupportUnstableFeatureController(setting, watchers, [ + ["foo-unsupported", "bar-unsupported"], + ["org.matrix.msc3827.stable", "org.matrix.msc3030"], + ]); + await prepareSetting(cli, controller); + + expect(controller.settingDisabled).toEqual(true); + }); }); });