feat(xtream): add portal status indicator in playlist-item cmp

This commit is contained in:
4gray committed 2025-02-22 20:00:20 +01:00
1 parent 23434d367b
commit a43d43782a
4 files changed
+112 -10

No files matched your search

@@ -18,13 +18,18 @@
[matTooltip]="'HOME.PLAYLISTS.ADDED_VIA_FILE' | translate"
>folder</mat-icon
>
<mat-icon
matListItemIcon
class="upload-type-icon"
*ngIf="item?.serverUrl"
[matTooltip]="'HOME.PLAYLISTS.XTREAM_PLAYLIST' | translate"
>public</mat-icon
>
<div class="icon-container" *ngIf="item?.serverUrl" matListItemIcon>
<mat-icon
class="upload-type-icon"
[matTooltip]="'HOME.PLAYLISTS.XTREAM_PLAYLIST' | translate"
>public</mat-icon
>
<span
class="status-dot"
[class]="getStatusClass()"
[matTooltip]="'Portal status: ' + portalStatus"
></span>
</div>
<mat-icon
matListItemIcon
class="upload-type-icon"
@@ -32,7 +37,9 @@
[matTooltip]="'HOME.PLAYLISTS.STALKER_PORTAL' | translate"
>dashboard</mat-icon
>
<div matListItemTitle>{{ item.title || item.filename }}</div>
<div matListItemTitle>
{{ item.title || item.filename }}
</div>
<div matListItemLine class="meta">
<ng-container
*ngIf="!item.serverUrl && !item.portalUrl; else xtreamPlaylist"
@@ -58,3 +58,34 @@ mat-list-item {
display: none;
}
}
.icon-container {
position: relative;
display: inline-flex;
}
.status-dot {
position: absolute;
width: 10px;
height: 10px;
border-radius: 50%;
right: 0;
bottom: 0;
border: 1px solid var(--background-color, #fff);
}
.status-active {
background-color: #4caf50;
}
.status-inactive {
background-color: #f44336;
}
.status-expired {
background-color: #ff9800;
}
.status-unavailable {
background-color: #444444;
}
@@ -1,12 +1,20 @@
import { DragDropModule } from '@angular/cdk/drag-drop';
import { DatePipe, NgIf } from '@angular/common';
import { Component, EventEmitter, Input, Output } from '@angular/core';
import {
Component,
EventEmitter,
Input,
OnInit,
Output,
inject,
} from '@angular/core';
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 { TranslateModule } from '@ngx-translate/core';
import { DataService } from '../../../services/data.service';
import { PlaylistMeta } from '../../../shared/playlist-meta.type';
@Component({
@@ -26,7 +34,7 @@ import { PlaylistMeta } from '../../../shared/playlist-meta.type';
TranslateModule,
],
})
export class PlaylistItemComponent {
export class PlaylistItemComponent implements OnInit {
@Input() item: PlaylistMeta;
@Input() showActions = true;
@@ -34,4 +42,49 @@ export class PlaylistItemComponent {
@Output() playlistClicked = new EventEmitter<string>();
@Output() refreshClicked = new EventEmitter<PlaylistMeta>();
@Output() removeClicked = new EventEmitter<string>();
portalStatus: 'active' | 'inactive' | 'expired' | 'unavailable' =
'unavailable';
private readonly dataService = inject(DataService);
// eslint-disable-next-line @typescript-eslint/no-misused-promises
async ngOnInit() {
if (this.item?.serverUrl) {
await this.checkPortalStatus();
}
}
private async checkPortalStatus() {
try {
const response = await this.dataService.fetchData(
`${this.item.serverUrl}/player_api.php`,
{
username: this.item.username,
password: this.item.password,
action: 'get_account_info',
}
);
if (!response?.user_info?.status) {
this.portalStatus = 'unavailable';
return;
}
if (response.user_info.status === 'Active') {
const expDate = new Date(
parseInt(response.user_info.exp_date) * 1000
);
this.portalStatus = expDate < new Date() ? 'expired' : 'active';
} else {
this.portalStatus = 'inactive';
}
} catch (error) {
console.error('Error checking portal status:', error);
this.portalStatus = 'unavailable';
}
}
getStatusClass(): string {
return `status-${this.portalStatus}`;
}
}
@@ -6,6 +6,17 @@
-webkit-box-orient: vertical;
font-size: 0.9em;
max-width: 290px;
transition: color 0.2s ease;
width: 100%;
position: relative;
overflow: hidden;
&:hover {
overflow: visible;
text-overflow: clip;
animation: marquee 6s linear infinite;
}
}
.active {