From 8fa3f33f379708ee8eb9bf5da377e1835770282e Mon Sep 17 00:00:00 2001 From: Robin Date: Thu, 25 Jun 2026 18:22:43 +0200 Subject: [PATCH] Let page background shine through spotlight tiles By giving spotlight tiles a transparent background in certain layouts. --- src/state/OneOnOnePortraitLayout.ts | 2 +- src/state/PipLayout.ts | 1 + src/state/SpotlightExpandedLayout.ts | 2 +- src/state/TileStore.ts | 30 ++++++++++++++++++++++---- src/state/TileViewModel.ts | 1 + src/tile/MediaView.module.css | 5 ++++- src/tile/MediaView.tsx | 3 +++ src/tile/SpotlightTile.test.tsx | 32 ++++++++++++++++++++++++---- src/tile/SpotlightTile.tsx | 6 ++++++ 9 files changed, 71 insertions(+), 11 deletions(-) diff --git a/src/state/OneOnOnePortraitLayout.ts b/src/state/OneOnOnePortraitLayout.ts index 9be80421b..98314a039 100644 --- a/src/state/OneOnOnePortraitLayout.ts +++ b/src/state/OneOnOnePortraitLayout.ts @@ -26,7 +26,7 @@ export function oneOnOnePortraitLayout( prevTiles: TileStore, ): [OneOnOnePortraitLayout, TileStore] { 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); const tiles = update.build(); diff --git a/src/state/PipLayout.ts b/src/state/PipLayout.ts index 6ac1e4f02..0cd6a77cb 100644 --- a/src/state/PipLayout.ts +++ b/src/state/PipLayout.ts @@ -21,6 +21,7 @@ export function pipLayout( update.registerSpotlight( media.spotlight, platform === "desktop" ? false : true, + "transparent", ); const tiles = update.build(); return [ diff --git a/src/state/SpotlightExpandedLayout.ts b/src/state/SpotlightExpandedLayout.ts index 59ab8ab98..45a37ca29 100644 --- a/src/state/SpotlightExpandedLayout.ts +++ b/src/state/SpotlightExpandedLayout.ts @@ -23,7 +23,7 @@ export function spotlightExpandedLayout( prevTiles: TileStore, ): [SpotlightExpandedLayout, TileStore] { const update = prevTiles.from(1); - update.registerSpotlight(media.spotlight, true); + update.registerSpotlight(media.spotlight, true, "transparent"); if (media.pip !== undefined) update.registerPipTile(media.pip); const tiles = update.build(); diff --git a/src/state/TileStore.ts b/src/state/TileStore.ts index 3cbed2809..5d4df462e 100644 --- a/src/state/TileStore.ts +++ b/src/state/TileStore.ts @@ -39,12 +39,29 @@ class SpotlightTileData { 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 constructor(media: MediaViewModel[], maximised: boolean) { + public constructor( + media: MediaViewModel[], + maximised: boolean, + bgStyle: "solid" | "transparent", + ) { this.media$ = new BehaviorSubject(media); 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 * 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) logger.debug( `[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 if (this.prevSpotlight === null) { - this.spotlight = new SpotlightTileData(media, maximised); + this.spotlight = new SpotlightTileData(media, maximised, bgStyle); } else { this.spotlight = this.prevSpotlight; this.spotlight.media = media; this.spotlight.maximised = maximised; + this.spotlight.bgStyle = bgStyle; } } diff --git a/src/state/TileViewModel.ts b/src/state/TileViewModel.ts index 81aac8298..bb96a16b8 100644 --- a/src/state/TileViewModel.ts +++ b/src/state/TileViewModel.ts @@ -37,6 +37,7 @@ export class SpotlightTileViewModel { public constructor( public readonly media$: Behavior, public readonly maximised$: Behavior, + public readonly bgStyle$: Behavior<"solid" | "transparent">, ) {} } diff --git a/src/tile/MediaView.module.css b/src/tile/MediaView.module.css index 5e7b3b28d..5b68210c8 100644 --- a/src/tile/MediaView.module.css +++ b/src/tile/MediaView.module.css @@ -41,13 +41,16 @@ Please see LICENSE in the repository root for full details. .bg { grid-area: content; - background-color: var(--video-tile-background); inline-size: 100%; block-size: 100%; border-radius: inherit; contain: strict; } +.media[data-bg-style="solid"] .bg { + background-color: var(--video-tile-background); +} + .avatar { position: absolute; top: 50%; diff --git a/src/tile/MediaView.tsx b/src/tile/MediaView.tsx index f860f41eb..9891e8218 100644 --- a/src/tile/MediaView.tsx +++ b/src/tile/MediaView.tsx @@ -43,6 +43,7 @@ interface Props extends ComponentProps { displayName: string; mxcAvatarUrl: string | undefined; avatarStyle?: "solid" | "translucent"; + bgStyle?: "solid" | "transparent"; focusable: boolean; primaryButton?: ReactNode; raisedHandTime?: Date; @@ -73,6 +74,7 @@ export const MediaView: FC = ({ displayName, mxcAvatarUrl, avatarStyle = "solid", + bgStyle = "solid", focusable, primaryButton, status, @@ -118,6 +120,7 @@ export const MediaView: FC = ({ ref={ref} data-testid="videoTile" data-video-fit={videoFit} + data-bg-style={bgStyle} {...props} >
diff --git a/src/tile/SpotlightTile.test.tsx b/src/tile/SpotlightTile.test.tsx index 69bdacf76..7924e557d 100644 --- a/src/tile/SpotlightTile.test.tsx +++ b/src/tile/SpotlightTile.test.tsx @@ -58,7 +58,13 @@ test("SpotlightTile is accessible", async () => { const toggleExpanded = vi.fn(); const { container } = render( const { container } = render(