mirror of
https://github.com/vector-im/element-call.git
synced 2026-08-29 21:15:19 +00:00
Add Posthog events for Call reconnect including the reason
This commit is contained in:
@@ -26,6 +26,7 @@ import {
|
|||||||
QualitySurveyEventTracker,
|
QualitySurveyEventTracker,
|
||||||
CallDisconnectedEventTracker,
|
CallDisconnectedEventTracker,
|
||||||
CallConnectDurationTracker,
|
CallConnectDurationTracker,
|
||||||
|
CallReconnectingTracker,
|
||||||
} from "./PosthogEvents";
|
} from "./PosthogEvents";
|
||||||
import { Config } from "../config/Config";
|
import { Config } from "../config/Config";
|
||||||
import { getUrlParams } from "../UrlParams";
|
import { getUrlParams } from "../UrlParams";
|
||||||
@@ -421,4 +422,5 @@ export class PosthogAnalytics {
|
|||||||
public eventQualitySurvey = new QualitySurveyEventTracker();
|
public eventQualitySurvey = new QualitySurveyEventTracker();
|
||||||
public eventCallDisconnected = new CallDisconnectedEventTracker();
|
public eventCallDisconnected = new CallDisconnectedEventTracker();
|
||||||
public eventCallConnectDuration = new CallConnectDurationTracker();
|
public eventCallConnectDuration = new CallConnectDurationTracker();
|
||||||
|
public eventCallReconnecting = new CallReconnectingTracker();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -89,6 +89,11 @@ describe("CallEnded", () => {
|
|||||||
roomEventEncryptionKeysSent: 10,
|
roomEventEncryptionKeysSent: 10,
|
||||||
roomEventEncryptionKeysReceived: 5,
|
roomEventEncryptionKeysReceived: 5,
|
||||||
roomEventEncryptionKeysReceivedAverageAge: 100,
|
roomEventEncryptionKeysReceivedAverageAge: 100,
|
||||||
|
callReconnectingCount: 0,
|
||||||
|
callReconnectingCountSyncing: 0,
|
||||||
|
callReconnectingCountMembershipConnected: 0,
|
||||||
|
callReconnectingCountCertainlyConnected: 0,
|
||||||
|
callReconnectingCountLivekit: 0,
|
||||||
},
|
},
|
||||||
{ send_instantly: true },
|
{ send_instantly: true },
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -24,12 +24,29 @@ interface CallEnded extends IPosthogEvent {
|
|||||||
roomEventEncryptionKeysSent: number;
|
roomEventEncryptionKeysSent: number;
|
||||||
roomEventEncryptionKeysReceived: number;
|
roomEventEncryptionKeysReceived: number;
|
||||||
roomEventEncryptionKeysReceivedAverageAge: number;
|
roomEventEncryptionKeysReceivedAverageAge: number;
|
||||||
|
callReconnectingCount: number;
|
||||||
|
callReconnectingCountSyncing: number;
|
||||||
|
callReconnectingCountMembershipConnected: number;
|
||||||
|
callReconnectingCountCertainlyConnected: number;
|
||||||
|
callReconnectingCountLivekit: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class CallEndedTracker {
|
export class CallEndedTracker {
|
||||||
private cache: { startTime?: Date; maxParticipantsCount: number } = {
|
private cache: {
|
||||||
|
startTime?: Date;
|
||||||
|
maxParticipantsCount: number;
|
||||||
|
reconnectingCount: number;
|
||||||
|
reconnectingCountByReason: Record<CallReconnectingReason, number>;
|
||||||
|
} = {
|
||||||
startTime: undefined,
|
startTime: undefined,
|
||||||
maxParticipantsCount: 0,
|
maxParticipantsCount: 0,
|
||||||
|
reconnectingCount: 0,
|
||||||
|
reconnectingCountByReason: {
|
||||||
|
syncing: 0,
|
||||||
|
membershipConnected: 0,
|
||||||
|
certainlyConnected: 0,
|
||||||
|
livekit: 0,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
public cacheStartCall(time: Date): void {
|
public cacheStartCall(time: Date): void {
|
||||||
@@ -43,6 +60,11 @@ export class CallEndedTracker {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public cacheReconnecting(reason: CallReconnectingReason): void {
|
||||||
|
this.cache.reconnectingCount++;
|
||||||
|
this.cache.reconnectingCountByReason[reason]++;
|
||||||
|
}
|
||||||
|
|
||||||
public track(
|
public track(
|
||||||
callId: string,
|
callId: string,
|
||||||
callParticipantsNow: number,
|
callParticipantsNow: number,
|
||||||
@@ -67,6 +89,15 @@ export class CallEndedTracker {
|
|||||||
.roomEventEncryptionKeysReceivedTotalAge /
|
.roomEventEncryptionKeysReceivedTotalAge /
|
||||||
rtcSession.statistics.counters.roomEventEncryptionKeysReceived
|
rtcSession.statistics.counters.roomEventEncryptionKeysReceived
|
||||||
: 0,
|
: 0,
|
||||||
|
callReconnectingCount: this.cache.reconnectingCount,
|
||||||
|
callReconnectingCountSyncing:
|
||||||
|
this.cache.reconnectingCountByReason.syncing,
|
||||||
|
callReconnectingCountMembershipConnected:
|
||||||
|
this.cache.reconnectingCountByReason.membershipConnected,
|
||||||
|
callReconnectingCountCertainlyConnected:
|
||||||
|
this.cache.reconnectingCountByReason.certainlyConnected,
|
||||||
|
callReconnectingCountLivekit:
|
||||||
|
this.cache.reconnectingCountByReason.livekit,
|
||||||
},
|
},
|
||||||
{ send_instantly: sendInstantly },
|
{ send_instantly: sendInstantly },
|
||||||
);
|
);
|
||||||
@@ -249,3 +280,25 @@ export class CallConnectDurationTracker {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type CallReconnectingReason =
|
||||||
|
| "syncing"
|
||||||
|
| "membershipConnected"
|
||||||
|
| "certainlyConnected"
|
||||||
|
| "livekit";
|
||||||
|
|
||||||
|
interface CallReconnecting extends IPosthogEvent {
|
||||||
|
eventName: "CallReconnecting";
|
||||||
|
callId: string;
|
||||||
|
reason: CallReconnectingReason;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class CallReconnectingTracker {
|
||||||
|
public track(callId: string, reason: CallReconnectingReason): void {
|
||||||
|
PosthogAnalytics.instance.trackEvent<CallReconnecting>({
|
||||||
|
eventName: "CallReconnecting",
|
||||||
|
callId,
|
||||||
|
reason,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -560,6 +560,7 @@ export function createCallViewModel$(
|
|||||||
connectionManager,
|
connectionManager,
|
||||||
matrixRTCSession,
|
matrixRTCSession,
|
||||||
localTransport$,
|
localTransport$,
|
||||||
|
callId: matrixRoom.roomId,
|
||||||
logger: logger.getChild(`[${Date.now()}]`),
|
logger: logger.getChild(`[${Date.now()}]`),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import {
|
|||||||
switchMap,
|
switchMap,
|
||||||
of,
|
of,
|
||||||
delay,
|
delay,
|
||||||
|
combineLatest,
|
||||||
} from "rxjs";
|
} from "rxjs";
|
||||||
import { logger as rootLogger } from "matrix-js-sdk/lib/logger";
|
import { logger as rootLogger } from "matrix-js-sdk/lib/logger";
|
||||||
|
|
||||||
@@ -36,9 +37,15 @@ import { type NodeStyleEventEmitter } from "../../../utils/test";
|
|||||||
*/
|
*/
|
||||||
const logger = rootLogger.getChild("[HomeserverConnected]");
|
const logger = rootLogger.getChild("[HomeserverConnected]");
|
||||||
|
|
||||||
|
export type HomeserverDisconnectReason =
|
||||||
|
| "syncing"
|
||||||
|
| "membershipConnected"
|
||||||
|
| "certainlyConnected";
|
||||||
|
|
||||||
export interface HomeserverConnected {
|
export interface HomeserverConnected {
|
||||||
combined$: Behavior<boolean>;
|
combined$: Behavior<boolean>;
|
||||||
rtsSession$: Behavior<Status>;
|
rtsSession$: Behavior<Status>;
|
||||||
|
disconnectReason$: Behavior<HomeserverDisconnectReason | null>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -116,5 +123,17 @@ export function createHomeserverConnected$(
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
return { combined$, rtsSession$ };
|
const disconnectReason$ = scope.behavior(
|
||||||
|
combineLatest([syncing$, membershipConnected$, certainlyConnected$]).pipe(
|
||||||
|
map(([syncing, membership, certainly]) => {
|
||||||
|
if (!syncing) return "syncing" as const;
|
||||||
|
if (!membership) return "membershipConnected" as const;
|
||||||
|
if (!certainly) return "certainlyConnected" as const;
|
||||||
|
return null;
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
null,
|
||||||
|
);
|
||||||
|
|
||||||
|
return { combined$, rtsSession$, disconnectReason$ };
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -217,7 +217,9 @@ describe("LocalMembership", () => {
|
|||||||
homeserverConnected: {
|
homeserverConnected: {
|
||||||
combined$: constant(true),
|
combined$: constant(true),
|
||||||
rtsSession$: constant(RTCMemberStatus.Connected),
|
rtsSession$: constant(RTCMemberStatus.Connected),
|
||||||
|
disconnectReason$: constant(null),
|
||||||
},
|
},
|
||||||
|
callId: "!test-room-id:example.org",
|
||||||
};
|
};
|
||||||
|
|
||||||
it("throws error on missing RTC config error", () => {
|
it("throws error on missing RTC config error", () => {
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import {
|
|||||||
catchError,
|
catchError,
|
||||||
combineLatest,
|
combineLatest,
|
||||||
distinctUntilChanged,
|
distinctUntilChanged,
|
||||||
|
filter,
|
||||||
from,
|
from,
|
||||||
fromEvent,
|
fromEvent,
|
||||||
map,
|
map,
|
||||||
@@ -34,6 +35,7 @@ import {
|
|||||||
startWith,
|
startWith,
|
||||||
switchMap,
|
switchMap,
|
||||||
tap,
|
tap,
|
||||||
|
withLatestFrom,
|
||||||
} from "rxjs";
|
} from "rxjs";
|
||||||
import { type Logger } from "matrix-js-sdk/lib/logger";
|
import { type Logger } from "matrix-js-sdk/lib/logger";
|
||||||
import { deepCompare } from "matrix-js-sdk/lib/utils";
|
import { deepCompare } from "matrix-js-sdk/lib/utils";
|
||||||
@@ -129,6 +131,7 @@ interface Props {
|
|||||||
createPublisherFactory: (connection: Connection) => Publisher;
|
createPublisherFactory: (connection: Connection) => Publisher;
|
||||||
joinMatrixRTC: (transport: LivekitTransportConfig) => void;
|
joinMatrixRTC: (transport: LivekitTransportConfig) => void;
|
||||||
homeserverConnected: HomeserverConnected;
|
homeserverConnected: HomeserverConnected;
|
||||||
|
callId: string;
|
||||||
localTransport$: Behavior<LocalTransport>;
|
localTransport$: Behavior<LocalTransport>;
|
||||||
matrixRTCSession: Pick<
|
matrixRTCSession: Pick<
|
||||||
MatrixRTCSession,
|
MatrixRTCSession,
|
||||||
@@ -152,6 +155,7 @@ interface Props {
|
|||||||
* @param props.logger The logger to use.
|
* @param props.logger The logger to use.
|
||||||
* @param props.muteStates The mute states for video and audio.
|
* @param props.muteStates The mute states for video and audio.
|
||||||
* @param props.matrixRTCSession The matrix RTC session to join.
|
* @param props.matrixRTCSession The matrix RTC session to join.
|
||||||
|
* @param props.callId The room ID used as the call identifier in analytics events.
|
||||||
* @returns
|
* @returns
|
||||||
* - publisher: The handle to create tracks and publish them to the room.
|
* - publisher: The handle to create tracks and publish them to the room.
|
||||||
* - connected$: the current connection state. Including matrix server and livekit server connection. (only considering the livekit server we are using for our own media publication)
|
* - connected$: the current connection state. Including matrix server and livekit server connection. (only considering the livekit server we are using for our own media publication)
|
||||||
@@ -169,6 +173,7 @@ export const createLocalMembership$ = ({
|
|||||||
logger: parentLogger,
|
logger: parentLogger,
|
||||||
muteStates,
|
muteStates,
|
||||||
matrixRTCSession,
|
matrixRTCSession,
|
||||||
|
callId,
|
||||||
}: Props): {
|
}: Props): {
|
||||||
/**
|
/**
|
||||||
* This request to start audio and video tracks.
|
* This request to start audio and video tracks.
|
||||||
@@ -519,6 +524,19 @@ export const createLocalMembership$ = ({
|
|||||||
false,
|
false,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
reconnecting$
|
||||||
|
.pipe(
|
||||||
|
distinctUntilChanged(),
|
||||||
|
filter(Boolean),
|
||||||
|
withLatestFrom(homeserverConnected.disconnectReason$, localConnectionState$),
|
||||||
|
scope.bind(),
|
||||||
|
)
|
||||||
|
.subscribe(([_, homeserverReason]) => {
|
||||||
|
const reason = homeserverReason !== null ? homeserverReason : "livekit";
|
||||||
|
PosthogAnalytics.instance.eventCallReconnecting.track(callId, reason);
|
||||||
|
PosthogAnalytics.instance.eventCallEnded.cacheReconnecting(reason);
|
||||||
|
});
|
||||||
|
|
||||||
// inform the widget about the connect and disconnect intent from the user.
|
// inform the widget about the connect and disconnect intent from the user.
|
||||||
scope
|
scope
|
||||||
.behavior(joinAndPublishRequested$.pipe(pairwise(), scope.bind()), [
|
.behavior(joinAndPublishRequested$.pipe(pairwise(), scope.bind()), [
|
||||||
|
|||||||
Reference in New Issue
Block a user