diff --git a/package.json b/package.json index 82ba8bea2..8f3e49d54 100644 --- a/package.json +++ b/package.json @@ -84,7 +84,7 @@ "livekit-client": "^2.5.7", "lodash": "^4.17.21", "loglevel": "^1.9.1", - "matrix-js-sdk": "matrix-org/matrix-js-sdk#0a29063bc9e61ee70ca43820d4bb91f6a27f1237", + "matrix-js-sdk": "matrix-org/matrix-js-sdk#b0174eccdb0e33f5df5d7b590938daf8ff5c7f7a", "matrix-widget-api": "^1.8.2", "normalize.css": "^8.0.1", "observable-hooks": "^4.2.3", diff --git a/src/analytics/PosthogEvents.ts b/src/analytics/PosthogEvents.ts index ca086dc21..492d5781d 100644 --- a/src/analytics/PosthogEvents.ts +++ b/src/analytics/PosthogEvents.ts @@ -14,13 +14,30 @@ import { PosthogAnalytics, RegistrationType, } from "./PosthogAnalytics"; +import { E2eeType } from "../e2ee/e2eeType"; +type EncryptionScheme = "none" | "shared" | "per_sender"; + +function mapE2eeType(type: E2eeType): EncryptionScheme { + switch (type) { + case E2eeType.NONE: + return "none"; + case E2eeType.SHARED_KEY: + return "shared"; + case E2eeType.PER_PARTICIPANT: + return "per_sender"; + } +} interface CallEnded extends IPosthogEvent { eventName: "CallEnded"; callId: string; callParticipantsOnLeave: number; callParticipantsMax: number; callDuration: number; + encryption: EncryptionScheme; + toDeviceEncryptionKeysSent: number; + toDeviceEncryptionKeysReceived: number; + toDeviceEncryptionKeysReceivedAverageAge: number; roomEventEncryptionKeysSent: number; roomEventEncryptionKeysReceived: number; roomEventEncryptionKeysReceivedAverageAge: number; @@ -46,6 +63,8 @@ export class CallEndedTracker { public track( callId: string, callParticipantsNow: number, + e2eeType: E2eeType, + rtcSession: MatrixRTCSession, sendInstantly: boolean, rtcSession: MatrixRTCSession, ): void { @@ -56,6 +75,17 @@ export class CallEndedTracker { callParticipantsMax: this.cache.maxParticipantsCount, callParticipantsOnLeave: callParticipantsNow, callDuration: (Date.now() - this.cache.startTime.getTime()) / 1000, + encryption: mapE2eeType(e2eeType), + toDeviceEncryptionKeysSent: + rtcSession.statistics.counters.toDeviceEncryptionKeysSent, + toDeviceEncryptionKeysReceived: + rtcSession.statistics.counters.toDeviceEncryptionKeysReceived, + toDeviceEncryptionKeysReceivedAverageAge: + rtcSession.statistics.counters.toDeviceEncryptionKeysReceived > 0 + ? rtcSession.statistics.totals + .toDeviceEncryptionKeysReceivedTotalAge / + rtcSession.statistics.counters.toDeviceEncryptionKeysReceived + : 0, roomEventEncryptionKeysSent: rtcSession.statistics.counters.roomEventEncryptionKeysSent, roomEventEncryptionKeysReceived: @@ -75,13 +105,15 @@ export class CallEndedTracker { interface CallStarted extends IPosthogEvent { eventName: "CallStarted"; callId: string; + encryption: EncryptionScheme; } export class CallStartedTracker { - public track(callId: string): void { + public track(callId: string, e2eeType: E2eeType): void { PosthogAnalytics.instance.trackEvent({ eventName: "CallStarted", callId: callId, + encryption: mapE2eeType(e2eeType), }); } } diff --git a/src/home/RegisteredView.tsx b/src/home/RegisteredView.tsx index db242414d..f7c16d4e6 100644 --- a/src/home/RegisteredView.tsx +++ b/src/home/RegisteredView.tsx @@ -9,7 +9,7 @@ import { useState, useCallback, FormEvent, FormEventHandler, FC } from "react"; import { useHistory } from "react-router-dom"; import { MatrixClient } from "matrix-js-sdk/src/client"; import { useTranslation } from "react-i18next"; -import { Heading, Text } from "@vector-im/compound-web"; +import { Dropdown, Heading, Text } from "@vector-im/compound-web"; import { logger } from "matrix-js-sdk/src/logger"; import { Button } from "@vector-im/compound-web"; @@ -35,6 +35,17 @@ import { useOptInAnalytics } from "../settings/settings"; interface Props { client: MatrixClient; } +const encryptionOptions = { + shared: { + label: "Shared key", + e2eeType: E2eeType.SHARED_KEY, + }, + sender: { + label: "Per-participant key", + e2eeType: E2eeType.PER_PARTICIPANT, + }, + none: { label: "None", e2eeType: E2eeType.NONE }, +}; export const RegisteredView: FC = ({ client }) => { const [loading, setLoading] = useState(false); @@ -49,6 +60,9 @@ export const RegisteredView: FC = ({ client }) => { [setJoinExistingCallModalOpen], ); + const [encryption, setEncryption] = + useState("shared"); + const onSubmit: FormEventHandler = useCallback( (e: FormEvent) => { e.preventDefault(); @@ -63,21 +77,13 @@ export const RegisteredView: FC = ({ client }) => { setError(undefined); setLoading(true); - const createRoomResult = await createRoom( + const { roomId, encryptionSystem } = await createRoom( client, roomName, - E2eeType.SHARED_KEY, + encryptionOptions[encryption].e2eeType, ); - if (!createRoomResult.password) - throw new Error("Failed to create room with shared secret"); - history.push( - getRelativeRoomUrl( - createRoomResult.roomId, - { kind: E2eeType.SHARED_KEY, secret: createRoomResult.password }, - roomName, - ), - ); + history.push(getRelativeRoomUrl(roomId, encryptionSystem, roomName)); } submit().catch((error) => { @@ -93,7 +99,7 @@ export const RegisteredView: FC = ({ client }) => { } }); }, - [client, history, setJoinExistingCallModalOpen], + [client, history, setJoinExistingCallModalOpen, encryption], ); const recentRooms = useGroupCallRooms(client); @@ -132,6 +138,19 @@ export const RegisteredView: FC = ({ client }) => { data-testid="home_callName" /> + + setEncryption(x as keyof typeof encryptionOptions) + } + values={Object.keys(encryptionOptions).map((value) => [ + value, + encryptionOptions[value as keyof typeof encryptionOptions] + .label, + ])} + placeholder="" + />