improve UP timeout behavior

Hopefully more reliable.
This commit is contained in:
Henry Hiles 2026-10-05 17:00:56 -04:00
commit 3c6c610f7c
Signed by: Henry-Hiles
SSH key fingerprint: SHA256:VKQUdS31Q90KvX7EkKMHMBpUspcmItAh86a+v7PGiIs
2 changed files with 93 additions and 55 deletions

View file

@ -18,6 +18,8 @@ import "package:unifiedpush_storage_shared_preferences/storage.dart";
import "package:window_manager/window_manager.dart"; import "package:window_manager/window_manager.dart";
class UnifiedPushController extends AsyncNotifier<bool> { class UnifiedPushController extends AsyncNotifier<bool> {
Completer<void>? _endpoint;
@override @override
Future<bool> build() async { Future<bool> build() async {
if (!Platform.isLinux && !Platform.isAndroid) return false; if (!Platform.isLinux && !Platform.isAndroid) return false;
@ -30,33 +32,45 @@ class UnifiedPushController extends AsyncNotifier<bool> {
shouldWriteService: false, shouldWriteService: false,
), ),
onNewEndpoint: (endpoint, instance) async { onNewEndpoint: (endpoint, instance) async {
final pushKey = endpoint.pubKeySet!.pubKey; final task = Future(() async {
await ref final pushKey = endpoint.pubKeySet!.pubKey;
.read(PushKeyController.provider(instance).notifier) await ref
.set(pushKey); .read(PushKeyController.provider(instance).notifier)
.set(pushKey);
await ref await ref
.read(ClientController.provider.notifier) .read(ClientController.provider.notifier)
.registerPusher( .registerPusher(
.new( .new(
appDisplayName: "Nexus", appDisplayName: "Nexus",
appId: "nexus.federated.nexus", appId: "nexus.federated.nexus",
data: .webPush( data: .webPush(
url: .parse(endpoint.url), url: .parse(endpoint.url),
auth: endpoint.pubKeySet!.auth, 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: pushKey,
),
);
state = .data(true); state = .data(true);
});
if (_endpoint case final completer? when !completer.isCompleted) {
completer.complete(task);
} else {
await task;
}
}, },
onRegistrationFailed: (reason, instance) { onRegistrationFailed: (reason, instance) {
throw reason; if (_endpoint case final completer? when !completer.isCompleted) {
completer.completeError(reason);
} else {
throw reason;
}
}, },
onMessage: (message, instance) async { onMessage: (message, instance) async {
debugPrint("UP message received for $instance"); debugPrint("UP message received for $instance");
@ -111,48 +125,68 @@ class UnifiedPushController extends AsyncNotifier<bool> {
onUnregistered: deregister, onUnregistered: deregister,
); );
ref.listen( if (!registered) return false;
ClientStateController.provider.select((value) => value?.deviceId),
(_, _) => register(),
);
if (registered) { try {
// Needs to be registered every startup return await register();
await register(); } catch (error, stackTrace) {
showError(error, stackTrace);
return false;
} }
return registered;
} }
Future<void> register() async { Future<bool> register() async {
state = .loading(); state = .loading();
try { try {
final deviceId = ref.read( final deviceIdCompleter = Completer<String>();
final subscription = ref.listen(
ClientStateController.provider.select((value) => value?.deviceId), ClientStateController.provider.select((value) => value?.deviceId),
(_, next) {
if (next != null && !deviceIdCompleter.isCompleted) {
deviceIdCompleter.complete(next);
}
},
fireImmediately: true,
);
final deviceId = await deviceIdCompleter.future.whenComplete(
subscription.close,
); );
if (deviceId == null) return;
final capabilities = await ref await Future(() async {
.read(ClientController.provider.notifier) final capabilities = await ref
.getCapabilities(); .read(ClientController.provider.notifier)
.getCapabilities();
if (capabilities.webpush?.vapid == null) { if (capabilities.webpush?.vapid == null) {
throw UnsupportedError( throw UnsupportedError(
"Your homeserver does not support MSC4174 (Web Push), and therefore cannot send notifications to Nexus.", "Your homeserver does not support MSC4174 (Web Push), and therefore cannot send notifications to Nexus.",
); );
} }
if (!await UnifiedPush.tryUseCurrentOrDefaultDistributor()) { if (!await UnifiedPush.tryUseCurrentOrDefaultDistributor()) {
throw Exception("No UnifiedPush distributors found."); throw Exception("No UnifiedPush distributors found.");
} }
await UnifiedPush.register( final endpoint = _endpoint = Completer<void>();
instance: deviceId,
vapid: capabilities.webpush?.vapid, try {
).timeout( await Future.wait([
.new(seconds: 15), UnifiedPush.register(
instance: deviceId,
vapid: capabilities.webpush?.vapid,
),
endpoint.future,
], eagerError: true);
} finally {
if (_endpoint == endpoint) _endpoint = null;
}
}).timeout(
.new(seconds: 10),
onTimeout: () => throw Exception("UnifiedPush registration timed out."), onTimeout: () => throw Exception("UnifiedPush registration timed out."),
); );
state = .data(true);
return true;
} catch (_) { } catch (_) {
state = .data(false); state = .data(false);
rethrow; rethrow;

View file

@ -7,7 +7,11 @@ import "package:unifiedpush/unifiedpush.dart";
class UnifiedPushAllowedController extends AsyncNotifier<String?> { class UnifiedPushAllowedController extends AsyncNotifier<String?> {
@override @override
Future<String?> build() async { Future<String?> build() async {
if (!await UnifiedPush.tryUseCurrentOrDefaultDistributor()) { if (!await UnifiedPush.tryUseCurrentOrDefaultDistributor().timeout(
.new(seconds: 10),
onTimeout: () =>
throw Exception("UnifiedPush distributor check timed out."),
)) {
return "No valid distributors found. ${Platform.isLinux return "No valid distributors found. ${Platform.isLinux
? "Try installing KUnifiedPush" ? "Try installing KUnifiedPush"
: Platform.isAndroid : Platform.isAndroid