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> {
@override
Future<void> build() => .wait(
providers.map((provider) => ref.read(provider.future)),
providers.map((provider) => ref.watch(provider.future)),
eagerError: true,
);

View file

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

View file

@ -151,54 +151,56 @@ class const App({super.key}) extends StatelessWidget {
builder: (_, ref, _) => ref
.watch(ClientController.provider)
.betterWhen(
data: (_) => ref
.watch(
MultiProviderController.provider(
.new([
NotificationController.provider,
UnifiedPushController.provider,
MemberListOpenedController.provider,
KeyController.provider(KeyController.roomKey),
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),
),
data: (_) => switch (ref.watch(
MultiProviderController.provider(
.new([
NotificationController.provider,
UnifiedPushController.provider,
MemberListOpenedController.provider,
KeyController.provider(KeyController.roomKey),
KeyController.provider(KeyController.spaceKey),
]),
),
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,
),
},
),
),
),