diff --git a/src/room/InCallView.tsx b/src/room/InCallView.tsx index 8849c9629..957acf07e 100644 --- a/src/room/InCallView.tsx +++ b/src/room/InCallView.tsx @@ -292,6 +292,7 @@ export const InCallView: FC = ({ data={maximisedParticipant.data} showSpeakingIndicator={false} showConnectionStats={showConnectionStats} + matrixInfo={matrixInfo} /> ); } @@ -310,6 +311,7 @@ export const InCallView: FC = ({ onToggleFullscreen={toggleFullscreen} showSpeakingIndicator={items.length > 2} showConnectionStats={showConnectionStats} + matrixInfo={matrixInfo} {...props} ref={props.ref as Ref} /> diff --git a/src/video-grid/VideoTile.module.css b/src/video-grid/VideoTile.module.css index cfd699c61..f0e3d7034 100644 --- a/src/video-grid/VideoTile.module.css +++ b/src/video-grid/VideoTile.module.css @@ -88,11 +88,11 @@ limitations under the License. flex-shrink: 0; } -.nameTag > svg[data-muted="true"] { +.muteIcon[data-muted="true"] { color: var(--cpd-color-icon-secondary); } -.nameTag > svg[data-muted="false"] { +.muteIcon[data-muted="false"] { color: var(--cpd-color-icon-primary); } @@ -103,6 +103,10 @@ limitations under the License. padding-inline: var(--cpd-space-2x); } +.errorIcon { + color: var(--cpd-color-icon-critical-primary); +} + .toolbar { position: absolute; top: 0; diff --git a/src/video-grid/VideoTile.tsx b/src/video-grid/VideoTile.tsx index b19d94097..f8d6ca8dc 100644 --- a/src/video-grid/VideoTile.tsx +++ b/src/video-grid/VideoTile.tsx @@ -36,13 +36,15 @@ import { } from "matrix-js-sdk/src/models/room-member"; import MicOnSolidIcon from "@vector-im/compound-design-tokens/icons/mic-on-solid.svg?react"; import MicOffSolidIcon from "@vector-im/compound-design-tokens/icons/mic-off-solid.svg?react"; -import { Text } from "@vector-im/compound-web"; +import ErrorIcon from "@vector-im/compound-design-tokens/icons/error.svg?react"; +import { Text, Tooltip } from "@vector-im/compound-web"; import { Avatar } from "../Avatar"; import styles from "./VideoTile.module.css"; import { useReactiveState } from "../useReactiveState"; import { AudioButton, FullscreenButton } from "../button/Button"; import { VideoTileSettingsModal } from "./VideoTileSettingsModal"; +import { MatrixInfo } from "../room/VideoPreview"; export interface ItemData { id: string; @@ -68,6 +70,9 @@ interface Props { style?: ComponentProps["style"]; showSpeakingIndicator: boolean; showConnectionStats: boolean; + // TODO: This dependency in particular should probably not be here. We can fix + // this with a view model. + matrixInfo: MatrixInfo; } export const VideoTile = forwardRef( @@ -83,6 +88,7 @@ export const VideoTile = forwardRef( targetHeight, showSpeakingIndicator, showConnectionStats, + matrixInfo, }, tileRef, ) => { @@ -108,13 +114,22 @@ export const VideoTile = forwardRef( } }, [member, setDisplayName]); - const muted = - useMediaTrack( - content === TileContent.UserMedia - ? Track.Source.Microphone - : Track.Source.ScreenShareAudio, - sfuParticipant, - ).isMuted !== false; + const audioInfo = useMediaTrack( + content === TileContent.UserMedia + ? Track.Source.Microphone + : Track.Source.ScreenShareAudio, + sfuParticipant, + ); + const videoInfo = useMediaTrack( + content === TileContent.UserMedia + ? Track.Source.Camera + : Track.Source.ScreenShare, + sfuParticipant, + ); + const muted = audioInfo.isMuted !== false; + const encrypted = + audioInfo.publication?.isEncrypted !== false && + videoInfo.publication?.isEncrypted !== false; const MicIcon = muted ? MicOffSolidIcon : MicOnSolidIcon; @@ -202,12 +217,29 @@ export const VideoTile = forwardRef( height={20} aria-label={muted ? t("microphone_off") : t("microphone_on")} data-muted={muted} + className={styles.muteIcon} /> {sfuParticipant.isLocal ? t("video_tile.sfu_participant_local") : displayName} + {matrixInfo.roomEncrypted && !encrypted && ( + + + + )} {showConnectionStats && ( )}