fix(groups): assignation en lot 1 requête + trackBy (plus de wipe/429)

This commit is contained in:
2026-09-27 13:52:20 -04:00
parent 9b620aaab6
commit 63a2bddf47
3 changed files with 72 additions and 19 deletions
@@ -183,28 +183,34 @@ export class SubscriptionsComponent {
}
}
/** Crée le groupe suggéré (ou complète l'existant) et y assigne les chaînes. */
/** Crée le groupe suggéré (ou complète l'existant) : 2 requêtes max, pas de tempête. */
applySuggestion(s: GroupSuggestion): Promise<void> {
if (this.suggestBusy()) return Promise.resolve();
this.suggestBusy.set(true);
const assignAll = (groupId: string): Promise<void> => {
const calls = s.channelIds
.filter((sid) => !(this.members()[String(sid)] || []).includes(groupId))
.map((sid) => new Promise<void>((resolve) => {
const current = this.members()[String(sid)] || [];
this.subs.setSubscriptionGroups(sid, [...current, groupId]).subscribe({ next: () => resolve(), error: () => resolve() });
}));
return Promise.all(calls).then(() => undefined);
};
const done = () => this.suggestBusy.set(false);
const run = (gid: string): Promise<void> => new Promise<void>((resolve) => {
this.subs.setGroupMembers(gid, this.memberIdsOf(gid, s)).subscribe({ next: () => resolve(), error: () => resolve() });
});
if (s.kind === 'complete' && s.existingGroupId) {
return assignAll(s.existingGroupId).finally(done);
return run(s.existingGroupId).finally(done);
}
return new Promise<void>((resolve) => {
this.subs.createGroup(s.name, s.color, s.icon).subscribe((g) => resolve(g?.id ? assignAll(g.id) : undefined));
this.subs.createGroup(s.name, s.color, s.icon).subscribe((g) => {
if (g?.id) run(g.id).then(() => resolve());
else resolve();
});
}).finally(done);
}
/** Ids déjà dans le groupe + nouveaux (union), pour l'assignation en lot. */
private memberIdsOf(groupId: string, s: GroupSuggestion): number[] {
const members = this.members();
const current = Object.keys(members)
.filter((k) => (members[k] || []).includes(groupId))
.map(Number);
return [...new Set([...current, ...s.channelIds])];
}
/** Applique toutes les suggestions d'un coup, dans l'ordre. */
async applyAllSuggestions(): Promise<void> {
if (this.suggestBusy()) return;
@@ -213,6 +219,19 @@ export class SubscriptionsComponent {
}
}
// --- trackBy : DOM stable (pas de re-rendu complet + recharge d'images à chaque tick) ---
trackBySubscriptionId(_idx: number, item: SubscriptionItem): number {
return item.subscriptionId;
}
trackByGroupId(_idx: number, group: SubscriptionGroup): string {
return group.id;
}
trackBySuggestion(_idx: number, s: GroupSuggestion): string {
return `${s.kind}:${s.existingGroupId || s.name}`;
}
readonly items = computed(() => this.subs.items());
readonly groups = computed(() => this.subs.subscriptionGroups());
readonly members = computed(() => this.subs.groupMembers());