69 lines
2.4 KiB
TypeScript
69 lines
2.4 KiB
TypeScript
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<AttendanceCounts | null>(null);
|
|
|
|
/** Latest authoritative counts pushed by the server (null until first message). */
|
|
get counts$(): Observable<AttendanceCounts | null> {
|
|
return this.counts$$.asObservable();
|
|
}
|
|
|
|
async start(): Promise<void> {
|
|
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<void> {
|
|
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<void> {
|
|
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<void> {
|
|
if (this.connection?.state !== HubConnectionState.Connected) {
|
|
throw new Error('Not connected to the attendance hub.');
|
|
}
|
|
await this.connection.invoke('SetCount', category, value);
|
|
}
|
|
}
|