migrated custom timetable events to Marianum-Connect and refactored push notification handling
This commit is contained in:
@@ -77,15 +77,24 @@ class PushMessageHandler {
|
||||
String? openChatToken,
|
||||
}) async {
|
||||
final data = message.data;
|
||||
// The device stays registered even when the user turns notifications off,
|
||||
// so silent sync pushes (deletes, data refresh) keep arriving. When off we
|
||||
// still process the message but skip raising a visible notification.
|
||||
final notificationsEnabled = await _registrationStore.notificationsEnabled();
|
||||
switch (classifyPush(data)) {
|
||||
case PushKind.connect:
|
||||
await _handleConnect(message, foreground: foreground);
|
||||
await _handleConnect(
|
||||
message,
|
||||
foreground: foreground,
|
||||
notificationsEnabled: notificationsEnabled,
|
||||
);
|
||||
break;
|
||||
case PushKind.nextcloud:
|
||||
await _handleNextcloud(
|
||||
data,
|
||||
foreground: foreground,
|
||||
openChatToken: openChatToken,
|
||||
notificationsEnabled: notificationsEnabled,
|
||||
);
|
||||
break;
|
||||
case PushKind.unknown:
|
||||
@@ -96,7 +105,11 @@ class PushMessageHandler {
|
||||
Future<void> _handleConnect(
|
||||
RemoteMessage message, {
|
||||
required bool foreground,
|
||||
required bool notificationsEnabled,
|
||||
}) async {
|
||||
// Connect pushes carry no silent side effects, so nothing to do when the
|
||||
// user has notifications off.
|
||||
if (!notificationsEnabled) return;
|
||||
// On iOS the alert is delivered natively by the system; only Android needs
|
||||
// to render the plaintext payload locally.
|
||||
final data = message.data;
|
||||
@@ -114,6 +127,7 @@ class PushMessageHandler {
|
||||
Map<String, dynamic> data, {
|
||||
required bool foreground,
|
||||
required String? openChatToken,
|
||||
required bool notificationsEnabled,
|
||||
}) async {
|
||||
final subjectBase64 = data['subject'] as String;
|
||||
final signatureBase64 = data['signature'] as String;
|
||||
@@ -153,6 +167,11 @@ class PushMessageHandler {
|
||||
return;
|
||||
}
|
||||
|
||||
// Notifications turned off: the push was still processed (deletes above,
|
||||
// plus the foreground badge/provider refresh in NotificationController) —
|
||||
// only the visible tray notification is suppressed.
|
||||
if (!notificationsEnabled) return;
|
||||
|
||||
await _renderer.render(subject);
|
||||
}
|
||||
|
||||
|
||||
@@ -350,21 +350,26 @@ class PushRegistration {
|
||||
}
|
||||
}
|
||||
|
||||
/// Registers this device when push is both user-enabled and backend-capable.
|
||||
/// Only registers when the OS notification permission is *already* granted —
|
||||
/// it never triggers the OS prompt itself. Requesting the permission is the
|
||||
/// job of the first Talk visit (see `maybePromptTalkNotifications`), which
|
||||
/// keeps the prompt out of the cold-start path. Safe to call on every start —
|
||||
/// Nextcloud dedups an unchanged registration — which also self-heals a
|
||||
/// device whose registration was lost.
|
||||
static Future<void> syncSubscription({
|
||||
required bool enabled,
|
||||
required bool capable,
|
||||
}) async {
|
||||
if (!(enabled && capable)) return;
|
||||
/// Registers this device whenever the backend advertises the push capability.
|
||||
/// Deliberately independent of the in-app notification toggle: a user who
|
||||
/// turned notifications off stays registered so silent sync pushes keep
|
||||
/// flowing — the display is suppressed downstream via the mirrored flag (see
|
||||
/// [PushRegistrationStore.notificationsEnabled]). Only registers when the OS
|
||||
/// notification permission is *already* granted — it never triggers the OS
|
||||
/// prompt itself. Requesting the permission is the job of the first Talk visit
|
||||
/// (see `maybePromptTalkNotifications`), which keeps the prompt out of the
|
||||
/// cold-start path. Safe to call on every start — Nextcloud dedups an
|
||||
/// unchanged registration — which also self-heals a device whose registration
|
||||
/// was lost.
|
||||
/// Returns whether registration was actually *attempted* (all gates passed).
|
||||
/// Even a partial success persists the `general` device identifier, so the
|
||||
/// caller re-emits telemetry on `true` to reflect the fresh registration in
|
||||
/// the same session instead of lagging until the next launch.
|
||||
static Future<bool> syncSubscription({required bool capable}) async {
|
||||
if (!capable) return false;
|
||||
if (!await isOsPermissionGranted()) {
|
||||
log('Push: OS notification permission not granted, skipping registration');
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
final registration = PushRegistration();
|
||||
// register() below refreshes an unchanged subscription anyway; the check
|
||||
@@ -373,6 +378,7 @@ class PushRegistration {
|
||||
log('Push: registered endpoints outdated, re-registering');
|
||||
}
|
||||
await registration.register();
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Re-registers after an FCM token refresh. The Nextcloud device identifier
|
||||
|
||||
@@ -27,6 +27,11 @@ class PushRegistrationStore {
|
||||
// (AccountData writes `nextcloud_app_password` group-scoped).
|
||||
static const _usernameKey = 'nextcloud_username';
|
||||
static const _baseUrlKey = 'nextcloud_base_url';
|
||||
// Mirror of the in-app notification toggle (`notificationSettings.enabled`),
|
||||
// written group-scoped so the FCM background isolate (no bloc access) and the
|
||||
// iOS NSE can gate rendering. The device stays *registered* when off so silent
|
||||
// sync pushes keep flowing — only the visible alert is suppressed.
|
||||
static const _notificationsEnabledKey = 'push_notifications_enabled';
|
||||
|
||||
static const _perTypeKeys = [
|
||||
_deviceIdentifierKey,
|
||||
@@ -82,6 +87,19 @@ class PushRegistrationStore {
|
||||
await _storage.write(key: _baseUrlKey, value: baseUrl);
|
||||
}
|
||||
|
||||
/// Mirrors the in-app notification toggle so the background isolate / iOS NSE
|
||||
/// can read it without bloc access.
|
||||
Future<void> setNotificationsEnabled(bool enabled) =>
|
||||
_storage.write(
|
||||
key: _notificationsEnabledKey,
|
||||
value: enabled ? '1' : '0',
|
||||
);
|
||||
|
||||
/// The mirrored notification toggle. Defaults to `true` when unset (fresh
|
||||
/// install / pre-mirror build) so a missing mirror never silences pushes.
|
||||
Future<bool> notificationsEnabled() async =>
|
||||
await _storage.read(key: _notificationsEnabledKey) != '0';
|
||||
|
||||
Future<String?> deviceIdentifier(PushRegistrationType type) =>
|
||||
_storage.read(key: keyFor(_deviceIdentifierKey, type));
|
||||
|
||||
|
||||
Reference in New Issue
Block a user