more reliable UP deregistration with proper cross-platform support

This commit is contained in:
Henry Hiles 2026-09-10 14:14:10 -04:00
commit 06720238b8
Signed by: Henry-Hiles
SSH key fingerprint: SHA256:VKQUdS31Q90KvX7EkKMHMBpUspcmItAh86a+v7PGiIs
4 changed files with 70 additions and 27 deletions

View file

@ -4,6 +4,7 @@ import "package:nexus/controllers/shared_prefs.dart";
class KeyController(final String key) extends Notifier<String?> { class KeyController(final String key) extends Notifier<String?> {
static const String spaceKey = "space"; static const String spaceKey = "space";
static const String roomKey = "room"; static const String roomKey = "room";
static const String pushKeyKey = "pushKey";
@override @override
String? build() => String? build() =>

View file

@ -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<String?> {
@override
String? build() => json.decode(
ref.watch(KeyController.provider(KeyController.pushKeyKey)) ?? "{}",
)[instance];
Future<void> 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, String?, String>(
PushKeyController.new,
);
}

View file

@ -1,10 +1,12 @@
import "dart:convert"; import "dart:convert";
import "dart:io"; import "dart:io";
import "package:flutter/foundation.dart";
import "package:flutter_riverpod/flutter_riverpod.dart"; import "package:flutter_riverpod/flutter_riverpod.dart";
import "package:intl/intl.dart"; import "package:intl/intl.dart";
import "package:nexus/controllers/key.dart"; import "package:nexus/controllers/key.dart";
import "package:nexus/controllers/notifications.dart"; import "package:nexus/controllers/notifications.dart";
import "package:nexus/controllers/push_key.dart";
import "package:nexus/controllers/rooms.dart"; import "package:nexus/controllers/rooms.dart";
import "package:nexus/main.dart"; import "package:nexus/main.dart";
import "package:nexus/controllers/client.dart"; import "package:nexus/controllers/client.dart";
@ -26,7 +28,11 @@ class UnifiedPushController extends AsyncNotifier<bool> {
storage: UnifiedPushStorageSharedPreferences(), storage: UnifiedPushStorageSharedPreferences(),
background: isInBackground, background: isInBackground,
), ),
onNewEndpoint: (endpoint, instance) => client.registerPusher( onNewEndpoint: (endpoint, instance) async {
final pushKey = endpoint.pubKeySet!.pubKey;
ref.watch(PushKeyController.provider(instance).notifier).set(pushKey);
await client.registerPusher(
.new( .new(
appDisplayName: "Nexus", appDisplayName: "Nexus",
appId: "nexus.federated.nexus", appId: "nexus.federated.nexus",
@ -38,9 +44,10 @@ class UnifiedPushController extends AsyncNotifier<bool> {
"Nexus on ${toBeginningOfSentenceCase(Platform.operatingSystem)}", "Nexus on ${toBeginningOfSentenceCase(Platform.operatingSystem)}",
kind: .webPush, kind: .webPush,
lang: "en", lang: "en",
pushKey: endpoint.pubKeySet!.pubKey, pushKey: pushKey,
),
), ),
);
},
onMessage: (message, instance) async { onMessage: (message, instance) async {
if (message.decrypted == false) { if (message.decrypted == false) {
throw Exception( throw Exception(
@ -88,7 +95,12 @@ class UnifiedPushController extends AsyncNotifier<bool> {
if (isInBackground) exit(0); if (isInBackground) exit(0);
}, },
onRegistrationFailed: (error, instance) => throw error, 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) { if (registered) {
@ -128,17 +140,17 @@ class UnifiedPushController extends AsyncNotifier<bool> {
Future<void> deregister() async { Future<void> deregister() async {
final clientState = ref.watch(ClientStateController.provider); final clientState = ref.watch(ClientStateController.provider);
final key = await UnifiedPushStorageSharedPreferences().keys.get( final key = ref.watch(PushKeyController.provider(clientState!.deviceId!));
clientState!.deviceId!,
);
if (key == null) return;
if (key != null) {
await ref await ref
.watch(ClientController.provider.notifier) .watch(ClientController.provider.notifier)
.deregisterPusher( .deregisterPusher(.new(appId: "nexus.federated.nexus", pushKey: key));
.new(appId: "nexus.federated.nexus", pushKey: key.split("=").first), } else {
debugPrint(
"No matching pushKey found. Skipping deregistration from homeserver.",
); );
}
await UnifiedPush.unregister(clientState.deviceId!); await UnifiedPush.unregister(clientState.deviceId!);
ref.invalidateSelf(); ref.invalidateSelf();

View file

@ -11,7 +11,7 @@ class UnifiedPushAllowedController extends AsyncNotifier<String?> {
return "No valid distributors found. Try installing ${Platform.isLinux return "No valid distributors found. Try installing ${Platform.isLinux
? "KUnifiedPush" ? "KUnifiedPush"
: Platform.isAndroid : Platform.isAndroid
? "FCM or NTFY" ? "Google Play Services or NTFY"
: "NTFY"}."; : "NTFY"}.";
} }