From 7994d095900e8bc2063372e273a595fa29e20015 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Tue, 11 Jul 2023 12:04:01 +0200 Subject: [PATCH 1/2] Add user-media volume control MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/video-grid/VideoTile.tsx | 28 +++- .../VideoTileSettingsModal.module.css | 120 ++++++++++++++++++ src/video-grid/VideoTileSettingsModal.tsx | 85 +++++++++++++ 3 files changed, 230 insertions(+), 3 deletions(-) create mode 100644 src/video-grid/VideoTileSettingsModal.module.css create mode 100644 src/video-grid/VideoTileSettingsModal.tsx diff --git a/src/video-grid/VideoTile.tsx b/src/video-grid/VideoTile.tsx index 1ea4b296a..e736896e0 100644 --- a/src/video-grid/VideoTile.tsx +++ b/src/video-grid/VideoTile.tsx @@ -34,7 +34,9 @@ import styles from "./VideoTile.module.css"; import { ReactComponent as MicIcon } from "../icons/Mic.svg"; import { ReactComponent as MicMutedIcon } from "../icons/MicMuted.svg"; import { useReactiveState } from "../useReactiveState"; -import { FullscreenButton } from "../button/Button"; +import { AudioButton, FullscreenButton } from "../button/Button"; +import { useModalTriggerState } from "../Modal"; +import { VideoTileSettingsModal } from "./VideoTileSettingsModal"; export interface ItemData { id: string; @@ -111,10 +113,14 @@ export const VideoTile = forwardRef( onToggleFullscreen(data.id); }, [data, onToggleFullscreen]); + const { + modalState: videoTileSettingsModalState, + modalProps: videoTileSettingsModalProps, + } = useModalTriggerState(); + const onOptionsPress = videoTileSettingsModalState.open; + const toolbarButtons: JSX.Element[] = []; if (!sfuParticipant.isLocal) { - // TODO local volume option, which would also go here - if (content === TileContent.ScreenShare) { toolbarButtons.push( ( onPress={onFullscreen} /> ); + } else { + // Due to the LK SDK this sadly only works for user-media atm + toolbarButtons.push( + + ); } } @@ -182,6 +198,12 @@ export const VideoTile = forwardRef( : Track.Source.ScreenShare } /> + {videoTileSettingsModalState.isOpen && !maximised && ( + + )} ); } diff --git a/src/video-grid/VideoTileSettingsModal.module.css b/src/video-grid/VideoTileSettingsModal.module.css new file mode 100644 index 000000000..29892f37e --- /dev/null +++ b/src/video-grid/VideoTileSettingsModal.module.css @@ -0,0 +1,120 @@ +/* +Copyright 2022 - 2023 New Vector Ltd + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +.videoTileSettingsModal { + width: 700px; + height: 316px; + display: flex; +} + +.content { + position: relative; + margin: 27px 34px; + height: 100%; + display: flex; + flex-direction: column; + justify-content: center; +} + +.localVolumePercentage { + width: 3ch; +} + +.localVolumeSlider[type="range"] { + -ms-appearance: none; + -moz-appearance: none; + -webkit-appearance: none; + appearance: none; + + background-color: transparent; + --slider-color: var(--quinary-content); + --slider-height: 4px; + --thumb-color: var(--accent); + --thumb-radius: 100%; + --thumb-size: 16px; + --thumb-margin-top: -6px; + + cursor: pointer; + width: 100%; +} + +.localVolumeSlider[type="range"]::-moz-range-track { + -moz-appearance: none; + appearance: none; + + background-color: var(--slider-color); + height: var(--slider-height); +} +.localVolumeSlider[type="range"]::-ms-track { + -ms-appearance: none; + appearance: none; + + background-color: var(--slider-color); + height: var(--slider-height); +} +.localVolumeSlider[type="range"]::-webkit-slider-runnable-track { + -webkit-appearance: none; + appearance: none; + + background-color: var(--slider-color); + height: var(--slider-height); +} + +.localVolumeSlider[type="range"]::-moz-range-thumb { + -moz-appearance: none; + appearance: none; + + height: var(--thumb-size); + width: var(--thumb-size); + margin-top: var(--thumb-margin-top); + border-radius: var(--thumb-radius); + background: var(--thumb-color); +} +.localVolumeSlider[type="range"]::-ms-thumb { + -ms-appearance: none; + appearance: none; + + height: var(--thumb-size); + width: var(--thumb-size); + margin-top: var(--thumb-margin-top); + border-radius: var(--thumb-radius); + background: var(--thumb-color); +} +.localVolumeSlider[type="range"]::-webkit-slider-thumb { + -webkit-appearance: none; + appearance: none; + + height: var(--thumb-size); + width: var(--thumb-size); + margin-top: var(--thumb-margin-top); + border-radius: var(--thumb-radius); + background: var(--thumb-color); +} + +.localVolumeSlider[type="range"]::-moz-range-progress { + -moz-appearance: none; + appearance: none; + + height: var(--slider-height); + background: var(--thumb-color); +} +.localVolumeSlider[type="range"]::-ms-fill-lower { + -moz-appearance: none; + appearance: none; + + height: var(--slider-height); + background: var(--thumb-color); +} diff --git a/src/video-grid/VideoTileSettingsModal.tsx b/src/video-grid/VideoTileSettingsModal.tsx new file mode 100644 index 000000000..e7e967e04 --- /dev/null +++ b/src/video-grid/VideoTileSettingsModal.tsx @@ -0,0 +1,85 @@ +/* +Copyright 2022 - 2023 New Vector Ltd + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +import React, { ChangeEvent, useState } from "react"; +import { useTranslation } from "react-i18next"; + +import { FieldRow } from "../input/Input"; +import { Modal } from "../Modal"; +import styles from "./VideoTileSettingsModal.module.css"; +import { VolumeIcon } from "../button/VolumeIcon"; +import { ItemData } from "./VideoTile"; +import { RemoteParticipant } from "livekit-client"; + +interface LocalVolumeProps { + participant: RemoteParticipant; +} + +const LocalVolume: React.FC = ({ + participant, +}: LocalVolumeProps) => { + const [localVolume, setLocalVolume] = useState( + participant.getVolume() + ); + + const onLocalVolumeChanged = (event: ChangeEvent) => { + const value: number = +event.target.value; + setLocalVolume(value); + participant.setVolume(value); + }; + + return ( + <> + + + + + + ); +}; + +// TODO: Extend ModalProps +interface Props { + data: ItemData; + onClose: () => void; +} + +export const VideoTileSettingsModal = ({ data, onClose, ...rest }: Props) => { + const { t } = useTranslation(); + + return ( + +
+ {} +
+
+ ); +}; From 08aebf05e96aff6450bf3c477701c7ba7bf8469e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C5=A0imon=20Brandner?= Date: Tue, 11 Jul 2023 12:08:06 +0200 Subject: [PATCH 2/2] Delint MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Šimon Brandner --- src/video-grid/VideoTileSettingsModal.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/video-grid/VideoTileSettingsModal.tsx b/src/video-grid/VideoTileSettingsModal.tsx index e7e967e04..eed2ccf40 100644 --- a/src/video-grid/VideoTileSettingsModal.tsx +++ b/src/video-grid/VideoTileSettingsModal.tsx @@ -16,13 +16,13 @@ limitations under the License. import React, { ChangeEvent, useState } from "react"; import { useTranslation } from "react-i18next"; +import { RemoteParticipant } from "livekit-client"; import { FieldRow } from "../input/Input"; import { Modal } from "../Modal"; import styles from "./VideoTileSettingsModal.module.css"; import { VolumeIcon } from "../button/VolumeIcon"; import { ItemData } from "./VideoTile"; -import { RemoteParticipant } from "livekit-client"; interface LocalVolumeProps { participant: RemoteParticipant; @@ -78,7 +78,7 @@ export const VideoTileSettingsModal = ({ data, onClose, ...rest }: Props) => { {...rest} >
- {} +
);