From d2883a9e2b674537cc606373768ec30750e79ccf Mon Sep 17 00:00:00 2001 From: 4gray Date: Sat, 4 Jul 2026 18:15:54 +0200 Subject: [PATCH] feat(playback): restyle inline player header as now-playing bar MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds a back button (emits the existing closed output — in watch state back returns to browse, not route navigation) and replaces the hardcoded header strings with i18n keys (PORTALS.NOW_PLAYING, CLOSE_PLAYER, BACK_TO_BROWSE). Co-Authored-By: Claude Fable 5 --- apps/web/src/assets/i18n/en.json | 4 +++ .../portal-inline-player.component.html | 29 ++++++++++++++----- .../portal-inline-player.component.scss | 16 +++++----- .../portal-inline-player.component.spec.ts | 25 +++++++++++++++- .../portal-inline-player.component.ts | 2 ++ 5 files changed, 60 insertions(+), 16 deletions(-) diff --git a/apps/web/src/assets/i18n/en.json b/apps/web/src/assets/i18n/en.json index 54ccd5cd2..0bab726d9 100644 --- a/apps/web/src/assets/i18n/en.json +++ b/apps/web/src/assets/i18n/en.json @@ -918,6 +918,10 @@ }, "STREAM_URL_COPIED": "Stream URL copied to clipboard", "COPY_STREAM_URL": "Copy Stream URL", + "NOW_PLAYING": "Now playing", + "CLOSE_PLAYER": "Close player", + "BACK_TO_BROWSE": "Back to details", + "ABOUT": "About", "SELECT_CATEGORY": "Select a category", "PAGE": "Page", "CONTENT_NOT_AVAILABLE": "This content is not available on this server", diff --git a/libs/ui/playback/src/lib/portal-inline-player/portal-inline-player.component.html b/libs/ui/playback/src/lib/portal-inline-player/portal-inline-player.component.html index 4e9352281..c35f35191 100644 --- a/libs/ui/playback/src/lib/portal-inline-player/portal-inline-player.component.html +++ b/libs/ui/playback/src/lib/portal-inline-player/portal-inline-player.component.html @@ -1,17 +1,30 @@ @if (hasPlayback()) {
-
Now Playing
+
+
+ {{ 'PORTALS.NOW_PLAYING' | translate }} +

{{ title() }} + @if (episodeMetadataText(); as episodeText) { + + · {{ episodeText }} + + }

- @if (episodeMetadataText(); as episodeText) { -
- {{ episodeText }} -
- }
diff --git a/libs/ui/playback/src/lib/portal-inline-player/portal-inline-player.component.scss b/libs/ui/playback/src/lib/portal-inline-player/portal-inline-player.component.scss index 725e9f689..667acc51a 100644 --- a/libs/ui/playback/src/lib/portal-inline-player/portal-inline-player.component.scss +++ b/libs/ui/playback/src/lib/portal-inline-player/portal-inline-player.component.scss @@ -35,10 +35,14 @@ .player-shell__title-row { display: flex; gap: 12px; - align-items: flex-start; + align-items: center; justify-content: space-between; } +.player-shell__back { + flex: none; +} + .player-shell__heading { min-width: 0; } @@ -49,17 +53,15 @@ font-weight: 700; line-height: 1.2; color: #161616; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; } .player-shell__episode-meta { - margin-top: 4px; - overflow: hidden; color: rgba(26, 26, 26, 0.64); - font-size: 0.9rem; + font-size: 0.95rem; font-weight: 600; - line-height: 1.35; - text-overflow: ellipsis; - white-space: nowrap; } .player-shell__actions { diff --git a/libs/ui/playback/src/lib/portal-inline-player/portal-inline-player.component.spec.ts b/libs/ui/playback/src/lib/portal-inline-player/portal-inline-player.component.spec.ts index 048cc3698..1f332f05a 100644 --- a/libs/ui/playback/src/lib/portal-inline-player/portal-inline-player.component.spec.ts +++ b/libs/ui/playback/src/lib/portal-inline-player/portal-inline-player.component.spec.ts @@ -1,6 +1,7 @@ import { Component, input, output } from '@angular/core'; import { ComponentFixture, TestBed } from '@angular/core/testing'; import { By } from '@angular/platform-browser'; +import { TranslateModule } from '@ngx-translate/core'; import type { PortalInlinePlayerComponent as PortalInlinePlayerComponentInstance } from './portal-inline-player.component'; jest.unstable_mockModule('video.js', () => ({ @@ -46,7 +47,7 @@ describe('PortalInlinePlayerComponent', () => { beforeEach(async () => { await TestBed.configureTestingModule({ - imports: [PortalInlinePlayerComponent], + imports: [PortalInlinePlayerComponent, TranslateModule.forRoot()], }) .overrideComponent(PortalInlinePlayerComponent, { remove: { @@ -120,4 +121,26 @@ describe('PortalInlinePlayerComponent', () => { expect(events).toEqual(['ended', 'previous', 'next']); }); + + it('emits closed from the back button in the now-playing bar', () => { + let closedCount = 0; + fixture.componentRef.setInput('playback', { + streamUrl: 'https://example.test/vod/1.mp4', + title: 'Movie', + }); + ( + component as unknown as { + closed: { subscribe: (fn: () => void) => void }; + } + ).closed.subscribe(() => closedCount++); + + fixture.detectChanges(); + + const backButton = fixture.nativeElement.querySelector( + '[data-testid="inline-player-back"]' + ) as HTMLButtonElement; + expect(backButton).toBeTruthy(); + backButton.click(); + expect(closedCount).toBe(1); + }); }); diff --git a/libs/ui/playback/src/lib/portal-inline-player/portal-inline-player.component.ts b/libs/ui/playback/src/lib/portal-inline-player/portal-inline-player.component.ts index 686789587..3721ff1f0 100644 --- a/libs/ui/playback/src/lib/portal-inline-player/portal-inline-player.component.ts +++ b/libs/ui/playback/src/lib/portal-inline-player/portal-inline-player.component.ts @@ -9,6 +9,7 @@ import { import { MatButtonModule } from '@angular/material/button'; import { MatIconModule } from '@angular/material/icon'; import { MatTooltipModule } from '@angular/material/tooltip'; +import { TranslateModule } from '@ngx-translate/core'; import { PlayerContentInfo, ResolvedPortalPlayback, @@ -29,6 +30,7 @@ import type { MatButtonModule, MatIconModule, MatTooltipModule, + TranslateModule, WebPlayerViewComponent, ], changeDetection: ChangeDetectionStrategy.OnPush,