More platform-independent approach to managing registered state

This commit is contained in:
Henry Hiles 2026-09-22 14:34:08 -04:00
commit 41806be9bf
Signed by: Henry-Hiles
SSH key fingerprint: SHA256:VKQUdS31Q90KvX7EkKMHMBpUspcmItAh86a+v7PGiIs
2 changed files with 49 additions and 42 deletions

View file

@ -15,6 +15,7 @@ import "package:nexus/controllers/rooms.dart";
import "package:nexus/controllers/space_edges.dart"; import "package:nexus/controllers/space_edges.dart";
import "package:nexus/controllers/sync_status.dart"; import "package:nexus/controllers/sync_status.dart";
import "package:nexus/controllers/top_level_spaces.dart"; import "package:nexus/controllers/top_level_spaces.dart";
import "package:nexus/controllers/unified_push.dart";
import "package:nexus/helpers/extensions/gomuks_buffer.dart"; import "package:nexus/helpers/extensions/gomuks_buffer.dart";
import "package:nexus/main.dart"; import "package:nexus/main.dart";
import "package:nexus/models/capabilities.dart"; import "package:nexus/models/capabilities.dart";
@ -344,7 +345,10 @@ class ClientController extends AsyncNotifier<int> {
Future<File> downloadMedia(DownloadMediaRequest request) async => Future<File> downloadMedia(DownloadMediaRequest request) async =>
.new((await _sendCommand("download_media", request.toJson()))["path"]); .new((await _sendCommand("download_media", request.toJson()))["path"]);
Future<void> logout() => _sendCommand("logout"); Future<void> logout() async {
await ref.watch(UnifiedPushController.provider.notifier).deregister();
await _sendCommand("logout");
}
Future<void> markRead(Room room) async { Future<void> markRead(Room room) async {
final eventRowId = room.timeline[room.timeline.keys.reduce(max)]; final eventRowId = room.timeline[room.timeline.keys.reduce(max)];

View file

@ -32,11 +32,11 @@ class UnifiedPushController extends AsyncNotifier<bool> {
onNewEndpoint: (endpoint, instance) async { onNewEndpoint: (endpoint, instance) async {
final pushKey = endpoint.pubKeySet!.pubKey; final pushKey = endpoint.pubKeySet!.pubKey;
await ref await ref
.watch(PushKeyController.provider(instance).notifier) .read(PushKeyController.provider(instance).notifier)
.set(pushKey); .set(pushKey);
await ref await ref
.watch(ClientController.provider.notifier) .read(ClientController.provider.notifier)
.registerPusher( .registerPusher(
.new( .new(
appDisplayName: "Nexus", appDisplayName: "Nexus",
@ -52,6 +52,8 @@ class UnifiedPushController extends AsyncNotifier<bool> {
pushKey: pushKey, pushKey: pushKey,
), ),
); );
state = .data(true);
}, },
onMessage: (message, instance) async { onMessage: (message, instance) async {
debugPrint("UP message received for $instance"); debugPrint("UP message received for $instance");
@ -61,14 +63,14 @@ class UnifiedPushController extends AsyncNotifier<bool> {
); );
} }
final event = await ref final event = await ref
.watch(ClientController.provider.notifier) .read(ClientController.provider.notifier)
.handlePush(json.decode(String.fromCharCodes(message.content))); .handlePush(json.decode(String.fromCharCodes(message.content)));
if (event == null || if (event == null ||
event.unreadType?.shouldNotify() != true || event.unreadType?.shouldNotify() != true ||
(!isInBackground && (!isInBackground &&
await windowManager.isFocused().onError((_, _) => false) && await windowManager.isFocused().onError((_, _) => false) &&
await ref.watch( await ref.read(
KeyController.provider(KeyController.roomKey).future, KeyController.provider(KeyController.roomKey).future,
) == ) ==
event.roomId)) { event.roomId)) {
@ -103,13 +105,7 @@ class UnifiedPushController extends AsyncNotifier<bool> {
if (isInBackground) exit(0); if (isInBackground) exit(0);
}, },
onRegistrationFailed: (error, instance) => throw error, onUnregistered: deregister,
onUnregistered: (instance) async {
await ref
.watch(PushKeyController.provider(instance).notifier)
.set(null);
ref.invalidateSelf();
},
); );
if (registered) { if (registered) {
@ -121,14 +117,16 @@ class UnifiedPushController extends AsyncNotifier<bool> {
} }
Future<void> register([bool alreadyRegistered = false]) async { Future<void> register([bool alreadyRegistered = false]) async {
state = .loading();
try {
final clientStateProvider = ClientStateController.provider; final clientStateProvider = ClientStateController.provider;
while (ref.watch(clientStateProvider)?.deviceId == null) { while (ref.read(clientStateProvider)?.deviceId == null) {
await Future.delayed(.new(milliseconds: 250)); await Future.delayed(.new(milliseconds: 250));
} }
final clientState = ref.watch(clientStateProvider); final clientState = ref.read(clientStateProvider);
final capabilities = await ref final capabilities = await ref
.watch(ClientController.provider.notifier) .read(ClientController.provider.notifier)
.getCapabilities(); .getCapabilities();
if (capabilities.webpush?.vapid == null) { if (capabilities.webpush?.vapid == null) {
@ -146,28 +144,33 @@ class UnifiedPushController extends AsyncNotifier<bool> {
instance: clientState!.deviceId!, instance: clientState!.deviceId!,
vapid: capabilities.webpush?.vapid, vapid: capabilities.webpush?.vapid,
); );
} catch (_) {
if (!alreadyRegistered) ref.invalidateSelf(); state = .data(false);
rethrow;
}
} }
Future<void> deregister() async { Future<void> deregister([String? instance]) async {
final clientState = ref.watch(ClientStateController.provider); final clientState = ref.read(ClientStateController.provider);
final key = await ref.watch(
PushKeyController.provider(clientState!.deviceId!).future, final keyProvider = PushKeyController.provider(
instance ?? clientState!.deviceId!,
); );
final key = await ref.read(keyProvider.future);
if (key != null) { if (key != null) {
await ref await ref
.watch(ClientController.provider.notifier) .read(ClientController.provider.notifier)
.deregisterPusher(.new(appId: "nexus.federated.nexus", pushKey: key)); .deregisterPusher(.new(appId: "nexus.federated.nexus", pushKey: key));
await ref.read(keyProvider.notifier).set(null);
} else { } else {
debugPrint( debugPrint(
"No matching pushKey found. Skipping deregistration from homeserver.", "No matching pushKey found. Skipping deregistration from homeserver.",
); );
} }
await UnifiedPush.unregister(clientState.deviceId!); await UnifiedPush.unregister(instance ?? clientState!.deviceId!);
ref.invalidateSelf(); state = .data(false);
} }
static final provider = AsyncNotifierProvider<UnifiedPushController, bool>( static final provider = AsyncNotifierProvider<UnifiedPushController, bool>(