mirror of
https://github.com/vector-im/element-call.git
synced 2026-05-10 10:24:44 +00:00
Track reconnect duration and align with existing analytics pattern
This commit is contained in:
@@ -27,7 +27,6 @@ import {
|
||||
CallDisconnectedEventTracker,
|
||||
CallConnectDurationTracker,
|
||||
CallReconnectingTracker,
|
||||
type CallReconnectingReason,
|
||||
} from "./PosthogEvents";
|
||||
import { Config } from "../config/Config";
|
||||
import { getUrlParams } from "../UrlParams";
|
||||
@@ -423,13 +422,5 @@ export class PosthogAnalytics {
|
||||
public eventQualitySurvey = new QualitySurveyEventTracker();
|
||||
public eventCallDisconnected = new CallDisconnectedEventTracker();
|
||||
public eventCallConnectDuration = new CallConnectDurationTracker();
|
||||
private readonly _eventCallReconnecting = new CallReconnectingTracker();
|
||||
|
||||
public trackCallReconnecting(
|
||||
callId: string,
|
||||
reason: CallReconnectingReason,
|
||||
): void {
|
||||
this._eventCallReconnecting.track(callId, reason);
|
||||
this.eventCallEnded.cacheReconnecting(reason);
|
||||
}
|
||||
public eventCallReconnecting = new CallReconnectingTracker();
|
||||
}
|
||||
|
||||
@@ -211,12 +211,13 @@ describe("CallReconnecting", () => {
|
||||
|
||||
it("tracks event with correct shape", () => {
|
||||
const tracker = new CallReconnectingTracker();
|
||||
tracker.track("!room:example.org", "syncing");
|
||||
tracker.track("!room:example.org", "syncing", 3.5);
|
||||
|
||||
expect(PosthogAnalytics.instance.trackEvent).toHaveBeenCalledWith({
|
||||
eventName: "CallReconnecting",
|
||||
callId: "!room:example.org",
|
||||
reason: "syncing",
|
||||
reconnectDuration: 3.5,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -227,10 +228,10 @@ describe("CallReconnecting", () => {
|
||||
"livekit",
|
||||
] as CallReconnectingReason[])("tracks reason %s correctly", (reason) => {
|
||||
const tracker = new CallReconnectingTracker();
|
||||
tracker.track("!room:example.org", reason);
|
||||
tracker.track("!room:example.org", reason, 1.0);
|
||||
|
||||
expect(PosthogAnalytics.instance.trackEvent).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ reason }),
|
||||
expect.objectContaining({ reason, reconnectDuration: 1.0 }),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -291,14 +291,20 @@ interface CallReconnecting extends IPosthogEvent {
|
||||
eventName: "CallReconnecting";
|
||||
callId: string;
|
||||
reason: CallReconnectingReason;
|
||||
reconnectDuration: number;
|
||||
}
|
||||
|
||||
export class CallReconnectingTracker {
|
||||
public track(callId: string, reason: CallReconnectingReason): void {
|
||||
public track(
|
||||
callId: string,
|
||||
reason: CallReconnectingReason,
|
||||
reconnectDuration: number,
|
||||
): void {
|
||||
PosthogAnalytics.instance.trackEvent<CallReconnecting>({
|
||||
eventName: "CallReconnecting",
|
||||
callId,
|
||||
reason,
|
||||
reconnectDuration,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -693,11 +693,11 @@ describe("LocalMembership", () => {
|
||||
PosthogAnalytics.resetInstance();
|
||||
});
|
||||
|
||||
it("fires CallReconnecting with the homeserver reason when homeserver disconnects", async () => {
|
||||
it("fires CallReconnecting with homeserver reason and duration when reconnected", async () => {
|
||||
const scope = new ObservableScope();
|
||||
const trackSpy = vi.spyOn(
|
||||
PosthogAnalytics.instance,
|
||||
"trackCallReconnecting",
|
||||
PosthogAnalytics.instance.eventCallReconnecting,
|
||||
"track",
|
||||
);
|
||||
|
||||
const hsConnected$ = new BehaviorSubject<boolean>(true);
|
||||
@@ -726,23 +726,26 @@ describe("LocalMembership", () => {
|
||||
|
||||
await flushPromises();
|
||||
|
||||
// Simulate homeserver disconnect with syncing as the reason
|
||||
// Disconnect with syncing reason, then reconnect
|
||||
disconnectReason$.next("syncing");
|
||||
hsConnected$.next(false);
|
||||
disconnectReason$.next(null);
|
||||
hsConnected$.next(true);
|
||||
|
||||
expect(trackSpy).toHaveBeenCalledWith(
|
||||
defaultCreateLocalMemberValues.callId,
|
||||
"syncing",
|
||||
expect.any(Number),
|
||||
);
|
||||
|
||||
scope.end();
|
||||
});
|
||||
|
||||
it("reports livekit reason when livekit disconnects but homeserver is fine", async () => {
|
||||
it("reports livekit reason when livekit disconnects then reconnects", async () => {
|
||||
const scope = new ObservableScope();
|
||||
const trackSpy = vi.spyOn(
|
||||
PosthogAnalytics.instance,
|
||||
"trackCallReconnecting",
|
||||
PosthogAnalytics.instance.eventCallReconnecting,
|
||||
"track",
|
||||
);
|
||||
|
||||
const connectionState$ = new BehaviorSubject<ConnectionState>(
|
||||
@@ -776,22 +779,24 @@ describe("LocalMembership", () => {
|
||||
|
||||
await flushPromises();
|
||||
|
||||
// Livekit drops while homeserver stays connected
|
||||
// Livekit drops then recovers
|
||||
connectionState$.next(ConnectionState.LivekitDisconnected);
|
||||
connectionState$.next(ConnectionState.LivekitConnected);
|
||||
|
||||
expect(trackSpy).toHaveBeenCalledWith(
|
||||
defaultCreateLocalMemberValues.callId,
|
||||
"livekit",
|
||||
expect.any(Number),
|
||||
);
|
||||
|
||||
scope.end();
|
||||
});
|
||||
|
||||
it("fires one event per reconnection, not once per condition change", async () => {
|
||||
it("fires one event per completed reconnection cycle", async () => {
|
||||
const scope = new ObservableScope();
|
||||
const trackSpy = vi.spyOn(
|
||||
PosthogAnalytics.instance,
|
||||
"trackCallReconnecting",
|
||||
PosthogAnalytics.instance.eventCallReconnecting,
|
||||
"track",
|
||||
);
|
||||
|
||||
const hsConnected$ = new BehaviorSubject<boolean>(true);
|
||||
@@ -820,26 +825,29 @@ describe("LocalMembership", () => {
|
||||
|
||||
await flushPromises();
|
||||
|
||||
// First reconnect
|
||||
// First full reconnect cycle
|
||||
disconnectReason$.next("membershipConnected");
|
||||
hsConnected$.next(false);
|
||||
// Reconnected
|
||||
disconnectReason$.next(null);
|
||||
hsConnected$.next(true);
|
||||
// Second reconnect
|
||||
// Second full reconnect cycle
|
||||
disconnectReason$.next("certainlyConnected");
|
||||
hsConnected$.next(false);
|
||||
disconnectReason$.next(null);
|
||||
hsConnected$.next(true);
|
||||
|
||||
expect(trackSpy).toHaveBeenCalledTimes(2);
|
||||
expect(trackSpy).toHaveBeenNthCalledWith(
|
||||
1,
|
||||
defaultCreateLocalMemberValues.callId,
|
||||
"membershipConnected",
|
||||
expect.any(Number),
|
||||
);
|
||||
expect(trackSpy).toHaveBeenNthCalledWith(
|
||||
2,
|
||||
defaultCreateLocalMemberValues.callId,
|
||||
"certainlyConnected",
|
||||
expect.any(Number),
|
||||
);
|
||||
|
||||
scope.end();
|
||||
|
||||
@@ -25,7 +25,6 @@ import {
|
||||
catchError,
|
||||
combineLatest,
|
||||
distinctUntilChanged,
|
||||
filter,
|
||||
from,
|
||||
fromEvent,
|
||||
map,
|
||||
@@ -35,7 +34,6 @@ import {
|
||||
startWith,
|
||||
switchMap,
|
||||
tap,
|
||||
withLatestFrom,
|
||||
} from "rxjs";
|
||||
import { type Logger } from "matrix-js-sdk/lib/logger";
|
||||
import { deepCompare } from "matrix-js-sdk/lib/utils";
|
||||
@@ -55,6 +53,7 @@ import {
|
||||
import { ElementWidgetActions, widget } from "../../../widget.ts";
|
||||
import { getUrlParams } from "../../../UrlParams.ts";
|
||||
import { PosthogAnalytics } from "../../../analytics/PosthogAnalytics.ts";
|
||||
import { type CallReconnectingReason } from "../../../analytics/PosthogEvents.ts";
|
||||
import { MatrixRTCMode } from "../../../settings/settings.ts";
|
||||
import { Config } from "../../../config/Config.ts";
|
||||
import {
|
||||
@@ -524,19 +523,29 @@ export const createLocalMembership$ = ({
|
||||
false,
|
||||
);
|
||||
|
||||
let reconnectStart: { time: number; reason: CallReconnectingReason } | null =
|
||||
null;
|
||||
reconnecting$
|
||||
.pipe(
|
||||
distinctUntilChanged(),
|
||||
filter(Boolean),
|
||||
withLatestFrom(
|
||||
homeserverConnected.disconnectReason$,
|
||||
localConnectionState$,
|
||||
),
|
||||
scope.bind(),
|
||||
)
|
||||
.subscribe(([_, homeserverReason]) => {
|
||||
const reason = homeserverReason !== null ? homeserverReason : "livekit";
|
||||
PosthogAnalytics.instance.trackCallReconnecting(callId, reason);
|
||||
.pipe(distinctUntilChanged(), scope.bind())
|
||||
.subscribe((reconnecting) => {
|
||||
if (reconnecting) {
|
||||
const homeserverReason = homeserverConnected.disconnectReason$.value;
|
||||
reconnectStart = {
|
||||
time: Date.now(),
|
||||
reason: homeserverReason !== null ? homeserverReason : "livekit",
|
||||
};
|
||||
} else if (reconnectStart !== null) {
|
||||
const duration = (Date.now() - reconnectStart.time) / 1000;
|
||||
PosthogAnalytics.instance.eventCallReconnecting.track(
|
||||
callId,
|
||||
reconnectStart.reason,
|
||||
duration,
|
||||
);
|
||||
PosthogAnalytics.instance.eventCallEnded.cacheReconnecting(
|
||||
reconnectStart.reason,
|
||||
);
|
||||
reconnectStart = null;
|
||||
}
|
||||
});
|
||||
|
||||
// inform the widget about the connect and disconnect intent from the user.
|
||||
|
||||
Reference in New Issue
Block a user