Merge remote-tracking branch 'origin/main' into dbkr/spatial_audio_ff_only
This commit is contained in:
commit
4ad5ea49c2
7 changed files with 144 additions and 51 deletions
|
|
@ -44,6 +44,7 @@ import { useEventTarget } from "./useEvents";
|
||||||
declare global {
|
declare global {
|
||||||
interface Window {
|
interface Window {
|
||||||
matrixclient: MatrixClient;
|
matrixclient: MatrixClient;
|
||||||
|
isPasswordlessUser: boolean;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -118,9 +119,6 @@ export const ClientProvider: FC<Props> = ({ children }) => {
|
||||||
if (widget) {
|
if (widget) {
|
||||||
// We're inside a widget, so let's engage *matryoshka mode*
|
// We're inside a widget, so let's engage *matryoshka mode*
|
||||||
logger.log("Using a matryoshka client");
|
logger.log("Using a matryoshka client");
|
||||||
PosthogAnalytics.instance.setRegistrationType(
|
|
||||||
RegistrationType.Registered
|
|
||||||
);
|
|
||||||
return {
|
return {
|
||||||
client: await widget.client,
|
client: await widget.client,
|
||||||
isPasswordlessUser: false,
|
isPasswordlessUser: false,
|
||||||
|
|
@ -138,11 +136,6 @@ export const ClientProvider: FC<Props> = ({ children }) => {
|
||||||
session;
|
session;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
PosthogAnalytics.instance.setRegistrationType(
|
|
||||||
passwordlessUser
|
|
||||||
? RegistrationType.Guest
|
|
||||||
: RegistrationType.Registered
|
|
||||||
);
|
|
||||||
return {
|
return {
|
||||||
client: await initClient(
|
client: await initClient(
|
||||||
{
|
{
|
||||||
|
|
@ -345,7 +338,8 @@ export const ClientProvider: FC<Props> = ({ children }) => {
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
window.matrixclient = client;
|
window.matrixclient = client;
|
||||||
}, [client]);
|
window.isPasswordlessUser = isPasswordlessUser;
|
||||||
|
}, [client, isPasswordlessUser]);
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
return <ErrorView error={error} />;
|
return <ErrorView error={error} />;
|
||||||
|
|
|
||||||
|
|
@ -16,9 +16,11 @@ limitations under the License.
|
||||||
|
|
||||||
import posthog, { CaptureOptions, PostHog, Properties } from "posthog-js";
|
import posthog, { CaptureOptions, PostHog, Properties } from "posthog-js";
|
||||||
import { logger } from "matrix-js-sdk/src/logger";
|
import { logger } from "matrix-js-sdk/src/logger";
|
||||||
|
import { MatrixClient } from "matrix-js-sdk";
|
||||||
|
import { Buffer } from "buffer";
|
||||||
|
|
||||||
import { widget } from "./widget";
|
import { widget } from "./widget";
|
||||||
import { getSetting, settingsBus } from "./settings/useSetting";
|
import { getSetting, setSetting, settingsBus } from "./settings/useSetting";
|
||||||
import {
|
import {
|
||||||
CallEndedTracker,
|
CallEndedTracker,
|
||||||
CallStartedTracker,
|
CallStartedTracker,
|
||||||
|
|
@ -28,6 +30,7 @@ import {
|
||||||
MuteMicrophoneTracker,
|
MuteMicrophoneTracker,
|
||||||
} from "./PosthogEvents";
|
} from "./PosthogEvents";
|
||||||
import { Config } from "./config/Config";
|
import { Config } from "./config/Config";
|
||||||
|
import { getUrlParams } from "./UrlParams";
|
||||||
|
|
||||||
/* Posthog analytics tracking.
|
/* Posthog analytics tracking.
|
||||||
*
|
*
|
||||||
|
|
@ -96,6 +99,7 @@ export class PosthogAnalytics {
|
||||||
// set true during the constructor if posthog config is present, otherwise false
|
// set true during the constructor if posthog config is present, otherwise false
|
||||||
private static internalInstance = null;
|
private static internalInstance = null;
|
||||||
|
|
||||||
|
private identificationPromise: Promise<void>;
|
||||||
private readonly enabled: boolean = false;
|
private readonly enabled: boolean = false;
|
||||||
private anonymity = Anonymity.Disabled;
|
private anonymity = Anonymity.Disabled;
|
||||||
private platformSuperProperties = {};
|
private platformSuperProperties = {};
|
||||||
|
|
@ -113,11 +117,17 @@ export class PosthogAnalytics {
|
||||||
project_api_key: Config.instance.config.posthog?.api_key,
|
project_api_key: Config.instance.config.posthog?.api_key,
|
||||||
api_host: Config.instance.config.posthog?.api_host,
|
api_host: Config.instance.config.posthog?.api_host,
|
||||||
};
|
};
|
||||||
if (
|
|
||||||
posthogConfig.project_api_key &&
|
if (posthogConfig.project_api_key && posthogConfig.api_host) {
|
||||||
posthogConfig.api_host &&
|
if (
|
||||||
PosthogAnalytics.getPlatformProperties().matrixBackend === "jssdk"
|
PosthogAnalytics.getPlatformProperties().matrixBackend === "embedded"
|
||||||
) {
|
) {
|
||||||
|
const { analyticsID } = getUrlParams();
|
||||||
|
// if the embedding platform (element web) already got approval to communicating with posthog
|
||||||
|
// element call can also send events to posthog
|
||||||
|
setSetting("opt-in-analytics", Boolean(analyticsID));
|
||||||
|
}
|
||||||
|
|
||||||
this.posthog.init(posthogConfig.project_api_key, {
|
this.posthog.init(posthogConfig.project_api_key, {
|
||||||
api_host: posthogConfig.api_host,
|
api_host: posthogConfig.api_host,
|
||||||
autocapture: false,
|
autocapture: false,
|
||||||
|
|
@ -132,9 +142,9 @@ export class PosthogAnalytics {
|
||||||
} else {
|
} else {
|
||||||
this.enabled = false;
|
this.enabled = false;
|
||||||
}
|
}
|
||||||
const optInAnalytics = getSetting("opt-in-analytics", false);
|
|
||||||
this.updateAnonymityFromSettingsAndIdentifyUser(optInAnalytics);
|
|
||||||
this.startListeningToSettingsChanges();
|
this.startListeningToSettingsChanges();
|
||||||
|
const optInAnalytics = getSetting("opt-in-analytics", false);
|
||||||
|
this.updateAnonymityAndIdentifyUser(optInAnalytics);
|
||||||
}
|
}
|
||||||
|
|
||||||
private sanitizeProperties = (
|
private sanitizeProperties = (
|
||||||
|
|
@ -155,6 +165,12 @@ export class PosthogAnalytics {
|
||||||
// drop device ID, which is a UUID persisted in local storage
|
// drop device ID, which is a UUID persisted in local storage
|
||||||
properties["$device_id"] = null;
|
properties["$device_id"] = null;
|
||||||
}
|
}
|
||||||
|
// the url leaks a lot of private data like the call name or the user.
|
||||||
|
// Its stripped down to the bare minimum to only give insights about the host (develop, main or sfu)
|
||||||
|
properties["$current_url"] = (properties["$current_url"] as string)
|
||||||
|
.split("/")
|
||||||
|
.slice(0, 3)
|
||||||
|
.join("");
|
||||||
|
|
||||||
return properties;
|
return properties;
|
||||||
};
|
};
|
||||||
|
|
@ -166,7 +182,7 @@ export class PosthogAnalytics {
|
||||||
}
|
}
|
||||||
|
|
||||||
private static getPlatformProperties(): PlatformProperties {
|
private static getPlatformProperties(): PlatformProperties {
|
||||||
const appVersion = import.meta.env.VITE_APP_VERSION || "unknown";
|
const appVersion = import.meta.env.VITE_APP_VERSION || "dev";
|
||||||
return {
|
return {
|
||||||
appVersion,
|
appVersion,
|
||||||
matrixBackend: widget ? "embedded" : "jssdk",
|
matrixBackend: widget ? "embedded" : "jssdk",
|
||||||
|
|
@ -211,36 +227,86 @@ export class PosthogAnalytics {
|
||||||
.join("");
|
.join("");
|
||||||
}
|
}
|
||||||
|
|
||||||
public async identifyUser(analyticsIdGenerator: () => string): Promise<void> {
|
public async identifyUser(analyticsIdGenerator: () => string) {
|
||||||
// There might be a better way to get the client here.
|
// There might be a better way to get the client here.
|
||||||
const client = window.matrixclient;
|
|
||||||
|
|
||||||
if (this.anonymity == Anonymity.Pseudonymous) {
|
if (this.anonymity == Anonymity.Pseudonymous) {
|
||||||
// Check the user's account_data for an analytics ID to use. Storing the ID in account_data allows
|
// Check the user's account_data for an analytics ID to use. Storing the ID in account_data allows
|
||||||
// different devices to send the same ID.
|
// different devices to send the same ID.
|
||||||
|
let analyticsID = await this.getAnalyticsId();
|
||||||
try {
|
try {
|
||||||
const accountData = await client.getAccountDataFromServer(
|
if (!analyticsID && !widget) {
|
||||||
PosthogAnalytics.ANALYTICS_EVENT_TYPE
|
// only try setting up a new analytics ID in the standalone app.
|
||||||
);
|
|
||||||
let analyticsID = accountData?.id;
|
|
||||||
if (!analyticsID) {
|
|
||||||
// Couldn't retrieve an analytics ID from user settings, so create one and set it on the server.
|
// Couldn't retrieve an analytics ID from user settings, so create one and set it on the server.
|
||||||
// Note there's a race condition here - if two devices do these steps at the same time, last write
|
// Note there's a race condition here - if two devices do these steps at the same time, last write
|
||||||
// wins, and the first writer will send tracking with an ID that doesn't match the one on the server
|
// wins, and the first writer will send tracking with an ID that doesn't match the one on the server
|
||||||
// until the next time account data is refreshed and this function is called (most likely on next
|
// until the next time account data is refreshed and this function is called (most likely on next
|
||||||
// page load). This will happen pretty infrequently, so we can tolerate the possibility.
|
// page load). This will happen pretty infrequently, so we can tolerate the possibility.
|
||||||
analyticsID = analyticsIdGenerator();
|
const accountDataAnalyticsId = analyticsIdGenerator();
|
||||||
await client.setAccountData(
|
await this.setAccountAnalyticsId(accountDataAnalyticsId);
|
||||||
PosthogAnalytics.ANALYTICS_EVENT_TYPE,
|
analyticsID = await this.hashedEcAnalyticsId(accountDataAnalyticsId);
|
||||||
Object.assign({ id: analyticsID }, accountData)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
this.posthog.identify(analyticsID);
|
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// The above could fail due to network requests, but not essential to starting the application,
|
// The above could fail due to network requests, but not essential to starting the application,
|
||||||
// so swallow it.
|
// so swallow it.
|
||||||
logger.log("Unable to identify user for tracking" + e.toString());
|
logger.log("Unable to identify user for tracking" + e.toString());
|
||||||
}
|
}
|
||||||
|
if (analyticsID) {
|
||||||
|
this.posthog.identify(analyticsID);
|
||||||
|
} else {
|
||||||
|
logger.info(
|
||||||
|
"No analyticsID is availble. Should not try to setup posthog"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async getAnalyticsId() {
|
||||||
|
const client: MatrixClient = window.matrixclient;
|
||||||
|
let accountAnalyticsId;
|
||||||
|
if (widget) {
|
||||||
|
accountAnalyticsId = getUrlParams().analyticsID;
|
||||||
|
} else {
|
||||||
|
const accountData = await client.getAccountDataFromServer(
|
||||||
|
PosthogAnalytics.ANALYTICS_EVENT_TYPE
|
||||||
|
);
|
||||||
|
accountAnalyticsId = accountData?.id;
|
||||||
|
}
|
||||||
|
if (accountAnalyticsId) {
|
||||||
|
// we dont just use the element web analytics ID because that would allow to associate
|
||||||
|
// users between the two posthog instances. By using a hash from the username and the element web analytics id
|
||||||
|
// it is not possible to conclude the element web posthog user id from the element call user id and vice versa.
|
||||||
|
return await this.hashedEcAnalyticsId(accountAnalyticsId);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
async hashedEcAnalyticsId(accountAnalyticsId: string): Promise<string> {
|
||||||
|
const client: MatrixClient = window.matrixclient;
|
||||||
|
const posthogIdMaterial = "ec" + accountAnalyticsId + client.getUserId();
|
||||||
|
const bufferForPosthogId = await crypto.subtle.digest(
|
||||||
|
"sha-256",
|
||||||
|
Buffer.from(posthogIdMaterial, "utf-8")
|
||||||
|
);
|
||||||
|
const view = new Int32Array(bufferForPosthogId);
|
||||||
|
return Array.from(view)
|
||||||
|
.map((b) => Math.abs(b).toString(16).padStart(2, "0"))
|
||||||
|
.join("");
|
||||||
|
}
|
||||||
|
|
||||||
|
async setAccountAnalyticsId(analyticsID: string) {
|
||||||
|
if (!widget) {
|
||||||
|
const client = window.matrixclient;
|
||||||
|
|
||||||
|
// the analytics ID only needs to be set in the standalone version.
|
||||||
|
const accountData = await client.getAccountDataFromServer(
|
||||||
|
PosthogAnalytics.ANALYTICS_EVENT_TYPE
|
||||||
|
);
|
||||||
|
await client.setAccountData(
|
||||||
|
PosthogAnalytics.ANALYTICS_EVENT_TYPE,
|
||||||
|
Object.assign({ id: analyticsID }, accountData)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -255,12 +321,11 @@ export class PosthogAnalytics {
|
||||||
this.setAnonymity(Anonymity.Disabled);
|
this.setAnonymity(Anonymity.Disabled);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async updateSuperProperties(): Promise<void> {
|
public updateSuperProperties() {
|
||||||
// Update super properties in posthog with our platform (app version, platform).
|
// Update super properties in posthog with our platform (app version, platform).
|
||||||
// These properties will be subsequently passed in every event.
|
// These properties will be subsequently passed in every event.
|
||||||
//
|
//
|
||||||
// This only needs to be done once per page lifetime. Note that getPlatformProperties
|
// This only needs to be done once per page lifetime. Note that getPlatformProperties
|
||||||
// is async and can involve a network request if we are running in a browser.
|
|
||||||
this.platformSuperProperties = PosthogAnalytics.getPlatformProperties();
|
this.platformSuperProperties = PosthogAnalytics.getPlatformProperties();
|
||||||
this.registerSuperProperties({
|
this.registerSuperProperties({
|
||||||
...this.platformSuperProperties,
|
...this.platformSuperProperties,
|
||||||
|
|
@ -276,7 +341,7 @@ export class PosthogAnalytics {
|
||||||
return this.eventSignup.getSignupEndTime() > new Date(0);
|
return this.eventSignup.getSignupEndTime() > new Date(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async updateAnonymityFromSettingsAndIdentifyUser(
|
public async updateAnonymityAndIdentifyUser(
|
||||||
pseudonymousOptIn: boolean
|
pseudonymousOptIn: boolean
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
// Update this.anonymity based on the user's analytics opt-in settings
|
// Update this.anonymity based on the user's analytics opt-in settings
|
||||||
|
|
@ -284,22 +349,36 @@ export class PosthogAnalytics {
|
||||||
? Anonymity.Pseudonymous
|
? Anonymity.Pseudonymous
|
||||||
: Anonymity.Disabled;
|
: Anonymity.Disabled;
|
||||||
this.setAnonymity(anonymity);
|
this.setAnonymity(anonymity);
|
||||||
|
|
||||||
if (anonymity === Anonymity.Pseudonymous) {
|
if (anonymity === Anonymity.Pseudonymous) {
|
||||||
await this.identifyUser(PosthogAnalytics.getRandomAnalyticsId);
|
this.setRegistrationType(
|
||||||
|
window.matrixclient.isGuest() || window.isPasswordlessUser
|
||||||
|
? RegistrationType.Guest
|
||||||
|
: RegistrationType.Registered
|
||||||
|
);
|
||||||
|
// store the promise to await posthog-tracking-events until the identification is done.
|
||||||
|
this.identificationPromise = this.identifyUser(
|
||||||
|
PosthogAnalytics.getRandomAnalyticsId
|
||||||
|
);
|
||||||
|
await this.identificationPromise;
|
||||||
if (this.userRegisteredInThisSession()) {
|
if (this.userRegisteredInThisSession()) {
|
||||||
this.eventSignup.track();
|
this.eventSignup.track();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (anonymity !== Anonymity.Disabled) {
|
if (anonymity !== Anonymity.Disabled) {
|
||||||
await this.updateSuperProperties();
|
this.updateSuperProperties();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public trackEvent<E extends IPosthogEvent>(
|
public async trackEvent<E extends IPosthogEvent>(
|
||||||
{ eventName, ...properties }: E,
|
{ eventName, ...properties }: E,
|
||||||
options?: IPostHogEventOptions
|
options?: IPostHogEventOptions
|
||||||
): void {
|
): Promise<void> {
|
||||||
|
if (this.identificationPromise) {
|
||||||
|
// only make calls to posthog after the identificaion is done
|
||||||
|
await this.identificationPromise;
|
||||||
|
}
|
||||||
if (
|
if (
|
||||||
this.anonymity == Anonymity.Disabled ||
|
this.anonymity == Anonymity.Disabled ||
|
||||||
this.anonymity == Anonymity.Anonymous
|
this.anonymity == Anonymity.Anonymous
|
||||||
|
|
@ -318,7 +397,7 @@ export class PosthogAnalytics {
|
||||||
// Note that for new accounts, pseudonymousAnalyticsOptIn won't be set, so updateAnonymityFromSettings
|
// Note that for new accounts, pseudonymousAnalyticsOptIn won't be set, so updateAnonymityFromSettings
|
||||||
// won't be called (i.e. this.anonymity will be left as the default, until the setting changes)
|
// won't be called (i.e. this.anonymity will be left as the default, until the setting changes)
|
||||||
settingsBus.on("opt-in-analytics", (optInAnalytics) => {
|
settingsBus.on("opt-in-analytics", (optInAnalytics) => {
|
||||||
this.updateAnonymityFromSettingsAndIdentifyUser(optInAnalytics);
|
this.updateAnonymityAndIdentifyUser(optInAnalytics);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ import {
|
||||||
|
|
||||||
interface CallEnded extends IPosthogEvent {
|
interface CallEnded extends IPosthogEvent {
|
||||||
eventName: "CallEnded";
|
eventName: "CallEnded";
|
||||||
callName: string;
|
callId: string;
|
||||||
callParticipantsOnLeave: number;
|
callParticipantsOnLeave: number;
|
||||||
callParticipantsMax: number;
|
callParticipantsMax: number;
|
||||||
callDuration: number;
|
callDuration: number;
|
||||||
|
|
@ -45,10 +45,10 @@ export class CallEndedTracker {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
track(callName: string, callParticipantsNow: number) {
|
track(callId: string, callParticipantsNow: number) {
|
||||||
PosthogAnalytics.instance.trackEvent<CallEnded>({
|
PosthogAnalytics.instance.trackEvent<CallEnded>({
|
||||||
eventName: "CallEnded",
|
eventName: "CallEnded",
|
||||||
callName,
|
callId: callId,
|
||||||
callParticipantsMax: this.cache.maxParticipantsCount,
|
callParticipantsMax: this.cache.maxParticipantsCount,
|
||||||
callParticipantsOnLeave: callParticipantsNow,
|
callParticipantsOnLeave: callParticipantsNow,
|
||||||
callDuration: (Date.now() - this.cache.startTime.getTime()) / 1000,
|
callDuration: (Date.now() - this.cache.startTime.getTime()) / 1000,
|
||||||
|
|
@ -58,14 +58,14 @@ export class CallEndedTracker {
|
||||||
|
|
||||||
interface CallStarted extends IPosthogEvent {
|
interface CallStarted extends IPosthogEvent {
|
||||||
eventName: "CallStarted";
|
eventName: "CallStarted";
|
||||||
callName: string;
|
callId: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class CallStartedTracker {
|
export class CallStartedTracker {
|
||||||
track(callName: string) {
|
track(callId: string) {
|
||||||
PosthogAnalytics.instance.trackEvent<CallStarted>({
|
PosthogAnalytics.instance.trackEvent<CallStarted>({
|
||||||
eventName: "CallStarted",
|
eventName: "CallStarted",
|
||||||
callName,
|
callId: callId,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -75,6 +75,10 @@ export interface UrlParams {
|
||||||
* The factor by which to scale the interface's font size.
|
* The factor by which to scale the interface's font size.
|
||||||
*/
|
*/
|
||||||
fontScale: number | null;
|
fontScale: number | null;
|
||||||
|
/**
|
||||||
|
* The Posthog analytics ID. It is only available if the user has given consent for sharing telemetry in element web.
|
||||||
|
*/
|
||||||
|
analyticsID: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -130,6 +134,7 @@ export const getUrlParams = (
|
||||||
lang: getParam("lang"),
|
lang: getParam("lang"),
|
||||||
fonts: getAllParams("font"),
|
fonts: getAllParams("font"),
|
||||||
fontScale: Number.isNaN(fontScale) ? null : fontScale,
|
fontScale: Number.isNaN(fontScale) ? null : fontScale,
|
||||||
|
analyticsID: getParam("analyticsID"),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -142,6 +142,10 @@ export function GroupCallView({
|
||||||
]);
|
]);
|
||||||
|
|
||||||
await groupCall.enter();
|
await groupCall.enter();
|
||||||
|
|
||||||
|
PosthogAnalytics.instance.eventCallEnded.cacheStartCall(new Date());
|
||||||
|
PosthogAnalytics.instance.eventCallStarted.track(groupCall.groupCallId);
|
||||||
|
|
||||||
await Promise.all([
|
await Promise.all([
|
||||||
widget.api.setAlwaysOnScreen(true),
|
widget.api.setAlwaysOnScreen(true),
|
||||||
widget.api.transport.reply(ev.detail, {}),
|
widget.api.transport.reply(ev.detail, {}),
|
||||||
|
|
@ -159,6 +163,9 @@ export function GroupCallView({
|
||||||
if (isEmbedded && !preload) {
|
if (isEmbedded && !preload) {
|
||||||
// In embedded mode, bypass the lobby and just enter the call straight away
|
// In embedded mode, bypass the lobby and just enter the call straight away
|
||||||
groupCall.enter();
|
groupCall.enter();
|
||||||
|
|
||||||
|
PosthogAnalytics.instance.eventCallEnded.cacheStartCall(new Date());
|
||||||
|
PosthogAnalytics.instance.eventCallStarted.track(groupCall.groupCallId);
|
||||||
}
|
}
|
||||||
}, [groupCall, isEmbedded, preload]);
|
}, [groupCall, isEmbedded, preload]);
|
||||||
|
|
||||||
|
|
@ -178,7 +185,7 @@ export function GroupCallView({
|
||||||
}
|
}
|
||||||
|
|
||||||
PosthogAnalytics.instance.eventCallEnded.track(
|
PosthogAnalytics.instance.eventCallEnded.track(
|
||||||
groupCall.room.name,
|
groupCall.groupCallId,
|
||||||
participantCount
|
participantCount
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -346,7 +346,7 @@ export function useGroupCall(groupCall: GroupCall): UseGroupCallReturnType {
|
||||||
}
|
}
|
||||||
|
|
||||||
PosthogAnalytics.instance.eventCallEnded.cacheStartCall(new Date());
|
PosthogAnalytics.instance.eventCallEnded.cacheStartCall(new Date());
|
||||||
PosthogAnalytics.instance.eventCallStarted.track(groupCall.room.name);
|
PosthogAnalytics.instance.eventCallStarted.track(groupCall.groupCallId);
|
||||||
|
|
||||||
groupCall.enter().catch((error) => {
|
groupCall.enter().catch((error) => {
|
||||||
console.error(error);
|
console.error(error);
|
||||||
|
|
|
||||||
|
|
@ -20,12 +20,15 @@ import { useMemo, useState, useEffect, useCallback } from "react";
|
||||||
// Bus to notify other useSetting consumers when a setting is changed
|
// Bus to notify other useSetting consumers when a setting is changed
|
||||||
export const settingsBus = new EventEmitter();
|
export const settingsBus = new EventEmitter();
|
||||||
|
|
||||||
|
const getSettingKey = (name: string): string => {
|
||||||
|
return `matrix-setting-${name}`;
|
||||||
|
};
|
||||||
// Like useState, but reads from and persists the value to localStorage
|
// Like useState, but reads from and persists the value to localStorage
|
||||||
const useSetting = <T>(
|
const useSetting = <T>(
|
||||||
name: string,
|
name: string,
|
||||||
defaultValue: T
|
defaultValue: T
|
||||||
): [T, (value: T) => void] => {
|
): [T, (value: T) => void] => {
|
||||||
const key = useMemo(() => `matrix-setting-${name}`, [name]);
|
const key = useMemo(() => getSettingKey(name), [name]);
|
||||||
|
|
||||||
const [value, setValue] = useState<T>(() => {
|
const [value, setValue] = useState<T>(() => {
|
||||||
const item = localStorage.getItem(key);
|
const item = localStorage.getItem(key);
|
||||||
|
|
@ -51,13 +54,17 @@ const useSetting = <T>(
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
export const getSetting = <T>(name: string, defaultValue: T): T => {
|
|
||||||
const key = `matrix-setting-${name}`;
|
|
||||||
|
|
||||||
const item = localStorage.getItem(key);
|
export const getSetting = <T>(name: string, defaultValue: T): T => {
|
||||||
|
const item = localStorage.getItem(getSettingKey(name));
|
||||||
return item === null ? defaultValue : JSON.parse(item);
|
return item === null ? defaultValue : JSON.parse(item);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const setSetting = <T>(name: string, newValue: T) => {
|
||||||
|
localStorage.setItem(getSettingKey(name), JSON.stringify(newValue));
|
||||||
|
settingsBus.emit(name, newValue);
|
||||||
|
};
|
||||||
|
|
||||||
export const canEnableSpatialAudio = () => {
|
export const canEnableSpatialAudio = () => {
|
||||||
const { userAgent } = navigator;
|
const { userAgent } = navigator;
|
||||||
// Spatial audio means routing audio through audio contexts. On Chrome,
|
// Spatial audio means routing audio through audio contexts. On Chrome,
|
||||||
|
|
@ -78,6 +85,7 @@ export const useSpatialAudio = (): [boolean, (val: boolean) => void] => {
|
||||||
|
|
||||||
return [false, (_: boolean) => {}];
|
return [false, (_: boolean) => {}];
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useShowInspector = () => useSetting("show-inspector", false);
|
export const useShowInspector = () => useSetting("show-inspector", false);
|
||||||
export const useOptInAnalytics = () => useSetting("opt-in-analytics", false);
|
export const useOptInAnalytics = () => useSetting("opt-in-analytics", false);
|
||||||
export const useKeyboardShortcuts = () =>
|
export const useKeyboardShortcuts = () =>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue