mirror of
https://github.com/vector-im/element-call.git
synced 2026-06-30 18:02:56 +00:00
@@ -74,6 +74,7 @@ import {
|
|||||||
useAppBarSubtitle,
|
useAppBarSubtitle,
|
||||||
} from "../AppBar.tsx";
|
} from "../AppBar.tsx";
|
||||||
import { useBehavior } from "../useBehavior.ts";
|
import { useBehavior } from "../useBehavior.ts";
|
||||||
|
import { constant } from "../state/Behavior.ts";
|
||||||
import { Toast } from "../Toast.tsx";
|
import { Toast } from "../Toast.tsx";
|
||||||
import overlayStyles from "../Overlay.module.css";
|
import overlayStyles from "../Overlay.module.css";
|
||||||
import { useTrackProcessorObservable$ } from "../livekit/TrackProcessorContext.tsx";
|
import { useTrackProcessorObservable$ } from "../livekit/TrackProcessorContext.tsx";
|
||||||
@@ -94,6 +95,7 @@ declare module "react" {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const logger = rootLogger.getChild("[InCallView]");
|
const logger = rootLogger.getChild("[InCallView]");
|
||||||
|
const noOutline$ = constant(false);
|
||||||
|
|
||||||
export interface ActiveCallProps extends Omit<
|
export interface ActiveCallProps extends Omit<
|
||||||
InCallViewProps,
|
InCallViewProps,
|
||||||
@@ -433,6 +435,9 @@ export const InCallView: FC<InCallViewProps> = ({
|
|||||||
const showSpeakingIndicators = useBehavior(vm.showSpeakingIndicators$);
|
const showSpeakingIndicators = useBehavior(vm.showSpeakingIndicators$);
|
||||||
const showNameTags = useBehavior(vm.showNameTags$);
|
const showNameTags = useBehavior(vm.showNameTags$);
|
||||||
const showRingingStatus = vm.ringingStatusLocation === "tile";
|
const showRingingStatus = vm.ringingStatusLocation === "tile";
|
||||||
|
const showOutline = useBehavior(
|
||||||
|
model instanceof GridTileViewModel ? model.showOutline$ : noOutline$,
|
||||||
|
);
|
||||||
|
|
||||||
return model instanceof GridTileViewModel ? (
|
return model instanceof GridTileViewModel ? (
|
||||||
<GridTile
|
<GridTile
|
||||||
@@ -446,6 +451,7 @@ export const InCallView: FC<InCallViewProps> = ({
|
|||||||
showSpeakingIndicators={showSpeakingIndicators}
|
showSpeakingIndicators={showSpeakingIndicators}
|
||||||
showNameTags={showNameTags}
|
showNameTags={showNameTags}
|
||||||
showRingingStatus={showRingingStatus}
|
showRingingStatus={showRingingStatus}
|
||||||
|
showOutline={showOutline}
|
||||||
focusable={!contentObscured}
|
focusable={!contentObscured}
|
||||||
/>
|
/>
|
||||||
) : (
|
) : (
|
||||||
|
|||||||
@@ -155,6 +155,7 @@ import {
|
|||||||
createRingingMedia,
|
createRingingMedia,
|
||||||
type RingingMediaViewModel,
|
type RingingMediaViewModel,
|
||||||
} from "../media/RingingMediaViewModel.ts";
|
} from "../media/RingingMediaViewModel.ts";
|
||||||
|
import { type GridTileViewModel } from "../TileViewModel.ts";
|
||||||
|
|
||||||
const logger = rootLogger.getChild("[CallViewModel]");
|
const logger = rootLogger.getChild("[CallViewModel]");
|
||||||
//TODO
|
//TODO
|
||||||
@@ -1482,6 +1483,7 @@ export function createCallViewModel$(
|
|||||||
({ tiles: prevTiles }, [media, visibleTiles]) => {
|
({ tiles: prevTiles }, [media, visibleTiles]) => {
|
||||||
let layout: Layout;
|
let layout: Layout;
|
||||||
let newTiles: TileStore;
|
let newTiles: TileStore;
|
||||||
|
let pip: GridTileViewModel | undefined;
|
||||||
switch (media.type) {
|
switch (media.type) {
|
||||||
case "grid":
|
case "grid":
|
||||||
case "spotlight-landscape":
|
case "spotlight-landscape":
|
||||||
@@ -1507,6 +1509,7 @@ export function createCallViewModel$(
|
|||||||
landscapePipAlignment$,
|
landscapePipAlignment$,
|
||||||
prevTiles,
|
prevTiles,
|
||||||
);
|
);
|
||||||
|
pip = layout.pip;
|
||||||
break;
|
break;
|
||||||
case "one-on-one-portrait":
|
case "one-on-one-portrait":
|
||||||
[layout, newTiles] = oneOnOnePortraitLayout(
|
[layout, newTiles] = oneOnOnePortraitLayout(
|
||||||
@@ -1515,12 +1518,17 @@ export function createCallViewModel$(
|
|||||||
portraitPipAlignment$,
|
portraitPipAlignment$,
|
||||||
prevTiles,
|
prevTiles,
|
||||||
);
|
);
|
||||||
|
pip = layout.pip;
|
||||||
break;
|
break;
|
||||||
case "pip":
|
case "pip":
|
||||||
[layout, newTiles] = pipLayout(media, prevTiles);
|
[layout, newTiles] = pipLayout(media, prevTiles);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (const tile of newTiles.gridTiles) {
|
||||||
|
tile.setShowOutline(tile === pip);
|
||||||
|
}
|
||||||
|
|
||||||
return { layout, tiles: newTiles };
|
return { layout, tiles: newTiles };
|
||||||
},
|
},
|
||||||
{ layout: null, tiles: TileStore.empty() },
|
{ layout: null, tiles: TileStore.empty() },
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
|
|||||||
Please see LICENSE in the repository root for full details.
|
Please see LICENSE in the repository root for full details.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { BehaviorSubject } from "rxjs";
|
||||||
|
|
||||||
import { type Behavior } from "./Behavior";
|
import { type Behavior } from "./Behavior";
|
||||||
import { type MediaViewModel } from "./media/MediaViewModel";
|
import { type MediaViewModel } from "./media/MediaViewModel";
|
||||||
import { type RingingMediaViewModel } from "./media/RingingMediaViewModel";
|
import { type RingingMediaViewModel } from "./media/RingingMediaViewModel";
|
||||||
@@ -17,12 +19,18 @@ function createId(): string {
|
|||||||
|
|
||||||
export class GridTileViewModel {
|
export class GridTileViewModel {
|
||||||
public readonly id = createId();
|
public readonly id = createId();
|
||||||
|
private readonly _showOutline$ = new BehaviorSubject(false);
|
||||||
|
public readonly showOutline$: Behavior<boolean> = this._showOutline$;
|
||||||
|
|
||||||
public constructor(
|
public constructor(
|
||||||
public readonly media$: Behavior<
|
public readonly media$: Behavior<
|
||||||
UserMediaViewModel | RingingMediaViewModel
|
UserMediaViewModel | RingingMediaViewModel
|
||||||
>,
|
>,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
|
public setShowOutline(value: boolean): void {
|
||||||
|
this._showOutline$.next(value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class SpotlightTileViewModel {
|
export class SpotlightTileViewModel {
|
||||||
|
|||||||
@@ -66,6 +66,11 @@ borders don't support gradients */
|
|||||||
opacity: 1;
|
opacity: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.tile.outline {
|
||||||
|
outline: var(--cpd-border-width-1) solid
|
||||||
|
var(--cpd-color-border-interactive-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
@media (hover: hover) {
|
@media (hover: hover) {
|
||||||
.tile:hover {
|
.tile:hover {
|
||||||
outline: var(--cpd-border-width-2) solid
|
outline: var(--cpd-border-width-2) solid
|
||||||
|
|||||||
@@ -78,6 +78,7 @@ test("GridTile is accessible", async () => {
|
|||||||
showSpeakingIndicators
|
showSpeakingIndicators
|
||||||
showNameTags
|
showNameTags
|
||||||
showRingingStatus
|
showRingingStatus
|
||||||
|
showOutline
|
||||||
focusable
|
focusable
|
||||||
/>
|
/>
|
||||||
</ReactionsSenderProvider>,
|
</ReactionsSenderProvider>,
|
||||||
@@ -110,6 +111,7 @@ test("GridTile displays ringing media", async () => {
|
|||||||
showSpeakingIndicators
|
showSpeakingIndicators
|
||||||
showNameTags
|
showNameTags
|
||||||
showRingingStatus
|
showRingingStatus
|
||||||
|
showOutline
|
||||||
focusable
|
focusable
|
||||||
/>
|
/>
|
||||||
</ReactionsSenderProvider>,
|
</ReactionsSenderProvider>,
|
||||||
|
|||||||
@@ -398,6 +398,7 @@ interface GridTileProps {
|
|||||||
showSpeakingIndicators: boolean;
|
showSpeakingIndicators: boolean;
|
||||||
showNameTags: boolean;
|
showNameTags: boolean;
|
||||||
showRingingStatus: boolean;
|
showRingingStatus: boolean;
|
||||||
|
showOutline: boolean;
|
||||||
focusable: boolean;
|
focusable: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -406,7 +407,9 @@ export const GridTile: FC<GridTileProps> = ({
|
|||||||
vm,
|
vm,
|
||||||
showSpeakingIndicators,
|
showSpeakingIndicators,
|
||||||
showRingingStatus,
|
showRingingStatus,
|
||||||
|
showOutline,
|
||||||
onOpenProfile,
|
onOpenProfile,
|
||||||
|
className,
|
||||||
...props
|
...props
|
||||||
}) => {
|
}) => {
|
||||||
const ourRef = useRef<HTMLDivElement | null>(null);
|
const ourRef = useRef<HTMLDivElement | null>(null);
|
||||||
@@ -423,6 +426,7 @@ export const GridTile: FC<GridTileProps> = ({
|
|||||||
displayName={displayName}
|
displayName={displayName}
|
||||||
mxcAvatarUrl={mxcAvatarUrl}
|
mxcAvatarUrl={mxcAvatarUrl}
|
||||||
showStatus={showRingingStatus}
|
showStatus={showRingingStatus}
|
||||||
|
className={classNames(className, { [styles.outline]: showOutline })}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
@@ -435,6 +439,7 @@ export const GridTile: FC<GridTileProps> = ({
|
|||||||
onOpenProfile={onOpenProfile}
|
onOpenProfile={onOpenProfile}
|
||||||
displayName={displayName}
|
displayName={displayName}
|
||||||
mxcAvatarUrl={mxcAvatarUrl}
|
mxcAvatarUrl={mxcAvatarUrl}
|
||||||
|
className={classNames(className, { [styles.outline]: showOutline })}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
@@ -446,6 +451,7 @@ export const GridTile: FC<GridTileProps> = ({
|
|||||||
showSpeakingIndicators={showSpeakingIndicators}
|
showSpeakingIndicators={showSpeakingIndicators}
|
||||||
displayName={displayName}
|
displayName={displayName}
|
||||||
mxcAvatarUrl={mxcAvatarUrl}
|
mxcAvatarUrl={mxcAvatarUrl}
|
||||||
|
className={classNames(className, { [styles.outline]: showOutline })}
|
||||||
{...props}
|
{...props}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user