mirror of
https://github.com/vector-im/element-call.git
synced 2026-07-18 18:59:23 +00:00
Let page background shine through spotlight tiles
By giving spotlight tiles a transparent background in certain layouts.
This commit is contained in:
@@ -26,7 +26,7 @@ export function oneOnOnePortraitLayout(
|
|||||||
prevTiles: TileStore,
|
prevTiles: TileStore,
|
||||||
): [OneOnOnePortraitLayout, TileStore] {
|
): [OneOnOnePortraitLayout, TileStore] {
|
||||||
const update = prevTiles.from(media.pip === undefined ? 0 : 1);
|
const update = prevTiles.from(media.pip === undefined ? 0 : 1);
|
||||||
update.registerSpotlight([media.spotlight], true);
|
update.registerSpotlight([media.spotlight], true, "transparent");
|
||||||
if (media.pip !== undefined) update.registerGridTile(media.pip);
|
if (media.pip !== undefined) update.registerGridTile(media.pip);
|
||||||
const tiles = update.build();
|
const tiles = update.build();
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ export function pipLayout(
|
|||||||
update.registerSpotlight(
|
update.registerSpotlight(
|
||||||
media.spotlight,
|
media.spotlight,
|
||||||
platform === "desktop" ? false : true,
|
platform === "desktop" ? false : true,
|
||||||
|
"transparent",
|
||||||
);
|
);
|
||||||
const tiles = update.build();
|
const tiles = update.build();
|
||||||
return [
|
return [
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ export function spotlightExpandedLayout(
|
|||||||
prevTiles: TileStore,
|
prevTiles: TileStore,
|
||||||
): [SpotlightExpandedLayout, TileStore] {
|
): [SpotlightExpandedLayout, TileStore] {
|
||||||
const update = prevTiles.from(1);
|
const update = prevTiles.from(1);
|
||||||
update.registerSpotlight(media.spotlight, true);
|
update.registerSpotlight(media.spotlight, true, "transparent");
|
||||||
if (media.pip !== undefined) update.registerPipTile(media.pip);
|
if (media.pip !== undefined) update.registerPipTile(media.pip);
|
||||||
const tiles = update.build();
|
const tiles = update.build();
|
||||||
|
|
||||||
|
|||||||
@@ -39,12 +39,29 @@ class SpotlightTileData {
|
|||||||
this.maximised$.next(value);
|
this.maximised$.next(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private readonly bgStyle$: BehaviorSubject<"solid" | "transparent">;
|
||||||
|
public get bgStyle(): "solid" | "transparent" {
|
||||||
|
return this.bgStyle$.value;
|
||||||
|
}
|
||||||
|
public set bgStyle(value: "solid" | "transparent") {
|
||||||
|
this.bgStyle$.next(value);
|
||||||
|
}
|
||||||
|
|
||||||
public readonly vm: SpotlightTileViewModel;
|
public readonly vm: SpotlightTileViewModel;
|
||||||
|
|
||||||
public constructor(media: MediaViewModel[], maximised: boolean) {
|
public constructor(
|
||||||
|
media: MediaViewModel[],
|
||||||
|
maximised: boolean,
|
||||||
|
bgStyle: "solid" | "transparent",
|
||||||
|
) {
|
||||||
this.media$ = new BehaviorSubject(media);
|
this.media$ = new BehaviorSubject(media);
|
||||||
this.maximised$ = new BehaviorSubject(maximised);
|
this.maximised$ = new BehaviorSubject(maximised);
|
||||||
this.vm = new SpotlightTileViewModel(this.media$, this.maximised$);
|
this.bgStyle$ = new BehaviorSubject(bgStyle);
|
||||||
|
this.vm = new SpotlightTileViewModel(
|
||||||
|
this.media$,
|
||||||
|
this.maximised$,
|
||||||
|
this.bgStyle$,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -157,7 +174,11 @@ export class TileStoreBuilder {
|
|||||||
* Sets the contents of the spotlight tile. If this is never called, there
|
* Sets the contents of the spotlight tile. If this is never called, there
|
||||||
* will be no spotlight tile.
|
* will be no spotlight tile.
|
||||||
*/
|
*/
|
||||||
public registerSpotlight(media: MediaViewModel[], maximised: boolean): void {
|
public registerSpotlight(
|
||||||
|
media: MediaViewModel[],
|
||||||
|
maximised: boolean,
|
||||||
|
bgStyle: "solid" | "transparent" = "solid",
|
||||||
|
): void {
|
||||||
if (DEBUG_ENABLED)
|
if (DEBUG_ENABLED)
|
||||||
logger.debug(
|
logger.debug(
|
||||||
`[TileStore, ${this.generation}] register spotlight: ${media.map((m) => m.displayName$.value)}`,
|
`[TileStore, ${this.generation}] register spotlight: ${media.map((m) => m.displayName$.value)}`,
|
||||||
@@ -169,11 +190,12 @@ export class TileStoreBuilder {
|
|||||||
|
|
||||||
// Reuse the previous spotlight tile if it exists
|
// Reuse the previous spotlight tile if it exists
|
||||||
if (this.prevSpotlight === null) {
|
if (this.prevSpotlight === null) {
|
||||||
this.spotlight = new SpotlightTileData(media, maximised);
|
this.spotlight = new SpotlightTileData(media, maximised, bgStyle);
|
||||||
} else {
|
} else {
|
||||||
this.spotlight = this.prevSpotlight;
|
this.spotlight = this.prevSpotlight;
|
||||||
this.spotlight.media = media;
|
this.spotlight.media = media;
|
||||||
this.spotlight.maximised = maximised;
|
this.spotlight.maximised = maximised;
|
||||||
|
this.spotlight.bgStyle = bgStyle;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ export class SpotlightTileViewModel {
|
|||||||
public constructor(
|
public constructor(
|
||||||
public readonly media$: Behavior<MediaViewModel[]>,
|
public readonly media$: Behavior<MediaViewModel[]>,
|
||||||
public readonly maximised$: Behavior<boolean>,
|
public readonly maximised$: Behavior<boolean>,
|
||||||
|
public readonly bgStyle$: Behavior<"solid" | "transparent">,
|
||||||
) {}
|
) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -41,13 +41,16 @@ Please see LICENSE in the repository root for full details.
|
|||||||
|
|
||||||
.bg {
|
.bg {
|
||||||
grid-area: content;
|
grid-area: content;
|
||||||
background-color: var(--video-tile-background);
|
|
||||||
inline-size: 100%;
|
inline-size: 100%;
|
||||||
block-size: 100%;
|
block-size: 100%;
|
||||||
border-radius: inherit;
|
border-radius: inherit;
|
||||||
contain: strict;
|
contain: strict;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.media[data-bg-style="solid"] .bg {
|
||||||
|
background-color: var(--video-tile-background);
|
||||||
|
}
|
||||||
|
|
||||||
.avatar {
|
.avatar {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 50%;
|
top: 50%;
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ interface Props extends ComponentProps<typeof animated.div> {
|
|||||||
displayName: string;
|
displayName: string;
|
||||||
mxcAvatarUrl: string | undefined;
|
mxcAvatarUrl: string | undefined;
|
||||||
avatarStyle?: "solid" | "translucent";
|
avatarStyle?: "solid" | "translucent";
|
||||||
|
bgStyle?: "solid" | "transparent";
|
||||||
focusable: boolean;
|
focusable: boolean;
|
||||||
primaryButton?: ReactNode;
|
primaryButton?: ReactNode;
|
||||||
raisedHandTime?: Date;
|
raisedHandTime?: Date;
|
||||||
@@ -73,6 +74,7 @@ export const MediaView: FC<Props> = ({
|
|||||||
displayName,
|
displayName,
|
||||||
mxcAvatarUrl,
|
mxcAvatarUrl,
|
||||||
avatarStyle = "solid",
|
avatarStyle = "solid",
|
||||||
|
bgStyle = "solid",
|
||||||
focusable,
|
focusable,
|
||||||
primaryButton,
|
primaryButton,
|
||||||
status,
|
status,
|
||||||
@@ -118,6 +120,7 @@ export const MediaView: FC<Props> = ({
|
|||||||
ref={ref}
|
ref={ref}
|
||||||
data-testid="videoTile"
|
data-testid="videoTile"
|
||||||
data-video-fit={videoFit}
|
data-video-fit={videoFit}
|
||||||
|
data-bg-style={bgStyle}
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
<div className={styles.bg}>
|
<div className={styles.bg}>
|
||||||
|
|||||||
@@ -58,7 +58,13 @@ test("SpotlightTile is accessible", async () => {
|
|||||||
const toggleExpanded = vi.fn();
|
const toggleExpanded = vi.fn();
|
||||||
const { container } = render(
|
const { container } = render(
|
||||||
<SpotlightTile
|
<SpotlightTile
|
||||||
vm={new SpotlightTileViewModel(constant([vm1, vm2]), constant(false))}
|
vm={
|
||||||
|
new SpotlightTileViewModel(
|
||||||
|
constant([vm1, vm2]),
|
||||||
|
constant(false),
|
||||||
|
constant("solid"),
|
||||||
|
)
|
||||||
|
}
|
||||||
targetWidth={300}
|
targetWidth={300}
|
||||||
targetHeight={200}
|
targetHeight={200}
|
||||||
expanded={false}
|
expanded={false}
|
||||||
@@ -101,7 +107,13 @@ test("Screen share volume UI is shown when screen share has audio", async () =>
|
|||||||
const { container } = render(
|
const { container } = render(
|
||||||
<TooltipProvider>
|
<TooltipProvider>
|
||||||
<SpotlightTile
|
<SpotlightTile
|
||||||
vm={new SpotlightTileViewModel(constant([vm]), constant(false))}
|
vm={
|
||||||
|
new SpotlightTileViewModel(
|
||||||
|
constant([vm]),
|
||||||
|
constant(false),
|
||||||
|
constant("solid"),
|
||||||
|
)
|
||||||
|
}
|
||||||
targetWidth={300}
|
targetWidth={300}
|
||||||
targetHeight={200}
|
targetHeight={200}
|
||||||
expanded={false}
|
expanded={false}
|
||||||
@@ -132,7 +144,13 @@ test("Screen share volume UI is hidden when screen share has no audio", async ()
|
|||||||
const toggleExpanded = vi.fn();
|
const toggleExpanded = vi.fn();
|
||||||
const { container } = render(
|
const { container } = render(
|
||||||
<SpotlightTile
|
<SpotlightTile
|
||||||
vm={new SpotlightTileViewModel(constant([vm]), constant(false))}
|
vm={
|
||||||
|
new SpotlightTileViewModel(
|
||||||
|
constant([vm]),
|
||||||
|
constant(false),
|
||||||
|
constant("solid"),
|
||||||
|
)
|
||||||
|
}
|
||||||
targetWidth={300}
|
targetWidth={300}
|
||||||
targetHeight={200}
|
targetHeight={200}
|
||||||
expanded={false}
|
expanded={false}
|
||||||
@@ -168,7 +186,13 @@ test("SpotlightTile displays ringing media", async () => {
|
|||||||
const toggleExpanded = vi.fn();
|
const toggleExpanded = vi.fn();
|
||||||
const { container } = render(
|
const { container } = render(
|
||||||
<SpotlightTile
|
<SpotlightTile
|
||||||
vm={new SpotlightTileViewModel(constant([vm]), constant(false))}
|
vm={
|
||||||
|
new SpotlightTileViewModel(
|
||||||
|
constant([vm]),
|
||||||
|
constant(false),
|
||||||
|
constant("solid"),
|
||||||
|
)
|
||||||
|
}
|
||||||
targetWidth={300}
|
targetWidth={300}
|
||||||
targetHeight={200}
|
targetHeight={200}
|
||||||
expanded={false}
|
expanded={false}
|
||||||
|
|||||||
@@ -65,6 +65,7 @@ interface SpotlightItemBaseProps {
|
|||||||
displayName: string;
|
displayName: string;
|
||||||
mxcAvatarUrl: string | undefined;
|
mxcAvatarUrl: string | undefined;
|
||||||
showNameTags: boolean;
|
showNameTags: boolean;
|
||||||
|
bgStyle: "solid" | "transparent";
|
||||||
focusable: boolean;
|
focusable: boolean;
|
||||||
"aria-hidden"?: boolean;
|
"aria-hidden"?: boolean;
|
||||||
}
|
}
|
||||||
@@ -243,6 +244,7 @@ interface SpotlightItemProps {
|
|||||||
targetHeight: number;
|
targetHeight: number;
|
||||||
showNameTags: boolean;
|
showNameTags: boolean;
|
||||||
showRingingStatus: boolean;
|
showRingingStatus: boolean;
|
||||||
|
bgStyle: "solid" | "transparent";
|
||||||
focusable: boolean;
|
focusable: boolean;
|
||||||
intersectionObserver$: Observable<IntersectionObserver>;
|
intersectionObserver$: Observable<IntersectionObserver>;
|
||||||
/**
|
/**
|
||||||
@@ -259,6 +261,7 @@ const SpotlightItem: FC<SpotlightItemProps> = ({
|
|||||||
targetHeight,
|
targetHeight,
|
||||||
showNameTags,
|
showNameTags,
|
||||||
showRingingStatus,
|
showRingingStatus,
|
||||||
|
bgStyle,
|
||||||
focusable,
|
focusable,
|
||||||
intersectionObserver$,
|
intersectionObserver$,
|
||||||
snap,
|
snap,
|
||||||
@@ -295,6 +298,7 @@ const SpotlightItem: FC<SpotlightItemProps> = ({
|
|||||||
displayName,
|
displayName,
|
||||||
mxcAvatarUrl,
|
mxcAvatarUrl,
|
||||||
showNameTags,
|
showNameTags,
|
||||||
|
bgStyle,
|
||||||
focusable,
|
focusable,
|
||||||
"aria-hidden": ariaHidden,
|
"aria-hidden": ariaHidden,
|
||||||
};
|
};
|
||||||
@@ -412,6 +416,7 @@ export const SpotlightTile: FC<Props> = ({
|
|||||||
const [ourRef, root$] = useObservableRef<HTMLDivElement | null>(null);
|
const [ourRef, root$] = useObservableRef<HTMLDivElement | null>(null);
|
||||||
const ref = useMergedRefs(ourRef, theirRef);
|
const ref = useMergedRefs(ourRef, theirRef);
|
||||||
const maximised = useBehavior(vm.maximised$);
|
const maximised = useBehavior(vm.maximised$);
|
||||||
|
const bgStyle = useBehavior(vm.bgStyle$);
|
||||||
const media = useBehavior(vm.media$);
|
const media = useBehavior(vm.media$);
|
||||||
const [visibleId, setVisibleId] = useState<string | undefined>(media[0]?.id);
|
const [visibleId, setVisibleId] = useState<string | undefined>(media[0]?.id);
|
||||||
const latestMedia = useLatest(media);
|
const latestMedia = useLatest(media);
|
||||||
@@ -516,6 +521,7 @@ export const SpotlightTile: FC<Props> = ({
|
|||||||
targetHeight={targetHeight}
|
targetHeight={targetHeight}
|
||||||
showRingingStatus={showRingingStatus}
|
showRingingStatus={showRingingStatus}
|
||||||
showNameTags={showNameTags}
|
showNameTags={showNameTags}
|
||||||
|
bgStyle={bgStyle}
|
||||||
focusable={focusable}
|
focusable={focusable}
|
||||||
intersectionObserver$={intersectionObserver$}
|
intersectionObserver$={intersectionObserver$}
|
||||||
// This is how we get the container to scroll to the right media
|
// This is how we get the container to scroll to the right media
|
||||||
|
|||||||
Reference in New Issue
Block a user