Merge branch 'livekit' into firefox-audio-output

This commit is contained in:
Robin
2024-12-13 14:55:27 -05:00
191 changed files with 4613 additions and 2033 deletions

View File

@@ -0,0 +1,108 @@
/*
Copyright 2022-2024 New Vector Ltd.
SPDX-License-Identifier: AGPL-3.0-only
Please see LICENSE in the repository root for full details.
*/
import { type ChangeEvent, type FC, useCallback } from "react";
import { useTranslation } from "react-i18next";
import { FieldRow, InputField } from "../input/Input";
import {
useSetting,
duplicateTiles as duplicateTilesSetting,
debugTileLayout as debugTileLayoutSetting,
showNonMemberTiles as showNonMemberTilesSetting,
} from "./settings";
import type { MatrixClient } from "matrix-js-sdk/src/client";
interface Props {
client: MatrixClient;
}
export const DeveloperSettingsTab: FC<Props> = ({ client }) => {
const { t } = useTranslation();
const [duplicateTiles, setDuplicateTiles] = useSetting(duplicateTilesSetting);
const [debugTileLayout, setDebugTileLayout] = useSetting(
debugTileLayoutSetting,
);
const [showNonMemberTiles, setShowNonMemberTiles] = useSetting(
showNonMemberTilesSetting,
);
return (
<>
<p>
{t("developer_mode.hostname", {
hostname: window.location.hostname || "unknown",
})}
</p>
<p>
{t("version", {
productName: import.meta.env.VITE_PRODUCT_NAME || "Element Call",
version: import.meta.env.VITE_APP_VERSION || "dev",
})}
</p>
<p>
{t("developer_mode.crypto_version", {
version: client.getCrypto()?.getVersion() || "unknown",
})}
</p>
<p>
{t("developer_mode.matrix_id", {
id: client.getUserId() || "unknown",
})}
</p>
<p>
{t("developer_mode.device_id", {
id: client.getDeviceId() || "unknown",
})}
</p>
<FieldRow>
<InputField
id="duplicateTiles"
type="number"
label={t("developer_mode.duplicate_tiles_label")}
value={duplicateTiles.toString()}
min={0}
onChange={useCallback(
(event: ChangeEvent<HTMLInputElement>): void => {
const value = event.target.valueAsNumber;
if (value < 0) {
return;
}
setDuplicateTiles(Number.isNaN(value) ? 0 : value);
},
[setDuplicateTiles],
)}
/>
</FieldRow>
<FieldRow>
<InputField
id="debugTileLayout"
type="checkbox"
checked={debugTileLayout}
label={t("developer_mode.debug_tile_layout_label")}
onChange={(event: ChangeEvent<HTMLInputElement>): void =>
setDebugTileLayout(event.target.checked)
}
/>
</FieldRow>
<FieldRow>
<InputField
id="showNonMemberTiles"
type="checkbox"
label={t("developer_mode.show_non_member_tiles")}
checked={!!showNonMemberTiles}
onChange={useCallback(
(event: ChangeEvent<HTMLInputElement>): void => {
setShowNonMemberTiles(event.target.checked);
},
[setShowNonMemberTiles],
)}
/>
</FieldRow>
</>
);
};

View File

@@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
Please see LICENSE in the repository root for full details.
*/
import { ChangeEvent, FC, useCallback, useId } from "react";
import { type ChangeEvent, type FC, useCallback, useId } from "react";
import {
Heading,
InlineField,
@@ -15,7 +15,7 @@ import {
} from "@vector-im/compound-web";
import { useTranslation } from "react-i18next";
import { MediaDevice } from "../livekit/MediaDevicesContext";
import { type MediaDevice } from "../livekit/MediaDevicesContext";
import styles from "./DeviceSelection.module.css";
interface Props {

View File

@@ -5,15 +5,17 @@ SPDX-License-Identifier: AGPL-3.0-only
Please see LICENSE in the repository root for full details.
*/
import { FC, useCallback } from "react";
import { type ChangeEvent, type FC, useCallback } from "react";
import { randomString } from "matrix-js-sdk/src/randomstring";
import { useTranslation } from "react-i18next";
import { Trans, useTranslation } from "react-i18next";
import { Button, Text } from "@vector-im/compound-web";
import { logger } from "matrix-js-sdk/src/logger";
import { FieldRow, InputField, ErrorMessage } from "../input/Input";
import { useSubmitRageshake, useRageshakeRequest } from "./submit-rageshake";
import feedbackStyles from "../input/FeedbackInput.module.css";
import { AnalyticsNotice } from "../analytics/AnalyticsNotice";
import { useOptInAnalytics } from "./settings";
interface Props {
roomId?: string;
@@ -52,8 +54,32 @@ export const FeedbackSettingsTab: FC<Props> = ({ roomId }) => {
[submitRageshake, roomId, sendRageshakeRequest],
);
const [optInAnalytics, setOptInAnalytics] = useOptInAnalytics();
const optInDescription = (
<Text size="sm">
<Trans i18nKey="settings.opt_in_description">
<AnalyticsNotice />
<br />
You may withdraw consent by unchecking this box. If you are currently in
a call, this setting will take effect at the end of the call.
</Trans>
</Text>
);
return (
<div>
<h4>{t("common.analytics")}</h4>
<FieldRow>
<InputField
id="optInAnalytics"
type="checkbox"
checked={optInAnalytics ?? undefined}
description={optInDescription}
onChange={(event: ChangeEvent<HTMLInputElement>): void => {
setOptInAnalytics?.(event.target.checked);
}}
/>
</FieldRow>
<h4>{t("settings.feedback_tab_h4")}</h4>
<Text>{t("settings.feedback_tab_body")}</Text>
<form onSubmit={onSubmitFeedback}>

View File

@@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
Please see LICENSE in the repository root for full details.
*/
import { ChangeEvent, FC } from "react";
import { type ChangeEvent, type FC } from "react";
import { useTranslation } from "react-i18next";
import { Text } from "@vector-im/compound-web";
@@ -14,6 +14,7 @@ import {
showHandRaisedTimer as showHandRaisedTimerSetting,
showReactions as showReactionsSetting,
playReactionsSound as playReactionsSoundSetting,
developerMode as developerModeSetting,
useSetting,
} from "./settings";
@@ -36,23 +37,23 @@ export const PreferencesSettingsTab: FC = () => {
fn(e.target.checked);
};
const [developerMode, setDeveloperMode] = useSetting(developerModeSetting);
return (
<div>
<h4>{t("settings.preferences_tab_h4")}</h4>
<Text>{t("settings.preferences_tab_body")}</Text>
<Text>{t("settings.preferences_tab.introduction")}</Text>
<FieldRow>
<InputField
id="showHandRaisedTimer"
label={t("settings.preferences_tab_show_hand_raised_timer_label")}
label={t("settings.preferences_tab.show_hand_raised_timer_label")}
description={t(
"settings.preferences_tab_show_hand_raised_timer_description",
"settings.preferences_tab.show_hand_raised_timer_description",
)}
type="checkbox"
checked={showHandRaisedTimer}
onChange={(e) => onChangeSetting(e, setShowHandRaisedTimer)}
/>
</FieldRow>
<h5>{t("settings.preferences_tab.reactions_title")}</h5>
<FieldRow>
<InputField
id="showReactions"
@@ -75,6 +76,20 @@ export const PreferencesSettingsTab: FC = () => {
onChange={(e) => onChangeSetting(e, setPlayReactionSound)}
/>
</FieldRow>
<FieldRow>
<InputField
id="developerSettingsTab"
type="checkbox"
checked={developerMode}
label={t("settings.preferences_tab.developer_mode_label")}
description={t(
"settings.preferences_tab.developer_mode_label_description",
)}
onChange={(event: ChangeEvent<HTMLInputElement>): void =>
setDeveloperMode(event.target.checked)
}
/>
</FieldRow>
</div>
);
};

View File

@@ -5,8 +5,8 @@ SPDX-License-Identifier: AGPL-3.0-only
Please see LICENSE in the repository root for full details.
*/
import { FC, useCallback, useEffect, useMemo, useRef } from "react";
import { MatrixClient } from "matrix-js-sdk/src/client";
import { type FC, useCallback, useEffect, useMemo, useRef } from "react";
import { type MatrixClient } from "matrix-js-sdk/src/client";
import { useTranslation } from "react-i18next";
import { logger } from "matrix-js-sdk/src/logger";

View File

@@ -6,7 +6,7 @@ Please see LICENSE in the repository root for full details.
*/
import { useTranslation } from "react-i18next";
import { FC, useCallback } from "react";
import { type FC, useCallback } from "react";
import { Button } from "@vector-im/compound-web";
import { logger } from "matrix-js-sdk/src/logger";

View File

@@ -5,16 +5,14 @@ SPDX-License-Identifier: AGPL-3.0-only
Please see LICENSE in the repository root for full details.
*/
import { ChangeEvent, FC, useCallback } from "react";
import { Trans, useTranslation } from "react-i18next";
import { MatrixClient } from "matrix-js-sdk/src/matrix";
import { Root as Form, Text } from "@vector-im/compound-web";
import { type FC, useState } from "react";
import { useTranslation } from "react-i18next";
import { type MatrixClient } from "matrix-js-sdk/src/matrix";
import { Root as Form } from "@vector-im/compound-web";
import { Modal } from "../Modal";
import styles from "./SettingsModal.module.css";
import { Tab, TabContainer } from "../tabs/Tabs";
import { FieldRow, InputField } from "../input/Input";
import { AnalyticsNotice } from "../analytics/AnalyticsNotice";
import { type Tab, TabContainer } from "../tabs/Tabs";
import { ProfileSettingsTab } from "./ProfileSettingsTab";
import { FeedbackSettingsTab } from "./FeedbackSettingsTab";
import {
@@ -24,14 +22,13 @@ import {
import { widget } from "../widget";
import {
useSetting,
developerSettingsTab as developerSettingsTabSetting,
duplicateTiles as duplicateTilesSetting,
useOptInAnalytics,
soundEffectVolumeSetting,
developerMode,
} from "./settings";
import { PreferencesSettingsTab } from "./PreferencesSettingsTab";
import { Slider } from "../Slider";
import { DeviceSelection } from "./DeviceSelection";
import { DeveloperSettingsTab } from "./DeveloperSettingsTab";
type SettingsTab =
| "audio"
@@ -63,27 +60,12 @@ export const SettingsModal: FC<Props> = ({
}) => {
const { t } = useTranslation();
const [optInAnalytics, setOptInAnalytics] = useOptInAnalytics();
const [developerSettingsTab, setDeveloperSettingsTab] = useSetting(
developerSettingsTabSetting,
);
const [duplicateTiles, setDuplicateTiles] = useSetting(duplicateTilesSetting);
const optInDescription = (
<Text size="sm">
<Trans i18nKey="settings.opt_in_description">
<AnalyticsNotice />
<br />
You may withdraw consent by unchecking this box. If you are currently in
a call, this setting will take effect at the end of the call.
</Trans>
</Text>
);
const devices = useMediaDevices();
useMediaDeviceNames(devices, open);
const [soundVolume, setSoundVolume] = useSetting(soundEffectVolumeSetting);
const [soundVolumeRaw, setSoundVolumeRaw] = useState(soundVolume);
const [showDeveloperSettingsTab] = useSetting(developerMode);
const audioTab: Tab<SettingsTab> = {
key: "audio",
@@ -108,8 +90,9 @@ export const SettingsModal: FC<Props> = ({
<p>{t("settings.audio_tab.effect_volume_description")}</p>
<Slider
label={t("video_tile.volume")}
value={soundVolume}
onValueChange={setSoundVolume}
value={soundVolumeRaw}
onValueChange={setSoundVolumeRaw}
onValueCommit={setSoundVolume}
min={0}
max={1}
step={0.01}
@@ -152,95 +135,16 @@ export const SettingsModal: FC<Props> = ({
content: <FeedbackSettingsTab roomId={roomId} />,
};
const moreTab: Tab<SettingsTab> = {
key: "more",
name: t("settings.more_tab_title"),
content: (
<>
<h4>{t("settings.developer_tab_title")}</h4>
<p>
{t("version", {
productName: import.meta.env.VITE_PRODUCT_NAME || "Element Call",
version: import.meta.env.VITE_APP_VERSION || "dev",
})}
</p>
<FieldRow>
<InputField
id="developerSettingsTab"
type="checkbox"
checked={developerSettingsTab}
label={t("settings.developer_settings_label")}
description={t("settings.developer_settings_label_description")}
onChange={(event: ChangeEvent<HTMLInputElement>): void =>
setDeveloperSettingsTab(event.target.checked)
}
/>
</FieldRow>
<h4>{t("common.analytics")}</h4>
<FieldRow>
<InputField
id="optInAnalytics"
type="checkbox"
checked={optInAnalytics ?? undefined}
description={optInDescription}
onChange={(event: ChangeEvent<HTMLInputElement>): void => {
setOptInAnalytics?.(event.target.checked);
}}
/>
</FieldRow>
</>
),
};
const developerTab: Tab<SettingsTab> = {
key: "developer",
name: t("settings.developer_tab_title"),
content: (
<>
<p>
{t("version", {
productName: import.meta.env.VITE_PRODUCT_NAME || "Element Call",
version: import.meta.env.VITE_APP_VERSION || "dev",
})}
</p>
<p>
{t("crypto_version", {
version: client.getCrypto()?.getVersion() || "unknown",
})}
</p>
<p>
{t("matrix_id", {
id: client.getUserId() || "unknown",
})}
</p>
<p>
{t("device_id", {
id: client.getDeviceId() || "unknown",
})}
</p>
<FieldRow>
<InputField
id="duplicateTiles"
type="number"
label={t("settings.duplicate_tiles_label")}
value={duplicateTiles.toString()}
onChange={useCallback(
(event: ChangeEvent<HTMLInputElement>): void => {
const value = event.target.valueAsNumber;
setDuplicateTiles(Number.isNaN(value) ? 0 : value);
},
[setDuplicateTiles],
)}
/>
</FieldRow>
</>
),
content: <DeveloperSettingsTab client={client} />,
};
const tabs = [audioTab, videoTab];
if (widget === null) tabs.push(profileTab);
tabs.push(preferencesTab, feedbackTab, moreTab);
if (developerSettingsTab) tabs.push(developerTab);
tabs.push(preferencesTab, feedbackTab);
if (showDeveloperSettingsTab) tabs.push(developerTab);
return (
<Modal

View File

@@ -29,9 +29,11 @@ Please see LICENSE in the repository root for full details.
import EventEmitter from "events";
import { throttle } from "lodash-es";
import { Logger, logger } from "matrix-js-sdk/src/logger";
import { type Logger, logger } from "matrix-js-sdk/src/logger";
import { randomString } from "matrix-js-sdk/src/randomstring";
import loglevel, { LoggingMethod } from "loglevel";
import { type LoggingMethod } from "loglevel";
import type loglevel from "loglevel";
// the length of log data we keep in indexeddb (and include in the reports)
const MAX_LOG_SIZE = 1024 * 1024 * 5; // 5 MB
@@ -84,6 +86,7 @@ class ConsoleLogger extends EventEmitter {
// run.
// Example line:
// 2017-01-18T11:23:53.214Z W Failed to set badge count
// eslint-disable-next-line @typescript-eslint/no-base-to-string
let line = `${ts} ${level} ${args.join(" ")}\n`;
// Do some cleanup
line = line.replace(/token=[a-zA-Z0-9-]+/gm, "token=xxxxx");

View File

@@ -6,7 +6,7 @@ Please see LICENSE in the repository root for full details.
*/
import { logger } from "matrix-js-sdk/src/logger";
import { BehaviorSubject, Observable } from "rxjs";
import { BehaviorSubject, type Observable } from "rxjs";
import { useObservableEagerState } from "observable-hooks";
import { PosthogAnalytics } from "../analytics/PosthogAnalytics";
@@ -68,13 +68,16 @@ export const useOptInAnalytics = (): [
return PosthogAnalytics.instance.isEnabled() ? setting : [false, null];
};
export const developerSettingsTab = new Setting(
"developer-settings-tab",
false,
);
export const developerMode = new Setting("developer-settings-tab", false);
export const duplicateTiles = new Setting("duplicate-tiles", 0);
export const showNonMemberTiles = new Setting<boolean>(
"show-non-member-tiles",
false,
);
export const debugTileLayout = new Setting("debug-tile-layout", false);
export const audioInput = new Setting<string | undefined>(
"audio-input",
undefined,

View File

@@ -5,20 +5,20 @@ SPDX-License-Identifier: AGPL-3.0-only
Please see LICENSE in the repository root for full details.
*/
import { ComponentProps, useCallback, useEffect, useState } from "react";
import { type ComponentProps, useCallback, useEffect, useState } from "react";
import { logger } from "matrix-js-sdk/src/logger";
import {
ClientEvent,
Crypto,
MatrixClient,
MatrixEvent,
type Crypto,
type MatrixClient,
type MatrixEvent,
} from "matrix-js-sdk/src/matrix";
import { getLogsForReport } from "./rageshake";
import { useClient } from "../ClientContext";
import { Config } from "../config/Config";
import { ElementCallOpenTelemetry } from "../otel/otel";
import { RageshakeRequestModal } from "../room/RageshakeRequestModal";
import { type RageshakeRequestModal } from "../room/RageshakeRequestModal";
const gzip = async (text: string): Promise<Blob> => {
// pako is relatively large (200KB), so we only import it when needed