translation fixes
This commit is contained in:
@@ -204,6 +204,7 @@
|
|||||||
"change_device_button": "Change audio device",
|
"change_device_button": "Change audio device",
|
||||||
"default": "Default",
|
"default": "Default",
|
||||||
"default_named": "Default <2>({{name}})</2>",
|
"default_named": "Default <2>({{name}})</2>",
|
||||||
|
"default_numbered": "Default {{n}}",
|
||||||
"handset": "Handset",
|
"handset": "Handset",
|
||||||
"loudspeaker": "Loudspeaker",
|
"loudspeaker": "Loudspeaker",
|
||||||
"microphone": "Microphone",
|
"microphone": "Microphone",
|
||||||
|
|||||||
@@ -98,7 +98,6 @@ export const Default: Story = {
|
|||||||
selectedVideo: undefined,
|
selectedVideo: undefined,
|
||||||
selectAudioButtonOption: undefined,
|
selectAudioButtonOption: undefined,
|
||||||
selectVideoButtonOption: undefined,
|
selectVideoButtonOption: undefined,
|
||||||
videoToggles: [],
|
|
||||||
},
|
},
|
||||||
parameters: {
|
parameters: {
|
||||||
layout: "fullscreen",
|
layout: "fullscreen",
|
||||||
@@ -135,12 +134,12 @@ export const WithAudioAndVideoOptions: Story = {
|
|||||||
audioEnabled: false,
|
audioEnabled: false,
|
||||||
videoEnabled: true,
|
videoEnabled: true,
|
||||||
audioOptions: [
|
audioOptions: [
|
||||||
{ label: "Microphone 1", id: "1" },
|
{ label: { type: "name", name: "Microphone 1" }, id: "1" },
|
||||||
{ label: "Microphone 2", id: "2" },
|
{ label: { type: "name", name: "Microphone 2" }, id: "2" },
|
||||||
],
|
],
|
||||||
videoOptions: [
|
videoOptions: [
|
||||||
{ label: "Camera 1", id: "1" },
|
{ label: { type: "name", name: "Camera 1" }, id: "1" },
|
||||||
{ label: "Camera 2", id: "2" },
|
{ label: { type: "name", name: "Camera 2" }, id: "2" },
|
||||||
],
|
],
|
||||||
selectedAudio: "2",
|
selectedAudio: "2",
|
||||||
selectedVideo: "1",
|
selectedVideo: "1",
|
||||||
|
|||||||
@@ -123,11 +123,29 @@ describe("createCallFooterViewModel", () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
expect(vm.audioOptions$?.value).toEqual([
|
expect(vm.audioOptions$?.value).toEqual([
|
||||||
{ id: "mic1", label: "Audio Device 1" },
|
{
|
||||||
{ id: "mic2", label: "Microphone 2" },
|
id: "mic1",
|
||||||
|
label: {
|
||||||
|
number: 1,
|
||||||
|
type: "number",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "mic2",
|
||||||
|
label: {
|
||||||
|
name: "Microphone 2",
|
||||||
|
type: "name",
|
||||||
|
},
|
||||||
|
},
|
||||||
]);
|
]);
|
||||||
expect(vm.videoOptions$?.value).toEqual([
|
expect(vm.videoOptions$?.value).toEqual([
|
||||||
{ id: "cam1", label: "Camera 1" },
|
{
|
||||||
|
id: "cam1",
|
||||||
|
label: {
|
||||||
|
name: "Camera 1",
|
||||||
|
type: "name",
|
||||||
|
},
|
||||||
|
},
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -11,7 +11,6 @@ import { supportsBackgroundProcessors } from "@livekit/track-processors";
|
|||||||
import { type CallViewModel } from "../state/CallViewModel/CallViewModel";
|
import { type CallViewModel } from "../state/CallViewModel/CallViewModel";
|
||||||
import { type MenuOptions } from "./MediaMuteAndSwitchButton";
|
import { type MenuOptions } from "./MediaMuteAndSwitchButton";
|
||||||
import { type MediaDevices } from "../state/MediaDevices";
|
import { type MediaDevices } from "../state/MediaDevices";
|
||||||
import { mediaDeviceLabelToString } from "../settings/DeviceSelection";
|
|
||||||
import {
|
import {
|
||||||
backgroundBlur as backgroundBlurSettings,
|
backgroundBlur as backgroundBlurSettings,
|
||||||
debugTileLayout as debugTileLayoutSetting,
|
debugTileLayout as debugTileLayoutSetting,
|
||||||
@@ -77,10 +76,7 @@ function buildDeviceBehaviors(
|
|||||||
map((available) =>
|
map((available) =>
|
||||||
[...available.entries()].map(([id, label]) => ({
|
[...available.entries()].map(([id, label]) => ({
|
||||||
id,
|
id,
|
||||||
label: mediaDeviceLabelToString(
|
|
||||||
label,
|
label,
|
||||||
(n) => "Audio Device " + n,
|
|
||||||
),
|
|
||||||
})),
|
})),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@@ -100,10 +96,7 @@ function buildDeviceBehaviors(
|
|||||||
map((available) =>
|
map((available) =>
|
||||||
[...available.entries()].map(([id, label]) => ({
|
[...available.entries()].map(([id, label]) => ({
|
||||||
id,
|
id,
|
||||||
label: mediaDeviceLabelToString(
|
|
||||||
label,
|
label,
|
||||||
(n) => "Camera " + n,
|
|
||||||
),
|
|
||||||
})),
|
})),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -23,8 +23,8 @@ export const Default: Story = {
|
|||||||
iconsAndLabels: "audio",
|
iconsAndLabels: "audio",
|
||||||
enabled: true,
|
enabled: true,
|
||||||
options: [
|
options: [
|
||||||
{ label: "option 1", id: "1" },
|
{ label: { type: "name", name: "Option 1" }, id: "1" },
|
||||||
{ label: "option 2", id: "2" },
|
{ label: { type: "name", name: "Option 2" }, id: "2" },
|
||||||
],
|
],
|
||||||
selectedOption: "1",
|
selectedOption: "1",
|
||||||
onMuteClick: fn(),
|
onMuteClick: fn(),
|
||||||
@@ -39,16 +39,11 @@ export const AudioMute: Story = {
|
|||||||
iconsAndLabels: "audio",
|
iconsAndLabels: "audio",
|
||||||
enabled: false,
|
enabled: false,
|
||||||
options: [
|
options: [
|
||||||
{ label: "Microphone 1", id: "1" },
|
{ label: { type: "name", name: "Microphone 1" }, id: "1" },
|
||||||
{ label: "Microphone 2", id: "2" },
|
{ label: { type: "name", name: "Microphone 2" }, id: "2" },
|
||||||
],
|
|
||||||
toggles: [
|
|
||||||
{
|
|
||||||
label: "example toggle",
|
|
||||||
id: "t0",
|
|
||||||
enabled: true,
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
|
videoBlurEnabled: true,
|
||||||
|
backgroundBlurToggleClick: fn(),
|
||||||
selectedOption: "2",
|
selectedOption: "2",
|
||||||
},
|
},
|
||||||
play: async ({ args, canvasElement }) => {
|
play: async ({ args, canvasElement }) => {
|
||||||
@@ -67,10 +62,10 @@ export const AudioUnmute: Story = {
|
|||||||
iconsAndLabels: "audio",
|
iconsAndLabels: "audio",
|
||||||
enabled: true,
|
enabled: true,
|
||||||
options: [
|
options: [
|
||||||
{ label: "Microphone 1", id: "1" },
|
{ label: { type: "name", name: "Microphone 1" }, id: "1" },
|
||||||
{ label: "Microphone 2", id: "2" },
|
{ label: { type: "name", name: "Microphone 2" }, id: "2" },
|
||||||
],
|
],
|
||||||
toggles: [],
|
|
||||||
selectedOption: "2",
|
selectedOption: "2",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -81,10 +76,10 @@ export const VideoMute: Story = {
|
|||||||
iconsAndLabels: "video",
|
iconsAndLabels: "video",
|
||||||
enabled: false,
|
enabled: false,
|
||||||
options: [
|
options: [
|
||||||
{ label: "Camera 1", id: "1" },
|
{ label: { type: "name", name: "Camera 1" }, id: "1" },
|
||||||
{ label: "Camera 2", id: "2" },
|
{ label: { type: "name", name: "Camera 2" }, id: "2" },
|
||||||
],
|
],
|
||||||
toggles: [],
|
|
||||||
selectedOption: "1",
|
selectedOption: "1",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
@@ -95,16 +90,11 @@ export const VideoUnmute: Story = {
|
|||||||
iconsAndLabels: "video",
|
iconsAndLabels: "video",
|
||||||
enabled: true,
|
enabled: true,
|
||||||
options: [
|
options: [
|
||||||
{ label: "Camera 1", id: "1" },
|
{ label: { type: "name", name: "Camera 1" }, id: "1" },
|
||||||
{ label: "Camera 2", id: "2" },
|
{ label: { type: "name", name: "Camera 2" }, id: "2" },
|
||||||
],
|
|
||||||
toggles: [
|
|
||||||
{
|
|
||||||
label: "Background blur",
|
|
||||||
id: "background_blurring",
|
|
||||||
enabled: false,
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
|
videoBlurEnabled: true,
|
||||||
|
backgroundBlurToggleClick: fn(),
|
||||||
selectedOption: "2",
|
selectedOption: "2",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -86,8 +86,8 @@ describe("MediaMuteAndSwitchButton", () => {
|
|||||||
iconsAndLabels="audio"
|
iconsAndLabels="audio"
|
||||||
enabled={true}
|
enabled={true}
|
||||||
options={[
|
options={[
|
||||||
{ label: "Microphone 1", id: "mic1" },
|
{ label: { type: "name", name: "Microphone 1" }, id: "mic1" },
|
||||||
{ label: "Microphone 2", id: "mic2" },
|
{ label: { type: "name", name: "Microphone 2" }, id: "mic2" },
|
||||||
]}
|
]}
|
||||||
selectedOption="mic1"
|
selectedOption="mic1"
|
||||||
onSelect={onSelect}
|
onSelect={onSelect}
|
||||||
@@ -110,8 +110,8 @@ describe("MediaMuteAndSwitchButton", () => {
|
|||||||
iconsAndLabels="audio"
|
iconsAndLabels="audio"
|
||||||
enabled={true}
|
enabled={true}
|
||||||
options={[
|
options={[
|
||||||
{ label: "Microphone 1", id: "mic1" },
|
{ label: { type: "name", name: "Microphone 1" }, id: "mic1" },
|
||||||
{ label: "Microphone 2", id: "mic2" },
|
{ label: { type: "name", name: "Microphone 2" }, id: "mic2" },
|
||||||
]}
|
]}
|
||||||
selectedOption="mic1"
|
selectedOption="mic1"
|
||||||
onSelect={onSelect}
|
onSelect={onSelect}
|
||||||
@@ -139,8 +139,8 @@ describe("MediaMuteAndSwitchButton", () => {
|
|||||||
iconsAndLabels="audio"
|
iconsAndLabels="audio"
|
||||||
enabled={true}
|
enabled={true}
|
||||||
options={[
|
options={[
|
||||||
{ label: "Microphone 1", id: "mic1" },
|
{ label: { type: "name", name: "Microphone 1" }, id: "mic1" },
|
||||||
{ label: "Microphone 2", id: "mic2" },
|
{ label: { type: "name", name: "Microphone 2" }, id: "mic2" },
|
||||||
]}
|
]}
|
||||||
selectedOption={selectedOption}
|
selectedOption={selectedOption}
|
||||||
onSelect={(id) => {
|
onSelect={(id) => {
|
||||||
@@ -222,8 +222,8 @@ describe("MediaMuteAndSwitchButton", () => {
|
|||||||
iconsAndLabels="audio"
|
iconsAndLabels="audio"
|
||||||
enabled={true}
|
enabled={true}
|
||||||
options={[
|
options={[
|
||||||
{ label: "Microphone 1", id: "mic1" },
|
{ label: { type: "name", name: "Microphone 1" }, id: "mic1" },
|
||||||
{ label: "Microphone 2", id: "mic2" },
|
{ label: { type: "name", name: "Microphone 2" }, id: "mic2" },
|
||||||
]}
|
]}
|
||||||
selectedOption="mic2"
|
selectedOption="mic2"
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ import {
|
|||||||
MenuItem,
|
MenuItem,
|
||||||
ToggleMenuItem,
|
ToggleMenuItem,
|
||||||
} from "@vector-im/compound-web";
|
} from "@vector-im/compound-web";
|
||||||
import { t } from "i18next";
|
|
||||||
import {
|
import {
|
||||||
CheckIcon,
|
CheckIcon,
|
||||||
ChevronUpIcon,
|
ChevronUpIcon,
|
||||||
@@ -22,12 +21,14 @@ import {
|
|||||||
VideoCallIcon,
|
VideoCallIcon,
|
||||||
} from "@vector-im/compound-design-tokens/assets/web/icons";
|
} from "@vector-im/compound-design-tokens/assets/web/icons";
|
||||||
import classNames from "classnames";
|
import classNames from "classnames";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
import styles from "./MediaMuteAndSwitchButton.module.css";
|
import styles from "./MediaMuteAndSwitchButton.module.css";
|
||||||
import { MicButton, VideoButton } from "../button";
|
import { MicButton, VideoButton } from "../button";
|
||||||
|
import { type DeviceLabel } from "../state/MediaDevices";
|
||||||
|
|
||||||
export interface MenuOptions {
|
export interface MenuOptions {
|
||||||
label: string;
|
label: DeviceLabel;
|
||||||
id: string;
|
id: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -67,7 +68,7 @@ export const MediaMuteAndSwitchButton: FC<MediaMuteAndSwitchButtonProps> = ({
|
|||||||
}) => {
|
}) => {
|
||||||
const [plannedSelection, setPlannedSelection] = useState<string | null>(null);
|
const [plannedSelection, setPlannedSelection] = useState<string | null>(null);
|
||||||
const [menuOpen, setMenuOpen] = useState(false);
|
const [menuOpen, setMenuOpen] = useState(false);
|
||||||
|
const { t } = useTranslation();
|
||||||
let button;
|
let button;
|
||||||
let toggles =
|
let toggles =
|
||||||
backgroundBlurToggleClick === undefined
|
backgroundBlurToggleClick === undefined
|
||||||
@@ -152,10 +153,21 @@ export const MediaMuteAndSwitchButton: FC<MediaMuteAndSwitchButtonProps> = ({
|
|||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
{options?.map((option) => (
|
{options?.map(({ label, id }) => {
|
||||||
|
const labelText = ((): string => {
|
||||||
|
switch (label.type) {
|
||||||
|
case "name":
|
||||||
|
return label.name;
|
||||||
|
case "number":
|
||||||
|
return t("settings.devices.default_numbered", {
|
||||||
|
n: label.number,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
return (
|
||||||
<MenuItem
|
<MenuItem
|
||||||
hideChevron
|
hideChevron
|
||||||
label={option.label}
|
label={labelText}
|
||||||
Icon={
|
Icon={
|
||||||
IconOptions && (
|
IconOptions && (
|
||||||
<IconOptions
|
<IconOptions
|
||||||
@@ -167,20 +179,19 @@ export const MediaMuteAndSwitchButton: FC<MediaMuteAndSwitchButtonProps> = ({
|
|||||||
}
|
}
|
||||||
onSelect={(e) => {
|
onSelect={(e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
if (option.id === selectedOption) return;
|
if (id === selectedOption) return;
|
||||||
setPlannedSelection(option.id);
|
setPlannedSelection(id);
|
||||||
onSelect?.(option.id);
|
onSelect?.(id);
|
||||||
}}
|
}}
|
||||||
key={option.id}
|
key={id}
|
||||||
>
|
>
|
||||||
{selectedOption === option.id && (
|
{selectedOption === id && <CheckIcon width={24} height={24} />}
|
||||||
<CheckIcon width={24} height={24} />
|
{selectedOption !== id && plannedSelection === id && (
|
||||||
)}
|
|
||||||
{selectedOption !== option.id && plannedSelection === option.id && (
|
|
||||||
<SpinnerIcon width={24} height={24} className={styles.rotate} />
|
<SpinnerIcon width={24} height={24} className={styles.rotate} />
|
||||||
)}
|
)}
|
||||||
</MenuItem>
|
</MenuItem>
|
||||||
))}
|
);
|
||||||
|
})}
|
||||||
{(toggles?.length ?? 0) > 0 && <hr />}
|
{(toggles?.length ?? 0) > 0 && <hr />}
|
||||||
{toggles?.map((toggle) => (
|
{toggles?.map((toggle) => (
|
||||||
<ToggleMenuItem
|
<ToggleMenuItem
|
||||||
|
|||||||
@@ -94,7 +94,7 @@ const logger = rootLogger.getChild("[InCallView]");
|
|||||||
|
|
||||||
export interface ActiveCallProps extends Omit<
|
export interface ActiveCallProps extends Omit<
|
||||||
InCallViewProps,
|
InCallViewProps,
|
||||||
"vm" | "livekitRoom" | "connState"
|
"vm" | "livekitRoom" | "connState" | "footerVm"
|
||||||
> {
|
> {
|
||||||
e2eeSystem: EncryptionSystem;
|
e2eeSystem: EncryptionSystem;
|
||||||
// TODO refactor those reasons into an enum
|
// TODO refactor those reasons into an enum
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import {
|
|||||||
Separator,
|
Separator,
|
||||||
} from "@vector-im/compound-web";
|
} from "@vector-im/compound-web";
|
||||||
import { useObservableEagerState } from "observable-hooks";
|
import { useObservableEagerState } from "observable-hooks";
|
||||||
import { t } from "i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
type AudioOutputDeviceLabel,
|
type AudioOutputDeviceLabel,
|
||||||
@@ -30,42 +30,11 @@ interface Props {
|
|||||||
numberedLabel: (number: number) => string;
|
numberedLabel: (number: number) => string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function mediaDeviceLabelToString(
|
export const DeviceSelection: FC<Props> = ({ device, title }) => {
|
||||||
label: DeviceLabel | AudioOutputDeviceLabel,
|
|
||||||
numberedLabel: (number: number) => string,
|
|
||||||
): string {
|
|
||||||
let labelText = "";
|
|
||||||
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")
|
|
||||||
: t("settings.devices.default_named", { name: label.name });
|
|
||||||
break;
|
|
||||||
case "speaker":
|
|
||||||
labelText = t("settings.devices.loudspeaker");
|
|
||||||
break;
|
|
||||||
case "earpiece":
|
|
||||||
labelText = t("settings.devices.handset");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return labelText;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const DeviceSelection: FC<Props> = ({
|
|
||||||
device,
|
|
||||||
title,
|
|
||||||
numberedLabel,
|
|
||||||
}) => {
|
|
||||||
const groupId = useId();
|
const groupId = useId();
|
||||||
const available = useObservableEagerState(device.available$);
|
const available = useObservableEagerState(device.available$);
|
||||||
const selectedId = useObservableEagerState(device.selected$)?.id;
|
const selectedId = useObservableEagerState(device.selected$)?.id;
|
||||||
|
const { t } = useTranslation();
|
||||||
const onChange = useCallback(
|
const onChange = useCallback(
|
||||||
(e: ChangeEvent<HTMLInputElement>) => {
|
(e: ChangeEvent<HTMLInputElement>) => {
|
||||||
device.select(e.target.value);
|
device.select(e.target.value);
|
||||||
@@ -90,7 +59,24 @@ export const DeviceSelection: FC<Props> = ({
|
|||||||
<Separator className={styles.separator} />
|
<Separator className={styles.separator} />
|
||||||
<div className={styles.options}>
|
<div className={styles.options}>
|
||||||
{[...available].map(([id, label]) => {
|
{[...available].map(([id, label]) => {
|
||||||
const labelText = mediaDeviceLabelToString(label, numberedLabel);
|
const labelText = ((): string => {
|
||||||
|
switch (label.type) {
|
||||||
|
case "name":
|
||||||
|
return label.name;
|
||||||
|
case "number":
|
||||||
|
return t("settings.devices.default_numbered", {
|
||||||
|
n: label.number,
|
||||||
|
});
|
||||||
|
case "default":
|
||||||
|
return label.name === null
|
||||||
|
? t("settings.devices.default")
|
||||||
|
: t("settings.devices.default_named", label.name);
|
||||||
|
case "speaker":
|
||||||
|
return t("settings.devices.loudspeaker");
|
||||||
|
case "earpiece":
|
||||||
|
return t("settings.devices.handset");
|
||||||
|
}
|
||||||
|
})();
|
||||||
return (
|
return (
|
||||||
<InlineField
|
<InlineField
|
||||||
key={id}
|
key={id}
|
||||||
|
|||||||
Reference in New Issue
Block a user