import { Component, inject, signal, computed, OnInit } from '@angular/core'; import { CommonModule, DatePipe } from '@angular/common'; import { ActivatedRoute, Router } from '@angular/router'; import { FormsModule } from '@angular/forms'; import { NzCardModule } from 'ng-zorro-antd/card'; import { NzButtonModule } from 'ng-zorro-antd/button'; import { NzIconModule } from 'ng-zorro-antd/icon'; import { NzTagModule } from 'ng-zorro-antd/tag'; import { NzSpinModule } from 'ng-zorro-antd/spin'; import { NzPopconfirmModule } from 'ng-zorro-antd/popconfirm'; import { NzDividerModule } from 'ng-zorro-antd/divider'; import { NzInputModule } from 'ng-zorro-antd/input'; import { NzModalModule } from 'ng-zorro-antd/modal'; import { NzFormModule } from 'ng-zorro-antd/form'; import { NzTableModule } from 'ng-zorro-antd/table'; import { NzToolTipModule } from 'ng-zorro-antd/tooltip'; import { NzEmptyModule } from 'ng-zorro-antd/empty'; import { ApiService } from '../../../core/services/api.service'; import { AuthService } from '../../../core/services/auth.service'; import { NotificationService } from '../../../core/services/notification.service'; import { UserCacheService, ResolvedUser } from '../../../core/services/user-cache.service'; import { ChannelWithSubscription, Subscription } from '../../../core/models'; import { RelativeTimePipe } from '../../../shared/pipes/relative-time.pipe'; import { CopyToClipboardDirective } from '../../../shared/directives/copy-to-clipboard.directive'; import { QrCodeDisplayComponent } from '../../../shared/components/qr-code-display/qr-code-display.component'; import { MetadataGridComponent, MetadataValueComponent } from '../../../shared/components/metadata-grid'; @Component({ selector: 'app-channel-detail', standalone: true, imports: [ CommonModule, DatePipe, FormsModule, NzCardModule, NzButtonModule, NzIconModule, NzTagModule, NzSpinModule, NzPopconfirmModule, NzDividerModule, NzInputModule, NzModalModule, NzFormModule, NzTableModule, NzToolTipModule, NzEmptyModule, RelativeTimePipe, CopyToClipboardDirective, QrCodeDisplayComponent, MetadataGridComponent, MetadataValueComponent, ], templateUrl: './channel-detail.component.html', styleUrl: './channel-detail.component.scss' }) export class ChannelDetailComponent implements OnInit { private route = inject(ActivatedRoute); private router = inject(Router); private apiService = inject(ApiService); private authService = inject(AuthService); private notification = inject(NotificationService); private userCacheService = inject(UserCacheService); channel = signal(null); subscriptions = signal([]); userNames = signal>(new Map()); loading = signal(true); loadingSubscriptions = signal(false); // Edit modal showEditModal = signal(false); editDisplayName = ''; editDescription = ''; saving = signal(false); // QR code data (computed from channel) qrCodeData = computed(() => { const channel = this.channel(); if (!channel || !channel.subscribe_key) return ''; return [ '@scn.channel.subscribe', 'v1', channel.display_name, channel.owner_user_id, channel.channel_id, channel.subscribe_key ].join('\n'); }); ngOnInit(): void { const channelId = this.route.snapshot.paramMap.get('id'); if (channelId) { this.loadChannel(channelId); } } loadChannel(channelId: string): void { const userId = this.authService.getUserId(); if (!userId) return; this.loading.set(true); this.apiService.getChannel(userId, channelId).subscribe({ next: (channel) => { this.channel.set(channel); this.loading.set(false); if (this.isOwner()) { this.loadSubscriptions(channelId); } }, error: () => { this.loading.set(false); } }); } loadSubscriptions(channelId: string): void { const userId = this.authService.getUserId(); if (!userId) return; this.loadingSubscriptions.set(true); this.apiService.getChannelSubscriptions(userId, channelId).subscribe({ next: (response) => { this.subscriptions.set(response.subscriptions); this.loadingSubscriptions.set(false); this.resolveUserNames(response.subscriptions); }, error: () => { this.loadingSubscriptions.set(false); } }); } private resolveUserNames(subscriptions: Subscription[]): void { const userIds = new Set(); for (const sub of subscriptions) { userIds.add(sub.subscriber_user_id); } for (const id of userIds) { this.userCacheService.resolveUser(id).subscribe(resolved => { this.userNames.update(map => new Map(map).set(id, resolved)); }); } } getUserDisplayName(userId: string): string { const resolved = this.userNames().get(userId); return resolved?.displayName || userId; } goBack(): void { this.router.navigate(['/channels']); } isOwner(): boolean { const channel = this.channel(); const userId = this.authService.getUserId(); return channel?.owner_user_id === userId; } // Edit methods openEditModal(): void { const channel = this.channel(); if (!channel) return; this.editDisplayName = channel.display_name; this.editDescription = channel.description_name || ''; this.showEditModal.set(true); } closeEditModal(): void { this.showEditModal.set(false); } saveChannel(): void { const channel = this.channel(); const userId = this.authService.getUserId(); if (!channel || !userId) return; this.saving.set(true); this.apiService.updateChannel(userId, channel.channel_id, { display_name: this.editDisplayName, description_name: this.editDescription || undefined }).subscribe({ next: (updated) => { this.channel.set(updated); this.notification.success('Channel updated'); this.closeEditModal(); this.saving.set(false); }, error: () => { this.saving.set(false); } }); } // Regenerate keys regenerateSubscribeKey(): void { const channel = this.channel(); const userId = this.authService.getUserId(); if (!channel || !userId) return; this.apiService.updateChannel(userId, channel.channel_id, { subscribe_key: 'true' }).subscribe({ next: (updated) => { this.channel.set(updated); this.notification.success('Subscribe key regenerated'); } }); } regenerateSendKey(): void { const channel = this.channel(); const userId = this.authService.getUserId(); if (!channel || !userId) return; this.apiService.updateChannel(userId, channel.channel_id, { send_key: 'true' }).subscribe({ next: (updated) => { this.channel.set(updated); this.notification.success('Send key regenerated'); } }); } getSubscriptionStatus(): { label: string; color: string } { const channel = this.channel(); if (!channel) return { label: 'Unknown', color: 'default' }; if (this.isOwner()) { 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' }; } }