From 52544d88144915d1326b0d83e85ec546dc6a1ceb Mon Sep 17 00:00:00 2001 From: Henry-Hiles Date: Thu, 10 Sep 2026 14:14:10 -0400 Subject: [PATCH] more reliable UP deregistration with proper cross-platform support --- lib/controllers/key.dart | 1 + lib/controllers/push_key.dart | 30 +++++++++++ lib/controllers/unified_push.dart | 62 ++++++++++++++--------- lib/controllers/unified_push_allowed.dart | 2 +- 4 files changed, 69 insertions(+), 26 deletions(-) create mode 100644 lib/controllers/push_key.dart diff --git a/lib/controllers/key.dart b/lib/controllers/key.dart index b78acf8..0d02d17 100644 --- a/lib/controllers/key.dart +++ b/lib/controllers/key.dart @@ -4,6 +4,7 @@ import "package:nexus/controllers/shared_prefs.dart"; class KeyController(final String key) extends Notifier { static const String spaceKey = "space"; static const String roomKey = "room"; + static const String pushKeyKey = "pushKey"; @override String? build() => diff --git a/lib/controllers/push_key.dart b/lib/controllers/push_key.dart new file mode 100644 index 0000000..b73aa89 --- /dev/null +++ b/lib/controllers/push_key.dart @@ -0,0 +1,30 @@ +import "dart:convert"; + +import "package:fast_immutable_collections/fast_immutable_collections.dart"; +import "package:flutter_riverpod/flutter_riverpod.dart"; +import "package:nexus/controllers/key.dart"; + +class PushKeyController(final String instance) extends Notifier { + @override + String? build() => json.decode( + ref.watch(KeyController.provider(KeyController.pushKeyKey)) ?? "{}", + )[instance]; + + Future set(String? value) async { + final provider = KeyController.provider(KeyController.pushKeyKey); + final notifier = ref.watch(provider.notifier); + final prefs = IMap(json.decode(ref.watch(provider) ?? "{}")); + + state = value; + notifier.set( + json.encode( + prefs.add(instance, value).where((_, value) => value != null), + ), + ); + } + + static final provider = + NotifierProvider.family( + PushKeyController.new, + ); +} diff --git a/lib/controllers/unified_push.dart b/lib/controllers/unified_push.dart index f9d8f8f..cb42577 100644 --- a/lib/controllers/unified_push.dart +++ b/lib/controllers/unified_push.dart @@ -1,10 +1,12 @@ import "dart:convert"; import "dart:io"; +import "package:flutter/foundation.dart"; import "package:flutter_riverpod/flutter_riverpod.dart"; import "package:intl/intl.dart"; import "package:nexus/controllers/key.dart"; import "package:nexus/controllers/notifications.dart"; +import "package:nexus/controllers/push_key.dart"; import "package:nexus/controllers/rooms.dart"; import "package:nexus/main.dart"; import "package:nexus/controllers/client.dart"; @@ -26,21 +28,26 @@ class UnifiedPushController extends AsyncNotifier { storage: UnifiedPushStorageSharedPreferences(), background: isInBackground, ), - onNewEndpoint: (endpoint, instance) => client.registerPusher( - .new( - appDisplayName: "Nexus", - appId: "nexus.federated.nexus", - data: PusherData.webPush( - url: Uri.parse(endpoint.url), - auth: endpoint.pubKeySet!.auth, + onNewEndpoint: (endpoint, instance) async { + final pushKey = endpoint.pubKeySet!.pubKey; + ref.watch(PushKeyController.provider(instance).notifier).set(pushKey); + + await client.registerPusher( + .new( + appDisplayName: "Nexus", + appId: "nexus.federated.nexus", + data: PusherData.webPush( + url: Uri.parse(endpoint.url), + auth: endpoint.pubKeySet!.auth, + ), + deviceDisplayName: + "Nexus on ${toBeginningOfSentenceCase(Platform.operatingSystem)}", + kind: .webPush, + lang: "en", + pushKey: pushKey, ), - deviceDisplayName: - "Nexus on ${toBeginningOfSentenceCase(Platform.operatingSystem)}", - kind: .webPush, - lang: "en", - pushKey: endpoint.pubKeySet!.pubKey, - ), - ), + ); + }, onMessage: (message, instance) async { if (message.decrypted == false) { throw Exception( @@ -88,7 +95,12 @@ class UnifiedPushController extends AsyncNotifier { if (isInBackground) exit(0); }, onRegistrationFailed: (error, instance) => throw error, - onUnregistered: (instance) => ref.invalidateSelf(), + onUnregistered: (instance) async { + await ref + .watch(PushKeyController.provider(instance).notifier) + .set(null); + ref.invalidateSelf(); + }, ); if (registered) { @@ -128,17 +140,17 @@ class UnifiedPushController extends AsyncNotifier { Future deregister() async { final clientState = ref.watch(ClientStateController.provider); - final key = await UnifiedPushStorageSharedPreferences().keys.get( - clientState!.deviceId!, - ); + final key = ref.watch(PushKeyController.provider(clientState!.deviceId!)); - if (key == null) return; - - await ref - .watch(ClientController.provider.notifier) - .deregisterPusher( - .new(appId: "nexus.federated.nexus", pushKey: key.split("=").first), - ); + if (key != null) { + await ref + .watch(ClientController.provider.notifier) + .deregisterPusher(.new(appId: "nexus.federated.nexus", pushKey: key)); + } else { + debugPrint( + "No matching pushKey found. Skipping deregistration from homeserver.", + ); + } await UnifiedPush.unregister(clientState.deviceId!); ref.invalidateSelf(); diff --git a/lib/controllers/unified_push_allowed.dart b/lib/controllers/unified_push_allowed.dart index 4f925cf..af50d62 100644 --- a/lib/controllers/unified_push_allowed.dart +++ b/lib/controllers/unified_push_allowed.dart @@ -11,7 +11,7 @@ class UnifiedPushAllowedController extends AsyncNotifier { return "No valid distributors found. Try installing ${Platform.isLinux ? "KUnifiedPush" : Platform.isAndroid - ? "FCM or NTFY" + ? "Google Play Services or NTFY" : "NTFY"}."; }