fix(ui): single watched indicator-toggle on episode cards

Watched state was signalled three times on one card: the green badge,
the hover toggle in the top-right cluster, and a checkmark appended to
the title. Now one control does both jobs — a toggle at the top-left
of the artwork that looks like the old green badge when watched
(always visible) and appears as a ghost circle on hover when not.
The right cluster keeps only the download action; the title checkmark
is gone.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
4grayandClaude Fable 5 committed 2026-07-04 20:23:28 +02:00
1 parent 182d934423
commit 46975c5260
3 files changed
+79 -30

No files matched your search

@@ -133,9 +133,27 @@
</div>
}
}
<app-watched-badge
[isWatched]="isEpisodeWatched(episode)"
/>
<button
mat-icon-button
class="episode-card__watched-toggle"
[class.episode-card__watched-toggle--watched]="
isEpisodeWatched(episode)
"
data-testid="episode-watched-toggle"
(click)="toggleWatched($event, episode)"
[matTooltip]="
(isEpisodeWatched(episode)
? 'XTREAM.MARK_UNWATCHED'
: 'XTREAM.MARK_WATCHED'
) | translate
"
>
<mat-icon>{{
isEpisodeWatched(episode)
? 'check_circle'
: 'radio_button_unchecked'
}}</mat-icon>
</button>
<div class="episode-card__actions">
@if (isElectron()) {
@if (isEpisodeDownloaded(episode)) {
@@ -188,23 +206,6 @@
</button>
}
}
<button
mat-icon-button
class="toggle-watched-btn"
(click)="toggleWatched($event, episode)"
[matTooltip]="
(isEpisodeWatched(episode)
? 'XTREAM.MARK_UNWATCHED'
: 'XTREAM.MARK_WATCHED'
) | translate
"
>
<mat-icon>{{
isEpisodeWatched(episode)
? 'check_circle'
: 'radio_button_unchecked'
}}</mat-icon>
</button>
</div>
<div class="episode-card__overlay">
<div class="episode-card__play">
@@ -208,20 +208,71 @@ app-season-tabs {
box-shadow: 0 0 0 2px color-mix(in srgb, var(--mat-sys-primary) 40%, transparent);
}
// Watched state
// Watched state — the sole indicator is the watched toggle on the
// thumbnail (plus the dimmed card); no extra checkmarks elsewhere.
&--watched {
opacity: 0.7;
&:hover {
opacity: 1;
}
}
.episode-card__title::after {
content: ' ✓';
color: var(--mat-sys-tertiary);
font-weight: bold;
margin-left: 4px;
// Watched indicator + toggle in one control, top-left on the artwork.
// Watched: always visible, green (the old badge look). Unwatched: ghost
// circle revealed on card hover.
&__watched-toggle {
position: absolute;
top: 6px;
left: 6px;
z-index: 2;
width: 28px;
height: 28px;
padding: 0;
display: inline-flex;
align-items: center;
justify-content: center;
color: rgba(255, 255, 255, 0.75);
background: rgba(0, 0, 0, 0.6);
border: 1px solid rgba(255, 255, 255, 0.14);
opacity: 0;
transition:
opacity 0.2s ease,
background 0.2s ease,
color 0.2s ease;
mat-icon {
font-size: 17px;
width: 17px;
height: 17px;
line-height: 17px;
}
&:hover {
color: white;
background: rgba(0, 0, 0, 0.85);
}
&:focus-visible {
opacity: 1;
}
&--watched {
opacity: 1;
color: white;
background: rgba(70, 211, 105, 0.9);
border-color: transparent;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
&:hover {
color: white;
background: rgba(70, 211, 105, 1);
}
}
}
&:hover .episode-card__watched-toggle {
opacity: 1;
}
&:hover {
@@ -303,8 +354,7 @@ app-season-tabs {
display: flex;
gap: 4px;
.download-btn,
.toggle-watched-btn {
.download-btn {
color: rgba(255, 255, 255, 0.75);
background: rgba(0, 0, 0, 0.6);
border: 1px solid rgba(255, 255, 255, 0.14);
@@ -31,7 +31,6 @@ import {
} from '@iptvnator/shared/interfaces';
import { DownloadsService } from '@iptvnator/services';
import { ProgressCapsuleComponent } from '../progress-capsule/progress-capsule.component';
import { WatchedBadgeComponent } from '../watched-badge/watched-badge.component';
import {
buildXtreamEpisodeDownloadRequest,
getEpisodeDownloadId,
@@ -76,7 +75,6 @@ export interface SeasonContainerPlaybackToggleRequest {
ProgressCapsuleComponent,
SeasonTabsComponent,
TranslateModule,
WatchedBadgeComponent,
],
})
export class SeasonContainerComponent implements OnInit {