@@ -19,6 +19,7 @@ import '../api/backoff.dart';
19
19
import '../api/route/realm.dart' ;
20
20
import '../log.dart' ;
21
21
import '../notifications/receive.dart' ;
22
+ import '../widgets/actions.dart' ;
22
23
import 'autocomplete.dart' ;
23
24
import 'database.dart' ;
24
25
import 'emoji.dart' ;
@@ -149,13 +150,31 @@ abstract class GlobalStore extends ChangeNotifier {
149
150
/// and/or [perAccountSync] .
150
151
Future <PerAccountStore > loadPerAccount (int accountId) async {
151
152
assert (_accounts.containsKey (accountId));
152
- final store = await doLoadPerAccount (accountId);
153
+ PerAccountStore ? store;
154
+ try {
155
+ store = await doLoadPerAccount (accountId);
156
+ } catch (e) {
157
+ switch (e) {
158
+ case ZulipApiException (code: 'INVALID_API_KEY' ):
159
+ // The API key is invalid and the store can never be loaded
160
+ // unless the user retries manually.
161
+ final zulipLocalizations = GlobalLocalizations .zulipLocalizations;
162
+ final account = getAccount (accountId)! ;
163
+ reportErrorToUserModally (
164
+ zulipLocalizations.errorCouldNotConnectTitle,
165
+ details: zulipLocalizations.errorInvalidApiKeyMessage (
166
+ account.realmUrl.toString ()));
167
+ await logOutAccount (this , accountId);
168
+ default :
169
+ rethrow ;
170
+ }
171
+ }
153
172
if (! _accounts.containsKey (accountId)) {
154
- // [removeAccount] was called during [doLoadPerAccount].
155
- store.dispose ();
173
+ // [removeAccount] was called during or after [doLoadPerAccount].
174
+ store? .dispose ();
156
175
throw AccountNotFoundException ();
157
176
}
158
- return store;
177
+ return store! ;
159
178
}
160
179
161
180
/// Load per-account data for the given account, unconditionally.
@@ -912,7 +931,13 @@ class UpdateMachine {
912
931
// at 1 kiB (at least on Android), and stack can be longer than that.
913
932
assert (debugLog ('Stack:\n $s ' ));
914
933
assert (debugLog ('Backing off, then will retry…' ));
915
- // TODO tell user if initial-fetch errors persist, or look non-transient
934
+ // TODO(#890): tell user if initial-fetch errors persist, or look non-transient
935
+ switch (e) {
936
+ case ZulipApiException (code: 'INVALID_API_KEY' ):
937
+ // We cannot recover from this error through retrying.
938
+ // Leave it to [GlobalStore.loadPerAccount].
939
+ rethrow ;
940
+ }
916
941
await (backoffMachine ?? = BackoffMachine ()).wait ();
917
942
assert (debugLog ('… Backoff wait complete, retrying initial fetch.' ));
918
943
}
@@ -1044,7 +1069,13 @@ class UpdateMachine {
1044
1069
}
1045
1070
} catch (e) {
1046
1071
if (_disposed) return ;
1047
- await _handlePollError (e);
1072
+ try {
1073
+ await _handlePollError (e);
1074
+ } on AccountNotFoundException {
1075
+ // Cannot recover by replacing the store because the account
1076
+ // was logged out.
1077
+ return ;
1078
+ }
1048
1079
assert (_disposed);
1049
1080
return ;
1050
1081
}
0 commit comments