feat(groups): 216 icones N&B + fermeture auto menu + suggestions auto
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { ChangeDetectionStrategy, Component, computed, inject, signal } from '@angular/core';
|
||||
import { ChangeDetectionStrategy, Component, computed, HostListener, inject, signal } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { DatePipe, DecimalPipe } from '@angular/common';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
@@ -7,7 +7,9 @@ import {
|
||||
GROUP_COLORS,
|
||||
GROUP_ICONS,
|
||||
GROUP_ICON_KEYWORDS,
|
||||
suggestGroupsForChannels,
|
||||
SubscriptionsService,
|
||||
type GroupSuggestion,
|
||||
type SubscriptionGroup,
|
||||
type SubscriptionItem,
|
||||
} from '../../../services/subscriptions.service';
|
||||
@@ -87,6 +89,52 @@ export class SubscriptionsComponent {
|
||||
// --- Assignation en lot ---
|
||||
bulkGroupId = signal<string>('');
|
||||
|
||||
// --- Suggestions auto de groupes (analyse des chaînes) ---
|
||||
showSuggestions = signal<boolean>(false);
|
||||
suggestBusy = signal<boolean>(false);
|
||||
|
||||
readonly suggestions = computed<GroupSuggestion[]>(() =>
|
||||
suggestGroupsForChannels(
|
||||
this.items().map((item) => ({
|
||||
subscriptionId: item.subscriptionId,
|
||||
title: item.channel?.title || '',
|
||||
handle: item.channel?.handle || null,
|
||||
groupIds: this.itemGroupIds(item),
|
||||
})),
|
||||
this.groups().map((g) => ({ id: g.id, name: g.name })),
|
||||
)
|
||||
);
|
||||
|
||||
/** Crée le groupe suggéré (ou complète l'existant) et y assigne les chaînes. */
|
||||
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);
|
||||
if (s.kind === 'complete' && s.existingGroupId) {
|
||||
return assignAll(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));
|
||||
}).finally(done);
|
||||
}
|
||||
|
||||
/** Applique toutes les suggestions d'un coup, dans l'ordre. */
|
||||
async applyAllSuggestions(): Promise<void> {
|
||||
if (this.suggestBusy()) return;
|
||||
for (const s of this.suggestions()) {
|
||||
await this.applySuggestion(s);
|
||||
}
|
||||
}
|
||||
|
||||
readonly items = computed(() => this.subs.items());
|
||||
readonly groups = computed(() => this.subs.subscriptionGroups());
|
||||
readonly members = computed(() => this.subs.groupMembers());
|
||||
@@ -217,6 +265,20 @@ export class SubscriptionsComponent {
|
||||
this.pickerFor.set(this.pickerFor() === subscriptionId ? null : subscriptionId);
|
||||
}
|
||||
|
||||
/** Ferme le menu dès que le focus/clic sort du menu (clic extérieur, Escape). */
|
||||
@HostListener('document:click', ['$event'])
|
||||
onDocumentClick(event: MouseEvent): void {
|
||||
if (this.pickerFor() === null) return;
|
||||
const el = event.target as HTMLElement | null;
|
||||
if (el?.closest?.('.picker-root')) return;
|
||||
this.pickerFor.set(null);
|
||||
}
|
||||
|
||||
@HostListener('document:keydown', ['$event'])
|
||||
onDocumentKey(event: KeyboardEvent): void {
|
||||
if (event.key === 'Escape' && this.pickerFor() !== null) this.pickerFor.set(null);
|
||||
}
|
||||
|
||||
toggleMembership(item: SubscriptionItem, groupId: string): void {
|
||||
const current = this.itemGroupIds(item);
|
||||
const next = current.includes(groupId)
|
||||
|
||||
Reference in New Issue
Block a user