Skip to content

Commit 60577d6

Browse files
app: Move initialization of GlobalStore from GlobalStoreWidget to ZulipApp
1 parent e932a05 commit 60577d6

File tree

6 files changed

+158
-168
lines changed

6 files changed

+158
-168
lines changed

lib/widgets/app.dart

+13
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import 'package:flutter/scheduler.dart';
77
import '../generated/l10n/zulip_localizations.dart';
88
import '../log.dart';
99
import '../model/actions.dart';
10+
import '../model/binding.dart';
1011
import '../model/localizations.dart';
1112
import '../model/store.dart';
1213
import '../notifications/display.dart';
@@ -151,10 +152,19 @@ class ZulipApp extends StatefulWidget {
151152
}
152153

153154
class _ZulipAppState extends State<ZulipApp> with WidgetsBindingObserver {
155+
GlobalStore? _globalStore;
156+
154157
@override
155158
void initState() {
156159
super.initState();
157160
WidgetsBinding.instance.addObserver(this);
161+
() async {
162+
final globalStore = await ZulipBinding.instance.getGlobalStoreUniquely();
163+
if (!mounted) return;
164+
setState(() {
165+
_globalStore = globalStore;
166+
});
167+
}();
158168
}
159169

160170
@override
@@ -212,7 +222,10 @@ class _ZulipAppState extends State<ZulipApp> with WidgetsBindingObserver {
212222

213223
@override
214224
Widget build(BuildContext context) {
225+
if (_globalStore == null) return const LoadingPlaceholder();
226+
215227
return GlobalStoreWidget(
228+
store: _globalStore!,
216229
child: Builder(builder: (context) {
217230
return MaterialApp(
218231
onGenerateTitle: (BuildContext context) {

lib/widgets/store.dart

+9-49
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
import 'package:flutter/material.dart';
22
import 'package:flutter/scheduler.dart';
33

4-
import '../model/binding.dart';
5-
import '../model/database.dart';
64
import '../model/settings.dart';
75
import '../model/store.dart';
86
import 'page.dart';
@@ -15,15 +13,14 @@ import 'page.dart';
1513
/// * [GlobalStoreWidget.of], to get access to the data.
1614
/// * [PerAccountStoreWidget], for the user's data associated with a
1715
/// particular Zulip account.
18-
class GlobalStoreWidget extends StatefulWidget {
19-
const GlobalStoreWidget({
16+
class GlobalStoreWidget extends InheritedNotifier<GlobalStore> {
17+
GlobalStoreWidget({
2018
super.key,
21-
this.placeholder = const LoadingPlaceholder(),
22-
required this.child,
23-
});
24-
25-
final Widget placeholder;
26-
final Widget child;
19+
required GlobalStore store,
20+
required Widget child,
21+
}) : super(notifier: store,
22+
child: _GlobalSettingsStoreInheritedWidget(
23+
store: store.settings, child: child));
2724

2825
/// The app's global data store.
2926
///
@@ -48,7 +45,7 @@ class GlobalStoreWidget extends StatefulWidget {
4845
/// * [PerAccountStoreWidget.of], for the user's data associated with a
4946
/// particular Zulip account.
5047
static GlobalStore of(BuildContext context) {
51-
final widget = context.dependOnInheritedWidgetOfExactType<_GlobalStoreInheritedWidget>();
48+
final widget = context.dependOnInheritedWidgetOfExactType<GlobalStoreWidget>();
5249
assert(widget != null, 'No GlobalStoreWidget ancestor');
5350
return widget!.store;
5451
}
@@ -75,47 +72,10 @@ class GlobalStoreWidget extends StatefulWidget {
7572
return widget!.store;
7673
}
7774

78-
@override
79-
State<GlobalStoreWidget> createState() => _GlobalStoreWidgetState();
80-
}
81-
82-
class _GlobalStoreWidgetState extends State<GlobalStoreWidget> {
83-
GlobalStore? store;
84-
85-
@override
86-
void initState() {
87-
super.initState();
88-
(() async {
89-
final store = await ZulipBinding.instance.getGlobalStoreUniquely();
90-
setState(() {
91-
this.store = store;
92-
});
93-
})();
94-
}
95-
96-
@override
97-
Widget build(BuildContext context) {
98-
final store = this.store;
99-
if (store == null) return widget.placeholder;
100-
return _GlobalStoreInheritedWidget(store: store, child: widget.child);
101-
}
102-
}
103-
104-
// This is separate from [GlobalStoreWidget] only because we need
105-
// a [StatefulWidget] to get hold of the store, and an [InheritedWidget] to
106-
// provide it to descendants, and one widget can't be both of those.
107-
class _GlobalStoreInheritedWidget extends InheritedNotifier<GlobalStore> {
108-
_GlobalStoreInheritedWidget({
109-
required GlobalStore store,
110-
required Widget child,
111-
}) : super(notifier: store,
112-
child: _GlobalSettingsStoreInheritedWidget(
113-
store: store.settings, child: child));
114-
11575
GlobalStore get store => notifier!;
11676
}
11777

118-
// This is like [_GlobalStoreInheritedWidget] except it subscribes to the
78+
// This is like [GlobalStoreWidget] except it subscribes to the
11979
// [GlobalSettingsStore] instead of the overall [GlobalStore].
12080
// That enables [settingsOf] to do the same.
12181
class _GlobalSettingsStoreInheritedWidget extends InheritedNotifier<GlobalSettingsStore> {

test/widgets/app_test.dart

+27-1
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,11 @@ import 'package:flutter/material.dart';
55
import 'package:flutter_test/flutter_test.dart';
66
import 'package:zulip/log.dart';
77
import 'package:zulip/model/actions.dart';
8-
import 'package:zulip/model/database.dart';
8+
import 'package:zulip/model/store.dart';
99
import 'package:zulip/widgets/app.dart';
1010
import 'package:zulip/widgets/home.dart';
1111
import 'package:zulip/widgets/page.dart';
12+
import 'package:zulip/widgets/store.dart';
1213

1314
import '../example_data.dart' as eg;
1415
import '../flutter_checks.dart';
@@ -23,6 +24,31 @@ import 'test_app.dart';
2324
void main() {
2425
TestZulipBinding.ensureInitialized();
2526

27+
testWidgets('ZulipApp loads data while showing placeholder', (tester) async {
28+
addTearDown(testBinding.reset);
29+
30+
GlobalStore? globalStore;
31+
await tester.pumpWidget(ZulipApp());
32+
unawaited(ZulipApp.navigator.then((navigator) {
33+
globalStore = GlobalStoreWidget.of(navigator.context);
34+
}));
35+
36+
// First, shows a loading page instead of child.
37+
check(tester.any(find.byType(CircularProgressIndicator))).isTrue();
38+
check(globalStore).isNull();
39+
40+
await tester.pump();
41+
// Then after loading, shows the ChooseAccountPage, with provided store.
42+
check(tester.any(find.byType(CircularProgressIndicator))).isFalse();
43+
check(tester.any(find.byType(ChooseAccountPage))).isTrue();
44+
check(globalStore).identicalTo(testBinding.globalStore);
45+
46+
await testBinding.globalStore.add(eg.selfAccount, eg.initialSnapshot());
47+
check(globalStore).isNotNull()
48+
.accountEntries.single
49+
.equals((accountId: eg.selfAccount.id, account: eg.selfAccount));
50+
});
51+
2652
group('ZulipApp initial navigation', () {
2753
late List<Route<dynamic>> pushedRoutes = [];
2854

test/widgets/content_test.dart

+6-7
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ import 'package:zulip/widgets/content.dart';
1515
import 'package:zulip/widgets/icons.dart';
1616
import 'package:zulip/widgets/message_list.dart';
1717
import 'package:zulip/widgets/page.dart';
18-
import 'package:zulip/widgets/store.dart';
1918
import 'package:zulip/widgets/text.dart';
2019

2120
import '../example_data.dart' as eg;
@@ -1139,9 +1138,9 @@ void main() {
11391138

11401139
final httpClient = prepareBoringImageHttpClient();
11411140

1142-
await tester.pumpWidget(GlobalStoreWidget(
1143-
child: PerAccountStoreWidget(accountId: eg.selfAccount.id,
1144-
child: RealmContentNetworkImage(src))));
1141+
await tester.pumpWidget(TestZulipApp(
1142+
accountId: eg.selfAccount.id,
1143+
child: RealmContentNetworkImage(src)));
11451144
await tester.pump();
11461145
await tester.pump();
11471146

@@ -1181,9 +1180,9 @@ void main() {
11811180
await store.addUser(user);
11821181

11831182
prepareBoringImageHttpClient();
1184-
await tester.pumpWidget(GlobalStoreWidget(
1185-
child: PerAccountStoreWidget(accountId: eg.selfAccount.id,
1186-
child: AvatarImage(userId: user.userId, size: size ?? 30))));
1183+
await tester.pumpWidget(TestZulipApp(
1184+
accountId: eg.selfAccount.id,
1185+
child: AvatarImage(userId: user.userId, size: size ?? 30)));
11871186
await tester.pump();
11881187
await tester.pump();
11891188
tester.widget(find.byType(AvatarImage));

0 commit comments

Comments
 (0)