From 2f1e455330fdbd9ca1b4ba79a540931a815691ff Mon Sep 17 00:00:00 2001 From: 4gray Date: Wed, 4 Feb 2026 22:13:23 +0100 Subject: [PATCH] refactor: refactor favorites and recently viewed components to use content card --- .../favorites-layout.component.html | 44 ++------ .../favorites-layout.component.scss | 97 +---------------- .../favorites-layout.component.ts | 10 +- apps/web/src/app/shared/components/index.ts | 1 + .../recently-viewed.component.html | 82 ++++---------- .../recently-viewed.component.scss | 100 +----------------- .../recently-viewed.component.ts | 10 +- .../search-results.component.html | 32 ++---- .../search-results.component.scss | 85 +-------------- .../search-results.component.ts | 4 +- 10 files changed, 55 insertions(+), 410 deletions(-) diff --git a/apps/web/src/app/shared/components/favorites-layout/favorites-layout.component.html b/apps/web/src/app/shared/components/favorites-layout/favorites-layout.component.html index 74ebad294..bd1762abe 100644 --- a/apps/web/src/app/shared/components/favorites-layout/favorites-layout.component.html +++ b/apps/web/src/app/shared/components/favorites-layout/favorites-layout.component.html @@ -38,40 +38,16 @@ @if (favorites().length) {
@for (item of favorites(); track $index) { -
-
- - - - {{ item.type }} - -
-
-

{{ item?.title || item?.details?.info?.name }}

-

- {{ item?.added_at | date: 'd MMM, HH:mm' }} -

-
-
+ }
} @else { diff --git a/apps/web/src/app/shared/components/favorites-layout/favorites-layout.component.scss b/apps/web/src/app/shared/components/favorites-layout/favorites-layout.component.scss index 32ecc6b24..c9fc7c64a 100644 --- a/apps/web/src/app/shared/components/favorites-layout/favorites-layout.component.scss +++ b/apps/web/src/app/shared/components/favorites-layout/favorites-layout.component.scss @@ -1,4 +1,5 @@ @use '@angular/material' as mat; +@use '../../../shared/styles/content-grid' as grid; :host { display: flex; @@ -47,107 +48,13 @@ } .items-grid { - display: grid; - grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); - gap: 20px; + @include grid.content-grid(140px, 20px); padding: 16px; width: 100%; box-sizing: border-box; margin: 0; } -.item-card { - cursor: pointer; - border-radius: 8px; - overflow: hidden; - transition: transform 0.2s ease, box-shadow 0.2s ease; - - &:hover { - transform: translateY(-3px); - box-shadow: 0 6px 16px rgba(0, 0, 0, 0.12); - } - - .thumbnail-wrapper { - position: relative; - - .poster { - width: 100%; - aspect-ratio: 2/3; - object-fit: cover; - display: block; - } - - .remove-button { - position: absolute; - top: 4px; - right: 4px; - z-index: 1; - background: rgba(0, 0, 0, 0.5); - width: 28px; - height: 28px; - padding: 0; - - &:hover { - background: rgba(0, 0, 0, 0.8); - } - - mat-icon { - color: white; - font-size: 18px; - width: 18px; - height: 18px; - } - } - - .type-badge { - position: absolute; - top: 6px; - left: 6px; - padding: 3px 6px; - border-radius: 4px; - font-size: 9px; - font-weight: 700; - text-transform: uppercase; - letter-spacing: 0.5px; - backdrop-filter: blur(8px); - -webkit-backdrop-filter: blur(8px); - background: rgba(0, 0, 0, 0.6); - color: #fff; - - &.live { - color: #ef5350; - } - &.movie { - color: #42a5f5; - } - &.series { - color: #66bb6a; - } - } - } - - .item-info { - padding: 8px 10px; - - h3 { - margin: 0; - font-size: 0.8rem; - font-weight: 500; - line-height: 1.3; - display: -webkit-box; - -webkit-line-clamp: 2; - -webkit-box-orient: vertical; - overflow: hidden; - } - - .added-date { - margin: 4px 0 0; - font-size: 0.7rem; - opacity: 0.6; - } - } -} - .no-items { display: flex; flex-direction: column; diff --git a/apps/web/src/app/shared/components/favorites-layout/favorites-layout.component.ts b/apps/web/src/app/shared/components/favorites-layout/favorites-layout.component.ts index 07b31e81d..4750186c0 100644 --- a/apps/web/src/app/shared/components/favorites-layout/favorites-layout.component.ts +++ b/apps/web/src/app/shared/components/favorites-layout/favorites-layout.component.ts @@ -1,12 +1,9 @@ -import { DatePipe } from '@angular/common'; import { Component, input, output } from '@angular/core'; -import { MatIconButton } from '@angular/material/button'; -import { MatCardModule } from '@angular/material/card'; import { MatIcon } from '@angular/material/icon'; -import { MatTooltip } from '@angular/material/tooltip'; import { TranslatePipe } from '@ngx-translate/core'; import { PlaylistSwitcherComponent, ResizableDirective } from 'components'; import { CategoryViewComponent } from '../../../xtream-tauri/category-view/category-view.component'; +import { ContentCardComponent } from '../content-card/content-card.component'; @Component({ selector: 'app-favorites-layout', @@ -17,11 +14,8 @@ import { CategoryViewComponent } from '../../../xtream-tauri/category-view/categ ], imports: [ CategoryViewComponent, - DatePipe, - MatCardModule, + ContentCardComponent, MatIcon, - MatIconButton, - MatTooltip, PlaylistSwitcherComponent, ResizableDirective, TranslatePipe, diff --git a/apps/web/src/app/shared/components/index.ts b/apps/web/src/app/shared/components/index.ts index abd4bb244..e5775eb61 100644 --- a/apps/web/src/app/shared/components/index.ts +++ b/apps/web/src/app/shared/components/index.ts @@ -1 +1,2 @@ +export * from './content-card/content-card.component'; export * from './header/header.component'; diff --git a/apps/web/src/app/xtream-tauri/recently-viewed/recently-viewed.component.html b/apps/web/src/app/xtream-tauri/recently-viewed/recently-viewed.component.html index 6e3da045b..2e4b318ee 100644 --- a/apps/web/src/app/xtream-tauri/recently-viewed/recently-viewed.component.html +++ b/apps/web/src/app/xtream-tauri/recently-viewed/recently-viewed.component.html @@ -32,39 +32,16 @@
@for (item of $any(group).value; track item.id) { -
-
- - - - {{ item.type }} - -
-
-

{{ item.title }}

-

- {{ item.viewed_at | date : 'd MMM, HH:mm' }} -

-
-
+ }
@@ -73,35 +50,16 @@ } @else {
@for (item of recentItems(); track item.id) { -
-
- - - - {{ item.type }} - -
-
-

{{ item.title }}

-

- {{ item.viewed_at | date : 'd MMM, HH:mm' }} -

-
-
+ }
} } @else { diff --git a/apps/web/src/app/xtream-tauri/recently-viewed/recently-viewed.component.scss b/apps/web/src/app/xtream-tauri/recently-viewed/recently-viewed.component.scss index b2a151909..b11640007 100644 --- a/apps/web/src/app/xtream-tauri/recently-viewed/recently-viewed.component.scss +++ b/apps/web/src/app/xtream-tauri/recently-viewed/recently-viewed.component.scss @@ -1,3 +1,5 @@ +@use '../../shared/styles/content-grid' as grid; + :host { display: flex; flex-direction: column; @@ -24,101 +26,7 @@ } .items-grid { - display: grid; - grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); - gap: 20px; -} - -.item-card { - cursor: pointer; - border-radius: 8px; - overflow: hidden; - transition: transform 0.2s ease, box-shadow 0.2s ease; - - &:hover { - transform: translateY(-3px); - box-shadow: 0 6px 16px rgba(0, 0, 0, 0.12); - } - - .thumbnail-wrapper { - position: relative; - - .poster { - width: 100%; - aspect-ratio: 2/3; - object-fit: cover; - display: block; - } - - .remove-button { - position: absolute; - top: 4px; - right: 4px; - z-index: 1; - background: rgba(0, 0, 0, 0.5); - width: 28px; - height: 28px; - padding: 0; - - &:hover { - background: rgba(0, 0, 0, 0.8); - } - - mat-icon { - color: white; - font-size: 18px; - width: 18px; - height: 18px; - } - } - - .type-badge { - position: absolute; - top: 6px; - left: 6px; - padding: 3px 6px; - border-radius: 4px; - font-size: 9px; - font-weight: 700; - text-transform: uppercase; - letter-spacing: 0.5px; - backdrop-filter: blur(8px); - -webkit-backdrop-filter: blur(8px); - background: rgba(0, 0, 0, 0.6); - color: #fff; - - &.live { - color: #ef5350; - } - &.movie { - color: #42a5f5; - } - &.series { - color: #66bb6a; - } - } - } - - .item-info { - padding: 8px 10px; - - h3 { - margin: 0; - font-size: 0.8rem; - font-weight: 500; - line-height: 1.3; - display: -webkit-box; - -webkit-line-clamp: 2; - -webkit-box-orient: vertical; - overflow: hidden; - } - - .viewed-at { - margin: 4px 0 0; - font-size: 0.7rem; - opacity: 0.6; - } - } + @include grid.content-grid(140px, 20px); } .no-items { @@ -180,4 +88,4 @@ background: var(--surface); } } -} \ No newline at end of file +} diff --git a/apps/web/src/app/xtream-tauri/recently-viewed/recently-viewed.component.ts b/apps/web/src/app/xtream-tauri/recently-viewed/recently-viewed.component.ts index 5a8bc51f6..8bdbc4fc0 100644 --- a/apps/web/src/app/xtream-tauri/recently-viewed/recently-viewed.component.ts +++ b/apps/web/src/app/xtream-tauri/recently-viewed/recently-viewed.component.ts @@ -1,22 +1,21 @@ -import { DatePipe, KeyValuePipe } from '@angular/common'; +import { KeyValuePipe } from '@angular/common'; import { Component, computed, inject, Optional } from '@angular/core'; import { MatButton, MatIconButton } from '@angular/material/button'; import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material/dialog'; import { MatIcon } from '@angular/material/icon'; -import { MatTooltip } from '@angular/material/tooltip'; import { ActivatedRoute, Router } from '@angular/router'; import groupBy from 'lodash/groupBy'; +import { ContentCardComponent } from '../../shared/components/content-card/content-card.component'; import { XtreamStore } from '../stores/xtream.store'; @Component({ selector: 'app-recently-viewed', imports: [ - DatePipe, + ContentCardComponent, KeyValuePipe, MatButton, MatIcon, MatIconButton, - MatTooltip, ], providers: [], templateUrl: './recently-viewed.component.html', @@ -88,8 +87,7 @@ export class RecentlyViewedComponent { } } - removeItem(event: Event, itemId: number) { - event.stopPropagation(); + onRemoveItem(itemId: number) { this.xtreamStore.removeRecentItem({ itemId, playlistId: this.currentPlaylist().id, diff --git a/apps/web/src/app/xtream-tauri/search-results/search-results.component.html b/apps/web/src/app/xtream-tauri/search-results/search-results.component.html index 498bdb4d1..bdec05359 100644 --- a/apps/web/src/app/xtream-tauri/search-results/search-results.component.html +++ b/apps/web/src/app/xtream-tauri/search-results/search-results.component.html @@ -75,31 +75,13 @@
@for (item of xtreamStore.searchResults(); track item.id) { -
-
- @if (item.poster_url) { - - } @else { -
- {{ item.type === 'live' ? 'live_tv' : item.type === 'series' ? 'tv' : 'movie' }} -
- } - - {{ item.type }} - -
-
-

{{ item.title }}

-
-
+ }
} } @else if (searchTerm() && searchTerm().length >= 3) { diff --git a/apps/web/src/app/xtream-tauri/search-results/search-results.component.scss b/apps/web/src/app/xtream-tauri/search-results/search-results.component.scss index 823db5e3d..538c1ed92 100644 --- a/apps/web/src/app/xtream-tauri/search-results/search-results.component.scss +++ b/apps/web/src/app/xtream-tauri/search-results/search-results.component.scss @@ -1,3 +1,5 @@ +@use '../../shared/styles/content-grid' as grid; + :host { display: flex; flex-direction: column; @@ -80,89 +82,8 @@ overflow-y: auto; .results-grid { - display: grid; - grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); - gap: 20px; + @include grid.content-grid(140px, 20px); padding-bottom: 1rem; - - .result-card { - cursor: pointer; - border-radius: 8px; - overflow: hidden; - transition: transform 0.2s ease, box-shadow 0.2s ease; - - &:hover { - transform: translateY(-3px); - box-shadow: 0 6px 16px rgba(0, 0, 0, 0.12); - } - - .thumbnail-wrapper { - position: relative; - - .poster, - .poster-placeholder { - width: 100%; - aspect-ratio: 2/3; - object-fit: cover; - display: block; - } - - .poster-placeholder { - display: flex; - align-items: center; - justify-content: center; - background: rgba(128, 128, 128, 0.1); - - mat-icon { - font-size: 36px; - width: 36px; - height: 36px; - opacity: 0.25; - } - } - - .type-badge { - position: absolute; - top: 6px; - left: 6px; - padding: 3px 6px; - border-radius: 4px; - font-size: 9px; - font-weight: 700; - text-transform: uppercase; - letter-spacing: 0.5px; - backdrop-filter: blur(8px); - -webkit-backdrop-filter: blur(8px); - background: rgba(0, 0, 0, 0.6); - color: #fff; - - &.live { - color: #ef5350; - } - &.movie { - color: #42a5f5; - } - &.series { - color: #66bb6a; - } - } - } - - .card-info { - padding: 8px 10px; - - h3 { - margin: 0; - font-size: 0.8rem; - font-weight: 500; - line-height: 1.3; - display: -webkit-box; - -webkit-line-clamp: 2; - -webkit-box-orient: vertical; - overflow: hidden; - } - } - } } } diff --git a/apps/web/src/app/xtream-tauri/search-results/search-results.component.ts b/apps/web/src/app/xtream-tauri/search-results/search-results.component.ts index 4f020a352..e60ab18dd 100644 --- a/apps/web/src/app/xtream-tauri/search-results/search-results.component.ts +++ b/apps/web/src/app/xtream-tauri/search-results/search-results.component.ts @@ -10,7 +10,6 @@ import { } from '@angular/core'; import { FormsModule } from '@angular/forms'; import { MatIconButton } from '@angular/material/button'; -import { MatCardModule } from '@angular/material/card'; import { MatCheckboxModule } from '@angular/material/checkbox'; import { MAT_DIALOG_DATA, @@ -26,6 +25,7 @@ import groupBy from 'lodash/groupBy'; import { DatabaseService } from 'services'; import { XtreamContentItem } from '../data-sources/xtream-data-source.interface'; import { ContentType } from '../xtream-state'; +import { ContentCardComponent } from '../../shared/components/content-card/content-card.component'; import { SearchFormComponent } from '../../shared/components/search-form/search-form.component'; import { SearchResultItemComponent } from '../../shared/components/search-result-item/search-result-item.component'; import { XtreamStore } from '../stores/xtream.store'; @@ -38,9 +38,9 @@ interface SearchResultsData { @Component({ selector: 'app-search-results', imports: [ + ContentCardComponent, FormsModule, KeyValuePipe, - MatCardModule, MatCheckboxModule, MatDialogModule, MatFormFieldModule,