Skip to content

Commit 89df63b

Browse files
notif: Concurrently initialize NotificationOpenManager
Move NotificationOpenManager initialization to NotificationService and wait for future to complete, while also loading the GlobalStore, displaying a loading spinner while both of them load concurrently. This is an optimization where previously the NotificationOpenManager initialization would happen sequentially before the GlobalStore load.
1 parent 0d58524 commit 89df63b

File tree

7 files changed

+56
-35
lines changed

7 files changed

+56
-35
lines changed

lib/main.dart

+2-10
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,20 @@
1-
import 'dart:async';
2-
31
import 'package:flutter/foundation.dart';
42
import 'package:flutter/widgets.dart';
53

64
import 'licenses.dart';
75
import 'log.dart';
86
import 'model/binding.dart';
9-
import 'notifications/open.dart';
107
import 'notifications/receive.dart';
118
import 'widgets/app.dart';
129

13-
Future<void> main() async {
10+
void main() {
1411
assert(() {
1512
debugLogEnabled = true;
1613
return true;
1714
}());
1815
LicenseRegistry.addLicense(additionalLicenses);
1916
WidgetsFlutterBinding.ensureInitialized();
2017
LiveZulipBinding.ensureInitialized();
21-
22-
// TODO remove this await here
23-
// TODO move this initialization to NotificationService.instance.start()
24-
await NotificationOpenManager.instance.init();
25-
26-
unawaited(NotificationService.instance.start());
18+
NotificationService.instance.start();
2719
runApp(const ZulipApp());
2820
}

lib/notifications/open.dart

+21-13
Original file line numberDiff line numberDiff line change
@@ -29,20 +29,28 @@ class NotificationOpenManager {
2929

3030
NotificationPayloadForOpen? _notifLaunchData;
3131

32+
Completer<void>? _intializedSignal;
33+
Future<void>? get intializationFuture => _intializedSignal?.future;
34+
3235
Future<void> init() async {
33-
switch (defaultTargetPlatform) {
34-
case TargetPlatform.iOS:
35-
case TargetPlatform.android:
36-
_notifLaunchData = await _notifPigeonApi.getNotificationDataFromLaunch();
37-
_notifPigeonApi.notificationTapEventsStream()
38-
.listen(_navigateForNotification);
39-
40-
case TargetPlatform.fuchsia:
41-
case TargetPlatform.linux:
42-
case TargetPlatform.macOS:
43-
case TargetPlatform.windows:
44-
// Do nothing; we don't offer notifications on these platforms.
45-
break;
36+
_intializedSignal = Completer();
37+
try {
38+
switch (defaultTargetPlatform) {
39+
case TargetPlatform.iOS:
40+
case TargetPlatform.android:
41+
_notifLaunchData = await _notifPigeonApi.getNotificationDataFromLaunch();
42+
_notifPigeonApi.notificationTapEventsStream()
43+
.listen(_navigateForNotification);
44+
45+
case TargetPlatform.fuchsia:
46+
case TargetPlatform.linux:
47+
case TargetPlatform.macOS:
48+
case TargetPlatform.windows:
49+
// Do nothing; we don't offer notifications on these platforms.
50+
break;
51+
}
52+
} finally {
53+
_intializedSignal!.complete();
4654
}
4755
}
4856

lib/notifications/receive.dart

+5
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import '../firebase_options.dart';
88
import '../log.dart';
99
import '../model/binding.dart';
1010
import 'display.dart';
11+
import 'open.dart';
1112

1213
@pragma('vm:entry-point')
1314
class NotificationService {
@@ -53,6 +54,8 @@ class NotificationService {
5354
Future<void> start() async {
5455
switch (defaultTargetPlatform) {
5556
case TargetPlatform.android:
57+
await NotificationOpenManager.instance.init();
58+
5659
await ZulipBinding.instance.firebaseInitializeApp(
5760
options: kFirebaseOptionsAndroid);
5861

@@ -77,6 +80,8 @@ class NotificationService {
7780
await _getFcmToken();
7881

7982
case TargetPlatform.iOS: // TODO(#324): defer requesting notif permission
83+
await NotificationOpenManager.instance.init();
84+
8085
await ZulipBinding.instance.firebaseInitializeApp(
8186
options: kFirebaseOptionsIos);
8287

lib/widgets/app.dart

+5
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,11 @@ class _ZulipAppState extends State<ZulipApp> with WidgetsBindingObserver {
202202
Widget build(BuildContext context) {
203203
final themeData = zulipThemeData(context);
204204
return GlobalStoreWidget(
205+
otherAsyncTasks: [
206+
if (NotificationOpenManager.instance.intializationFuture
207+
case final Future<void> future)
208+
future,
209+
],
205210
child: MaterialApp(
206211
onGenerateTitle: (BuildContext context) {
207212
return ZulipLocalizations.of(context).zulipAppTitle;

lib/widgets/store.dart

+23-9
Original file line numberDiff line numberDiff line change
@@ -9,17 +9,23 @@ import 'page.dart';
99
///
1010
/// There should be one of this widget, near the root of the tree.
1111
///
12+
/// Shows the [placeholder] widget while concurrently waiting for all
13+
/// the futures in [otherAsyncTasks] and the global store loading to
14+
/// finish, and then switches to showing the [child] widget.
15+
///
1216
/// See also:
1317
/// * [GlobalStoreWidget.of], to get access to the data.
1418
/// * [PerAccountStoreWidget], for the user's data associated with a
1519
/// particular Zulip account.
1620
class GlobalStoreWidget extends StatefulWidget {
1721
const GlobalStoreWidget({
1822
super.key,
23+
this.otherAsyncTasks = const [],
1924
this.placeholder = const LoadingPlaceholder(),
2025
required this.child,
2126
});
2227

28+
final Iterable<Future<void>> otherAsyncTasks;
2329
final Widget placeholder;
2430
final Widget child;
2531

@@ -56,24 +62,32 @@ class GlobalStoreWidget extends StatefulWidget {
5662
}
5763

5864
class _GlobalStoreWidgetState extends State<GlobalStoreWidget> {
65+
bool isLoading = true;
5966
GlobalStore? store;
6067

68+
Future<void> _loadGlobalStore() async {
69+
store = await ZulipBinding.instance.getGlobalStoreUniquely();
70+
}
71+
6172
@override
6273
void initState() {
6374
super.initState();
64-
(() async {
65-
final store = await ZulipBinding.instance.getGlobalStoreUniquely();
66-
setState(() {
67-
this.store = store;
68-
});
69-
})();
75+
() async {
76+
try {
77+
await Future.wait([
78+
_loadGlobalStore(),
79+
...widget.otherAsyncTasks,
80+
]);
81+
} finally {
82+
if (mounted) setState(() => isLoading = false);
83+
}
84+
}();
7085
}
7186

7287
@override
7388
Widget build(BuildContext context) {
74-
final store = this.store;
75-
if (store == null) return widget.placeholder;
76-
return _GlobalStoreInheritedWidget(store: store, child: widget.child);
89+
if (isLoading) return widget.placeholder;
90+
return _GlobalStoreInheritedWidget(store: store!, child: widget.child);
7791
}
7892
}
7993

test/notifications/display_test.dart

-2
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@ import 'package:zulip/model/localizations.dart';
1616
import 'package:zulip/model/narrow.dart';
1717
import 'package:zulip/model/store.dart';
1818
import 'package:zulip/notifications/display.dart';
19-
import 'package:zulip/notifications/open.dart';
2019
import 'package:zulip/notifications/receive.dart';
2120
import 'package:zulip/widgets/color.dart';
2221
import 'package:zulip/widgets/theme.dart';
@@ -107,7 +106,6 @@ void main() {
107106
testBinding.firebaseMessagingInitialToken = '012abc';
108107
addTearDown(NotificationService.debugReset);
109108
NotificationService.debugBackgroundIsolateIsLive = false;
110-
await NotificationOpenManager.instance.init();
111109
await NotificationService.instance.start();
112110
}
113111

test/notifications/open_test.dart

-1
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,6 @@ void main() {
8080
testBinding.firebaseMessagingInitialToken = '012abc';
8181
addTearDown(NotificationService.debugReset);
8282
NotificationService.debugBackgroundIsolateIsLive = false;
83-
await NotificationOpenManager.instance.init();
8483
await NotificationService.instance.start();
8584
}
8685

0 commit comments

Comments
 (0)