merged develop
This commit is contained in:
@@ -58,6 +58,21 @@ class SessionManager {
|
||||
|
||||
Session? get current => _current;
|
||||
|
||||
int _sessionEpoch = 0;
|
||||
|
||||
/// Bumped whenever the active account changes (sign-out, switch, another
|
||||
/// account signing in). Async work captures it when it starts and drops its
|
||||
/// result when it changed meanwhile, so a request of the previous account
|
||||
/// cannot land in the next account's state or cache.
|
||||
int get sessionEpoch => _sessionEpoch;
|
||||
|
||||
bool isCurrentSession(int epoch) => epoch == _sessionEpoch;
|
||||
|
||||
bool _isUiEngine = false;
|
||||
|
||||
/// Called from `main()`; background entry points never run it.
|
||||
void markUiEngine() => _isUiEngine = true;
|
||||
|
||||
/// Every signed-in account and which one is active.
|
||||
final ValueNotifier<AccountIndex> accounts = ValueNotifier(
|
||||
AccountIndex.empty,
|
||||
@@ -115,6 +130,7 @@ class SessionManager {
|
||||
}
|
||||
|
||||
Future<void> _writeActive(Session session) async {
|
||||
if (!_isSameAccount(_current, session)) _sessionEpoch++;
|
||||
await Future.wait([
|
||||
for (final MapEntry(:key, :value) in encodeSessionFields(session).entries)
|
||||
_writeSecret(key, value),
|
||||
@@ -134,6 +150,7 @@ class SessionManager {
|
||||
/// stay in their vaults; see [activate].
|
||||
Future<AccountEntry?> signOut() async {
|
||||
final removed = activeAccount;
|
||||
_sessionEpoch++;
|
||||
_loaded = Completer();
|
||||
_current = null;
|
||||
await Future.wait([
|
||||
@@ -193,6 +210,11 @@ class SessionManager {
|
||||
await _saveIndex(accounts.value.remove(id));
|
||||
}
|
||||
|
||||
static bool _isSameAccount(Session? a, Session? b) {
|
||||
if (a == null || b == null) return a == b;
|
||||
return identityOf(a) == identityOf(b) && a.isDemo == b.isDemo;
|
||||
}
|
||||
|
||||
Future<void> _saveIndex(AccountIndex index) async {
|
||||
accounts.value = index;
|
||||
await _secureStorage.write(key: _indexKey, value: index.encode());
|
||||
@@ -280,8 +302,15 @@ class SessionManager {
|
||||
Future<void> _migrateAndLoad() async {
|
||||
await _migrateFromLegacyStorage();
|
||||
await _migrateKeychainAccessibility();
|
||||
// On the startup critical path (and every background wake): read in
|
||||
// parallel instead of one keychain round-trip after the other.
|
||||
_current = await _readActive();
|
||||
await _loadIndex();
|
||||
if (!_loaded.isCompleted) _loaded.complete();
|
||||
}
|
||||
|
||||
/// The session in the active slots. On the startup critical path (and every
|
||||
/// background wake): read in parallel instead of one keychain round-trip
|
||||
/// after the other.
|
||||
Future<Session?> _readActive() async {
|
||||
final values = await Future.wait(
|
||||
_sessionFields.map((field) => _secureStorage.read(key: field)),
|
||||
);
|
||||
@@ -296,9 +325,35 @@ class SessionManager {
|
||||
} on Object {
|
||||
// Group keystore unavailable: fall back to the real password.
|
||||
}
|
||||
_current = decodeSession(raw);
|
||||
await _loadIndex();
|
||||
if (!_loaded.isCompleted) _loaded.complete();
|
||||
return decodeSession(raw);
|
||||
}
|
||||
|
||||
/// Username currently in the active slots. Other engines (widget task, push
|
||||
/// isolates) sign out, in or switch without this instance noticing.
|
||||
Future<String?> readStoredUsername() =>
|
||||
_secureStorage.read(key: SessionKeys.username);
|
||||
|
||||
/// Re-reads the session for long-lived background engines: they load once
|
||||
/// and would otherwise keep acting with an account that signed out or was
|
||||
/// switched away from in the app meanwhile. Keeps the known state when the
|
||||
/// keystore is unreadable.
|
||||
Future<void> reloadFromStorage() async {
|
||||
// The UI engine performs sign-in, sign-out and switches itself, so its
|
||||
// state is current; re-reading in between could resurrect the removed
|
||||
// account.
|
||||
if (_isUiEngine) return;
|
||||
try {
|
||||
final session = await _readActive();
|
||||
final index = AccountIndex.decode(
|
||||
await _secureStorage.read(key: _indexKey),
|
||||
);
|
||||
if (!_isSameAccount(_current, session)) _sessionEpoch++;
|
||||
_current = session;
|
||||
accounts.value = index;
|
||||
if (!_loaded.isCompleted) _loaded.complete();
|
||||
} on Object catch (e) {
|
||||
log('Session reload failed, keeping loaded state: $e');
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _loadIndex() async {
|
||||
|
||||
Reference in New Issue
Block a user