Files
element-call/src/settings/SettingsModal.tsx
T

320 lines
8.8 KiB
TypeScript
Raw Normal View History

2022-05-04 17:09:48 +01:00
/*
Copyright 2022-2024 New Vector Ltd.
2022-05-04 17:09:48 +01:00
SPDX-License-Identifier: AGPL-3.0-only
Please see LICENSE in the repository root for full details.
2022-05-04 17:09:48 +01:00
*/
2024-11-20 17:22:24 +01:00
import { ChangeEvent, FC, ReactNode, useCallback, useState } from "react";
import { Trans, useTranslation } from "react-i18next";
2024-08-29 17:37:52 +01:00
import { MatrixClient } from "matrix-js-sdk/src/matrix";
2024-11-20 17:22:24 +01:00
import {
Root as Form,
Separator,
Text,
Tooltip,
} from "@vector-im/compound-web";
2024-11-20 17:31:40 +01:00
import { BackgroundBlur } from "@livekit/track-processors";
import { logger } from "matrix-js-sdk/src/logger";
2022-06-06 22:42:48 +02:00
2022-01-05 16:54:13 -08:00
import { Modal } from "../Modal";
2021-12-06 17:34:10 -08:00
import styles from "./SettingsModal.module.css";
import { Tab, TabContainer } from "../tabs/Tabs";
2022-02-04 16:55:57 -08:00
import { FieldRow, InputField } from "../input/Input";
import { AnalyticsNotice } from "../analytics/AnalyticsNotice";
2023-05-05 11:44:35 +02:00
import { ProfileSettingsTab } from "./ProfileSettingsTab";
import { FeedbackSettingsTab } from "./FeedbackSettingsTab";
2023-08-02 15:29:37 -04:00
import {
useMediaDevices,
useMediaDeviceNames,
} from "../livekit/MediaDevicesContext";
2023-09-18 20:47:47 -04:00
import { widget } from "../widget";
2024-05-08 15:29:39 -04:00
import {
useSetting,
developerSettingsTab as developerSettingsTabSetting,
2024-05-08 16:00:42 -04:00
duplicateTiles as duplicateTilesSetting,
2024-11-20 17:22:24 +01:00
backgroundBlur as backgroundBlurSetting,
useOptInAnalytics,
2024-11-07 16:42:43 +00:00
soundEffectVolumeSetting,
2024-05-08 15:29:39 -04:00
} from "./settings";
2024-07-12 14:15:27 -04:00
import { isFirefox } from "../Platform";
2024-11-04 08:54:13 -01:00
import { PreferencesSettingsTab } from "./PreferencesSettingsTab";
2024-11-07 16:35:37 +00:00
import { Slider } from "../Slider";
2024-11-19 17:18:36 -05:00
import { DeviceSelection } from "./DeviceSelection";
2021-12-06 17:34:10 -08:00
2023-12-01 17:43:09 -05:00
type SettingsTab =
| "audio"
| "video"
| "profile"
2024-11-04 08:54:13 -01:00
| "preferences"
2023-12-01 17:43:09 -05:00
| "feedback"
| "more"
| "developer";
2022-06-06 22:42:48 +02:00
interface Props {
2023-09-17 14:35:35 -04:00
open: boolean;
onDismiss: () => void;
2023-12-01 17:43:09 -05:00
tab: SettingsTab;
onTabChange: (tab: SettingsTab) => void;
2023-05-05 11:44:35 +02:00
client: MatrixClient;
roomId?: string;
2022-06-06 22:42:48 +02:00
}
2023-12-01 17:43:09 -05:00
export const defaultSettingsTab: SettingsTab = "audio";
export const SettingsModal: FC<Props> = ({
open,
onDismiss,
tab,
onTabChange,
client,
roomId,
}) => {
2022-10-10 09:19:10 -04:00
const { t } = useTranslation();
2022-06-06 22:42:48 +02:00
const [optInAnalytics, setOptInAnalytics] = useOptInAnalytics();
2024-05-08 15:29:39 -04:00
const [developerSettingsTab, setDeveloperSettingsTab] = useSetting(
developerSettingsTabSetting,
);
2024-05-08 16:00:42 -04:00
const [duplicateTiles, setDuplicateTiles] = useSetting(duplicateTilesSetting);
2021-12-06 17:34:10 -08:00
2024-11-20 17:22:24 +01:00
// Generate a `Checkbox` input to turn blur on or off.
const BlurCheckbox: React.FC = (): ReactNode => {
const [blur, setBlur] = useSetting(backgroundBlurSetting);
2024-11-20 17:31:40 +01:00
let canBlur = true;
try {
// eslint-disable-next-line new-cap
BackgroundBlur(15);
} catch (e) {
logger.debug(
"Cannot blur, so we do not show the option in settings. error: ",
e,
);
canBlur = false;
}
return canBlur ? (
2024-11-20 17:22:24 +01:00
<>
<h4>{t("settings.background_blur_header")}</h4>
<FieldRow>
<Tooltip
label={
isFirefox() ? t("settings.blur_not_supported_by_browser") : ""
}
>
<InputField
id="activateBackgroundBlur"
label={t("settings.background_blur_label")}
description={t("settings.video_tab_activate_background_blur")}
type="checkbox"
checked={blur}
onChange={(b): void => setBlur(b.target.checked)}
disabled={isFirefox()}
/>
</Tooltip>
</FieldRow>
</>
2024-11-20 17:31:40 +01:00
) : null;
2024-11-20 17:22:24 +01:00
};
const optInDescription = (
<Text size="sm">
2023-11-20 13:00:43 +00:00
<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>
);
2023-08-02 15:29:37 -04:00
const devices = useMediaDevices();
2023-12-01 17:43:09 -05:00
useMediaDeviceNames(devices, open);
2024-11-07 16:42:43 +00:00
const [soundVolume, setSoundVolume] = useSetting(soundEffectVolumeSetting);
const [soundVolumeRaw, setSoundVolumeRaw] = useState(soundVolume);
2024-11-07 16:35:37 +00:00
const audioTab: Tab<SettingsTab> = {
key: "audio",
name: t("common.audio"),
content: (
<>
2024-11-19 17:18:36 -05:00
<Form>
<DeviceSelection
devices={devices.audioInput}
caption={t("common.microphone")}
2024-11-07 16:35:37 +00:00
/>
2024-11-19 17:18:36 -05:00
{!isFirefox() && (
<DeviceSelection
devices={devices.audioOutput}
caption={t("settings.speaker_device_selection_label")}
/>
)}
<div className={styles.volumeSlider}>
<label>{t("settings.audio_tab.effect_volume_label")}</label>
<p>{t("settings.audio_tab.effect_volume_description")}</p>
<Slider
label={t("video_tile.volume")}
value={soundVolumeRaw}
onValueChange={setSoundVolumeRaw}
onValueCommit={setSoundVolume}
2024-11-19 17:18:36 -05:00
min={0}
max={1}
step={0.01}
/>
</div>
</Form>
</>
),
};
const videoTab: Tab<SettingsTab> = {
key: "video",
name: t("common.video"),
2024-11-19 17:18:36 -05:00
content: (
2024-11-20 17:22:24 +01:00
<>
<Form>
<DeviceSelection
devices={devices.videoInput}
caption={t("common.camera")}
/>
</Form>
<Separator />
<BlurCheckbox />
</>
2024-11-19 17:18:36 -05:00
),
};
2024-11-04 08:54:13 -01:00
const preferencesTab: Tab<SettingsTab> = {
key: "preferences",
name: t("common.preferences"),
content: <PreferencesSettingsTab />,
};
const profileTab: Tab<SettingsTab> = {
key: "profile",
name: t("common.profile"),
content: <ProfileSettingsTab client={client} />,
};
const feedbackTab: Tab<SettingsTab> = {
key: "feedback",
name: t("settings.feedback_tab_title"),
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", {
2024-08-30 15:29:14 +02:00
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>
</>
),
};
2023-06-30 16:43:28 +01:00
const developerTab: Tab<SettingsTab> = {
key: "developer",
name: t("settings.developer_tab_title"),
content: (
<>
2024-12-02 15:43:19 +00:00
<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>
2024-12-02 15:43:19 +00:00
{t("developer_mode.crypto_version", {
version: client.getCrypto()?.getVersion() || "unknown",
})}
</p>
<p>
2024-12-02 15:43:19 +00:00
{t("developer_mode.matrix_id", {
id: client.getUserId() || "unknown",
})}
</p>
<p>
2024-12-02 15:43:19 +00:00
{t("developer_mode.device_id", {
id: client.getDeviceId() || "unknown",
})}
</p>
<FieldRow>
<InputField
id="duplicateTiles"
type="number"
2024-12-02 15:43:19 +00:00
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>
</>
),
};
2023-06-30 16:43:28 +01:00
2023-08-02 15:29:37 -04:00
const tabs = [audioTab, videoTab];
2023-09-18 20:47:47 -04:00
if (widget === null) tabs.push(profileTab);
2024-11-04 08:54:13 -01:00
tabs.push(preferencesTab, feedbackTab, moreTab);
2023-07-21 00:00:04 -04:00
if (developerSettingsTab) tabs.push(developerTab);
2023-06-30 16:43:28 +01:00
2021-12-06 17:34:10 -08:00
return (
<Modal
title={t("common.settings")}
2021-12-06 17:34:10 -08:00
className={styles.settingsModal}
2023-12-01 17:43:09 -05:00
open={open}
onDismiss={onDismiss}
tabbed
2021-12-06 17:34:10 -08:00
>
2023-05-05 11:44:35 +02:00
<TabContainer
label={t("common.settings")}
tab={tab}
onTabChange={onTabChange}
tabs={tabs}
/>
2021-12-06 17:34:10 -08:00
</Modal>
);
2022-05-31 10:43:05 -04:00
};