Skip to content

Commit 8685f9a

Browse files
committed
startup: Add beta-complete dialog
Fixes #1603.
1 parent 888952b commit 8685f9a

File tree

7 files changed

+113
-0
lines changed

7 files changed

+113
-0
lines changed

lib/widgets/app.dart

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,10 @@ class _ZulipAppState extends State<ZulipApp> with WidgetsBindingObserver {
160160
void initState() {
161161
super.initState();
162162
WidgetsBinding.instance.addObserver(this);
163+
164+
// On every startup is fine; the goal is to be assertive but stop short of a
165+
// rug-pull where we just disable all the app's features.
166+
showBetaCompleteDialog();
163167
}
164168

165169
@override

lib/widgets/dialog.dart

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
1+
import 'dart:async';
2+
3+
import 'package:flutter/foundation.dart';
14
import 'package:flutter/material.dart';
25

36
import '../generated/l10n/zulip_localizations.dart';
47
import 'actions.dart';
8+
import 'app.dart';
59

610
Widget _dialogActionText(String text) {
711
return Text(
@@ -112,3 +116,75 @@ DialogStatus<bool> showSuggestedActionDialog({
112116
]));
113117
return DialogStatus(future);
114118
}
119+
120+
bool debugDisableBetaCompleteDialog = false;
121+
void resetDebugDisableBetaCompleteDialog() {
122+
debugDisableBetaCompleteDialog = false;
123+
}
124+
125+
/// Show a brief dialog box saying that this beta channel has ended,
126+
/// offering a way to get the app from prod.
127+
void showBetaCompleteDialog() async {
128+
if (debugDisableBetaCompleteDialog) return;
129+
130+
final navigator = await ZulipApp.navigator;
131+
final context = navigator.context;
132+
assert(context.mounted);
133+
if (!context.mounted) return; // TODO(linter): this is impossible as there's no actual async gap, but the use_build_context_synchronously lint doesn't see that
134+
135+
switch (defaultTargetPlatform) {
136+
case TargetPlatform.android:
137+
case TargetPlatform.iOS:
138+
break;
139+
case TargetPlatform.macOS:
140+
case TargetPlatform.fuchsia:
141+
case TargetPlatform.linux:
142+
case TargetPlatform.windows:
143+
// Do nothing on these unsupported platforms.
144+
return;
145+
}
146+
147+
final zulipLocalizations = ZulipLocalizations.of(context);
148+
149+
final message = 'Since Zulip’s new Flutter app has launched, this beta app is no longer maintained. We strongly recommend uninstalling it and switching to the main Zulip application to get the latest features and bug fixes. Thank you for being a beta tester!';
150+
151+
unawaited(showDialog(
152+
context: context,
153+
builder: (BuildContext context) => AlertDialog(
154+
title: Text('Time to switch to the new app'),
155+
content: SingleChildScrollView(child: Text(message)),
156+
actions: [
157+
TextButton(
158+
onPressed: () => Navigator.pop(context),
159+
child: _dialogActionText('Got it')),
160+
...(switch (defaultTargetPlatform) {
161+
TargetPlatform.android => [
162+
TextButton(
163+
onPressed: () {
164+
Navigator.pop(context);
165+
PlatformActions.launchUrl(context,
166+
Uri.parse('https://github.com/zulip/zulip-flutter/releases/latest'));
167+
},
168+
child: _dialogActionText('Download official APKs (less common)')),
169+
TextButton(
170+
onPressed: () {
171+
Navigator.pop(context);
172+
PlatformActions.launchUrl(context,
173+
Uri.parse('https://play.google.com/store/apps/details?id=com.zulipmobile'));
174+
},
175+
child: _dialogActionText('Open Google Play Store'))
176+
],
177+
TargetPlatform.iOS => [
178+
TextButton(
179+
onPressed: () {
180+
Navigator.pop(context);
181+
PlatformActions.launchUrl(context,
182+
Uri.parse('https://apps.apple.com/app/zulip/id1203036395'));
183+
},
184+
child: _dialogActionText('Open App Store')),
185+
],
186+
TargetPlatform.macOS || TargetPlatform.fuchsia
187+
|| TargetPlatform.linux || TargetPlatform.windows => throw UnimplementedError(),
188+
}),
189+
])));
190+
}

test/notifications/open_test.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import 'package:zulip/model/narrow.dart';
1313
import 'package:zulip/notifications/open.dart';
1414
import 'package:zulip/notifications/receive.dart';
1515
import 'package:zulip/widgets/app.dart';
16+
import 'package:zulip/widgets/dialog.dart';
1617
import 'package:zulip/widgets/home.dart';
1718
import 'package:zulip/widgets/message_list.dart';
1819
import 'package:zulip/widgets/page.dart';
@@ -76,6 +77,8 @@ void main() {
7677
final zulipLocalizations = GlobalLocalizations.zulipLocalizations;
7778

7879
Future<void> init({bool addSelfAccount = true}) async {
80+
debugDisableBetaCompleteDialog = true;
81+
addTearDown(resetDebugDisableBetaCompleteDialog);
7982
if (addSelfAccount) {
8083
await testBinding.globalStore.add(eg.selfAccount, eg.initialSnapshot());
8184
}

test/widgets/app_test.dart

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import 'package:zulip/log.dart';
77
import 'package:zulip/model/actions.dart';
88
import 'package:zulip/model/database.dart';
99
import 'package:zulip/widgets/app.dart';
10+
import 'package:zulip/widgets/dialog.dart';
1011
import 'package:zulip/widgets/home.dart';
1112
import 'package:zulip/widgets/page.dart';
1213

@@ -27,6 +28,8 @@ void main() {
2728
late List<Route<dynamic>> pushedRoutes = [];
2829

2930
Future<void> prepare(WidgetTester tester) async {
31+
debugDisableBetaCompleteDialog = true;
32+
addTearDown(resetDebugDisableBetaCompleteDialog);
3033
addTearDown(testBinding.reset);
3134

3235
pushedRoutes = [];
@@ -64,6 +67,8 @@ void main() {
6467
late List<Route<void>> poppedRoutes;
6568

6669
Future<void> prepare(WidgetTester tester) async {
70+
debugDisableBetaCompleteDialog = true;
71+
addTearDown(resetDebugDisableBetaCompleteDialog);
6772
addTearDown(testBinding.reset);
6873

6974
pushedRoutes = [];
@@ -279,6 +284,8 @@ void main() {
279284
});
280285

281286
testWidgets('choosing an account clears the navigator stack', (tester) async {
287+
debugDisableBetaCompleteDialog = true;
288+
addTearDown(resetDebugDisableBetaCompleteDialog);
282289
addTearDown(testBinding.reset);
283290
await testBinding.globalStore.add(eg.selfAccount, eg.initialSnapshot());
284291
await testBinding.globalStore.add(eg.otherAccount, eg.initialSnapshot());
@@ -391,6 +398,8 @@ void main() {
391398
});
392399

393400
testWidgets('reportErrorToUserBriefly with details', (tester) async {
401+
debugDisableBetaCompleteDialog = true;
402+
addTearDown(resetDebugDisableBetaCompleteDialog);
394403
addTearDown(testBinding.reset);
395404
await tester.pumpWidget(const ZulipApp());
396405
const message = 'test error message';
@@ -418,6 +427,8 @@ void main() {
418427
});
419428

420429
Future<void> prepareSnackBarWithDetails(WidgetTester tester, String message, String details) async {
430+
debugDisableBetaCompleteDialog = true;
431+
addTearDown(resetDebugDisableBetaCompleteDialog);
421432
addTearDown(testBinding.reset);
422433
await tester.pumpWidget(const ZulipApp());
423434
await tester.pump();
@@ -484,6 +495,8 @@ void main() {
484495
});
485496

486497
testWidgets('reportErrorToUserModally', (tester) async {
498+
debugDisableBetaCompleteDialog = true;
499+
addTearDown(resetDebugDisableBetaCompleteDialog);
487500
addTearDown(testBinding.reset);
488501
await tester.pumpWidget(const ZulipApp());
489502
const title = 'test title';

test/widgets/home_test.dart

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import 'package:zulip/model/store.dart';
88
import 'package:zulip/widgets/about_zulip.dart';
99
import 'package:zulip/widgets/app.dart';
1010
import 'package:zulip/widgets/app_bar.dart';
11+
import 'package:zulip/widgets/dialog.dart';
1112
import 'package:zulip/widgets/home.dart';
1213
import 'package:zulip/widgets/icons.dart';
1314
import 'package:zulip/widgets/inbox.dart';
@@ -48,6 +49,8 @@ void main () {
4849
..onPopped = ((route, prevRoute) => lastPoppedRoute = route);
4950

5051
Future<void> prepare(WidgetTester tester) async {
52+
debugDisableBetaCompleteDialog = true;
53+
addTearDown(resetDebugDisableBetaCompleteDialog);
5154
addTearDown(testBinding.reset);
5255
topRoute = null;
5356
previousTopRoute = null;
@@ -272,6 +275,8 @@ void main () {
272275
});
273276

274277
testWidgets('menu buttons dismiss the menu', (tester) async {
278+
debugDisableBetaCompleteDialog = true;
279+
addTearDown(resetDebugDisableBetaCompleteDialog);
275280
addTearDown(testBinding.reset);
276281
topRoute = null;
277282
previousTopRoute = null;
@@ -328,6 +333,8 @@ void main () {
328333
}
329334

330335
Future<void> prepare(WidgetTester tester) async {
336+
debugDisableBetaCompleteDialog = true;
337+
addTearDown(resetDebugDisableBetaCompleteDialog);
331338
addTearDown(testBinding.reset);
332339
topRoute = null;
333340
previousTopRoute = null;
@@ -521,6 +528,8 @@ void main () {
521528
});
522529

523530
testWidgets('logging out while still loading', (tester) async {
531+
debugDisableBetaCompleteDialog = true;
532+
addTearDown(resetDebugDisableBetaCompleteDialog);
524533
// Regression test for: https://github.com/zulip/zulip-flutter/issues/1219
525534
addTearDown(testBinding.reset);
526535
await testBinding.globalStore.add(eg.selfAccount, eg.initialSnapshot());
@@ -537,6 +546,8 @@ void main () {
537546
});
538547

539548
testWidgets('logging out after fully loaded', (tester) async {
549+
debugDisableBetaCompleteDialog = true;
550+
addTearDown(resetDebugDisableBetaCompleteDialog);
540551
// Regression test for: https://github.com/zulip/zulip-flutter/issues/1219
541552
addTearDown(testBinding.reset);
542553
await testBinding.globalStore.add(eg.selfAccount, eg.initialSnapshot());

test/widgets/login_test.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import 'package:zulip/model/binding.dart';
1212
import 'package:zulip/model/database.dart';
1313
import 'package:zulip/model/localizations.dart';
1414
import 'package:zulip/widgets/app.dart';
15+
import 'package:zulip/widgets/dialog.dart';
1516
import 'package:zulip/widgets/home.dart';
1617
import 'package:zulip/widgets/login.dart';
1718
import 'package:zulip/widgets/page.dart';
@@ -83,6 +84,8 @@ void main() {
8384

8485
Future<void> prepare(WidgetTester tester,
8586
GetServerSettingsResult serverSettings) async {
87+
debugDisableBetaCompleteDialog = true;
88+
addTearDown(resetDebugDisableBetaCompleteDialog);
8689
addTearDown(testBinding.reset);
8790

8891
connection = testBinding.globalStore.apiConnection(

test/widgets/message_list_test.dart

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import 'package:zulip/widgets/autocomplete.dart';
2626
import 'package:zulip/widgets/color.dart';
2727
import 'package:zulip/widgets/compose_box.dart';
2828
import 'package:zulip/widgets/content.dart';
29+
import 'package:zulip/widgets/dialog.dart';
2930
import 'package:zulip/widgets/icons.dart';
3031
import 'package:zulip/widgets/message_list.dart';
3132
import 'package:zulip/widgets/page.dart';
@@ -73,6 +74,8 @@ void main() {
7374
bool skipAssertAccountExists = false,
7475
bool skipPumpAndSettle = false,
7576
}) async {
77+
debugDisableBetaCompleteDialog = true;
78+
addTearDown(resetDebugDisableBetaCompleteDialog);
7679
TypingNotifier.debugEnable = false;
7780
addTearDown(TypingNotifier.debugReset);
7881
addTearDown(testBinding.reset);

0 commit comments

Comments
 (0)