1+ import 'dart:async' ;
2+
13import 'package:flutter/foundation.dart' ;
24import 'package:flutter_it/flutter_it.dart' ;
35import 'package:school_data_hub_client/school_data_hub_client.dart' ;
6+ import 'package:school_data_hub_flutter/common/services/hub_stream_service.dart' ;
47import 'package:school_data_hub_flutter/common/services/notification_service.dart' ;
58import 'package:school_data_hub_flutter/core/session/hub_session_manager.dart' ;
69import 'package:school_data_hub_flutter/features/learning/competence_report/data/competence_report_api_service.dart' ;
@@ -16,12 +19,128 @@ class CompetenceReportManager {
1619 ValueListenable <Map <int , List <CompetenceReport >>> get reportsByPupil =>
1720 _reportsByPupil;
1821
22+ StreamSubscription <dynamic >? _hubSubscription;
23+
1924 CompetenceReportManager ();
2025
26+ Future <CompetenceReportManager > init () async {
27+ _hubSubscription = di <HubStreamService >().events.listen (_onHubEvent);
28+ return this ;
29+ }
30+
2131 void dispose () {
32+ _hubSubscription? .cancel ();
33+ _hubSubscription = null ;
2234 _reportsByPupil.dispose ();
2335 }
2436
37+ void _onHubEvent (dynamic event) {
38+ if (event is CompetenceReport ) {
39+ upsertReportFromStream (event);
40+ } else if (event is CompetenceReportCheck ) {
41+ upsertCheckFromStream (event);
42+ } else if (event is HubDeleteEvent ) {
43+ if (event.objectType == HubObjectType .competenceReport) {
44+ deleteReportFromStream (event.id);
45+ } else if (event.objectType == HubObjectType .competenceReportCheck) {
46+ deleteCheckFromStream (event.id);
47+ }
48+ } else if (event is HubReconnected ) {
49+ for (final pupilId in _reportsByPupil.value.keys.toList ()) {
50+ fetchReportsForPupil (pupilId);
51+ }
52+ }
53+ }
54+
55+ void upsertReportFromStream (CompetenceReport report) {
56+ final map = Map <int , List <CompetenceReport >>.from (_reportsByPupil.value);
57+ final list = List <CompetenceReport >.from (map[report.pupilId] ?? []);
58+ final index = list.indexWhere ((r) =>
59+ r.id == report.id ||
60+ (r.reportId == report.reportId && report.reportId.isNotEmpty));
61+ if (index >= 0 ) {
62+ list[index] = report;
63+ } else {
64+ list.add (report);
65+ }
66+ map[report.pupilId] = list;
67+ _reportsByPupil.value = map;
68+ }
69+
70+ void upsertCheckFromStream (CompetenceReportCheck check) {
71+ final map = _reportsByPupil.value;
72+ final list = map[check.pupilId];
73+ if (list == null || list.isEmpty) {
74+ fetchReportsForPupil (check.pupilId);
75+ return ;
76+ }
77+ final reportIndex = list.indexWhere ((r) => r.id == check.competenceReportId);
78+ if (reportIndex == - 1 ) {
79+ fetchReportsForPupil (check.pupilId);
80+ return ;
81+ }
82+ final report = list[reportIndex];
83+ final checks = List <CompetenceReportCheck >.from (
84+ report.competenceReportChecks ?? [],
85+ );
86+ final checkIndex = checks.indexWhere ((c) =>
87+ c.id == check.id ||
88+ (c.publicId == check.publicId && check.publicId.isNotEmpty));
89+ if (checkIndex >= 0 ) {
90+ checks[checkIndex] = check;
91+ } else {
92+ checks.add (check);
93+ }
94+ final updatedReport = report.copyWith (competenceReportChecks: checks);
95+ final newList = List <CompetenceReport >.from (list);
96+ newList[reportIndex] = updatedReport;
97+ final newMap = Map <int , List <CompetenceReport >>.from (map);
98+ newMap[check.pupilId] = newList;
99+ _reportsByPupil.value = newMap;
100+ }
101+
102+ void deleteReportFromStream (int reportId) {
103+ final map = Map <int , List <CompetenceReport >>.from (_reportsByPupil.value);
104+ var changed = false ;
105+ for (final entry in map.entries) {
106+ final list = entry.value;
107+ final newList = list.where ((r) => r.id != reportId).toList ();
108+ if (newList.length != list.length) {
109+ map[entry.key] = newList;
110+ changed = true ;
111+ break ;
112+ }
113+ }
114+ if (changed) {
115+ _reportsByPupil.value = map;
116+ }
117+ }
118+
119+ void deleteCheckFromStream (int checkId) {
120+ final map = Map <int , List <CompetenceReport >>.from (_reportsByPupil.value);
121+ var changed = false ;
122+ for (final pupilId in map.keys) {
123+ final list = map[pupilId]! ;
124+ for (var i = 0 ; i < list.length; i++ ) {
125+ final report = list[i];
126+ final checks = report.competenceReportChecks;
127+ if (checks == null ) continue ;
128+ final newChecks = checks.where ((c) => c.id != checkId).toList ();
129+ if (newChecks.length != checks.length) {
130+ final newList = List <CompetenceReport >.from (list);
131+ newList[i] = report.copyWith (competenceReportChecks: newChecks);
132+ map[pupilId] = newList;
133+ changed = true ;
134+ break ;
135+ }
136+ }
137+ if (changed) break ;
138+ }
139+ if (changed) {
140+ _reportsByPupil.value = map;
141+ }
142+ }
143+
25144 List <CompetenceReport > getReportsForPupil (int pupilId) {
26145 return _reportsByPupil.value[pupilId] ?? [];
27146 }
@@ -40,18 +159,14 @@ class CompetenceReportManager {
40159 required DateTime achievedAt,
41160 }) async {
42161 final createdBy = di <HubSessionManager >().userName! ;
43- final report = await _reportApiService.postCompetenceReport (
162+ await _reportApiService.postCompetenceReport (
44163 pupilId: pupilId,
45164 schoolSemesterId: schoolSemesterId,
46165 achievement: achievement,
47166 achievedAt: achievedAt,
48167 createdBy: createdBy,
49168 );
50169
51- final map = Map <int , List <CompetenceReport >>.from (_reportsByPupil.value);
52- map[pupilId] = [...(map[pupilId] ?? []), report];
53- _reportsByPupil.value = map;
54-
55170 _notificationService.showSnackBar (
56171 NotificationType .success,
57172 'Zeugnis erstellt' ,
@@ -66,23 +181,14 @@ class CompetenceReportManager {
66181 ({DateTime ? value})? modifiedAt,
67182 }) async {
68183 final modifiedBy = di <HubSessionManager >().userName! ;
69- final updated = await _reportApiService.updateCompetenceReport (
184+ await _reportApiService.updateCompetenceReport (
70185 reportId,
71186 achievement: achievement,
72187 achievedAt: achievedAt,
73188 modifiedBy: (value: modifiedBy),
74189 modifiedAt: modifiedAt ?? (value: DateTime .now ().toUtc ()),
75190 );
76191
77- final map = Map <int , List <CompetenceReport >>.from (_reportsByPupil.value);
78- final list = List <CompetenceReport >.from (map[pupilId] ?? []);
79- final index = list.indexWhere ((r) => r.reportId == reportId);
80- if (index != - 1 ) {
81- list[index] = updated;
82- }
83- map[pupilId] = list;
84- _reportsByPupil.value = map;
85-
86192 _notificationService.showSnackBar (
87193 NotificationType .success,
88194 'Zeugnis aktualisiert' ,
@@ -95,13 +201,6 @@ class CompetenceReportManager {
95201 }) async {
96202 final success = await _reportApiService.deleteCompetenceReport (reportId);
97203 if (success) {
98- final map =
99- Map <int , List <CompetenceReport >>.from (_reportsByPupil.value);
100- final list = List <CompetenceReport >.from (map[pupilId] ?? []);
101- list.removeWhere ((r) => r.reportId == reportId);
102- map[pupilId] = list;
103- _reportsByPupil.value = map;
104-
105204 _notificationService.showSnackBar (
106205 NotificationType .success,
107206 'Zeugnis gelöscht' ,
@@ -128,8 +227,6 @@ class CompetenceReportManager {
128227 shouldPrint: shouldPrint,
129228 );
130229
131- await fetchReportsForPupil (pupilId);
132-
133230 _notificationService.showSnackBar (
134231 NotificationType .success,
135232 'Zeugniseintrag erstellt' ,
@@ -150,8 +247,6 @@ class CompetenceReportManager {
150247 shouldPrint: shouldPrint,
151248 );
152249
153- await fetchReportsForPupil (pupilId);
154-
155250 _notificationService.showSnackBar (
156251 NotificationType .success,
157252 'Zeugniseintrag aktualisiert' ,
@@ -165,8 +260,6 @@ class CompetenceReportManager {
165260 final success =
166261 await _checkApiService.deleteCompetenceReportCheck (publicId);
167262 if (success) {
168- await fetchReportsForPupil (pupilId);
169-
170263 _notificationService.showSnackBar (
171264 NotificationType .success,
172265 'Zeugniseintrag gelöscht' ,
0 commit comments