Posthog add reconnect event (#3953)
* Add Posthog events for Call reconnect including the reason * Expose single trackCallReconnecting() entry point on PosthogAnalytics * Track reconnect duration and align with existing analytics pattern * Refactor combined$ to return [connected, reason] tuple * Update firefoxUserPrefs to allow getUserMedia and enumerateDevices on CI --------- Co-authored-by: Valere <bill.carson@valrsoft.com> Co-authored-by: Robin <robin@robin.town> Co-authored-by: Timo K <toger5@hotmail.de>
This commit is contained in:
co-authored by
Valere
Robin
Timo K
parent
b9f73e3e9a
commit
cec3a799af
@@ -61,7 +61,6 @@ import {
|
||||
type FailedToStartError,
|
||||
} from "../remoteMembers/Connection.ts";
|
||||
import { type HomeserverConnected } from "./HomeserverConnected.ts";
|
||||
import { and$ } from "../../../utils/observable.ts";
|
||||
import { type LocalTransport } from "./LocalTransport.ts";
|
||||
import { areLivekitTransportsEqual } from "../remoteMembers/MatrixLivekitMembers.ts";
|
||||
|
||||
@@ -129,6 +128,7 @@ interface Props {
|
||||
createPublisherFactory: (connection: Connection) => Publisher;
|
||||
joinMatrixRTC: (transport: LivekitTransportConfig) => void;
|
||||
homeserverConnected: HomeserverConnected;
|
||||
roomId: string;
|
||||
localTransport$: Behavior<LocalTransport>;
|
||||
matrixRTCSession: Pick<
|
||||
MatrixRTCSession,
|
||||
@@ -152,6 +152,7 @@ interface Props {
|
||||
* @param props.logger The logger to use.
|
||||
* @param props.muteStates The mute states for video and audio.
|
||||
* @param props.matrixRTCSession The matrix RTC session to join.
|
||||
* @param props.roomId The room ID used as the call identifier in analytics events.
|
||||
* @returns
|
||||
* - 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)
|
||||
@@ -169,6 +170,7 @@ export const createLocalMembership$ = ({
|
||||
logger: parentLogger,
|
||||
muteStates,
|
||||
matrixRTCSession,
|
||||
roomId: roomId,
|
||||
}: Props): {
|
||||
/**
|
||||
* This request to start audio and video tracks.
|
||||
@@ -494,20 +496,35 @@ export const createLocalMembership$ = ({
|
||||
);
|
||||
|
||||
/**
|
||||
* Whether we are "fully" connected to the call. Accounts for both the
|
||||
* connection to the MatrixRTC session and the LiveKit publish connection.
|
||||
* The disconnect reason for the combined Matrix + LiveKit connection, or null
|
||||
* when fully connected. Homeserver reasons take priority over livekit.
|
||||
* Both connectivity state and reason come from the same combineLatest emission,
|
||||
* avoiding any race between the two.
|
||||
*/
|
||||
const matrixAndLivekitConnected$ = scope.behavior(
|
||||
and$(
|
||||
const connectionDisconnectReason$ = scope.behavior(
|
||||
combineLatest([
|
||||
homeserverConnected.combined$,
|
||||
localConnectionState$.pipe(
|
||||
map((state) => state === ConnectionState.LivekitConnected),
|
||||
),
|
||||
).pipe(
|
||||
]).pipe(
|
||||
map(([[hsConnected, hsReason], livekitConnected]) => {
|
||||
if (!hsConnected) return hsReason!;
|
||||
if (!livekitConnected) return "livekit" as const;
|
||||
return null;
|
||||
}),
|
||||
tap((v) => logger.debug("livekit+matrix: Connected state changed", v)),
|
||||
),
|
||||
);
|
||||
|
||||
/**
|
||||
* Whether we are "fully" connected to the call. Accounts for both the
|
||||
* connection to the MatrixRTC session and the LiveKit publish connection.
|
||||
*/
|
||||
const matrixAndLivekitConnected$ = scope.behavior(
|
||||
connectionDisconnectReason$.pipe(map((reason) => reason === null)),
|
||||
);
|
||||
|
||||
/**
|
||||
* Whether we should tell the user that we're reconnecting to the call.
|
||||
*/
|
||||
@@ -519,6 +536,33 @@ export const createLocalMembership$ = ({
|
||||
false,
|
||||
);
|
||||
|
||||
let reconnectStart: {
|
||||
time: number;
|
||||
reason: NonNullable<(typeof connectionDisconnectReason$)["value"]>;
|
||||
} | null = null;
|
||||
connectionDisconnectReason$
|
||||
.pipe(distinctUntilChanged(), pairwise(), scope.bind())
|
||||
.subscribe(([prev, reason]) => {
|
||||
if (reason !== null) {
|
||||
// Only begin tracking when transitioning FROM connected (null → non-null).
|
||||
// This prevents the initial startup phase — where we may be non-null before
|
||||
// the first real connection — from being counted as a reconnect.
|
||||
if (prev === null) {
|
||||
reconnectStart ??= { time: Date.now(), reason };
|
||||
}
|
||||
} else if (reconnectStart !== null) {
|
||||
PosthogAnalytics.instance.eventCallReconnecting.track(
|
||||
roomId,
|
||||
reconnectStart.reason,
|
||||
(Date.now() - reconnectStart.time) / 1000,
|
||||
);
|
||||
PosthogAnalytics.instance.eventCallEnded.cacheReconnecting(
|
||||
reconnectStart.reason,
|
||||
);
|
||||
reconnectStart = null;
|
||||
}
|
||||
});
|
||||
|
||||
// inform the widget about the connect and disconnect intent from the user.
|
||||
scope
|
||||
.behavior(joinAndPublishRequested$.pipe(pairwise(), scope.bind()), [
|
||||
@@ -606,7 +650,7 @@ export const createLocalMembership$ = ({
|
||||
// TODO refactor this based no livekitState$
|
||||
combineLatest([participant$, homeserverConnected.combined$])
|
||||
.pipe(scope.bind())
|
||||
.subscribe(([participant, connected]) => {
|
||||
.subscribe(([participant, [connected]]) => {
|
||||
if (!participant) return;
|
||||
const publications = participant.trackPublications.values();
|
||||
if (connected) {
|
||||
|
||||
Reference in New Issue
Block a user