use SharedPreferencesAsync to avoid race condition in Sharedprefs
This was so painful to debug...
This commit is contained in:
parent
0d1ace3fd0
commit
e8fb3a8523
10 changed files with 69 additions and 44 deletions
17
.vscode/launch.json
vendored
Normal file
17
.vscode/launch.json
vendored
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Flutter",
|
||||
"type": "dart",
|
||||
"request": "launch",
|
||||
"program": "lib/main.dart"
|
||||
},
|
||||
{
|
||||
"name": "Flutter (Headless)",
|
||||
"type": "dart",
|
||||
"request": "launch",
|
||||
"program": "lib/main.dart",
|
||||
"env": { "FLUTTER_HEADLESS": "1" }
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -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,
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -7,10 +7,11 @@ import "package:flutter_riverpod/flutter_riverpod.dart";
|
|||
import "package:media_kit/media_kit.dart";
|
||||
import "package:nexus/controllers/client.dart";
|
||||
import "package:nexus/controllers/client_state.dart";
|
||||
import "package:nexus/controllers/key.dart";
|
||||
import "package:nexus/controllers/member_list_opened.dart";
|
||||
import "package:nexus/controllers/multi_provider.dart";
|
||||
import "package:nexus/controllers/notification.dart";
|
||||
import "package:nexus/controllers/settings.dart";
|
||||
import "package:nexus/controllers/shared_prefs.dart";
|
||||
import "package:nexus/controllers/unified_push.dart";
|
||||
import "package:nexus/helpers/extensions/better_when.dart";
|
||||
import "package:nexus/helpers/extensions/scheme_to_theme.dart";
|
||||
|
|
@ -81,9 +82,9 @@ void main(List<String> args) async {
|
|||
await windowManager.setMinimumSize(Size.square(500));
|
||||
}
|
||||
|
||||
isInBackground = args.contains("--unifiedpush-bg");
|
||||
isInBackground = Platform.environment["FLUTTER_HEADLESS"] != null;
|
||||
|
||||
LicenseRegistry.addLicense(() => Stream.fromIterable(fontLicenses));
|
||||
LicenseRegistry.addLicense(() => .fromIterable(fontLicenses));
|
||||
|
||||
FlutterError.onError = (FlutterErrorDetails details) =>
|
||||
showError(details.exception.toString(), details.stack);
|
||||
|
|
@ -103,14 +104,13 @@ void main(List<String> args) async {
|
|||
// ignore: dead_code
|
||||
if (false && kDebugMode) Logger(),
|
||||
],
|
||||
child: App(isInBackground),
|
||||
child: App(),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class const App(final bool isInBackground, {super.key})
|
||||
extends StatelessWidget {
|
||||
class const App({super.key}) extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) => DynamicColorBuilder(
|
||||
builder: (lightDynamic, darkDynamic) => Consumer(
|
||||
|
|
@ -151,10 +151,12 @@ class const App(final bool isInBackground, {super.key})
|
|||
.watch(
|
||||
MultiProviderController.provider(
|
||||
IListConst([
|
||||
SharedPrefsController.provider,
|
||||
ClientController.provider,
|
||||
NotificationController.provider,
|
||||
UnifiedPushController.provider,
|
||||
MemberListOpenedController.provider,
|
||||
KeyController.provider(KeyController.roomKey),
|
||||
KeyController.provider(KeyController.spaceKey),
|
||||
]),
|
||||
),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -14,7 +14,9 @@ class const ChatPage({super.key}) extends ConsumerWidget {
|
|||
final isDesktop = constraints.maxWidth > 650;
|
||||
final showMembersByDefault = constraints.maxWidth > 1000;
|
||||
final initComplete = ref.watch(InitCompleteController.provider);
|
||||
final roomId = ref.watch(KeyController.provider(KeyController.roomKey));
|
||||
final roomId = ref
|
||||
.watch(KeyController.provider(KeyController.roomKey))
|
||||
.requireValue;
|
||||
|
||||
return SafeArea(
|
||||
child: Scaffold(
|
||||
|
|
|
|||
|
|
@ -43,7 +43,9 @@ final class const RoomChat({
|
|||
final composerSize = useState<double>(64);
|
||||
|
||||
final userId = ref.watch(ClientStateController.provider)?.userId;
|
||||
final memberListOpened = ref.watch(MemberListOpenedController.provider);
|
||||
final memberListOpened = ref
|
||||
.watch(MemberListOpenedController.provider)
|
||||
.requireValue;
|
||||
final theme = Theme.of(context);
|
||||
|
||||
final nothing = Center(
|
||||
|
|
|
|||
|
|
@ -23,13 +23,13 @@ class const Sidebar({required final bool isDesktop, super.key})
|
|||
final selectedSpaceProvider = KeyController.provider(
|
||||
KeyController.spaceKey,
|
||||
);
|
||||
final selectedSpaceId = ref.watch(selectedSpaceProvider);
|
||||
final selectedSpaceId = ref.watch(selectedSpaceProvider).requireValue;
|
||||
final selectedSpaceIdNotifier = ref.watch(selectedSpaceProvider.notifier);
|
||||
|
||||
final selectedRoomController = KeyController.provider(
|
||||
KeyController.roomKey,
|
||||
);
|
||||
final selectedRoomId = ref.watch(selectedRoomController);
|
||||
final selectedRoomId = ref.watch(selectedRoomController).requireValue;
|
||||
final selectedRoomIdNotifier = ref.watch(selectedRoomController.notifier);
|
||||
|
||||
final spaces = ref.watch(SpacesController.provider);
|
||||
|
|
|
|||
Loading…
Reference in a new issue