Skip to content

Commit 742ab6a

Browse files
committed
squash! nav: Always clear stack before navigating to HomePage
Note to self: drop this commit message when squashing. Signed-off-by: Zixuan James Li <[email protected]>
1 parent ae8f49d commit 742ab6a

File tree

4 files changed

+101
-10
lines changed

4 files changed

+101
-10
lines changed

lib/widgets/app.dart

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -266,8 +266,12 @@ class ChooseAccountPage extends StatelessWidget {
266266
// The default trailing padding with M3 is 24px. Decrease by 12 because
267267
// IconButton (the "…" button) comes with 12px padding on all sides.
268268
contentPadding: const EdgeInsetsDirectional.only(start: 16, end: 12),
269-
onTap: () => Navigator.push(context,
270-
HomePage.buildRoute(accountId: accountId))));
269+
onTap: () async {
270+
final navigator = Navigator.of(context);
271+
navigator.popUntil((route) => route.isFirst);
272+
unawaited(navigator.pushReplacement(
273+
HomePage.buildRoute(accountId: accountId)));
274+
}));
271275
}
272276

273277
@override

lib/widgets/login.dart

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -395,10 +395,10 @@ class _LoginPageState extends State<LoginPage> {
395395
return;
396396
}
397397

398-
unawaited(Navigator.of(context).pushAndRemoveUntil(
399-
HomePage.buildRoute(accountId: accountId),
400-
(route) => (route is! _LoginSequenceRoute)),
401-
);
398+
final navigator = Navigator.of(context);
399+
navigator.popUntil((route) => route.isFirst);
400+
unawaited(Navigator.of(context).pushReplacement(
401+
HomePage.buildRoute(accountId: accountId)));
402402
}
403403

404404
Future<int> _getUserId(String email, String apiKey) async {

test/widgets/app_test.dart

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import 'dart:async';
2+
13
import 'package:checks/checks.dart';
24
import 'package:flutter/material.dart';
35
import 'package:flutter_test/flutter_test.dart';
@@ -22,13 +24,20 @@ void main() {
2224

2325
group('ZulipApp initial navigation', () {
2426
late List<Route<dynamic>> pushedRoutes = [];
27+
late List<Route<dynamic>> poppedRoutes = [];
2528

2629
Future<void> prepare(WidgetTester tester) async {
2730
addTearDown(testBinding.reset);
2831

2932
pushedRoutes = [];
30-
final testNavObserver = TestNavigatorObserver()
31-
..onPushed = (route, prevRoute) => pushedRoutes.add(route);
33+
poppedRoutes = [];
34+
final testNavObserver = TestNavigatorObserver();
35+
testNavObserver.onPushed = (route, prevRoute) => pushedRoutes.add(route);
36+
testNavObserver.onPopped = (route, prevRoute) => poppedRoutes.add(route);
37+
testNavObserver.onReplaced = (route, prevRoute) {
38+
poppedRoutes.add(prevRoute!);
39+
pushedRoutes.add(route!);
40+
};
3241
await tester.pumpWidget(ZulipApp(navigatorObservers: [testNavObserver]));
3342
await tester.pump();
3443
}
@@ -53,6 +62,44 @@ void main() {
5362
..page.isA<HomePage>(),
5463
]);
5564
});
65+
66+
testWidgets('choosing an account clears the navigator stack', (tester) async {
67+
addTearDown(testBinding.reset);
68+
await testBinding.globalStore.add(eg.selfAccount, eg.initialSnapshot());
69+
await testBinding.globalStore.add(eg.otherAccount, eg.initialSnapshot());
70+
71+
await prepare(tester);
72+
73+
final navigator = await ZulipApp.navigator;
74+
unawaited(navigator.push(
75+
MaterialWidgetRoute(page: const ChooseAccountPage())));
76+
await tester.pumpAndSettle();
77+
78+
check(poppedRoutes).isEmpty();
79+
check(pushedRoutes).deepEquals(<Condition<Object?>>[
80+
(it) => it.isA<MaterialAccountWidgetRoute>()
81+
..accountId.equals(eg.selfAccount.id)
82+
..page.isA<HomePage>(),
83+
(it) => it.isA<MaterialWidgetRoute>()
84+
..page.isA<ChooseAccountPage>()
85+
]);
86+
pushedRoutes.clear();
87+
88+
await tester.tap(find.text(eg.otherAccount.email));
89+
await tester.pump();
90+
check(poppedRoutes).deepEquals(<Condition<Object?>>[
91+
(it) => it.isA<MaterialWidgetRoute>()
92+
..page.isA<ChooseAccountPage>(),
93+
(it) => it.isA<MaterialAccountWidgetRoute>()
94+
..accountId.equals(eg.selfAccount.id)
95+
..page.isA<HomePage>(),
96+
]);
97+
check(pushedRoutes).deepEquals(<Condition<Object?>>[
98+
(it) => it.isA<MaterialAccountWidgetRoute>()
99+
..accountId.equals(eg.otherAccount.id)
100+
..page.isA<HomePage>(),
101+
]);
102+
});
56103
});
57104

58105
group('ChooseAccountPage', () {

test/widgets/login_test.dart

Lines changed: 42 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ void main() {
7070
group('LoginPage', () {
7171
late FakeApiConnection connection;
7272
late List<Route<dynamic>> pushedRoutes;
73+
late List<Route<dynamic>> poppedRoutes;
7374

7475
void checkNotLoggedInStartingRoutes() {
7576
final expected = <Condition<Object?>>[
@@ -88,8 +89,14 @@ void main() {
8889
zulipFeatureLevel: serverSettings.zulipFeatureLevel);
8990

9091
pushedRoutes = [];
91-
final testNavObserver = TestNavigatorObserver()
92-
..onPushed = (route, prevRoute) => pushedRoutes.add(route);
92+
poppedRoutes = [];
93+
final testNavObserver = TestNavigatorObserver();
94+
testNavObserver.onPushed = (route, prevRoute) => pushedRoutes.add(route);
95+
testNavObserver.onPopped = (route, prevRoute) => poppedRoutes.add(route);
96+
testNavObserver.onReplaced = (route, prevRoute) {
97+
poppedRoutes.add(prevRoute!);
98+
pushedRoutes.add(route!);
99+
};
93100
await tester.pumpWidget(ZulipApp(navigatorObservers: [testNavObserver]));
94101
await tester.pump();
95102
final navigator = await ZulipApp.navigator;
@@ -143,6 +150,39 @@ void main() {
143150
id: testBinding.globalStore.accounts.single.id));
144151
});
145152

153+
testWidgets('logging into a different account', (tester) async {
154+
await testBinding.globalStore.add(eg.selfAccount, eg.initialSnapshot());
155+
final serverSettings = eg.serverSettings();
156+
await prepare(tester, serverSettings);
157+
check(poppedRoutes).isEmpty();
158+
check(pushedRoutes).deepEquals(<Condition<Object?>>[
159+
(it) => it.isA<MaterialAccountWidgetRoute>()
160+
..accountId.equals(eg.selfAccount.id)
161+
..page.isA<HomePage>(),
162+
(it) => it.isA<MaterialWidgetRoute>()
163+
..page.isA<LoginPage>(),
164+
]);
165+
pushedRoutes.clear();
166+
167+
await login(tester, eg.otherAccount);
168+
final loggedInAccount = testBinding.globalStore.accounts.singleWhere(
169+
(account) => account != eg.selfAccount);
170+
check(loggedInAccount).equals(eg.otherAccount.copyWith(
171+
id: loggedInAccount.id));
172+
check(poppedRoutes).deepEquals(<Condition<Object?>>[
173+
(it) => it.isA<MaterialWidgetRoute>()
174+
..page.isA<LoginPage>(),
175+
(it) => it.isA<MaterialAccountWidgetRoute>()
176+
..accountId.equals(eg.selfAccount.id)
177+
..page.isA<HomePage>(),
178+
]);
179+
check(pushedRoutes).deepEquals(<Condition<Object?>>[
180+
(it) => it.isA<MaterialAccountWidgetRoute>()
181+
..accountId.equals(loggedInAccount.id)
182+
..page.isA<HomePage>(),
183+
]);
184+
});
185+
146186
testWidgets('trims whitespace on username', (tester) async {
147187
final serverSettings = eg.serverSettings();
148188
await prepare(tester, serverSettings);

0 commit comments

Comments
 (0)