Skip to content

Commit 4573e40

Browse files
fix hub stream behaviour, gradually migrate encryption to new format, guard di in ClientHelper
1 parent 4c6e1e0 commit 4573e40

3 files changed

Lines changed: 35 additions & 35 deletions

File tree

school_data_hub_flutter/lib/app_utils/download_and_decrypt_file.dart

Lines changed: 22 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -8,27 +8,28 @@ import 'package:school_data_hub_client/school_data_hub_client.dart';
88
import 'package:school_data_hub_flutter/app_utils/custom_encrypter.dart';
99
import 'package:school_data_hub_flutter/common/services/notification_service.dart';
1010

11+
DefaultCacheManager get _cacheManager => di<DefaultCacheManager>();
12+
NotificationService get _notificationService => di<NotificationService>();
13+
1114
Future<File?> downloadAndDecryptFile({
1215
required String documentId,
1316
required bool decrypt,
1417
}) async {
15-
final cacheManager = di<DefaultCacheManager>();
16-
final notificationService = di<NotificationService>();
17-
1818
// Check cache
19-
final fileInfo = await cacheManager.getFileFromCache(documentId);
19+
final fileInfo = await _cacheManager.getFileFromCache(documentId);
2020

2121
if (fileInfo != null && await fileInfo.file.exists()) {
2222
if (!decrypt) {
2323
return fileInfo.file;
2424
}
2525

2626
final fileBytes = await fileInfo.file.readAsBytes();
27-
final (:bytes, :wasLegacy) =
28-
await customEncrypter.decryptFileBytesAsync(fileBytes);
27+
final (:bytes, :wasLegacy) = await customEncrypter.decryptFileBytesAsync(
28+
fileBytes,
29+
);
2930

3031
if (wasLegacy) {
31-
_migrateToNewFormat(documentId, bytes, cacheManager);
32+
_migrateToNewFormat(documentId, bytes);
3233
}
3334

3435
final tempDir = await Directory.systemTemp.createTemp();
@@ -41,12 +42,12 @@ Future<File?> downloadAndDecryptFile({
4142
}
4243

4344
// Download
44-
notificationService.apiRunning(true);
45+
_notificationService.apiRunning(true);
4546
final ByteData? byteData = await di<Client>().files.getImage(documentId);
46-
notificationService.apiRunning(false);
47+
_notificationService.apiRunning(false);
4748

4849
if (byteData == null) {
49-
notificationService.showSnackBar(
50+
_notificationService.showSnackBar(
5051
NotificationType.error,
5152
'Fehler beim Laden der Datei',
5253
);
@@ -55,7 +56,7 @@ Future<File?> downloadAndDecryptFile({
5556

5657
Uint8List fileBytes = byteData.buffer.asUint8List();
5758
// Cache it
58-
await cacheManager.putFile(documentId, fileBytes);
59+
await _cacheManager.putFile(documentId, fileBytes);
5960

6061
if (!decrypt) {
6162
final tempDir = await Directory.systemTemp.createTemp();
@@ -65,11 +66,12 @@ Future<File?> downloadAndDecryptFile({
6566
return tempFile;
6667
}
6768

68-
final (:bytes, :wasLegacy) =
69-
await customEncrypter.decryptFileBytesAsync(fileBytes);
69+
final (:bytes, :wasLegacy) = await customEncrypter.decryptFileBytesAsync(
70+
fileBytes,
71+
);
7072

7173
if (wasLegacy) {
72-
_migrateToNewFormat(documentId, bytes, cacheManager);
74+
_migrateToNewFormat(documentId, bytes);
7375
}
7476

7577
final tempDir = await Directory.systemTemp.createTemp();
@@ -83,20 +85,17 @@ Future<File?> downloadAndDecryptFile({
8385

8486
/// Fire-and-forget: re-encrypt [plainBytes] in the new format, update the
8587
/// server file, and refresh the local cache — all without blocking the caller.
86-
void _migrateToNewFormat(
87-
String documentId,
88-
Uint8List plainBytes,
89-
DefaultCacheManager cacheManager,
90-
) {
88+
void _migrateToNewFormat(String documentId, Uint8List plainBytes) {
9189
Future(() async {
9290
try {
9391
final newEncrypted = customEncrypter.encryptTheseBytes(plainBytes);
9492
final byteData = ByteData.sublistView(newEncrypted);
95-
final ok = await di<Client>()
96-
.files
97-
.replaceEncryptedFileBytes(documentId, byteData);
93+
final ok = await di<Client>().files.replaceEncryptedFileBytes(
94+
documentId,
95+
byteData,
96+
);
9897
if (ok) {
99-
await cacheManager.putFile(documentId, newEncrypted);
98+
await _cacheManager.putFile(documentId, newEncrypted);
10099
}
101100
} catch (_) {
102101
// Migration is best-effort; silently ignore failures.

school_data_hub_flutter/lib/common/services/hub_stream_service.dart

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,10 @@ class HubStreamService with WidgetsBindingObserver {
9999
break;
100100
case AppLifecycleState.resumed:
101101
_appInForeground = true;
102+
if (isConnected) {
103+
_log.info('[HUB] App resumed — already connected, skipping');
104+
break;
105+
}
102106
_log.info('[HUB] App resumed — scheduling reconnect');
103107
_scheduleReconnect(
104108
isReconnect: true,
@@ -138,8 +142,8 @@ class HubStreamService with WidgetsBindingObserver {
138142
String reason = 'unknown',
139143
}) {
140144
if (!_appInForeground || _disposed) return;
145+
if (_connecting) return;
141146
if (_reconnectTimer != null) return;
142-
_cancelReconnectTimer();
143147
final delayWithJitter = _withJitter(delayMs);
144148
_log.info(
145149
'[HUB] reconnect_scheduled reason=$reason delayMs=$delayWithJitter',

school_data_hub_flutter/lib/core/client/client_helper.dart

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,6 @@ import 'package:school_data_hub_flutter/common/services/hub_stream_service.dart'
44
import 'package:school_data_hub_flutter/common/services/notification_service.dart';
55
import 'package:school_data_hub_flutter/core/session/hub_session_manager.dart';
66

7-
final _notificationService = di<NotificationService>();
8-
final _hubSessionManager = di<HubSessionManager>();
9-
107
class ClientHelper {
118
// Make it a singleton
129
static final ClientHelper _instance = ClientHelper._internal();
@@ -18,12 +15,12 @@ class ClientHelper {
1815
String? errorMessage,
1916
}) async {
2017
try {
21-
_notificationService.apiRunning(true);
18+
di<NotificationService>().apiRunning(true);
2219
final result = await call();
23-
_notificationService.apiRunning(false);
20+
di<NotificationService>().apiRunning(false);
2421
return result;
2522
} on ServerpodClientException catch (e) {
26-
_notificationService.apiRunning(false);
23+
di<NotificationService>().apiRunning(false);
2724

2825
// 502 / 503 during a server restart are expected transients.
2926
// Suppress them while the hub stream is not yet connected so the
@@ -39,23 +36,23 @@ class ClientHelper {
3936
}
4037
}
4138

42-
_notificationService.showInformationDialog(
39+
di<NotificationService>().showInformationDialog(
4340
NotificationType.error,
4441
'API Fehler: ${errorMessage ?? "Unbekannt"}: $e',
4542
);
4643

4744
if (e.toString().contains('Not authorized') ||
4845
e.toString().contains('401')) {
49-
_notificationService.showInformationDialog(
46+
di<NotificationService>().showInformationDialog(
5047
NotificationType.error,
5148
'Authentication required. Please log in again.',
5249
);
53-
_hubSessionManager.signOutDevice();
50+
di<HubSessionManager>().signOutDevice();
5451
}
5552
return null;
5653
} catch (e) {
57-
_notificationService.apiRunning(false);
58-
_notificationService.showInformationDialog(
54+
di<NotificationService>().apiRunning(false);
55+
di<NotificationService>().showInformationDialog(
5956
NotificationType.error,
6057
'API Fehler:\n $errorMessage: $e',
6158
);

0 commit comments

Comments
 (0)