mirror of
https://github.com/vector-im/element-call.git
synced 2026-03-25 06:40:26 +00:00
Merge branch 'livekit' into toger5/track-processor-blur
This commit is contained in:
@@ -13,6 +13,7 @@ import {
|
||||
useSetting,
|
||||
duplicateTiles as duplicateTilesSetting,
|
||||
debugTileLayout as debugTileLayoutSetting,
|
||||
showNonMemberTiles as showNonMemberTilesSetting,
|
||||
} from "./settings";
|
||||
import type { MatrixClient } from "matrix-js-sdk/src/client";
|
||||
|
||||
@@ -26,6 +27,9 @@ export const DeveloperSettingsTab: FC<Props> = ({ client }) => {
|
||||
const [debugTileLayout, setDebugTileLayout] = useSetting(
|
||||
debugTileLayoutSetting,
|
||||
);
|
||||
const [showNonMemberTiles, setShowNonMemberTiles] = useSetting(
|
||||
showNonMemberTilesSetting,
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -85,6 +89,20 @@ export const DeveloperSettingsTab: FC<Props> = ({ client }) => {
|
||||
}
|
||||
/>
|
||||
</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>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -16,3 +16,7 @@
|
||||
flex-direction: column;
|
||||
gap: var(--cpd-space-4x);
|
||||
}
|
||||
|
||||
.secondary {
|
||||
color: var(--cpd-color-text-secondary);
|
||||
}
|
||||
|
||||
@@ -5,7 +5,14 @@ SPDX-License-Identifier: AGPL-3.0-only
|
||||
Please see LICENSE in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { type ChangeEvent, type FC, useCallback, useId } from "react";
|
||||
import {
|
||||
type ChangeEvent,
|
||||
type FC,
|
||||
type ReactElement,
|
||||
type ReactNode,
|
||||
useCallback,
|
||||
useId,
|
||||
} from "react";
|
||||
import {
|
||||
Heading,
|
||||
InlineField,
|
||||
@@ -13,16 +20,23 @@ import {
|
||||
RadioControl,
|
||||
Separator,
|
||||
} from "@vector-im/compound-web";
|
||||
import { Trans, useTranslation } from "react-i18next";
|
||||
|
||||
import { type MediaDevice } from "../livekit/MediaDevicesContext";
|
||||
import styles from "./DeviceSelection.module.css";
|
||||
|
||||
interface Props {
|
||||
devices: MediaDevice;
|
||||
caption: string;
|
||||
title: string;
|
||||
numberedLabel: (number: number) => string;
|
||||
}
|
||||
|
||||
export const DeviceSelection: FC<Props> = ({ devices, caption }) => {
|
||||
export const DeviceSelection: FC<Props> = ({
|
||||
devices,
|
||||
title,
|
||||
numberedLabel,
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const groupId = useId();
|
||||
const onChange = useCallback(
|
||||
(e: ChangeEvent<HTMLInputElement>) => {
|
||||
@@ -31,7 +45,7 @@ export const DeviceSelection: FC<Props> = ({ devices, caption }) => {
|
||||
[devices],
|
||||
);
|
||||
|
||||
if (devices.available.length == 0) return null;
|
||||
if (devices.available.size == 0) return null;
|
||||
|
||||
return (
|
||||
<div className={styles.selection}>
|
||||
@@ -42,29 +56,53 @@ export const DeviceSelection: FC<Props> = ({ devices, caption }) => {
|
||||
as="h4"
|
||||
className={styles.title}
|
||||
>
|
||||
{caption}
|
||||
{title}
|
||||
</Heading>
|
||||
<Separator className={styles.separator} />
|
||||
<div className={styles.options}>
|
||||
{devices.available.map(({ deviceId, label }, index) => (
|
||||
<InlineField
|
||||
key={deviceId}
|
||||
name={groupId}
|
||||
control={
|
||||
<RadioControl
|
||||
checked={deviceId === devices.selectedId}
|
||||
onChange={onChange}
|
||||
value={deviceId}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<Label>
|
||||
{!!label && label.trim().length > 0
|
||||
? label
|
||||
: `${caption} ${index + 1}`}
|
||||
</Label>
|
||||
</InlineField>
|
||||
))}
|
||||
{[...devices.available].map(([id, label]) => {
|
||||
let labelText: ReactNode;
|
||||
switch (label.type) {
|
||||
case "name":
|
||||
labelText = label.name;
|
||||
break;
|
||||
case "number":
|
||||
labelText = numberedLabel(label.number);
|
||||
break;
|
||||
case "default":
|
||||
labelText =
|
||||
label.name === null ? (
|
||||
t("settings.devices.default")
|
||||
) : (
|
||||
<Trans
|
||||
i18nKey="settings.devices.default_named"
|
||||
name={label.name}
|
||||
>
|
||||
Default{" "}
|
||||
<span className={styles.secondary}>
|
||||
({{ name: label.name } as unknown as ReactElement})
|
||||
</span>
|
||||
</Trans>
|
||||
);
|
||||
break;
|
||||
}
|
||||
|
||||
return (
|
||||
<InlineField
|
||||
key={id}
|
||||
name={groupId}
|
||||
control={
|
||||
<RadioControl
|
||||
checked={id === devices.selectedId}
|
||||
onChange={onChange}
|
||||
value={id}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<Label>{labelText}</Label>
|
||||
</InlineField>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -41,13 +41,13 @@ export const PreferencesSettingsTab: FC = () => {
|
||||
|
||||
return (
|
||||
<div>
|
||||
<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}
|
||||
|
||||
@@ -26,7 +26,6 @@ import {
|
||||
backgroundBlur as backgroundBlurSetting,
|
||||
developerMode,
|
||||
} from "./settings";
|
||||
import { isFirefox } from "../Platform";
|
||||
import { PreferencesSettingsTab } from "./PreferencesSettingsTab";
|
||||
import { Slider } from "../Slider";
|
||||
import { DeviceSelection } from "./DeviceSelection";
|
||||
@@ -107,14 +106,16 @@ export const SettingsModal: FC<Props> = ({
|
||||
<Form>
|
||||
<DeviceSelection
|
||||
devices={devices.audioInput}
|
||||
caption={t("common.microphone")}
|
||||
title={t("settings.devices.microphone")}
|
||||
numberedLabel={(n) =>
|
||||
t("settings.devices.microphone_numbered", { n })
|
||||
}
|
||||
/>
|
||||
<DeviceSelection
|
||||
devices={devices.audioOutput}
|
||||
title={t("settings.devices.speaker")}
|
||||
numberedLabel={(n) => t("settings.devices.speaker_numbered", { n })}
|
||||
/>
|
||||
{!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>
|
||||
@@ -141,7 +142,8 @@ export const SettingsModal: FC<Props> = ({
|
||||
<Form>
|
||||
<DeviceSelection
|
||||
devices={devices.videoInput}
|
||||
caption={t("common.camera")}
|
||||
title={t("settings.devices.camera")}
|
||||
numberedLabel={(n) => t("settings.devices.camera_numbered", { n })}
|
||||
/>
|
||||
</Form>
|
||||
<Separator />
|
||||
|
||||
@@ -31,17 +31,17 @@ export class Setting<T> {
|
||||
}
|
||||
}
|
||||
|
||||
this._value = new BehaviorSubject(initialValue);
|
||||
this.value = this._value;
|
||||
this._value$ = new BehaviorSubject(initialValue);
|
||||
this.value$ = this._value$;
|
||||
}
|
||||
|
||||
private readonly key: string;
|
||||
|
||||
private readonly _value: BehaviorSubject<T>;
|
||||
public readonly value: Observable<T>;
|
||||
private readonly _value$: BehaviorSubject<T>;
|
||||
public readonly value$: Observable<T>;
|
||||
|
||||
public readonly setValue = (value: T): void => {
|
||||
this._value.next(value);
|
||||
this._value$.next(value);
|
||||
localStorage.setItem(this.key, JSON.stringify(value));
|
||||
};
|
||||
}
|
||||
@@ -50,7 +50,7 @@ export class Setting<T> {
|
||||
* React hook that returns a settings's current value and a setter.
|
||||
*/
|
||||
export function useSetting<T>(setting: Setting<T>): [T, (value: T) => void] {
|
||||
return [useObservableEagerState(setting.value), setting.setValue];
|
||||
return [useObservableEagerState(setting.value$), setting.setValue];
|
||||
}
|
||||
|
||||
// null = undecided
|
||||
@@ -72,6 +72,10 @@ 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>(
|
||||
|
||||
Reference in New Issue
Block a user