widget refresh enhancements

This commit is contained in:
2026-08-06 20:22:32 +02:00
parent 646e2c0451
commit ab23422a86
22 changed files with 1027 additions and 99 deletions
+33 -1
View File
@@ -3,6 +3,7 @@ import 'dart:developer';
import 'package:crypton/crypton.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import '../background/widget_background_task.dart';
import '../notification/notification_service.dart';
import 'chat_thread_store.dart';
import 'nid_store.dart';
@@ -12,6 +13,10 @@ import 'push_registration_store.dart';
import 'push_renderer.dart';
import 'push_subject.dart';
/// Wire value of the FCM `type` field for silent widget-refresh pushes.
/// Mirrors PUSH_TYPE_WIDGET_REFRESH in MarianumConnect's MarMobileApiService.
const String widgetRefreshPushType = 'widget-refresh';
/// How an incoming FCM payload should be interpreted.
enum PushKind {
/// Encrypted Nextcloud push-v2 notification (`subject` + `signature`).
@@ -20,6 +25,11 @@ enum PushKind {
/// Plaintext MarianumConnect direct push (`source == "connect"`).
connect,
/// Silent MarianumConnect push requesting a home-widget data refresh
/// (`source == "connect"` + `type == "widget-refresh"`). Never rendered,
/// processed even with notifications off.
widgetRefresh,
/// Neither — ignored.
unknown,
}
@@ -31,7 +41,10 @@ PushKind classifyPush(Map<String, dynamic> data) {
final hasSubject = (data['subject'] as String?)?.isNotEmpty ?? false;
final hasSignature = (data['signature'] as String?)?.isNotEmpty ?? false;
if (hasSubject && hasSignature) return PushKind.nextcloud;
if (data['source'] == 'connect') return PushKind.connect;
if (data['source'] == 'connect') {
if (data['type'] == widgetRefreshPushType) return PushKind.widgetRefresh;
return PushKind.connect;
}
return PushKind.unknown;
}
@@ -97,11 +110,30 @@ class PushMessageHandler {
notificationsEnabled: notificationsEnabled,
);
break;
case PushKind.widgetRefresh:
// Deliberately before any notificationsEnabled gate: silent sync
// pushes must work with notifications off.
await _handleWidgetRefresh();
break;
case PushKind.unknown:
break;
}
}
Future<void> _handleWidgetRefresh() async {
try {
// The iOS FCM handler runs in the main isolate with a ~25s APNs
// budget — bound the inline refresh below that so the completion
// handler always fires in time.
await WidgetBackgroundTask.requestImmediateRefresh(
force: false,
inlineTimeout: const Duration(seconds: 20),
);
} on Exception catch (e) {
log('[push] widget refresh failed: $e');
}
}
Future<void> _handleConnect(
RemoteMessage message, {
required bool foreground,