mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-08 17:06:15 -08:00
style: enhance favorites and recently viewed UI with improved layout and functionality
This commit is contained in:
1 parent
139944281c
commit
d64283e33d
5 files changed
+243
-184
No files matched your search
+31
-37
@@ -38,46 +38,40 @@
|
||||
@if (favorites().length) {
|
||||
<div class="items-grid">
|
||||
@for (item of favorites(); track $index) {
|
||||
<mat-card class="item-card" (click)="openFavorite(item)">
|
||||
<img
|
||||
[src]="
|
||||
item.poster_url ||
|
||||
item.cover ||
|
||||
'assets/default-' +
|
||||
selectedCategoryId() +
|
||||
'.png'
|
||||
"
|
||||
(error)="
|
||||
$event.target.src =
|
||||
<div class="item-card" (click)="openFavorite(item)">
|
||||
<div class="thumbnail-wrapper">
|
||||
<button
|
||||
mat-icon-button
|
||||
class="remove-button"
|
||||
[matTooltip]="'CHANNELS.REMOVE_FAVORITE' | translate"
|
||||
(click)="$event.stopPropagation(); removeFromFavorites(item)"
|
||||
>
|
||||
<mat-icon>close</mat-icon>
|
||||
</button>
|
||||
<img
|
||||
[src]="
|
||||
item.poster_url ||
|
||||
item.cover ||
|
||||
'./assets/images/default-poster.png'
|
||||
"
|
||||
[alt]="item.title"
|
||||
class="item-image"
|
||||
/>
|
||||
<mat-card-content>
|
||||
<h3>
|
||||
{{ item?.title || item?.details?.info?.name }}
|
||||
</h3>
|
||||
"
|
||||
(error)="
|
||||
$event.target.src =
|
||||
'./assets/images/default-poster.png'
|
||||
"
|
||||
[alt]="item.title"
|
||||
class="poster"
|
||||
/>
|
||||
<span class="type-badge" [class]="item.type">
|
||||
{{ item.type }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="item-info">
|
||||
<h3>{{ item?.title || item?.details?.info?.name }}</h3>
|
||||
<p class="added-date">
|
||||
Added:
|
||||
{{ item?.added_at | date: 'd MMMM, y, HH:MM' }}
|
||||
{{ item?.added_at | date: 'd MMM, HH:mm' }}
|
||||
</p>
|
||||
</mat-card-content>
|
||||
<button
|
||||
mat-icon-button
|
||||
color="warn"
|
||||
class="favorite-button"
|
||||
[matTooltip]="
|
||||
'CHANNELS.REMOVE_FAVORITE' | translate
|
||||
"
|
||||
(click)="
|
||||
$event.stopPropagation();
|
||||
removeFromFavorites(item)
|
||||
"
|
||||
>
|
||||
<mat-icon>delete</mat-icon>
|
||||
</button>
|
||||
</mat-card>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
} @else {
|
||||
|
||||
+81
-40
@@ -48,8 +48,8 @@
|
||||
|
||||
.items-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
|
||||
gap: 16px;
|
||||
grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
|
||||
gap: 20px;
|
||||
padding: 16px;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
@@ -57,54 +57,95 @@
|
||||
}
|
||||
|
||||
.item-card {
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
transition: transform 0.2s;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
||||
|
||||
&:hover {
|
||||
transform: scale(1.02);
|
||||
transform: translateY(-3px);
|
||||
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.12);
|
||||
}
|
||||
}
|
||||
|
||||
.item-image {
|
||||
width: 100%;
|
||||
aspect-ratio: 2/3;
|
||||
object-fit: cover;
|
||||
}
|
||||
.thumbnail-wrapper {
|
||||
position: relative;
|
||||
|
||||
.favorite-button {
|
||||
position: absolute;
|
||||
top: 8px;
|
||||
right: 8px;
|
||||
z-index: 1;
|
||||
}
|
||||
.poster {
|
||||
width: 100%;
|
||||
aspect-ratio: 2/3;
|
||||
object-fit: cover;
|
||||
display: block;
|
||||
}
|
||||
|
||||
mat-card-content {
|
||||
padding: 12px;
|
||||
flex-grow: 1;
|
||||
.remove-button {
|
||||
position: absolute;
|
||||
top: 4px;
|
||||
right: 4px;
|
||||
z-index: 1;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
padding: 0;
|
||||
|
||||
h3 {
|
||||
margin: 0;
|
||||
font-weight: normal;
|
||||
font-size: 14px;
|
||||
line-height: 1.2;
|
||||
max-height: 2.4em;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
display: -webkit-box;
|
||||
-webkit-line-clamp: 2;
|
||||
-webkit-box-orient: vertical;
|
||||
white-space: normal;
|
||||
&: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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.added-date {
|
||||
font-size: 12px;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
margin: 4px 0 0 0;
|
||||
.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 {
|
||||
|
||||
@@ -32,36 +32,39 @@
|
||||
</h3>
|
||||
<div class="items-grid">
|
||||
@for (item of $any(group).value; track item.id) {
|
||||
<mat-card class="item-card" (click)="openItem(item)">
|
||||
<button
|
||||
mat-icon-button
|
||||
class="remove-button"
|
||||
(click)="removeItem($event, item.id)"
|
||||
>
|
||||
<mat-icon>close</mat-icon>
|
||||
</button>
|
||||
<img
|
||||
[src]="
|
||||
item.poster_url ||
|
||||
'./assets/images/default-poster.png'
|
||||
"
|
||||
(error)="
|
||||
$event.target.src =
|
||||
<div class="item-card" (click)="openItem(item)">
|
||||
<div class="thumbnail-wrapper">
|
||||
<button
|
||||
mat-icon-button
|
||||
class="remove-button"
|
||||
matTooltip="Remove from history"
|
||||
(click)="removeItem($event, item.id)"
|
||||
>
|
||||
<mat-icon>close</mat-icon>
|
||||
</button>
|
||||
<img
|
||||
[src]="
|
||||
item.poster_url ||
|
||||
'./assets/images/default-poster.png'
|
||||
"
|
||||
[alt]="item.title"
|
||||
class="poster"
|
||||
/>
|
||||
"
|
||||
(error)="
|
||||
$event.target.src =
|
||||
'./assets/images/default-poster.png'
|
||||
"
|
||||
[alt]="item.title"
|
||||
class="poster"
|
||||
/>
|
||||
<span class="type-badge" [class]="item.type">
|
||||
{{ item.type }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="item-info">
|
||||
<h3>{{ item.title }}</h3>
|
||||
<p [class]="item.type" class="type-badge">
|
||||
{{ item.type | titlecase }}
|
||||
</p>
|
||||
<p class="viewed-at">
|
||||
{{ item.viewed_at | date : 'd MMMM, y, HH:mm' }}
|
||||
{{ item.viewed_at | date : 'd MMM, HH:mm' }}
|
||||
</p>
|
||||
</div>
|
||||
</mat-card>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
@@ -70,32 +73,35 @@
|
||||
} @else {
|
||||
<div class="items-grid">
|
||||
@for (item of recentItems(); track item.id) {
|
||||
<mat-card class="item-card" (click)="openItem(item)">
|
||||
<button
|
||||
mat-icon-button
|
||||
class="remove-button"
|
||||
(click)="removeItem($event, item.id)"
|
||||
>
|
||||
<mat-icon>close</mat-icon>
|
||||
</button>
|
||||
<img
|
||||
[src]="item.poster_url || './assets/images/default-poster.png'"
|
||||
(error)="
|
||||
$event.target.src = './assets/images/default-poster.png'
|
||||
"
|
||||
[alt]="item.title"
|
||||
class="poster"
|
||||
/>
|
||||
<div class="item-card" (click)="openItem(item)">
|
||||
<div class="thumbnail-wrapper">
|
||||
<button
|
||||
mat-icon-button
|
||||
class="remove-button"
|
||||
matTooltip="Remove from history"
|
||||
(click)="removeItem($event, item.id)"
|
||||
>
|
||||
<mat-icon>close</mat-icon>
|
||||
</button>
|
||||
<img
|
||||
[src]="item.poster_url || './assets/images/default-poster.png'"
|
||||
(error)="
|
||||
$event.target.src = './assets/images/default-poster.png'
|
||||
"
|
||||
[alt]="item.title"
|
||||
class="poster"
|
||||
/>
|
||||
<span class="type-badge" [class]="item.type">
|
||||
{{ item.type }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="item-info">
|
||||
<h3>{{ item.title }}</h3>
|
||||
<p [class]="item.type" class="type-badge">
|
||||
{{ item.type | titlecase }}
|
||||
</p>
|
||||
<p class="viewed-at">
|
||||
{{ item.viewed_at | date : 'd MMMM, y, hh:mm' }}
|
||||
{{ item.viewed_at | date : 'd MMM, HH:mm' }}
|
||||
</p>
|
||||
</div>
|
||||
</mat-card>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
} } @else {
|
||||
|
||||
@@ -25,59 +25,99 @@
|
||||
|
||||
.items-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
||||
grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.item-card {
|
||||
cursor: pointer;
|
||||
transition: transform 0.2s;
|
||||
position: relative;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
transition: transform 0.2s ease, box-shadow 0.2s ease;
|
||||
|
||||
&:hover {
|
||||
transform: scale(1.02);
|
||||
transform: translateY(-3px);
|
||||
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.12);
|
||||
}
|
||||
|
||||
.remove-button {
|
||||
position: absolute;
|
||||
top: 5px;
|
||||
right: 5px;
|
||||
z-index: 1;
|
||||
background: rgba(0, 0, 0, 0.5);
|
||||
|
||||
&:hover {
|
||||
background: rgba(0, 0, 0, 0.7);
|
||||
.thumbnail-wrapper {
|
||||
position: relative;
|
||||
|
||||
.poster {
|
||||
width: 100%;
|
||||
aspect-ratio: 2/3;
|
||||
object-fit: cover;
|
||||
display: block;
|
||||
}
|
||||
|
||||
mat-icon {
|
||||
color: white;
|
||||
.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.poster {
|
||||
width: 100%;
|
||||
height: 280px;
|
||||
object-fit: cover;
|
||||
}
|
||||
.item-info {
|
||||
padding: 8px 10px;
|
||||
|
||||
.item-info {
|
||||
padding: 16px;
|
||||
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;
|
||||
}
|
||||
|
||||
h3 {
|
||||
margin: 0;
|
||||
font-size: 16px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 8px 0;
|
||||
}
|
||||
|
||||
.viewed-at {
|
||||
font-size: 12px;
|
||||
.viewed-at {
|
||||
margin: 4px 0 0;
|
||||
font-size: 0.7rem;
|
||||
opacity: 0.6;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,26 +133,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
.type-badge {
|
||||
display: inline-block;
|
||||
padding: 0.25rem 0.5rem;
|
||||
border-radius: 4px;
|
||||
font-size: 0.8rem;
|
||||
text-transform: uppercase;
|
||||
&.live {
|
||||
background: #e53935;
|
||||
color: white;
|
||||
}
|
||||
&.movie {
|
||||
background: #1e88e5;
|
||||
color: white;
|
||||
}
|
||||
&.series {
|
||||
background: #43a047;
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
|
||||
.playlist-section {
|
||||
.section-title {
|
||||
margin: 0 0 1.5rem;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { DatePipe, KeyValuePipe, TitleCasePipe } from '@angular/common';
|
||||
import { DatePipe, KeyValuePipe } from '@angular/common';
|
||||
import { Component, computed, inject, Optional } from '@angular/core';
|
||||
import { MatButton, MatIconButton } from '@angular/material/button';
|
||||
import { MatCard } from '@angular/material/card';
|
||||
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 { XtreamStore } from '../stores/xtream.store';
|
||||
@@ -14,11 +14,9 @@ import { XtreamStore } from '../stores/xtream.store';
|
||||
DatePipe,
|
||||
KeyValuePipe,
|
||||
MatButton,
|
||||
MatCard,
|
||||
MatIcon,
|
||||
MatIconButton,
|
||||
MatIconButton,
|
||||
TitleCasePipe,
|
||||
MatTooltip,
|
||||
],
|
||||
providers: [],
|
||||
templateUrl: './recently-viewed.component.html',
|
||||
|
||||
Reference in new issue
Block a user