Skip to content
This repository was archived by the owner on Sep 11, 2024. It is now read-only.

Commit ce75d33

Browse files
toger5robintown
andauthored
Pass analyticsID to the elementCall iFrame (#9637)
Co-authored-by: Robin <robin@robin.town> Co-authored-by: Timo K <timok@element.io>
1 parent b81582d commit ce75d33

File tree

4 files changed

+63
-3
lines changed

4 files changed

+63
-3
lines changed

src/PosthogAnalytics.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ export class PosthogAnalytics {
134134
private readonly enabled: boolean = false;
135135
private static _instance = null;
136136
private platformSuperProperties = {};
137-
private static ANALYTICS_EVENT_TYPE = "im.vector.analytics";
137+
public static readonly ANALYTICS_EVENT_TYPE = "im.vector.analytics";
138138
private propertiesForNextEvent: Partial<Record<"$set" | "$set_once", UserProperties>> = {};
139139
private userPropertyCache: UserProperties = {};
140140
private authenticationType: Signup["authenticationType"] = "Other";

src/models/Call.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ import { getCurrentLanguage } from "../languageHandler";
5353
import DesktopCapturerSourcePicker from "../components/views/elements/DesktopCapturerSourcePicker";
5454
import Modal from "../Modal";
5555
import { FontWatcher } from "../settings/watchers/FontWatcher";
56+
import { PosthogAnalytics } from "../PosthogAnalytics";
5657

5758
const TIMEOUT_MS = 16000;
5859

@@ -626,6 +627,15 @@ export class ElementCall extends Call {
626627
}
627628

628629
private constructor(public readonly groupCall: GroupCall, client: MatrixClient) {
630+
const accountAnalyticsData = client.getAccountData(PosthogAnalytics.ANALYTICS_EVENT_TYPE);
631+
// The analyticsID is passed directly to element call (EC) since this codepath is only for EC and no other widget.
632+
// We really don't want the same analyticID's for the EC and EW posthog instances (Data on posthog should be limited/anonymized as much as possible).
633+
// This is prohibited in EC where a hashed version of the analyticsID is used for the actual posthog identification.
634+
// We can pass the raw EW analyticsID here since we need to trust EC with not sending sensitive data to posthog (EC has access to more sensible data than the analyticsID e.g. the username)
635+
const analyticsID: string = accountAnalyticsData?.getContent().pseudonymousAnalyticsOptIn
636+
? accountAnalyticsData?.getContent().id
637+
: "";
638+
629639
// Splice together the Element Call URL for this call
630640
const params = new URLSearchParams({
631641
embed: "",
@@ -637,6 +647,7 @@ export class ElementCall extends Call {
637647
baseUrl: client.baseUrl,
638648
lang: getCurrentLanguage().replace("_", "-"),
639649
fontScale: `${SettingsStore.getValue("baseFontSize") / FontWatcher.DEFAULT_SIZE}`,
650+
analyticsID,
640651
});
641652

642653
// Set custom fonts

test/models/Call-test.ts

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import { Room, RoomEvent } from "matrix-js-sdk/src/models/room";
2323
import { RoomStateEvent } from "matrix-js-sdk/src/models/room-state";
2424
import { Widget } from "matrix-widget-api";
2525
import { GroupCallIntent } from "matrix-js-sdk/src/webrtc/groupCall";
26+
import { MatrixEvent } from "matrix-js-sdk/src/matrix";
2627

2728
import type { Mocked } from "jest-mock";
2829
import type { MatrixClient, IMyDevice } from "matrix-js-sdk/src/client";
@@ -40,6 +41,7 @@ import { ElementWidgetActions } from "../../src/stores/widgets/ElementWidgetActi
4041
import SettingsStore from "../../src/settings/SettingsStore";
4142
import Modal, { IHandle } from "../../src/Modal";
4243
import PlatformPeg from "../../src/PlatformPeg";
44+
import { PosthogAnalytics } from "../../src/PosthogAnalytics";
4345

4446
jest.spyOn(MediaDeviceHandler, "getDevices").mockResolvedValue({
4547
[MediaDeviceKindEnum.AudioInput]: [
@@ -622,6 +624,53 @@ describe("ElementCall", () => {
622624

623625
SettingsStore.getValue = originalGetValue;
624626
});
627+
628+
it("passes analyticsID through widget URL", async () => {
629+
client.getAccountData.mockImplementation((eventType: string) => {
630+
if (eventType === PosthogAnalytics.ANALYTICS_EVENT_TYPE) {
631+
return new MatrixEvent({ content: { id: "123456789987654321", pseudonymousAnalyticsOptIn: true } });
632+
}
633+
return undefined;
634+
});
635+
await ElementCall.create(room);
636+
const call = Call.get(room);
637+
if (!(call instanceof ElementCall)) throw new Error("Failed to create call");
638+
639+
const urlParams = new URLSearchParams(new URL(call.widget.url).hash.slice(1));
640+
expect(urlParams.get("analyticsID")).toBe("123456789987654321");
641+
});
642+
643+
it("does not pass analyticsID if `pseudonymousAnalyticsOptIn` set to false", async () => {
644+
client.getAccountData.mockImplementation((eventType: string) => {
645+
if (eventType === PosthogAnalytics.ANALYTICS_EVENT_TYPE) {
646+
return new MatrixEvent({
647+
content: { id: "123456789987654321", pseudonymousAnalyticsOptIn: false },
648+
});
649+
}
650+
return undefined;
651+
});
652+
await ElementCall.create(room);
653+
const call = Call.get(room);
654+
if (!(call instanceof ElementCall)) throw new Error("Failed to create call");
655+
656+
const urlParams = new URLSearchParams(new URL(call.widget.url).hash.slice(1));
657+
expect(urlParams.get("analyticsID")).toBe("");
658+
});
659+
660+
it("passes empty analyticsID if the id is not in the account data", async () => {
661+
client.getAccountData.mockImplementation((eventType: string) => {
662+
if (eventType === PosthogAnalytics.ANALYTICS_EVENT_TYPE) {
663+
return new MatrixEvent({ content: {} });
664+
}
665+
return undefined;
666+
});
667+
await ElementCall.create(room);
668+
const call = Call.get(room);
669+
if (!(call instanceof ElementCall)) throw new Error("Failed to create call");
670+
671+
const urlParams = new URLSearchParams(new URL(call.widget.url).hash.slice(1));
672+
expect(urlParams.get("analyticsID")).toBe("");
673+
});
625674
});
626675

627676
describe("instance in a non-video room", () => {

test/test-utils/test-utils.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -136,15 +136,15 @@ export function createTestClient(): MatrixClient {
136136
getTurnServers: jest.fn().mockReturnValue([]),
137137
getTurnServersExpiry: jest.fn().mockReturnValue(2 ^ 32),
138138
getThirdpartyUser: jest.fn().mockResolvedValue([]),
139-
getAccountData: (type) => {
139+
getAccountData: jest.fn().mockImplementation((type) => {
140140
return mkEvent({
141141
user: undefined,
142142
room: undefined,
143143
type,
144144
event: true,
145145
content: {},
146146
});
147-
},
147+
}),
148148
mxcUrlToHttp: (mxc) => `http://this.is.a.url/${mxc.substring(6)}`,
149149
setAccountData: jest.fn(),
150150
setRoomAccountData: jest.fn(),

0 commit comments

Comments
 (0)