diff --git a/src/settings/Settings.tsx b/src/settings/Settings.tsx index 6be0a6b46fb..c5ae541610b 100644 --- a/src/settings/Settings.tsx +++ b/src/settings/Settings.tsx @@ -47,6 +47,7 @@ import ServerSupportUnstableFeatureController from "./controllers/ServerSupportU import { WatchManager } from "./WatchManager"; import { CustomTheme } from "../theme"; import SettingsStore from "./SettingsStore"; +import AnalyticsController from "./controllers/AnalyticsController"; export const defaultWatchManager = new WatchManager(); @@ -597,11 +598,13 @@ export const SETTINGS: { [setting: string]: ISetting } = { displayName: _td("settings|showbold"), default: false, invertedSettingName: "feature_hidebold", + controller: new AnalyticsController("WebSettingsNotificationsShowBoldToggle"), }, "Notifications.tac_only_notifications": { supportedLevels: LEVELS_DEVICE_ONLY_SETTINGS_WITH_CONFIG, displayName: _td("settings|tac_only_notifications"), default: true, + controller: new AnalyticsController("WebSettingsNotificationsTACOnlyNotificationsToggle"), }, "feature_ask_to_join": { isFeature: true, diff --git a/src/settings/controllers/AnalyticsController.ts b/src/settings/controllers/AnalyticsController.ts new file mode 100644 index 00000000000..5c127ed3b9a --- /dev/null +++ b/src/settings/controllers/AnalyticsController.ts @@ -0,0 +1,42 @@ +/* +Copyright 2024 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 SettingController from "./SettingController"; +import { SettingLevel } from "../SettingLevel"; +import PosthogTrackers, { InteractionName } from "../../PosthogTrackers"; + +/** + * Controller that sends events to analytics when a setting is changed. + * Since it will only trigger events when the setting is changed, + * (and the value isn't reported: only the fact that it's been toggled) + * it won't be useful for tracking what percentage of a userbase has a given setting + * enabled, but many of our settings can be set per device and Posthog only supports + * per-user properties, so this isn't straightforward. This is only for seeing how + * often people interact with the settings. + */ +export default class AnalyticsController extends SettingController { + /** + * + * @param interactionName The name of the event to send to analytics + */ + public constructor(private interactionName: InteractionName) { + super(); + } + + public onChange(_level: SettingLevel, _roomId: string | null, _newValue: any): void { + PosthogTrackers.trackInteraction(this.interactionName); + } +} diff --git a/test/settings/controllers/AnalyticsController-test.ts b/test/settings/controllers/AnalyticsController-test.ts new file mode 100644 index 00000000000..69b624994ed --- /dev/null +++ b/test/settings/controllers/AnalyticsController-test.ts @@ -0,0 +1,35 @@ +/* +Copyright 2024 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 PosthogTrackers from "../../../src/PosthogTrackers"; +import AnalyticsController from "../../../src/settings/controllers/AnalyticsController"; +import { SettingLevel } from "../../../src/settings/SettingLevel"; + +describe("AnalyticsController", () => { + afterEach(() => { + jest.restoreAllMocks(); + }); + + it("Tracks a Posthog interaction on change", () => { + const trackInteractionSpy = jest.spyOn(PosthogTrackers, "trackInteraction"); + + const controller = new AnalyticsController("WebSettingsNotificationsTACOnlyNotificationsToggle"); + + controller.onChange(SettingLevel.DEVICE, null, false); + + expect(trackInteractionSpy).toHaveBeenCalledWith("WebSettingsNotificationsTACOnlyNotificationsToggle"); + }); +}); diff --git a/yarn.lock b/yarn.lock index 51ab9eac8ca..7084d345dd9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1850,9 +1850,9 @@ integrity sha512-Es6WcD0nO5l+2BOQS4uLfNPYQaNDfbot3X1XUoloz+x0mPDS3eeORZJl06HXjwBG1fOGwCRnzK88LMdxKRrd6Q== "@matrix-org/analytics-events@^0.19.0": - version "0.19.0" - resolved "https://registry.yarnpkg.com/@matrix-org/analytics-events/-/analytics-events-0.19.0.tgz#e20e4df54530ed1c755ab728e9c22891e376f9e2" - integrity sha512-wN/hbpTpOxz2u3zHbsJgVMi88oKmK1yqeSZuif3yNW68XQnV2cc0XGUEpl0fgLOl6fj1bZOtxbDg5rCLbqf4CQ== + version "0.19.1" + resolved "https://registry.yarnpkg.com/@matrix-org/analytics-events/-/analytics-events-0.19.1.tgz#304d48b7b8e51117dfd9c1cf46a65e8bee9f46cb" + integrity sha512-bYWJIkOzl4Jt02fg1+XoQv5xZ3mP6qBzNmytt1UVfKLaGgLBSvlk9gXx2/3GhSuPBq99/CljEX/zABJ//mexAw== "@matrix-org/emojibase-bindings@^1.1.2": version "1.1.3" @@ -8653,16 +8653,7 @@ string-length@^4.0.1: char-regex "^1.0.2" strip-ansi "^6.0.0" -"string-width-cjs@npm:string-width@^4.2.0": - version "4.2.3" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" - integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== - dependencies: - emoji-regex "^8.0.0" - is-fullwidth-code-point "^3.0.0" - strip-ansi "^6.0.1" - -string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: +"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -8756,14 +8747,7 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" -"strip-ansi-cjs@npm:strip-ansi@^6.0.1": - version "6.0.1" - resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" - integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== - dependencies: - ansi-regex "^5.0.1" - -strip-ansi@^6.0.0, strip-ansi@^6.0.1: +"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -9561,7 +9545,7 @@ which@^2.0.1: dependencies: isexe "^2.0.0" -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -9579,15 +9563,6 @@ wrap-ansi@^6.2.0: string-width "^4.1.0" strip-ansi "^6.0.0" -wrap-ansi@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" - integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== - dependencies: - ansi-styles "^4.0.0" - string-width "^4.1.0" - strip-ansi "^6.0.0" - wrap-ansi@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214"