Watch
1
0
Fork
You've already forked nexus
0
forked from Nexus/nexus

use SharedPreferencesAsync to avoid race condition in Sharedprefs

This was so painful to debug...
This commit is contained in:
Henry Hiles 2026-09-20 15:45:32 -04:00
commit e8fb3a8523
Signed by: Henry-Hiles
SSH key fingerprint: SHA256:VKQUdS31Q90KvX7EkKMHMBpUspcmItAh86a+v7PGiIs
10 changed files with 69 additions and 44 deletions

View file

@ -1,28 +1,28 @@
import "package:flutter_riverpod/flutter_riverpod.dart";
import "package:nexus/controllers/shared_prefs.dart";
class KeyController(final String key) extends Notifier<String?> {
class KeyController(final String key) extends AsyncNotifier<String?> {
static const String spaceKey = "space";
static const String roomKey = "room";
static const String pushKeyKey = "pushKey";
@override
String? build() =>
ref.watch(SharedPrefsController.provider).requireValue.getString(key);
Future<String?> build() =>
ref.watch(SharedPrefsController.provider).getString(key);
Future<void> set(String? value) async {
final prefs = ref.watch(SharedPrefsController.provider).requireValue;
state = value;
final prefs = ref.watch(SharedPrefsController.provider);
state = .data(value);
if (value == null) {
prefs.remove(key);
await prefs.remove(key);
} else {
prefs.setString(key, value);
await prefs.setString(key, value);
}
}
static final provider =
NotifierProvider.family<KeyController, String?, String>(
AsyncNotifierProvider.family<KeyController, String?, String>(
KeyController.new,
);
}

View file

@ -1,22 +1,20 @@
import "package:flutter_riverpod/flutter_riverpod.dart";
import "package:nexus/controllers/shared_prefs.dart";
class MemberListOpenedController extends Notifier<bool> {
class MemberListOpenedController extends AsyncNotifier<bool> {
static const String key = "memberListOpened";
@override
bool build() =>
ref.watch(SharedPrefsController.provider).requireValue.getBool(key) ??
true;
Future<bool> build() async =>
await ref.watch(SharedPrefsController.provider).getBool(key) ?? true;
Future<void> set(bool value) async {
final prefs = ref.watch(SharedPrefsController.provider).requireValue;
state = value;
prefs.setBool(key, value);
state = .data(value);
await ref.watch(SharedPrefsController.provider).setBool(key, value);
}
static final provider = NotifierProvider<MemberListOpenedController, bool>(
MemberListOpenedController.new,
);
static final provider =
AsyncNotifierProvider<MemberListOpenedController, bool>(
MemberListOpenedController.new,
);
}

View file

@ -4,18 +4,19 @@ 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?> {
class PushKeyController(final String instance) extends AsyncNotifier<String?> {
@override
String? build() => json.decode(
ref.watch(KeyController.provider(KeyController.pushKeyKey)) ?? "{}",
Future<String?> build() async => json.decode(
await ref.watch(KeyController.provider(KeyController.pushKeyKey).future) ??
"{}",
)[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) ?? "{}"));
final prefs = IMap(json.decode(await ref.watch(provider.future) ?? "{}"));
state = value;
state = .data(value);
notifier.set(
json.encode(
prefs.add(instance, value).where((_, value) => value != null).unlock,
@ -24,7 +25,7 @@ class PushKeyController(final String instance) extends Notifier<String?> {
}
static final provider =
NotifierProvider.family<PushKeyController, String?, String>(
AsyncNotifierProvider.family<PushKeyController, String?, String>(
PushKeyController.new,
);
}

View file

@ -1,12 +1,12 @@
import "package:flutter_riverpod/flutter_riverpod.dart";
import "package:shared_preferences/shared_preferences.dart";
class SharedPrefsController extends AsyncNotifier<SharedPreferences> {
class SharedPrefsController extends Notifier<SharedPreferencesAsync> {
@override
Future<SharedPreferences> build() async => .getInstance();
SharedPreferencesAsync build() => SharedPreferencesAsync();
static final provider =
AsyncNotifierProvider<SharedPrefsController, SharedPreferences>(
NotifierProvider<SharedPrefsController, SharedPreferencesAsync>(
SharedPrefsController.new,
);
}

View file

@ -13,7 +13,6 @@ import "package:nexus/controllers/client.dart";
import "package:nexus/controllers/client_state.dart";
import "package:nexus/models/content/message.dart";
import "package:nexus/models/content/sticker.dart";
import "package:nexus/models/requests/register_pusher.dart";
import "package:unifiedpush/unifiedpush.dart";
import "package:unifiedpush_storage_shared_preferences/storage.dart";
import "package:window_manager/window_manager.dart";
@ -40,8 +39,8 @@ class UnifiedPushController extends AsyncNotifier<bool> {
.new(
appDisplayName: "Nexus",
appId: "nexus.federated.nexus",
data: PusherData.webPush(
url: Uri.parse(endpoint.url),
data: .webPush(
url: .parse(endpoint.url),
auth: endpoint.pubKeySet!.auth,
),
deviceDisplayName:
@ -67,7 +66,9 @@ class UnifiedPushController extends AsyncNotifier<bool> {
event.unreadType?.shouldNotify() != true ||
(!isInBackground &&
await windowManager.isFocused().onError((_, _) => false) &&
ref.watch(KeyController.provider(KeyController.roomKey)) ==
await ref.watch(
KeyController.provider(KeyController.roomKey).future,
) ==
event.roomId)) {
if (isInBackground) exit(0);
return;
@ -146,7 +147,9 @@ class UnifiedPushController extends AsyncNotifier<bool> {
Future<void> deregister() async {
final clientState = ref.watch(ClientStateController.provider);
final key = ref.watch(PushKeyController.provider(clientState!.deviceId!));
final key = await ref.watch(
PushKeyController.provider(clientState!.deviceId!).future,
);
if (key != null) {
await ref