refactor: switch to standalone components

This commit is contained in:
4gray committed 2024-11-18 23:48:56 +01:00
1 parent d496a234dc
commit 3d4f549eef
13 files changed
+228 -164

No files matched your search

@@ -1,29 +1,28 @@
<span class="time"
>{{ item.start | momentDate: 'YYYYMMDDHHmm ZZ':'HH:mm' }}
>{{ item.start | momentDate: 'YYYYMMDDHHmm ZZ' : 'HH:mm' }}
-
{{ item.stop | momentDate: 'YYYYMMDDHHmm ZZ':'HH:mm' }}</span
>
<span
*ngIf="timeNow >= item.start && timeNow <= item.stop"
class="live badge"
[matTooltip]="'EPG.LIVE_NOW' | translate"
>live</span
>
<span
*ngIf="
item.start < timeNow &&
item.stop < timeNow &&
item.start > timeshiftUntil
"
class="rec badge"
[matTooltip]="'EPG.TIMESHIFT_AVAILABLE' | translate"
>r</span
>
<span
(click)="$event.stopPropagation(); showDescription(item)"
matListItemMeta
*ngIf="item.desc.length > 0"
class="description-icon"
[matTooltip]="'EPG.PROGRAM_DIALOG.SHOW_PROGRAM_DETAILS' | translate"
><mat-icon>info</mat-icon></span
{{ item.stop | momentDate: 'YYYYMMDDHHmm ZZ' : 'HH:mm' }}</span
>
@if (timeNow >= item.start && timeNow <= item.stop) {
<span class="live badge" [matTooltip]="'EPG.LIVE_NOW' | translate"
>live</span
>
}
@if (
item.start < timeNow && item.stop < timeNow && item.start > timeshiftUntil
) {
<span class="rec badge" [matTooltip]="'EPG.TIMESHIFT_AVAILABLE' | translate"
>r</span
>
}
@if (item.desc.length > 0) {
<span
(click)="$event.stopPropagation(); showDescription(item)"
matListItemMeta
class="description-icon"
[matTooltip]="'EPG.PROGRAM_DIALOG.SHOW_PROGRAM_DETAILS' | translate"
><mat-icon>info</mat-icon></span
>
}
@@ -1,9 +1,15 @@
import { Component, Input } from '@angular/core';
import { MatDialog } from '@angular/material/dialog';
import { MatIconModule } from '@angular/material/icon';
import { MatTooltipModule } from '@angular/material/tooltip';
import { TranslateModule } from '@ngx-translate/core';
import { MomentDatePipe } from '../../../../shared/pipes/moment-date.pipe';
import { EpgProgram } from '../../../models/epg-program.model';
import { EpgItemDescriptionComponent } from './../epg-item-description/epg-item-description.component';
@Component({
standalone: true,
imports: [MomentDatePipe, MatIconModule, MatTooltipModule, TranslateModule],
selector: 'app-epg-list-item',
templateUrl: './epg-list-item.component.html',
styleUrls: ['./epg-list-item.component.scss'],
@@ -1,9 +1,11 @@
<ng-container *ngIf="channel; else noEpg">
@if (channel) {
<!-- channel info panel -->
<div id="channel-header" color="primary">
<div class="channel-info">
<div class="channel-icon">
<img *ngIf="channel?.icon" [src]="channel.icon" width="48" />
@if (channel?.icon) {
<img [src]="channel.icon" width="48" />
}
</div>
<div class="channel-details">
<div class="channel-name">
@@ -18,7 +20,7 @@
</div>
</div>
</div>
<mat-divider></mat-divider>
<mat-divider />
<div id="date-navigator">
<button
class="previous-day"
@@ -41,44 +43,23 @@
</button>
</div>
</div>
<mat-divider></mat-divider>
<mat-divider />
<!-- program list -->
<mat-selection-list
id="program-list"
[multiple]="false"
hideSingleSelectionIndicator="true"
*ngIf="timeshiftUntil$ | async as timeshiftUntil"
>
<ng-container *ngIf="items?.length > 0; else epgNotAvailable">
<ng-container *ngFor="let item of items">
<mat-list-option
*ngIf="
item.start < timeshiftUntil || item.start >= timeNow;
else timeshiftItem
"
>
<div matListItemLine>
<app-epg-list-item
[timeshiftUntil]="timeshiftUntil"
[timeNow]="timeNow"
[item]="item"
></app-epg-list-item>
</div>
<p matListItemLine>{{ item?.title[0]?.value }}</p>
</mat-list-option>
<ng-template #timeshiftItem>
@let timeshiftUntil = timeshiftUntil$ | async;
@if (timeshiftUntil) {
<!-- program list -->
<mat-selection-list
id="program-list"
[multiple]="false"
hideSingleSelectionIndicator="true"
>
@if (items?.length > 0) {
<ng-container *ngFor="let item of items">
<mat-list-option
[value]="item"
[class.active]="
timeNow >= item.start && timeNow <= item.stop
"
(click)="
setEpgProgram(
item,
timeNow >= item.start && timeNow <= item.stop,
item.start > timeshiftUntil
)
*ngIf="
item.start < timeshiftUntil ||
item.start >= timeNow;
else timeshiftItem
"
>
<div matListItemLine>
@@ -86,30 +67,52 @@
[timeshiftUntil]="timeshiftUntil"
[timeNow]="timeNow"
[item]="item"
></app-epg-list-item>
/>
</div>
<p
matListItemLine
[innerHTML]="item?.title[0]?.value"
></p>
<p matListItemLine>{{ item?.title[0]?.value }}</p>
</mat-list-option>
</ng-template>
<mat-divider></mat-divider>
</ng-container>
</ng-container>
<ng-template #epgNotAvailable>
<p class="epg-not-available">
{{ 'EPG.EPG_NOT_AVAILABLE_DATE' | translate }}
</p>
</ng-template>
</mat-selection-list>
</ng-container>
<ng-template #noEpg>
<ng-template #timeshiftItem>
<mat-list-option
[value]="item"
[class.active]="
timeNow >= item.start && timeNow <= item.stop
"
(click)="
setEpgProgram(
item,
timeNow >= item.start &&
timeNow <= item.stop,
item.start > timeshiftUntil
)
"
>
<div matListItemLine>
<app-epg-list-item
[timeshiftUntil]="timeshiftUntil"
[timeNow]="timeNow"
[item]="item"
/>
</div>
<p
matListItemLine
[innerHTML]="item?.title[0]?.value"
></p>
</mat-list-option>
</ng-template>
<mat-divider />
</ng-container>
} @else {
<p class="epg-not-available">
{{ 'EPG.EPG_NOT_AVAILABLE_DATE' | translate }}
</p>
}
</mat-selection-list>
}
} @else {
<mat-list>
<mat-list-item>
{{ 'EPG.EPG_NOT_AVAILABLE_CHANNEL_TITLE' | translate }}<br />
{{ 'EPG.EPG_NOT_AVAILABLE_CHANNEL_DESCRIPTION' | translate }}
</mat-list-item>
</mat-list>
</ng-template>
}
@@ -1,10 +1,19 @@
import { AsyncPipe } from '@angular/common';
import { Component, NgZone } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { MatButtonModule } from '@angular/material/button';
import { MatDividerModule } from '@angular/material/divider';
import { MatIconModule } from '@angular/material/icon';
import { MatListModule } from '@angular/material/list';
import { MatTooltipModule } from '@angular/material/tooltip';
import { Store } from '@ngrx/store';
import { TranslateModule } from '@ngx-translate/core';
import moment from 'moment';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { EPG_GET_PROGRAM_DONE } from '../../../../../shared/ipc-commands';
import { DataService } from '../../../services/data.service';
import { MomentDatePipe } from '../../../shared/pipes/moment-date.pipe';
import {
resetActiveEpgProgram,
setActiveEpgProgram,
@@ -13,6 +22,7 @@ import {
import { selectActive } from '../../../state/selectors';
import { EpgChannel } from '../../models/epg-channel.model';
import { EpgProgram } from '../../models/epg-program.model';
import { EpgListItemComponent } from './epg-list-item/epg-list-item.component';
export interface EpgData {
channel: EpgChannel;
@@ -23,6 +33,19 @@ const DATE_FORMAT = 'YYYYMMDD';
const DATE_TIME_FORMAT = 'YYYYMMDDHHmm ZZ';
@Component({
standalone: true,
imports: [
AsyncPipe,
EpgListItemComponent,
FormsModule,
MatButtonModule,
MatDividerModule,
MatIconModule,
MatListModule,
MatTooltipModule,
MomentDatePipe,
TranslateModule,
],
selector: 'app-epg-list',
templateUrl: './epg-list.component.html',
styleUrls: ['./epg-list.component.scss'],
@@ -1,35 +1,36 @@
<div
class="channel-overlay"
*ngIf="channel && epgProgram"
[class.visible]="isVisible"
[class.hidden]="!isVisible"
>
<div class="channel-logo">
<img
[src]="epgProgram?.icon[0] || channel?.tvg?.logo"
width="68"
onerror="this.style.display='none'"
/>
</div>
<div class="content">
<div class="program-name">
{{ epgProgram?.title[0]?.value }} | {{ channel.name }}
@if (channel && epgProgram) {
<div
class="channel-overlay"
[class.visible]="isVisible"
[class.hidden]="!isVisible"
>
<div class="channel-logo">
<img
[src]="epgProgram?.icon[0] || channel?.tvg?.logo"
width="68"
onerror="this.style.display='none'"
/>
</div>
<div class="program-description">
{{ epgProgram?.desc[0]?.value }}
</div>
<div class="program-progress">
<div
class="program-finished"
[ngStyle]="{
width: (finishedDuration * 100) / generalDuration + '%'
}"
></div>
<div class="duration">
{{ start | momentDate: 'YYYYMMDDHHmm ZZ':'HH:mm' }}
-
{{ stop | momentDate: 'YYYYMMDDHHmm ZZ':'HH:mm' }}
<div class="content">
<div class="program-name">
{{ epgProgram?.title[0]?.value }} | {{ channel.name }}
</div>
<div class="program-description">
{{ epgProgram?.desc[0]?.value }}
</div>
<div class="program-progress">
<div
class="program-finished"
[ngStyle]="{
width: (finishedDuration * 100) / generalDuration + '%',
}"
></div>
<div class="duration">
{{ start | momentDate: 'YYYYMMDDHHmm ZZ' : 'HH:mm' }}
-
{{ stop | momentDate: 'YYYYMMDDHHmm ZZ' : 'HH:mm' }}
</div>
</div>
</div>
</div>
</div>
}
@@ -1,9 +1,13 @@
import { NgStyle } from '@angular/common';
import { Component, Input, OnChanges, SimpleChanges } from '@angular/core';
import moment from 'moment';
import { Channel } from '../../../../../shared/channel.interface';
import { MomentDatePipe } from '../../../shared/pipes/moment-date.pipe';
import { EpgProgram } from '../../models/epg-program.model';
@Component({
standalone: true,
imports: [MomentDatePipe, NgStyle],
selector: 'app-info-overlay',
templateUrl: './info-overlay.component.html',
styleUrls: ['./info-overlay.component.scss'],
@@ -22,7 +22,7 @@
<mat-icon>navigate_next</mat-icon>
</button>
<div class="today-date">
{{ today | momentDate: 'YYYYMMDD':'MMMM Do, dddd' }}
{{ today | momentDate: 'YYYYMMDD' : 'MMMM Do, dddd' }}
</div>
<button mat-icon-button (click)="zoomIn()" [disabled]="hourWidth >= 800">
<mat-icon>zoom_in</mat-icon>
@@ -50,24 +50,26 @@
[attr.y]="barHeight * i + barHeight"
stroke="black"
></rect>
<image
*ngIf="item.icon[0]"
[attr.y]="barHeight * i + barHeight + 1"
[attr.href]="item.icon[0]"
[attr.height]="barHeight - 2"
width="100"
[matTooltip]="item.name[0].value"
></image>
<foreignObject
*ngIf="!item.icon[0]"
[attr.y]="barHeight * i + barHeight"
width="100"
[attr.height]="barHeight"
>
<div class="channel-name">
{{ item.name[0].value }}
</div>
</foreignObject>
@if (item.icon[0]) {
<image
[attr.y]="barHeight * i + barHeight + 1"
[attr.href]="item.icon[0]"
[attr.height]="barHeight - 2"
width="100"
[matTooltip]="item.name[0].value"
></image>
}
@if (!item.icon[0]) {
<foreignObject
[attr.y]="barHeight * i + barHeight"
width="100"
[attr.height]="barHeight"
>
<div class="channel-name">
{{ item.name[0].value }}
</div>
</foreignObject>
}
</g>
</g>
</svg>
@@ -1,4 +1,5 @@
import { OverlayRef } from '@angular/cdk/overlay';
import { CommonModule } from '@angular/common';
import {
AfterViewInit,
Component,
@@ -8,19 +9,33 @@ import {
OnInit,
ViewChild,
} from '@angular/core';
import { MatButtonModule } from '@angular/material/button';
import { MatDialog } from '@angular/material/dialog';
import { MatIconModule } from '@angular/material/icon';
import { MatTooltipModule } from '@angular/material/tooltip';
import { TranslateModule } from '@ngx-translate/core';
import { addDays, differenceInMinutes, format, parse, subDays } from 'date-fns';
import {
EPG_GET_CHANNELS_BY_RANGE,
EPG_GET_CHANNELS_BY_RANGE_RESPONSE,
} from '../../../../../shared/ipc-commands';
import { DataService } from '../../../services/data.service';
import { MomentDatePipe } from '../../../shared/pipes/moment-date.pipe';
import { EpgChannel } from '../../models/epg-channel.model';
import { EpgProgram } from '../../models/epg-program.model';
import { EpgItemDescriptionComponent } from '../epg-list/epg-item-description/epg-item-description.component';
import { COMPONENT_OVERLAY_REF } from '../video-player/video-player.component';
@Component({
standalone: true,
imports: [
CommonModule,
MatButtonModule,
MatIconModule,
MatTooltipModule,
MomentDatePipe,
TranslateModule,
],
selector: 'app-multi-epg-container',
templateUrl: './multi-epg-container.component.html',
styleUrls: ['./multi-epg-container.component.scss'],
@@ -1,7 +1,12 @@
import { AsyncPipe, CommonModule } from '@angular/common';
import { Component, EventEmitter, Input, Output } from '@angular/core';
import { MatButtonModule } from '@angular/material/button';
import { MatIconModule } from '@angular/material/icon';
import { MatSnackBar } from '@angular/material/snack-bar';
import { MatToolbarModule } from '@angular/material/toolbar';
import { MatTooltipModule } from '@angular/material/tooltip';
import { Store } from '@ngrx/store';
import { TranslateService } from '@ngx-translate/core';
import { TranslateModule, TranslateService } from '@ngx-translate/core';
import { Channel } from '../../../../../../shared/channel.interface';
import { updateFavorites } from '../../../../state/actions';
import {
@@ -11,6 +16,16 @@ import {
} from '../../../../state/selectors';
@Component({
standalone: true,
imports: [
AsyncPipe,
CommonModule,
MatIconModule,
MatButtonModule,
MatToolbarModule,
MatTooltipModule,
TranslateModule,
],
selector: 'app-toolbar',
templateUrl: './toolbar.component.html',
styleUrls: ['./toolbar.component.scss'],
@@ -1,5 +1,6 @@
import { Overlay, OverlayRef } from '@angular/cdk/overlay';
import { ComponentPortal } from '@angular/cdk/portal';
import { AsyncPipe, CommonModule } from '@angular/common';
import {
Component,
InjectionToken,
@@ -8,8 +9,9 @@ import {
OnDestroy,
OnInit,
} from '@angular/core';
import { MatSidenavModule } from '@angular/material/sidenav';
import { MatSnackBar } from '@angular/material/snack-bar';
import { ActivatedRoute, Router } from '@angular/router';
import { ActivatedRoute, Router, RouterLink } from '@angular/router';
import { Store } from '@ngrx/store';
import { StorageMap } from '@ngx-pwa/local-storage';
import { Observable, combineLatestWith, filter, map, switchMap } from 'rxjs';
@@ -31,7 +33,14 @@ import {
selectChannels,
selectCurrentEpgProgram,
} from '../../../state/selectors';
import { AudioPlayerComponent } from '../audio-player/audio-player.component';
import { EpgListComponent } from '../epg-list/epg-list.component';
import { HtmlVideoPlayerComponent } from '../html-video-player/html-video-player.component';
import { InfoOverlayComponent } from '../info-overlay/info-overlay.component';
import { MultiEpgContainerComponent } from '../multi-epg/multi-epg-container.component';
import { VjsPlayerComponent } from '../vjs-player/vjs-player.component';
import { SidebarComponent } from './sidebar/sidebar.component';
import { ToolbarComponent } from './toolbar/toolbar.component';
/** Possible sidebar view options */
export type SidebarView = 'CHANNELS' | 'PLAYLISTS';
@@ -41,6 +50,20 @@ export const COMPONENT_OVERLAY_REF = new InjectionToken(
);
@Component({
standalone: true,
imports: [
AsyncPipe,
AudioPlayerComponent,
InfoOverlayComponent,
CommonModule,
EpgListComponent,
HtmlVideoPlayerComponent,
MatSidenavModule,
RouterLink,
SidebarComponent,
ToolbarComponent,
VjsPlayerComponent,
],
templateUrl: './video-player.component.html',
styleUrls: ['./video-player.component.scss'],
})
-25
View File
@@ -2,42 +2,17 @@ import { OverlayModule } from '@angular/cdk/overlay';
import { CommonModule } from '@angular/common';
import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { RecentPlaylistsComponent } from '../home/recent-playlists/recent-playlists.component';
import { SharedModule } from '../shared/shared.module';
import { AudioPlayerComponent } from './components/audio-player/audio-player.component';
import { EpgItemDescriptionComponent } from './components/epg-list/epg-item-description/epg-item-description.component';
import { EpgListItemComponent } from './components/epg-list/epg-list-item/epg-list-item.component';
import { EpgListComponent } from './components/epg-list/epg-list.component';
import { HtmlVideoPlayerComponent } from './components/html-video-player/html-video-player.component';
import { InfoOverlayComponent } from './components/info-overlay/info-overlay.component';
import { MultiEpgContainerComponent } from './components/multi-epg/multi-epg-container.component';
import { SidebarComponent } from './components/video-player/sidebar/sidebar.component';
import { ToolbarComponent } from './components/video-player/toolbar/toolbar.component';
import { VideoPlayerComponent } from './components/video-player/video-player.component';
import { VjsPlayerComponent } from './components/vjs-player/vjs-player.component';
const routes: Routes = [{ path: '', component: VideoPlayerComponent }];
@NgModule({
imports: [
AudioPlayerComponent,
CommonModule,
EpgItemDescriptionComponent,
HtmlVideoPlayerComponent,
OverlayModule,
RecentPlaylistsComponent,
RouterModule.forChild(routes),
SidebarComponent,
SharedModule,
VjsPlayerComponent,
],
declarations: [
EpgListComponent,
EpgListItemComponent,
InfoOverlayComponent,
MultiEpgContainerComponent,
ToolbarComponent,
VideoPlayerComponent,
],
})
export class PlayerModule {}
+1
View File
@@ -6,6 +6,7 @@ import moment from 'moment';
*/
@Pipe({
name: 'momentDate',
standalone: true,
})
export class MomentDatePipe implements PipeTransform {
transform(
-3
View File
@@ -6,10 +6,8 @@ import { NgxSkeletonLoaderModule } from 'ngx-skeleton-loader';
import { NgxWhatsNewModule } from 'ngx-whats-new';
import { MaterialModule } from '../material.module';
import { FilterPipe } from './pipes/filter.pipe';
import { MomentDatePipe } from './pipes/moment-date.pipe';
@NgModule({
declarations: [MomentDatePipe],
imports: [
FilterPipe,
CommonModule,
@@ -28,7 +26,6 @@ import { MomentDatePipe } from './pipes/moment-date.pipe';
FormsModule,
MaterialModule,
NgxWhatsNewModule,
MomentDatePipe,
ReactiveFormsModule,
TranslateModule,
],