mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-11 02:46:16 -08:00
Merge pull request #724 from 4gray/refactor/store-refactoring
refactor/store-refactoring
This commit is contained in:
44 files changed
+3287
-1070
No files matched your search
@@ -4,69 +4,148 @@
|
||||
<button class="hero__back-button" (click)="onBack()">
|
||||
<mat-icon>arrow_back</mat-icon>
|
||||
</button>
|
||||
|
||||
<!-- Backdrop Image -->
|
||||
<div
|
||||
class="hero__backdrop"
|
||||
[class.hero__backdrop--blurred]="!backdropUrl"
|
||||
[style.backgroundImage]="
|
||||
backdropUrl
|
||||
? 'url(' + backdropUrl + ')'
|
||||
: posterUrl
|
||||
? 'url(' + posterUrl + ')'
|
||||
: 'none'
|
||||
"
|
||||
></div>
|
||||
|
||||
<!-- Gradient Vignette Overlay -->
|
||||
<div class="hero__vignette"></div>
|
||||
|
||||
<!-- Hero Content -->
|
||||
<div class="hero__content">
|
||||
<!-- Poster Card -->
|
||||
<div class="poster">
|
||||
@if (posterUrl) {
|
||||
<img
|
||||
class="poster__image"
|
||||
[src]="posterUrl"
|
||||
(error)="
|
||||
$event.target.src = './assets/images/default-poster.png'
|
||||
"
|
||||
alt="Poster"
|
||||
@if (isLoading) {
|
||||
<div class="hero__content">
|
||||
<!-- Skeleton Poster -->
|
||||
<div class="poster">
|
||||
<ngx-skeleton-loader
|
||||
count="1"
|
||||
[theme]="{
|
||||
height: '100%',
|
||||
width: '100%',
|
||||
'border-radius': '8px',
|
||||
'aspect-ratio': '2/3',
|
||||
display: 'block'
|
||||
}"
|
||||
/>
|
||||
} @else {
|
||||
<div class="poster__placeholder">No Poster</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
<!-- Skeleton Details -->
|
||||
<div class="details">
|
||||
<!-- Title -->
|
||||
<ngx-skeleton-loader
|
||||
count="1"
|
||||
[theme]="{
|
||||
height: '40px',
|
||||
width: '60%',
|
||||
'margin-bottom': '16px',
|
||||
display: 'block'
|
||||
}"
|
||||
/>
|
||||
|
||||
<!-- Tags -->
|
||||
<div style="display: flex; gap: 8px; margin-bottom: 16px">
|
||||
<ngx-skeleton-loader
|
||||
count="3"
|
||||
[theme]="{
|
||||
height: '24px',
|
||||
width: '60px',
|
||||
display: 'block'
|
||||
}"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Description -->
|
||||
<ngx-skeleton-loader
|
||||
count="3"
|
||||
[theme]="{
|
||||
height: '16px',
|
||||
'margin-bottom': '8px',
|
||||
width: '100%',
|
||||
display: 'block'
|
||||
}"
|
||||
/>
|
||||
|
||||
<!-- Meta -->
|
||||
<div style="margin-top: 20px; display: flex; gap: 32px">
|
||||
<ngx-skeleton-loader
|
||||
count="2"
|
||||
[theme]="{
|
||||
height: '40px',
|
||||
width: '100px',
|
||||
display: 'block'
|
||||
}"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Buttons -->
|
||||
<div style="margin-top: 24px; display: flex; gap: 12px">
|
||||
<ngx-skeleton-loader
|
||||
count="2"
|
||||
[theme]="{
|
||||
height: '48px',
|
||||
width: '140px',
|
||||
display: 'block'
|
||||
}"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
} @else {
|
||||
<!-- Backdrop Image -->
|
||||
<div
|
||||
class="hero__backdrop"
|
||||
[class.hero__backdrop--blurred]="!backdropUrl"
|
||||
[style.backgroundImage]="
|
||||
backdropUrl
|
||||
? 'url(' + backdropUrl + ')'
|
||||
: posterUrl
|
||||
? 'url(' + posterUrl + ')'
|
||||
: 'none'
|
||||
"
|
||||
></div>
|
||||
|
||||
<!-- Details -->
|
||||
<div class="details">
|
||||
<h1 class="details__title">{{ title }}</h1>
|
||||
<!-- Gradient Vignette Overlay -->
|
||||
<div class="hero__vignette"></div>
|
||||
|
||||
<!-- Tags Slot -->
|
||||
<div class="details__tags">
|
||||
<ng-content select="[hero-tags]"></ng-content>
|
||||
<!-- Hero Content -->
|
||||
<div class="hero__content">
|
||||
<!-- Poster Card -->
|
||||
<div class="poster">
|
||||
@if (posterUrl) {
|
||||
<img
|
||||
class="poster__image"
|
||||
[src]="posterUrl"
|
||||
(error)="
|
||||
$event.target.src =
|
||||
'./assets/images/default-poster.png'
|
||||
"
|
||||
alt="Poster"
|
||||
/>
|
||||
} @else {
|
||||
<div class="poster__placeholder">No Poster</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
<!-- Description -->
|
||||
@if (description) {
|
||||
<p class="details__description">
|
||||
{{ description }}
|
||||
</p>
|
||||
}
|
||||
<!-- Details -->
|
||||
<div class="details">
|
||||
<h1 class="details__title">{{ title }}</h1>
|
||||
|
||||
<!-- Metadata Grid Slot -->
|
||||
<div class="details__meta">
|
||||
<ng-content select="[hero-meta]"></ng-content>
|
||||
<!-- Tags Slot -->
|
||||
<div class="details__tags">
|
||||
<ng-content select="[hero-tags]"></ng-content>
|
||||
</div>
|
||||
|
||||
<!-- Description -->
|
||||
@if (description) {
|
||||
<p class="details__description">
|
||||
{{ description }}
|
||||
</p>
|
||||
}
|
||||
|
||||
<!-- Metadata Grid Slot -->
|
||||
<div class="details__meta">
|
||||
<ng-content select="[hero-meta]"></ng-content>
|
||||
</div>
|
||||
|
||||
<!-- Action Buttons Slot -->
|
||||
<div class="action-buttons">
|
||||
<ng-content select="[hero-actions]"></ng-content>
|
||||
</div>
|
||||
|
||||
<!-- Additional Content (Seasons, Trailers, etc.) -->
|
||||
<ng-content></ng-content>
|
||||
</div>
|
||||
|
||||
<!-- Action Buttons Slot -->
|
||||
<div class="action-buttons">
|
||||
<ng-content select="[hero-actions]"></ng-content>
|
||||
</div>
|
||||
|
||||
<!-- Additional Content (Seasons, Trailers, etc.) -->
|
||||
<ng-content></ng-content>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
}
|
||||
</section>
|
||||
@@ -471,3 +471,30 @@
|
||||
.hero__content {
|
||||
animation: fadeInUp 0.6s ease-out 0.2s both;
|
||||
}
|
||||
|
||||
// Skeleton Loader Overrides
|
||||
ngx-skeleton-loader {
|
||||
display: block;
|
||||
width: 100%;
|
||||
|
||||
// Ensure the loader inside poster takes up the correct aspect ratio space
|
||||
::ng-deep .loader {
|
||||
display: block;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
:host-context(.dark-theme) {
|
||||
ngx-skeleton-loader {
|
||||
::ng-deep .loader {
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.poster {
|
||||
// ... existing styles ...
|
||||
// Ensure poster container has height/ratio for skeleton
|
||||
min-height: 330px; // Fallback
|
||||
aspect-ratio: 2 / 3;
|
||||
}
|
||||
@@ -2,11 +2,17 @@ import { 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 { NgxSkeletonLoaderComponent } from 'ngx-skeleton-loader';
|
||||
|
||||
@Component({
|
||||
selector: 'app-content-hero',
|
||||
standalone: true,
|
||||
imports: [CommonModule, MatIconModule, MatButtonModule],
|
||||
imports: [
|
||||
CommonModule,
|
||||
MatIconModule,
|
||||
MatButtonModule,
|
||||
NgxSkeletonLoaderComponent,
|
||||
],
|
||||
templateUrl: './content-hero.component.html',
|
||||
styleUrls: ['./content-hero.component.scss'],
|
||||
})
|
||||
@@ -15,6 +21,7 @@ export class ContentHeroComponent {
|
||||
@Input() description: string | undefined;
|
||||
@Input() posterUrl: string | undefined;
|
||||
@Input() backdropUrl: string | undefined;
|
||||
@Input() isLoading = false;
|
||||
|
||||
@Output() backClicked = new EventEmitter<void>();
|
||||
|
||||
|
||||
@@ -222,11 +222,14 @@ export class RecentPlaylistsComponent {
|
||||
onConfirm: async () => {
|
||||
try {
|
||||
// Delete all content and categories for this playlist
|
||||
// This returns the xtreamIds of favorites and recently viewed items
|
||||
const { favoritedXtreamIds, recentlyViewedXtreamIds } =
|
||||
await this.databaseService.deleteXtreamPlaylistContent(
|
||||
item._id
|
||||
);
|
||||
// This returns the xtreamIds of favorites, recently viewed items, and hidden categories
|
||||
const {
|
||||
favoritedXtreamIds,
|
||||
recentlyViewedXtreamIds,
|
||||
hiddenCategories,
|
||||
} = await this.databaseService.deleteXtreamPlaylistContent(
|
||||
item._id
|
||||
);
|
||||
|
||||
const updateDate = Date.now();
|
||||
|
||||
@@ -250,6 +253,7 @@ export class RecentPlaylistsComponent {
|
||||
JSON.stringify({
|
||||
favoritedXtreamIds,
|
||||
recentlyViewedXtreamIds,
|
||||
hiddenCategories,
|
||||
})
|
||||
);
|
||||
|
||||
|
||||
Reference in new issue
Block a user