diff --git a/package.json b/package.json index 691e948898..95d25fd7da 100644 --- a/package.json +++ b/package.json @@ -56,7 +56,7 @@ }, "dependencies": { "@babel/runtime": "^7.12.5", - "@matrix-org/analytics-events": "^0.2.0", + "@matrix-org/analytics-events": "^0.3.0", "@matrix-org/matrix-wysiwyg": "^0.3.2", "@matrix-org/react-sdk-module-api": "^0.0.3", "@sentry/browser": "^6.11.0", diff --git a/src/PosthogAnalytics.ts b/src/PosthogAnalytics.ts index b4e3a463bd..2bc3c98ae9 100644 --- a/src/PosthogAnalytics.ts +++ b/src/PosthogAnalytics.ts @@ -25,6 +25,11 @@ import SdkConfig from './SdkConfig'; import { MatrixClientPeg } from "./MatrixClientPeg"; import SettingsStore from "./settings/SettingsStore"; import { ScreenName } from "./PosthogTrackers"; +import { ActionPayload } from "./dispatcher/payloads"; +import { Action } from "./dispatcher/actions"; +import { SettingUpdatedPayload } from "./dispatcher/payloads/SettingUpdatedPayload"; +import dis from "./dispatcher/dispatcher"; +import { Layout } from "./settings/enums/Layout"; /* Posthog analytics tracking. * @@ -153,8 +158,41 @@ export class PosthogAnalytics { } else { this.enabled = false; } + + dis.register(this.onAction); + SettingsStore.monitorSetting("layout", null); + SettingsStore.monitorSetting("useCompactLayout", null); + this.onLayoutUpdated(); } + private onLayoutUpdated = () => { + let layout: UserProperties["WebLayout"]; + + switch (SettingsStore.getValue("layout")) { + case Layout.IRC: + layout = "IRC"; + break; + case Layout.Bubble: + layout = "Bubble"; + break; + case Layout.Group: + layout = SettingsStore.getValue("useCompactLayout") ? "Compact" : "Group"; + break; + } + + // This is known to clobber other devices but is a good enough solution + // to get an idea of how much use each layout gets. + this.setProperty("WebLayout", layout); + }; + + private onAction = (payload: ActionPayload) => { + if (payload.action !== Action.SettingUpdated) return; + const settingsPayload = payload as SettingUpdatedPayload; + if (["layout", "useCompactLayout"].includes(settingsPayload.settingName)) { + this.onLayoutUpdated(); + } + }; + // we persist the last `$screen_name` and send it for all events until it is replaced private lastScreen: ScreenName = "Loading"; @@ -192,7 +230,7 @@ export class PosthogAnalytics { private static async getPlatformProperties(): Promise { const platform = PlatformPeg.get(); - let appVersion; + let appVersion: string; try { appVersion = await platform.getAppVersion(); } catch (e) { diff --git a/test/PosthogAnalytics-test.ts b/test/PosthogAnalytics-test.ts index 2b0e650b71..fe27cc26aa 100644 --- a/test/PosthogAnalytics-test.ts +++ b/test/PosthogAnalytics-test.ts @@ -17,15 +17,14 @@ limitations under the License. import { mocked } from 'jest-mock'; import { PostHog } from 'posthog-js'; -import { - Anonymity, - - getRedactedCurrentLocation, - IPosthogEvent, - PosthogAnalytics, -} from '../src/PosthogAnalytics'; +import { Anonymity, getRedactedCurrentLocation, IPosthogEvent, PosthogAnalytics } from '../src/PosthogAnalytics'; import SdkConfig from '../src/SdkConfig'; import { getMockClientWithEventEmitter } from './test-utils'; +import SettingsStore from "../src/settings/SettingsStore"; +import { Layout } from "../src/settings/enums/Layout"; +import defaultDispatcher from "../src/dispatcher/dispatcher"; +import { Action } from "../src/dispatcher/actions"; +import { SettingLevel } from "../src/settings/SettingLevel"; const getFakePosthog = (): PostHog => ({ capture: jest.fn(), @@ -37,7 +36,7 @@ const getFakePosthog = (): PostHog => ({ export interface ITestEvent extends IPosthogEvent { eventName: "JestTestEvents"; - foo: string; + foo?: string; } describe("PosthogAnalytics", () => { @@ -180,4 +179,78 @@ describe("PosthogAnalytics", () => { expect(mocked(fakePosthog).identify.mock.calls[0][0]).toBe("existing_analytics_id"); }); }); + + describe("WebLayout", () => { + let analytics: PosthogAnalytics; + + beforeEach(() => { + SdkConfig.put({ + brand: "Testing", + posthog: { + project_api_key: "foo", + api_host: "bar", + }, + }); + + analytics = new PosthogAnalytics(fakePosthog); + analytics.setAnonymity(Anonymity.Pseudonymous); + }); + + it("should send layout IRC correctly", async () => { + await SettingsStore.setValue("layout", null, SettingLevel.DEVICE, Layout.IRC); + defaultDispatcher.dispatch({ + action: Action.SettingUpdated, + settingName: "layout", + }, true); + analytics.trackEvent({ + eventName: "JestTestEvents", + }); + expect(mocked(fakePosthog).capture.mock.calls[0][1]["$set"]).toStrictEqual({ + "WebLayout": "IRC", + }); + }); + + it("should send layout Bubble correctly", async () => { + await SettingsStore.setValue("layout", null, SettingLevel.DEVICE, Layout.Bubble); + defaultDispatcher.dispatch({ + action: Action.SettingUpdated, + settingName: "layout", + }, true); + analytics.trackEvent({ + eventName: "JestTestEvents", + }); + expect(mocked(fakePosthog).capture.mock.calls[0][1]["$set"]).toStrictEqual({ + "WebLayout": "Bubble", + }); + }); + + it("should send layout Group correctly", async () => { + await SettingsStore.setValue("layout", null, SettingLevel.DEVICE, Layout.Group); + defaultDispatcher.dispatch({ + action: Action.SettingUpdated, + settingName: "layout", + }, true); + analytics.trackEvent({ + eventName: "JestTestEvents", + }); + expect(mocked(fakePosthog).capture.mock.calls[0][1]["$set"]).toStrictEqual({ + "WebLayout": "Group", + }); + }); + + it("should send layout Compact correctly", async () => { + await SettingsStore.setValue("layout", null, SettingLevel.DEVICE, Layout.Group); + await SettingsStore.setValue("useCompactLayout", null, SettingLevel.DEVICE, true); + defaultDispatcher.dispatch({ + action: Action.SettingUpdated, + settingName: "useCompactLayout", + }, true); + analytics.trackEvent({ + eventName: "JestTestEvents", + }); + expect(mocked(fakePosthog).capture.mock.calls[0][1]["$set"]).toStrictEqual({ + "WebLayout": "Compact", + }); + }); + }); }); diff --git a/yarn.lock b/yarn.lock index a176b25e21..99b54b2c53 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1664,10 +1664,10 @@ resolved "https://registry.yarnpkg.com/@mapbox/whoots-js/-/whoots-js-3.1.0.tgz#497c67a1cef50d1a2459ba60f315e448d2ad87fe" integrity sha512-Es6WcD0nO5l+2BOQS4uLfNPYQaNDfbot3X1XUoloz+x0mPDS3eeORZJl06HXjwBG1fOGwCRnzK88LMdxKRrd6Q== -"@matrix-org/analytics-events@^0.2.0": - version "0.2.0" - resolved "https://registry.yarnpkg.com/@matrix-org/analytics-events/-/analytics-events-0.2.0.tgz#453925c939ecdd5ca6c797d293deb8cf0933f1b8" - integrity sha512-+0/Sydm4MNOcqd8iySJmojVPB74Axba4BXlwTsiKmL5fgYqdUkwmqkO39K7Pn8i+a+8pg11oNvBPkpWs3O5Qww== +"@matrix-org/analytics-events@^0.3.0": + version "0.3.0" + resolved "https://registry.yarnpkg.com/@matrix-org/analytics-events/-/analytics-events-0.3.0.tgz#a428f7e3f164ffadf38f35bc0f0f9a3e47369ce6" + integrity sha512-f1WIMA8tjNB3V5g1C34yIpIJK47z6IJ4SLiY4j+J9Gw4X8C3TKGTAx563rMcMvW3Uk/PFqnIBXtkavHBXoYJ9A== "@matrix-org/matrix-wysiwyg@^0.3.2": version "0.3.2"