More webapp changes+fixes

This commit is contained in:
2025-12-05 16:52:02 +01:00
parent c66cd0568f
commit 8e7a540c97
40 changed files with 1944 additions and 272 deletions

View File

@@ -0,0 +1,107 @@
import { Component, inject, input, signal, OnInit } from '@angular/core';
import { CommonModule } from '@angular/common';
import { NzSpinModule } from 'ng-zorro-antd/spin';
import { NzToolTipModule } from 'ng-zorro-antd/tooltip';
import { ApiService } from '../../../core/services/api.service';
import { AuthService } from '../../../core/services/auth.service';
import { UserCacheService, ResolvedUser } from '../../../core/services/user-cache.service';
import { Subscription } from '../../../core/models';
@Component({
selector: 'app-channel-subscribers',
standalone: true,
imports: [CommonModule, NzSpinModule, NzToolTipModule],
template: `
@if (loading()) {
<nz-spin nzSimple nzSize="small"></nz-spin>
} @else if (subscribers().length === 0) {
<span class="text-muted">None</span>
} @else {
<div class="subscribers-list">
@for (sub of subscribers(); track sub.subscription_id) {
<span
class="subscriber"
[class.unconfirmed]="!sub.confirmed"
nz-tooltip
[nzTooltipTitle]="getTooltip(sub)"
>
{{ getDisplayName(sub.subscriber_user_id) }}
</span>
}
</div>
}
`,
styles: [`
.text-muted {
color: #999;
}
.subscribers-list {
display: flex;
flex-wrap: wrap;
gap: 4px;
}
.subscriber {
background: #f0f0f0;
padding: 2px 8px;
border-radius: 4px;
font-size: 12px;
}
.subscriber.unconfirmed {
background: #fff7e6;
color: #d48806;
}
`]
})
export class ChannelSubscribersComponent implements OnInit {
private apiService = inject(ApiService);
private authService = inject(AuthService);
private userCacheService = inject(UserCacheService);
channelId = input.required<string>();
loading = signal(true);
subscribers = signal<Subscription[]>([]);
userNames = signal<Map<string, ResolvedUser>>(new Map());
ngOnInit(): void {
this.loadSubscribers();
}
private loadSubscribers(): void {
const userId = this.authService.getUserId();
if (!userId) {
this.loading.set(false);
return;
}
this.apiService.getChannelSubscriptions(userId, this.channelId()).subscribe({
next: (response) => {
this.subscribers.set(response.subscriptions);
this.loading.set(false);
this.resolveUserNames(response.subscriptions);
},
error: () => {
this.loading.set(false);
}
});
}
private resolveUserNames(subscriptions: Subscription[]): void {
const userIds = new Set(subscriptions.map(s => s.subscriber_user_id));
for (const userId of userIds) {
this.userCacheService.resolveUser(userId).subscribe(resolved => {
this.userNames.update(map => new Map(map).set(userId, resolved));
});
}
}
getDisplayName(userId: string): string {
const resolved = this.userNames().get(userId);
return resolved?.displayName || userId;
}
getTooltip(sub: Subscription): string {
const status = sub.confirmed ? 'Confirmed' : 'Pending';
return `${sub.subscriber_user_id} (${status})`;
}
}