fix race condition on startup

This commit is contained in:
Henry Hiles 2026-09-22 11:36:29 -04:00
commit 6e47594a91
Signed by: Henry-Hiles
SSH key fingerprint: SHA256:VKQUdS31Q90KvX7EkKMHMBpUspcmItAh86a+v7PGiIs
3 changed files with 74 additions and 68 deletions

View file

@ -7,7 +7,7 @@ class MultiProviderController(final IList<AsyncNotifierProvider> providers)
extends AsyncNotifier<void> { extends AsyncNotifier<void> {
@override @override
Future<void> build() => .wait( Future<void> build() => .wait(
providers.map((provider) => ref.read(provider.future)), providers.map((provider) => ref.watch(provider.future)),
eagerError: true, eagerError: true,
); );

View file

@ -22,7 +22,6 @@ class UnifiedPushController extends AsyncNotifier<bool> {
Future<bool> build() async { Future<bool> build() async {
if (!Platform.isLinux && !Platform.isAndroid) return false; if (!Platform.isLinux && !Platform.isAndroid) return false;
final client = ref.watch(ClientController.provider.notifier);
final registered = await UnifiedPush.initialize( final registered = await UnifiedPush.initialize(
linuxOptions: .new( linuxOptions: .new(
dbusName: "nexus.federated.nexus.UnifiedPush", dbusName: "nexus.federated.nexus.UnifiedPush",
@ -36,21 +35,23 @@ class UnifiedPushController extends AsyncNotifier<bool> {
.watch(PushKeyController.provider(instance).notifier) .watch(PushKeyController.provider(instance).notifier)
.set(pushKey); .set(pushKey);
await client.registerPusher( await ref
.new( .watch(ClientController.provider.notifier)
appDisplayName: "Nexus", .registerPusher(
appId: "nexus.federated.nexus", .new(
data: .webPush( appDisplayName: "Nexus",
url: .parse(endpoint.url), appId: "nexus.federated.nexus",
auth: endpoint.pubKeySet!.auth, data: .webPush(
), url: .parse(endpoint.url),
deviceDisplayName: auth: endpoint.pubKeySet!.auth,
"Nexus on ${toBeginningOfSentenceCase(Platform.operatingSystem)}", ),
kind: .webPush, deviceDisplayName:
lang: "en", "Nexus on ${toBeginningOfSentenceCase(Platform.operatingSystem)}",
pushKey: pushKey, kind: .webPush,
), lang: "en",
); pushKey: pushKey,
),
);
}, },
onMessage: (message, instance) async { onMessage: (message, instance) async {
debugPrint("UP message received for $instance"); debugPrint("UP message received for $instance");
@ -59,9 +60,9 @@ class UnifiedPushController extends AsyncNotifier<bool> {
"Failed to decrypt notification. Try toggling off and on UnifiedPush in settings.", "Failed to decrypt notification. Try toggling off and on UnifiedPush in settings.",
); );
} }
final event = await client.handlePush( final event = await ref
json.decode(String.fromCharCodes(message.content)), .watch(ClientController.provider.notifier)
); .handlePush(json.decode(String.fromCharCodes(message.content)));
if (event == null || if (event == null ||
event.unreadType?.shouldNotify() != true || event.unreadType?.shouldNotify() != true ||
@ -120,8 +121,11 @@ class UnifiedPushController extends AsyncNotifier<bool> {
} }
Future<void> register([bool alreadyRegistered = false]) async { Future<void> register([bool alreadyRegistered = false]) async {
final clientState = ref.watch(ClientStateController.provider); final clientStateProvider = ClientStateController.provider;
if (clientState?.deviceId == null) return ref.invalidateSelf(); while (ref.watch(clientStateProvider)?.deviceId == null) {
await Future.delayed(.new(milliseconds: 250));
}
final clientState = ref.watch(clientStateProvider);
final capabilities = await ref final capabilities = await ref
.watch(ClientController.provider.notifier) .watch(ClientController.provider.notifier)

View file

@ -151,54 +151,56 @@ class const App({super.key}) extends StatelessWidget {
builder: (_, ref, _) => ref builder: (_, ref, _) => ref
.watch(ClientController.provider) .watch(ClientController.provider)
.betterWhen( .betterWhen(
data: (_) => ref data: (_) => switch (ref.watch(
.watch( MultiProviderController.provider(
MultiProviderController.provider( .new([
.new([ NotificationController.provider,
NotificationController.provider, UnifiedPushController.provider,
UnifiedPushController.provider, MemberListOpenedController.provider,
MemberListOpenedController.provider, KeyController.provider(KeyController.roomKey),
KeyController.provider(KeyController.roomKey), KeyController.provider(KeyController.spaceKey),
KeyController.provider(KeyController.spaceKey),
]),
),
)
.betterWhen(
data: (_) => Consumer(
builder: (_, ref, _) {
final clientState = ref.watch(
ClientStateController.provider,
);
if (clientState == null ||
!clientState.isInitialized) {
return Loading();
}
if (!clientState.isLoggedIn) {
return SelectServerPage();
} else if (!clientState.isVerified) {
return VerifyPage();
} else {
return ChatPage();
}
},
),
),
loading: () => Scaffold(
appBar: Appbar(
actions: .new([
IconButton(
onPressed: () => showDialog(
context: context,
builder: (_) => SettingsPage(),
),
icon: Icon(Icons.settings),
),
]), ]),
), ),
body: Loading(), )) {
), AsyncData(value: _) || AsyncLoading(value: _?) => Consumer(
builder: (_, ref, _) {
final clientState = ref.watch(
ClientStateController.provider,
);
if (clientState == null || !clientState.isInitialized) {
return Loading();
}
if (!clientState.isLoggedIn) {
return SelectServerPage();
} else if (!clientState.isVerified) {
return VerifyPage();
} else {
return ChatPage();
}
},
),
AsyncLoading _ => Scaffold(
appBar: Appbar(
actions: .new([
IconButton(
onPressed: () => showDialog(
context: context,
builder: (_) => SettingsPage(),
),
icon: Icon(Icons.settings),
),
]),
),
body: Loading(),
),
AsyncError(:final error, :final stackTrace) => ErrorDialog(
error,
stackTrace,
),
},
), ),
), ),
), ),