Skip to content

Commit 34de78c

Browse files
sm-sayedichrisbobbe
andcommitted
app: On launch, go to the last visited account
Fixes: #524 [chris: rebased atop some notification-test refactors in the last few commits] Co-authored-by: Chris Bobbe <[email protected]>
1 parent a5a5d6b commit 34de78c

File tree

6 files changed

+126
-28
lines changed

6 files changed

+126
-28
lines changed

lib/widgets/app.dart

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import 'package:flutter/foundation.dart';
44
import 'package:flutter/material.dart';
55
import 'package:flutter/scheduler.dart';
66

7+
import '../basic.dart';
78
import '../generated/l10n/zulip_localizations.dart';
89
import '../log.dart';
910
import '../model/actions.dart';
@@ -211,13 +212,18 @@ class _ZulipAppState extends State<ZulipApp> with WidgetsBindingObserver {
211212
}
212213

213214
final globalStore = GlobalStoreWidget.of(context);
214-
// TODO(#524) choose initial account as last one used
215-
final initialAccountId = globalStore.accounts.firstOrNull?.id;
215+
final lastVisitedAccount = globalStore.lastVisitedAccount;
216+
final accountId = switch(lastVisitedAccount) {
217+
OptionNone() => globalStore.accountIds.firstOrNull,
218+
OptionSome(:var value) => value?.id,
219+
};
220+
216221
return [
217-
if (initialAccountId == null)
222+
if (accountId == null)
223+
// There are no accounts, or the last-visited account was logged out.
218224
MaterialWidgetRoute(page: const ChooseAccountPage())
219225
else
220-
HomePage.buildRoute(accountId: initialAccountId),
226+
HomePage.buildRoute(accountId: accountId),
221227
];
222228
}
223229

lib/widgets/share.dart

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import 'package:flutter/material.dart';
55
import 'package:flutter/scheduler.dart';
66
import 'package:mime/mime.dart';
77

8+
import '../basic.dart';
89
import '../generated/l10n/zulip_localizations.dart';
910
import '../host/android_intents.dart';
1011
import '../log.dart';
@@ -60,9 +61,12 @@ class ShareService {
6061

6162
final globalStore = GlobalStoreWidget.of(context);
6263

63-
// TODO(#524) use last account used, not the first in the list
6464
// TODO(#1779) allow selecting account, if there are multiple
65-
final accountId = globalStore.accounts.firstOrNull?.id;
65+
final lastVisitedAccount = globalStore.lastVisitedAccount;
66+
final accountId = switch(lastVisitedAccount) {
67+
OptionNone() || OptionSome(value: null) => globalStore.accountIds.firstOrNull,
68+
OptionSome(:var value?) => value.id,
69+
};
6670

6771
if (accountId == null) {
6872
final zulipLocalizations = ZulipLocalizations.of(context);

test/model/test_store.dart

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,13 +169,24 @@ class TestGlobalStore extends GlobalStore with _ApiConnectionsMixin, _DatabaseMi
169169
/// The given initial snapshot will be used to initialize a corresponding
170170
/// [PerAccountStore] when [perAccount] is subsequently called for this
171171
/// account, in particular when a [PerAccountStoreWidget] is mounted.
172-
Future<void> add(Account account, InitialSnapshot initialSnapshot) async {
172+
///
173+
/// To mark this account as [IntGlobalSetting.lastVisitedAccountId],
174+
/// pass true for [markLastVisited].
175+
Future<void> add(
176+
Account account,
177+
InitialSnapshot initialSnapshot, {
178+
bool markLastVisited = true,
179+
}) async {
173180
assert(initialSnapshot.zulipVersion == account.zulipVersion);
174181
assert(initialSnapshot.zulipMergeBase == account.zulipMergeBase);
175182
assert(initialSnapshot.zulipFeatureLevel == account.zulipFeatureLevel);
176183
await insertAccount(account.toCompanion(false));
177184
assert(!_initialSnapshots.containsKey(account.id));
178185
_initialSnapshots[account.id] = initialSnapshot;
186+
187+
if (markLastVisited) {
188+
await setLastVisitedAccount(account.id);
189+
}
179190
}
180191

181192
Duration? loadPerAccountDuration;

test/notifications/open_test.dart

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import 'package:flutter/material.dart';
66
import 'package:flutter_test/flutter_test.dart';
77
import 'package:zulip/api/model/model.dart';
88
import 'package:zulip/api/notifications.dart';
9+
import 'package:zulip/basic.dart';
910
import 'package:zulip/host/notifications.dart';
1011
import 'package:zulip/model/database.dart';
1112
import 'package:zulip/model/localizations.dart';
@@ -17,9 +18,11 @@ import 'package:zulip/widgets/home.dart';
1718
import 'package:zulip/widgets/message_list.dart';
1819
import 'package:zulip/widgets/page.dart';
1920

21+
import '../basic_checks.dart';
2022
import '../example_data.dart' as eg;
2123
import '../model/binding.dart';
2224
import '../model/narrow_checks.dart';
25+
import '../model/store_checks.dart';
2326
import '../stdlib_checks.dart';
2427
import '../test_navigation.dart';
2528
import '../widgets/checks.dart';
@@ -112,12 +115,11 @@ void main() {
112115
return;
113116
}
114117
await tester.pump();
115-
final accountIds = testBinding.globalStore.accountIds;
116-
final initialAccountId = accountIds.firstOrNull;
117-
if (initialAccountId == null) {
118-
takeChooseAccountPageRoute();
118+
final lastVisitedAccount = testBinding.globalStore.lastVisitedAccount;
119+
if (lastVisitedAccount case OptionSome(:var value?)) {
120+
takeHomePageRouteForAccount(value.id);
119121
} else {
120-
takeHomePageRouteForAccount(initialAccountId);
122+
takeChooseAccountPageRoute();
121123
}
122124
check(pushedRoutes).isEmpty();
123125
}
@@ -204,6 +206,22 @@ void main() {
204206
eg.dmMessage(from: eg.otherUser, to: [eg.selfUser]));
205207
}, variant: const TargetPlatformVariant({TargetPlatform.android, TargetPlatform.iOS}));
206208

209+
testWidgets('changes last visited account', (tester) async {
210+
addTearDown(testBinding.reset);
211+
await testBinding.globalStore.add(
212+
eg.selfAccount, eg.initialSnapshot(realmUsers: [eg.selfUser]));
213+
await testBinding.globalStore.add(
214+
eg.otherAccount, eg.initialSnapshot(realmUsers: [eg.otherUser]),
215+
markLastVisited: false);
216+
await prepare(tester);
217+
check(testBinding.globalStore).lastVisitedAccount
218+
.isA<OptionSome<Account?>>().value.equals(eg.selfAccount);
219+
220+
await checkOpenNotification(tester, eg.otherAccount, eg.streamMessage());
221+
check(testBinding.globalStore).lastVisitedAccount
222+
.isA<OptionSome<Account?>>().value.equals(eg.otherAccount);
223+
}, variant: const TargetPlatformVariant({TargetPlatform.android, TargetPlatform.iOS}));
224+
207225
testWidgets('account queried by realmUrl origin component', (tester) async {
208226
addTearDown(testBinding.reset);
209227
await testBinding.globalStore.add(
@@ -273,7 +291,8 @@ void main() {
273291

274292
testWidgets('wait for app to become ready', (tester) async {
275293
addTearDown(testBinding.reset);
276-
await testBinding.globalStore.add(eg.selfAccount, eg.initialSnapshot());
294+
await testBinding.globalStore.add(eg.selfAccount, eg.initialSnapshot(),
295+
markLastVisited: false);
277296
await prepare(tester, early: true);
278297
final message = eg.streamMessage();
279298
await openNotification(tester, eg.selfAccount, message);

test/widgets/app_test.dart

Lines changed: 70 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,15 @@ import 'dart:async';
33
import 'package:checks/checks.dart';
44
import 'package:flutter/material.dart';
55
import 'package:flutter_test/flutter_test.dart';
6+
import 'package:zulip/basic.dart';
67
import 'package:zulip/log.dart';
78
import 'package:zulip/model/actions.dart';
89
import 'package:zulip/model/database.dart';
910
import 'package:zulip/widgets/app.dart';
1011
import 'package:zulip/widgets/home.dart';
1112
import 'package:zulip/widgets/page.dart';
1213

14+
import '../basic_checks.dart';
1315
import '../example_data.dart' as eg;
1416
import '../flutter_checks.dart';
1517
import '../model/binding.dart';
@@ -43,18 +45,47 @@ void main() {
4345
]);
4446
});
4547

46-
testWidgets('when have accounts, go to home page for first account', (tester) async {
47-
// We'll need per-account data for the account that a page will be opened
48-
// for, but not for the other account.
49-
await testBinding.globalStore.add(eg.selfAccount, eg.initialSnapshot());
50-
await testBinding.globalStore.insertAccount(eg.otherAccount.toCompanion(false));
51-
await prepare(tester);
48+
group('when have accounts', () {
49+
testWidgets('with account(s) visited, go to home page for the last visited account', (tester) async {
50+
await testBinding.globalStore.add(eg.selfAccount, eg.initialSnapshot());
51+
await testBinding.globalStore.add(eg.otherAccount, eg.initialSnapshot(
52+
realmUsers: [eg.otherUser]));
53+
await prepare(tester);
54+
55+
check(pushedRoutes).deepEquals(<Condition<Object?>>[
56+
(it) => it.isA<MaterialAccountWidgetRoute>()
57+
..accountId.equals(eg.otherAccount.id)
58+
..page.isA<HomePage>(),
59+
]);
60+
});
5261

53-
check(pushedRoutes).deepEquals(<Condition<Object?>>[
54-
(it) => it.isA<MaterialAccountWidgetRoute>()
55-
..accountId.equals(eg.selfAccount.id)
56-
..page.isA<HomePage>(),
57-
]);
62+
testWidgets('with no account visited, go to home page for the first account', (tester) async {
63+
await testBinding.globalStore.add(
64+
eg.selfAccount, eg.initialSnapshot(),
65+
markLastVisited: false);
66+
await testBinding.globalStore.add(
67+
eg.otherAccount, eg.initialSnapshot(realmUsers: [eg.otherUser]),
68+
markLastVisited: false);
69+
check(testBinding.globalStore).lastVisitedAccount.isA<OptionNone<Account?>>();
70+
await prepare(tester);
71+
72+
check(pushedRoutes).deepEquals(<Condition<Object?>>[
73+
(it) => it.isA<MaterialAccountWidgetRoute>()
74+
..accountId.equals(eg.selfAccount.id)
75+
..page.isA<HomePage>(),
76+
]);
77+
});
78+
79+
testWidgets('with last visited account id matching none of the accounts, go to choose account', (tester) async {
80+
await testBinding.globalStore.add(eg.selfAccount, eg.initialSnapshot());
81+
await testBinding.globalStore.setLastVisitedAccount(eg.otherAccount.id);
82+
check(testBinding.globalStore).getAccount(eg.otherAccount.id).isNull();
83+
await prepare(tester);
84+
85+
check(pushedRoutes).deepEquals(<Condition<Object?>>[
86+
(it) => it.isA<WidgetRoute>().page.isA<ChooseAccountPage>(),
87+
]);
88+
});
5889
});
5990
});
6091

@@ -99,6 +130,7 @@ void main() {
99130
testWidgets('push route when popping last route on stack', (tester) async {
100131
// Set up the loading of per-account data to fail.
101132
await testBinding.globalStore.insertAccount(eg.selfAccount.toCompanion(false));
133+
await testBinding.globalStore.setLastVisitedAccount(eg.selfAccount.id);
102134
testBinding.globalStore.loadPerAccountDuration = Duration.zero;
103135
testBinding.globalStore.loadPerAccountException = eg.apiExceptionUnauthorized();
104136
await prepare(tester);
@@ -133,6 +165,7 @@ void main() {
133165
const loadPerAccountDuration = Duration(seconds: 30);
134166
assert(loadPerAccountDuration > kTryAnotherAccountWaitPeriod);
135167
await testBinding.globalStore.insertAccount(eg.selfAccount.toCompanion(false));
168+
await testBinding.globalStore.setLastVisitedAccount(eg.selfAccount.id);
136169
testBinding.globalStore.loadPerAccountDuration = loadPerAccountDuration;
137170
testBinding.globalStore.loadPerAccountException = eg.apiExceptionUnauthorized();
138171
await prepare(tester);
@@ -281,8 +314,9 @@ void main() {
281314
testWidgets('choosing an account clears the navigator stack', (tester) async {
282315
addTearDown(testBinding.reset);
283316
await testBinding.globalStore.add(eg.selfAccount, eg.initialSnapshot());
284-
await testBinding.globalStore.add(eg.otherAccount, eg.initialSnapshot(
285-
realmUsers: [eg.otherUser]));
317+
await testBinding.globalStore.add(
318+
eg.otherAccount, eg.initialSnapshot(realmUsers: [eg.otherUser]),
319+
markLastVisited: false);
286320

287321
final pushedRoutes = <Route<void>>[];
288322
final poppedRoutes = <Route<void>>[];
@@ -319,6 +353,29 @@ void main() {
319353
..page.isA<HomePage>();
320354
});
321355

356+
testWidgets('choosing an account changes the last visited account', (tester) async {
357+
addTearDown(testBinding.reset);
358+
await testBinding.globalStore.add(eg.selfAccount, eg.initialSnapshot());
359+
await testBinding.globalStore.add(
360+
eg.otherAccount, eg.initialSnapshot(realmUsers: [eg.otherUser]),
361+
markLastVisited: false);
362+
363+
await tester.pumpWidget(ZulipApp());
364+
await tester.pump();
365+
366+
final navigator = await ZulipApp.navigator;
367+
unawaited(navigator.push(MaterialWidgetRoute(page: const ChooseAccountPage())));
368+
await tester.pump();
369+
await tester.pump();
370+
371+
check(testBinding.globalStore).lastVisitedAccount
372+
.isA<OptionSome<Account?>>().value.equals(eg.selfAccount);
373+
await tester.tap(find.text(eg.otherAccount.email));
374+
await tester.pump();
375+
check(testBinding.globalStore).lastVisitedAccount
376+
.isA<OptionSome<Account?>>().value.equals(eg.otherAccount);
377+
});
378+
322379
group('log out', () {
323380
Future<(Widget, Widget)> prepare(WidgetTester tester, {required Account account}) async {
324381
await setupChooseAccountPage(tester, accounts: [account]);

test/widgets/home_test.dart

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -333,8 +333,9 @@ void main () {
333333
pushedRoutes = [];
334334
lastPoppedRoute = null;
335335
await testBinding.globalStore.add(eg.selfAccount, eg.initialSnapshot());
336-
await testBinding.globalStore.add(eg.otherAccount, eg.initialSnapshot(
337-
realmUsers: [eg.otherUser]));
336+
await testBinding.globalStore.add(
337+
eg.otherAccount, eg.initialSnapshot(realmUsers: [eg.otherUser]),
338+
markLastVisited: false);
338339
await tester.pumpWidget(ZulipApp(navigatorObservers: [testNavObserver]));
339340
await tester.pump(Duration.zero); // wait for the loading page
340341
checkOnLoadingPage();

0 commit comments

Comments
 (0)