Merge branch 'livekit' into firefox-audio-output
This commit is contained in:
108
src/settings/DeveloperSettingsTab.tsx
Normal file
108
src/settings/DeveloperSettingsTab.tsx
Normal 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>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@@ -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 {
|
||||
|
||||
@@ -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}>
|
||||
|
||||
@@ -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>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -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";
|
||||
|
||||
|
||||
@@ -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";
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user