Files
petitspas/frontend/lib/services/api/cards_stream_connector_web.dart
T
jmartinandCursor d42927297d feat(#173): SSE live des bulles + ack sans modale
Abonnement /cards/stream pour sync co-parent en temps réel ;
acquittement (arrêt maladie) en un clic, sans confirmation.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-05 15:41:34 +02:00

61 lines
1.5 KiB
Dart

// Connecteur web uniquement (import conditionnel).
// ignore_for_file: avoid_web_libraries_in_flutter, deprecated_member_use
import 'dart:html' as html;
/// Connecteur SSE navigateur (EventSource) — ticket #195.
/// Auth via `?access_token=` (EventSource ne permet pas Authorization).
class CardsStreamConnector {
html.EventSource? _source;
final Map<String, html.EventListener> _listeners = {};
Future<void> connect({
required String url,
String? bearerToken,
required void Function(String eventType, String data) onEvent,
required void Function(Object error) onError,
}) async {
close();
final source = html.EventSource(url);
_source = source;
const names = [
'card.created',
'card.updated',
'card.deleted',
'response.added',
'heartbeat',
];
for (final name in names) {
void listener(html.Event raw) {
final msg = raw as html.MessageEvent;
final data = msg.data?.toString() ?? '';
if (data.isEmpty) return;
onEvent(name, data);
}
_listeners[name] = listener;
source.addEventListener(name, listener);
}
void onErr(html.Event _) {
onError(StateError('SSE cards déconnecté'));
}
_listeners['error'] = onErr;
source.addEventListener('error', onErr);
}
void close() {
final source = _source;
if (source != null) {
_listeners.forEach((name, listener) {
source.removeEventListener(name, listener);
});
source.close();
}
_listeners.clear();
_source = null;
}
}