From fc80be1a495a6c7ecd6769f865a318eb23bd3138 Mon Sep 17 00:00:00 2001 From: Greg Price Date: Thu, 17 Oct 2024 21:08:27 -0700 Subject: [PATCH] store [nfc]: Offer singleton global store on binding This will be useful in the context of showing a notification, where there may not exist an element tree at all (because we may be in a background isolate), and even if there happens to be one (because we happen to be in the foreground) it doesn't otherwise relate to anything the code is doing. --- lib/model/binding.dart | 31 +++++++++++++++++++++++-------- lib/widgets/store.dart | 2 +- test/model/binding.dart | 7 +++++-- 3 files changed, 29 insertions(+), 11 deletions(-) diff --git a/lib/model/binding.dart b/lib/model/binding.dart index b15febacbc..7f014e1a5a 100644 --- a/lib/model/binding.dart +++ b/lib/model/binding.dart @@ -1,3 +1,5 @@ +import 'dart:async'; + import 'package:device_info_plus/device_info_plus.dart' as device_info_plus; import 'package:file_picker/file_picker.dart' as file_picker; import 'package:firebase_core/firebase_core.dart' as firebase_core; @@ -74,14 +76,20 @@ abstract class ZulipBinding { _instance = this; } - /// Prepare the app's [GlobalStore], loading the necessary data. + /// Get the app's singleton [GlobalStore], + /// calling [loadGlobalStore] if not already loaded. /// - /// Generally the app should call this function only once. + /// Where possible, use [GlobalStoreWidget.of] to get access to a [GlobalStore]. + /// Use this method only in contexts like notifications where + /// a widget tree may not exist. + Future getGlobalStore(); + + /// Like [getGlobalStore], but assert this method was not previously called. /// - /// This is part of the implementation of [GlobalStoreWidget]. - /// In application code, use [GlobalStoreWidget.of] to get access - /// to a [GlobalStore]. - Future loadGlobalStore(); + /// This is used by the implementation of [GlobalStoreWidget], + /// so that our test framework code can detect some cases where + /// a widget test neglects to clean up with `testBinding.reset`. + Future getGlobalStoreUniquely(); /// Checks whether the platform can launch [url], via package:url_launcher. /// @@ -324,9 +332,16 @@ class LiveZulipBinding extends ZulipBinding { } @override - Future loadGlobalStore() { - return LiveGlobalStore.load(); + Future getGlobalStore() => _globalStore ??= LiveGlobalStore.load(); + Future? _globalStore; + + @override + Future getGlobalStoreUniquely() { + assert(!_debugCalledGetGlobalStoreUniquely); + assert(_debugCalledGetGlobalStoreUniquely = true); + return getGlobalStore(); } + bool _debugCalledGetGlobalStoreUniquely = false; @override Future canLaunchUrl(Uri url) => url_launcher.canLaunchUrl(url); diff --git a/lib/widgets/store.dart b/lib/widgets/store.dart index 1929a225f6..bcb5f7c274 100644 --- a/lib/widgets/store.dart +++ b/lib/widgets/store.dart @@ -60,7 +60,7 @@ class _GlobalStoreWidgetState extends State { void initState() { super.initState(); (() async { - final store = await ZulipBinding.instance.loadGlobalStore(); + final store = await ZulipBinding.instance.getGlobalStoreUniquely(); setState(() { this.store = store; }); diff --git a/test/model/binding.dart b/test/model/binding.dart index 2673ccec08..b968566dc5 100644 --- a/test/model/binding.dart +++ b/test/model/binding.dart @@ -100,7 +100,10 @@ class TestZulipBinding extends ZulipBinding { } @override - Future loadGlobalStore() { + Future getGlobalStore() => Future.value(globalStore); + + @override + Future getGlobalStoreUniquely() { assert(() { if (_debugAlreadyLoadedStore) { throw FlutterError.fromParts([ @@ -121,7 +124,7 @@ class TestZulipBinding extends ZulipBinding { _debugAlreadyLoadedStore = true; return true; }()); - return Future.value(globalStore); + return getGlobalStore(); } /// The value that `ZulipBinding.instance.canLaunchUrl()` should return.