mirror of
https://github.com/vector-im/element-call.git
synced 2026-08-17 20:39:19 +00:00
Voice call Pip circular media view
This commit is contained in:
@@ -238,7 +238,7 @@ export const InCallView: FC<InCallViewProps> = ({
|
||||
// Merge the refs so they can attach to the same element
|
||||
const containerRef = useMergedRefs(containerRef1, containerRef2);
|
||||
|
||||
const { showControls, header: headerStyle } = useUrlParams();
|
||||
const { showControls, header: headerStyle, callIntent } = useUrlParams();
|
||||
|
||||
const muteAllAudio = useBehavior(muteAllAudio$);
|
||||
const toggleAudio = useBehavior(muteStates.audio.toggle$);
|
||||
@@ -503,6 +503,8 @@ export const InCallView: FC<InCallViewProps> = ({
|
||||
showRingingStatus={vm.ringingStatusLocation === "tile"}
|
||||
focusable={!contentObscured}
|
||||
aria-hidden={contentObscured}
|
||||
// Use the Circular rendering of the audio tile in PiP
|
||||
circularAudioTile={callIntent === "audio"}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -62,6 +62,42 @@ Please see LICENSE in the repository root for full details.
|
||||
opacity: 50%;
|
||||
}
|
||||
|
||||
/* When no video is shown, circular media has no tile box at all: just the
|
||||
avatar rendered on the app background */
|
||||
.media.circular .bg {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
/* A disc behind the avatar, poking out around its edge to form a circular
|
||||
speaking indicator. It uses the same gradient as the speaking border of grid
|
||||
tiles. */
|
||||
.media.circular .bg::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
border-radius: 50%;
|
||||
background:
|
||||
linear-gradient(
|
||||
119deg,
|
||||
rgba(13, 92, 189, 0.7) 0%,
|
||||
rgba(13, 189, 168, 0.7) 100%
|
||||
),
|
||||
linear-gradient(
|
||||
180deg,
|
||||
rgba(13, 92, 189, 0.9) 0%,
|
||||
rgba(13, 189, 168, 0.9) 100%
|
||||
);
|
||||
background-blend-mode: overlay, normal;
|
||||
opacity: 0; /* Hidden unless speaking */
|
||||
transition: opacity ease 0.15s;
|
||||
}
|
||||
|
||||
.media.circular.speaking .bg::before {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
/* CSS makes us put a condition here, even though all we want to do is
|
||||
unconditionally select the container so we can use cqmin units */
|
||||
@container mediaView (width > 0) {
|
||||
@@ -70,6 +106,13 @@ unconditionally select the container so we can use cqmin units */
|
||||
inline-size: 50cqmin;
|
||||
block-size: 50cqmin;
|
||||
}
|
||||
|
||||
.media.circular .bg::before {
|
||||
/* Sized so that the disc extends beyond the avatar by the width of the
|
||||
speaking indicator */
|
||||
inline-size: calc(50cqmin + 2 * var(--cpd-border-width-4));
|
||||
block-size: calc(50cqmin + 2 * var(--cpd-border-width-4));
|
||||
}
|
||||
}
|
||||
|
||||
.avatar > img {
|
||||
|
||||
@@ -54,6 +54,9 @@ interface Props extends ComponentProps<typeof animated.div> {
|
||||
rtcBackendIdentity?: string;
|
||||
// The focus url, mainly for debugging purposes
|
||||
focusUrl?: string;
|
||||
circularAudioTile?: boolean;
|
||||
// render a circular speaking indicator if circularAudioTile is enabled.
|
||||
circularSpeakingIndicator?: boolean;
|
||||
}
|
||||
|
||||
export const MediaView: FC<Props> = ({
|
||||
@@ -84,6 +87,8 @@ export const MediaView: FC<Props> = ({
|
||||
videoStreamStats,
|
||||
rtcBackendIdentity,
|
||||
focusUrl,
|
||||
circularAudioTile,
|
||||
circularSpeakingIndicator,
|
||||
...props
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
@@ -112,7 +117,9 @@ export const MediaView: FC<Props> = ({
|
||||
return (
|
||||
<animated.div
|
||||
className={classNames(styles.media, className, {
|
||||
[styles.circular]: circularAudioTile && !videoEnabled,
|
||||
[styles.mirror]: mirror,
|
||||
[styles.speaking]: circularSpeakingIndicator,
|
||||
})}
|
||||
style={style}
|
||||
ref={ref}
|
||||
|
||||
@@ -66,6 +66,7 @@ interface SpotlightItemBaseProps {
|
||||
mxcAvatarUrl: string | undefined;
|
||||
showNameTags: boolean;
|
||||
focusable: boolean;
|
||||
circularAudioTile?: boolean;
|
||||
"aria-hidden"?: boolean;
|
||||
}
|
||||
|
||||
@@ -103,8 +104,9 @@ const SpotlightRemoteUserMediaItem: FC<SpotlightRemoteUserMediaItemProps> = ({
|
||||
...props
|
||||
}) => {
|
||||
const waitingForMedia = useBehavior(vm.waitingForMedia$);
|
||||
const speaking = useBehavior(vm.speaking$);
|
||||
return (
|
||||
<MediaView waitingForMedia={waitingForMedia} mirror={false} {...props} />
|
||||
<MediaView waitingForMedia={waitingForMedia} mirror={false} {...props} circularSpeakingIndicator={speaking} />
|
||||
);
|
||||
};
|
||||
|
||||
@@ -243,6 +245,7 @@ interface SpotlightItemProps {
|
||||
targetHeight: number;
|
||||
showNameTags: boolean;
|
||||
showRingingStatus: boolean;
|
||||
circularAudioTile?: boolean;
|
||||
focusable: boolean;
|
||||
intersectionObserver$: Observable<IntersectionObserver>;
|
||||
/**
|
||||
@@ -259,6 +262,7 @@ const SpotlightItem: FC<SpotlightItemProps> = ({
|
||||
targetHeight,
|
||||
showNameTags,
|
||||
showRingingStatus,
|
||||
circularAudioTile,
|
||||
focusable,
|
||||
intersectionObserver$,
|
||||
snap,
|
||||
@@ -295,6 +299,7 @@ const SpotlightItem: FC<SpotlightItemProps> = ({
|
||||
displayName,
|
||||
mxcAvatarUrl,
|
||||
showNameTags,
|
||||
circularAudioTile,
|
||||
focusable,
|
||||
"aria-hidden": ariaHidden,
|
||||
};
|
||||
@@ -389,6 +394,7 @@ interface Props {
|
||||
showIndicators: boolean;
|
||||
showNameTags: boolean;
|
||||
showRingingStatus: boolean;
|
||||
circularAudioTile?: boolean;
|
||||
focusable: boolean;
|
||||
className?: string;
|
||||
style?: ComponentProps<typeof animated.div>["style"];
|
||||
@@ -404,6 +410,7 @@ export const SpotlightTile: FC<Props> = ({
|
||||
showIndicators,
|
||||
showNameTags,
|
||||
showRingingStatus,
|
||||
circularAudioTile,
|
||||
focusable = true,
|
||||
className,
|
||||
style,
|
||||
@@ -517,6 +524,7 @@ export const SpotlightTile: FC<Props> = ({
|
||||
showRingingStatus={showRingingStatus}
|
||||
showNameTags={showNameTags}
|
||||
focusable={focusable}
|
||||
circularAudioTile={circularAudioTile}
|
||||
intersectionObserver$={intersectionObserver$}
|
||||
// This is how we get the container to scroll to the right media
|
||||
// when the previous/next buttons are clicked: we temporarily
|
||||
|
||||
Reference in New Issue
Block a user