Files
SimpleCloudNotifier/webapp/src/app/features/channels/channel-list/channel-list.component.ts
Mike Schwörer c81143ecdc
All checks were successful
Build Docker and Deploy / Build Docker Container (push) Successful in 1m0s
Build Docker and Deploy / Run Unit-Tests (push) Successful in 9m18s
Build Docker and Deploy / Deploy to Server (push) Successful in 22s
More webapp changes+fixes
2025-12-07 04:21:11 +01:00

180 lines
5.7 KiB
TypeScript

import { Component, inject, signal, computed, OnInit } from '@angular/core';
import { CommonModule, DatePipe } from '@angular/common';
import { Router, RouterLink } from '@angular/router';
import { NzTableModule } from 'ng-zorro-antd/table';
import { NzButtonModule } from 'ng-zorro-antd/button';
import { NzIconModule } from 'ng-zorro-antd/icon';
import { NzTagModule } from 'ng-zorro-antd/tag';
import { NzBadgeModule } from 'ng-zorro-antd/badge';
import { NzEmptyModule } from 'ng-zorro-antd/empty';
import { NzCardModule } from 'ng-zorro-antd/card';
import { NzToolTipModule } from 'ng-zorro-antd/tooltip';
import { NzTabsModule } from 'ng-zorro-antd/tabs';
import { NzAlertModule } from 'ng-zorro-antd/alert';
import { ApiService } from '../../../core/services/api.service';
import { AuthService } from '../../../core/services/auth.service';
import { NotificationService } from '../../../core/services/notification.service';
import { SettingsService } from '../../../core/services/settings.service';
import { UserCacheService, ResolvedUser } from '../../../core/services/user-cache.service';
import { ChannelWithSubscription } from '../../../core/models';
import { RelativeTimePipe } from '../../../shared/pipes/relative-time.pipe';
import { ChannelSubscribersComponent } from '../channel-subscribers/channel-subscribers.component';
type ChannelTab = 'all' | 'owned' | 'foreign';
@Component({
selector: 'app-channel-list',
standalone: true,
imports: [
CommonModule,
DatePipe,
RouterLink,
NzTableModule,
NzButtonModule,
NzIconModule,
NzTagModule,
NzBadgeModule,
NzEmptyModule,
NzCardModule,
NzToolTipModule,
NzTabsModule,
NzAlertModule,
RelativeTimePipe,
ChannelSubscribersComponent,
],
templateUrl: './channel-list.component.html',
styleUrl: './channel-list.component.scss'
})
export class ChannelListComponent implements OnInit {
private apiService = inject(ApiService);
private authService = inject(AuthService);
private notification = inject(NotificationService);
private settingsService = inject(SettingsService);
private userCacheService = inject(UserCacheService);
private router = inject(Router);
allChannels = signal<ChannelWithSubscription[]>([]);
ownerNames = signal<Map<string, ResolvedUser>>(new Map());
loading = signal(false);
expertMode = this.settingsService.expertMode;
activeTab = signal<ChannelTab>('all');
channels = computed(() => {
const userId = this.authService.getUserId();
const all = this.allChannels();
const tab = this.activeTab();
switch (tab) {
case 'owned':
return all.filter(c => c.owner_user_id === userId);
case 'foreign':
return all.filter(c => c.owner_user_id !== userId);
default:
return all;
}
});
ngOnInit(): void {
this.loadChannels();
}
loadChannels(): void {
const userId = this.authService.getUserId();
if (!userId) return;
this.loading.set(true);
this.apiService.getChannels(userId, 'all_any').subscribe({
next: (response) => {
this.allChannels.set(response.channels);
this.loading.set(false);
this.resolveOwnerNames(response.channels);
},
error: () => {
this.loading.set(false);
}
});
}
onTabChange(index: number): void {
const tabs: ChannelTab[] = ['all', 'owned', 'foreign'];
this.activeTab.set(tabs[index]);
}
getTabDescription(): string | null {
switch (this.activeTab()) {
case 'owned':
return 'Channels that you own and can configure.';
case 'foreign':
return 'Channels owned by other users that you are subscribed to.';
default:
return null;
}
}
private resolveOwnerNames(channels: ChannelWithSubscription[]): void {
const uniqueOwnerIds = [...new Set(channels.map(c => c.owner_user_id))];
for (const ownerId of uniqueOwnerIds) {
this.userCacheService.resolveUser(ownerId).subscribe(resolved => {
this.ownerNames.update(map => new Map(map).set(ownerId, resolved));
});
}
}
getOwnerDisplayName(ownerId: string): string {
const resolved = this.ownerNames().get(ownerId);
return resolved?.displayName || ownerId;
}
isOwned(channel: ChannelWithSubscription): boolean {
return channel.owner_user_id === this.authService.getUserId();
}
viewChannel(channel: ChannelWithSubscription): void {
this.router.navigate(['/channels', channel.channel_id]);
}
getSubscriptionStatus(channel: ChannelWithSubscription): { label: string; color: string } {
const userId = this.authService.getUserId();
if (channel.owner_user_id === userId) {
if (channel.subscription) {
return { label: 'Owned & Subscribed', color: 'green' };
}
return { label: 'Owned', color: 'blue' };
}
if (channel.subscription) {
if (channel.subscription.confirmed) {
return { label: 'Subscribed', color: 'green' };
}
return { label: 'Pending', color: 'orange' };
}
return { label: 'Not Subscribed', color: 'default' };
}
toggleSelfSubscription(channel: ChannelWithSubscription, event: Event): void {
event.stopPropagation();
const userId = this.authService.getUserId();
if (!userId) return;
if (channel.subscription) {
// Unsubscribe
this.apiService.deleteSubscription(userId, channel.subscription.subscription_id).subscribe({
next: () => {
this.notification.success('Unsubscribed from channel');
this.loadChannels();
}
});
} else {
// Subscribe
this.apiService.createSubscription(userId, { channel_id: channel.channel_id }).subscribe({
next: () => {
this.notification.success('Subscribed to channel');
this.loadChannels();
}
});
}
}
}