import { Injectable } from '@angular/core'; import { BehaviorSubject, Observable } from 'rxjs'; import { HubConnection, HubConnectionBuilder, HubConnectionState, LogLevel, } from '@microsoft/signalr'; import { environment } from '../../../../environments/environment'; import { AttendanceCounts, AttendanceCategory } from '../models/attendance.model'; /** * Thin wrapper around the AttendanceHub SignalR connection. Exposes the latest * authoritative counts as an observable and a fire-and-forget increment() that * sends a batched delta to the server. The server broadcasts the new totals * back to every connected client. */ @Injectable({ providedIn: 'root' }) export class AttendanceSignalrService { // Hub lives at the host root; environment.apiUrl is e.g. http://localhost:42019/api private readonly hubUrl = environment.apiUrl.replace(/\/api\/?$/, '') + '/hubs/attendance'; private connection?: HubConnection; private readonly counts$$ = new BehaviorSubject(null); /** Latest authoritative counts pushed by the server (null until first message). */ get counts$(): Observable { return this.counts$$.asObservable(); } async start(): Promise { if (this.connection) { return; } this.connection = new HubConnectionBuilder() .withUrl(this.hubUrl) .withAutomaticReconnect() .configureLogging(LogLevel.Warning) .build(); this.connection.on('ReceiveCounts', (counts: AttendanceCounts) => { this.counts$$.next(counts); }); await this.connection.start(); } async stop(): Promise { if (this.connection) { await this.connection.stop(); this.connection = undefined; } } /** Send a batched delta (may be negative) for one age group. */ async increment(category: AttendanceCategory, delta: number): Promise { if (delta === 0 || this.connection?.state !== HubConnectionState.Connected) { return; } await this.connection.invoke('Increment', category, delta); } /** Overwrite one age group with an absolute value (clamped at zero server-side). */ async setCount(category: AttendanceCategory, value: number): Promise { if (this.connection?.state !== HubConnectionState.Connected) { throw new Error('Not connected to the attendance hub.'); } await this.connection.invoke('SetCount', category, value); } }