mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-10 18:36:15 -08:00
feat(playback): restyle inline player header as now-playing bar
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 <noreply@anthropic.com>
This commit is contained in:
1 parent
329be253eb
commit
d2883a9e2b
5 files changed
+60
-16
No files matched your search
@@ -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",
|
||||
|
||||
@@ -1,17 +1,30 @@
|
||||
@if (hasPlayback()) {
|
||||
<section class="player-shell" aria-label="Inline player">
|
||||
<header class="player-shell__header">
|
||||
<div class="player-shell__eyebrow">Now Playing</div>
|
||||
<div class="player-shell__title-row">
|
||||
<button
|
||||
mat-icon-button
|
||||
type="button"
|
||||
class="player-shell__back"
|
||||
data-testid="inline-player-back"
|
||||
[attr.aria-label]="'PORTALS.BACK_TO_BROWSE' | translate"
|
||||
[matTooltip]="'PORTALS.BACK_TO_BROWSE' | translate"
|
||||
(click)="onClose()"
|
||||
>
|
||||
<mat-icon>arrow_back</mat-icon>
|
||||
</button>
|
||||
<div class="player-shell__heading">
|
||||
<div class="player-shell__eyebrow">
|
||||
{{ 'PORTALS.NOW_PLAYING' | translate }}
|
||||
</div>
|
||||
<h3 class="player-shell__title" [matTooltip]="title()">
|
||||
{{ title() }}
|
||||
@if (episodeMetadataText(); as episodeText) {
|
||||
<span class="player-shell__episode-meta">
|
||||
· {{ episodeText }}
|
||||
</span>
|
||||
}
|
||||
</h3>
|
||||
@if (episodeMetadataText(); as episodeText) {
|
||||
<div class="player-shell__episode-meta">
|
||||
{{ episodeText }}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
<div class="player-shell__actions">
|
||||
<button
|
||||
@@ -22,7 +35,7 @@
|
||||
(click)="onCopied()"
|
||||
>
|
||||
<mat-icon>content_copy</mat-icon>
|
||||
Copy Stream URL
|
||||
{{ 'PORTALS.COPY_STREAM_URL' | translate }}
|
||||
</button>
|
||||
<button
|
||||
mat-stroked-button
|
||||
@@ -30,7 +43,7 @@
|
||||
(click)="onClose()"
|
||||
>
|
||||
<mat-icon>close</mat-icon>
|
||||
Close Player
|
||||
{{ 'PORTALS.CLOSE_PLAYER' | translate }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -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 {
|
||||
|
||||
+24
-1
@@ -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);
|
||||
});
|
||||
});
|
||||
@@ -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,
|
||||
|
||||
Reference in new issue
Block a user