feat: (BREAKING) Replace onMigration with advanced callback onInitStateChanged

This makes it possible to track
the process of initialization in the
client by just enhancing what
the onMigration callback already
does.
This commit is contained in:
Krille 2024-12-02 13:19:47 +01:00
parent cd31df9b27
commit 29aa28eecf
No known key found for this signature in database
GPG Key ID: E067ECD60F1A0652
2 changed files with 75 additions and 4 deletions

View File

@ -597,6 +597,7 @@ class Client extends MatrixApi {
bool? refreshToken, bool? refreshToken,
AuthenticationData? auth, AuthenticationData? auth,
AccountKind? kind, AccountKind? kind,
void Function(InitState)? onInitStateChanged,
}) async { }) async {
final response = await super.register( final response = await super.register(
kind: kind, kind: kind,
@ -632,6 +633,7 @@ class Client extends MatrixApi {
newHomeserver: homeserver, newHomeserver: homeserver,
newDeviceName: initialDeviceDisplayName ?? '', newDeviceName: initialDeviceDisplayName ?? '',
newDeviceID: deviceId_, newDeviceID: deviceId_,
onInitStateChanged: onInitStateChanged,
); );
return response; return response;
} }
@ -655,6 +657,7 @@ class Client extends MatrixApi {
@Deprecated('Deprecated in favour of identifier.') String? user, @Deprecated('Deprecated in favour of identifier.') String? user,
@Deprecated('Deprecated in favour of identifier.') String? medium, @Deprecated('Deprecated in favour of identifier.') String? medium,
@Deprecated('Deprecated in favour of identifier.') String? address, @Deprecated('Deprecated in favour of identifier.') String? address,
void Function(InitState)? onInitStateChanged,
}) async { }) async {
if (homeserver == null) { if (homeserver == null) {
final domain = identifier is AuthenticationUserIdentifier final domain = identifier is AuthenticationUserIdentifier
@ -704,6 +707,7 @@ class Client extends MatrixApi {
newHomeserver: homeserver_, newHomeserver: homeserver_,
newDeviceName: initialDeviceDisplayName ?? '', newDeviceName: initialDeviceDisplayName ?? '',
newDeviceID: deviceId_, newDeviceID: deviceId_,
onInitStateChanged: onInitStateChanged,
); );
return response; return response;
} }
@ -1931,7 +1935,11 @@ class Client extends MatrixApi {
bool waitUntilLoadCompletedLoaded = true, bool waitUntilLoadCompletedLoaded = true,
/// Will be called if the app performs a migration task from the [legacyDatabaseBuilder] /// Will be called if the app performs a migration task from the [legacyDatabaseBuilder]
@Deprecated('Use onInitStateChanged and listen to `InitState.migration`.')
void Function()? onMigration, void Function()? onMigration,
/// To track what actually happens you can set a callback here.
void Function(InitState)? onInitStateChanged,
}) async { }) async {
if ((newToken != null || if ((newToken != null ||
newUserID != null || newUserID != null ||
@ -1956,6 +1964,7 @@ class Client extends MatrixApi {
String? accessToken; String? accessToken;
String? userID; String? userID;
try { try {
onInitStateChanged?.call(InitState.initializing);
Logs().i('Initialize client $clientName'); Logs().i('Initialize client $clientName');
if (onLoginStateChanged.value == LoginState.loggedIn) { if (onLoginStateChanged.value == LoginState.loggedIn) {
throw ClientInitPreconditionError( throw ClientInitPreconditionError(
@ -2036,14 +2045,21 @@ class Client extends MatrixApi {
encryption?.pickledOlmAccount, encryption?.pickledOlmAccount,
); );
} }
onInitStateChanged?.call(InitState.finished);
onLoginStateChanged.add(LoginState.loggedIn); onLoginStateChanged.add(LoginState.loggedIn);
return; return;
} }
if (accessToken == null || homeserver == null || userID == null) { if (accessToken == null || homeserver == null || userID == null) {
if (legacyDatabaseBuilder != null) { if (legacyDatabaseBuilder != null) {
await _migrateFromLegacyDatabase(onMigration: onMigration); await _migrateFromLegacyDatabase(
if (isLogged()) return; onInitStateChanged: onInitStateChanged,
onMigration: onMigration,
);
if (isLogged()) {
onInitStateChanged?.call(InitState.finished);
return;
}
} }
// we aren't logged in // we aren't logged in
await encryption?.dispose(); await encryption?.dispose();
@ -2051,6 +2067,7 @@ class Client extends MatrixApi {
onLoginStateChanged.add(LoginState.loggedOut); onLoginStateChanged.add(LoginState.loggedOut);
Logs().i('User is not logged in.'); Logs().i('User is not logged in.');
_initLock = false; _initLock = false;
onInitStateChanged?.call(InitState.finished);
return; return;
} }
@ -2065,6 +2082,7 @@ class Client extends MatrixApi {
await encryption?.dispose(); await encryption?.dispose();
_encryption = null; _encryption = null;
} }
onInitStateChanged?.call(InitState.settingUpEncryption);
await encryption?.init(olmAccount); await encryption?.init(olmAccount);
final database = this.database; final database = this.database;
@ -2112,6 +2130,7 @@ class Client extends MatrixApi {
// ignore: deprecated_member_use_from_same_package // ignore: deprecated_member_use_from_same_package
presences.clear(); presences.clear();
if (waitUntilLoadCompletedLoaded) { if (waitUntilLoadCompletedLoaded) {
onInitStateChanged?.call(InitState.loadingData);
await userDeviceKeysLoading; await userDeviceKeysLoading;
await roomsLoading; await roomsLoading;
await _accountDataLoading; await _accountDataLoading;
@ -2127,14 +2146,18 @@ class Client extends MatrixApi {
/// Timeout of 0, so that we don't see a spinner for 30 seconds. /// Timeout of 0, so that we don't see a spinner for 30 seconds.
firstSyncReceived = _sync(timeout: Duration.zero); firstSyncReceived = _sync(timeout: Duration.zero);
if (waitForFirstSync) { if (waitForFirstSync) {
onInitStateChanged?.call(InitState.waitingForFirstSync);
await firstSyncReceived; await firstSyncReceived;
} }
onInitStateChanged?.call(InitState.finished);
return; return;
} on ClientInitPreconditionError { } on ClientInitPreconditionError {
onInitStateChanged?.call(InitState.error);
rethrow; rethrow;
} catch (e, s) { } catch (e, s) {
Logs().wtf('Client initialization failed', e, s); Logs().wtf('Client initialization failed', e, s);
onLoginStateChanged.addError(e, s); onLoginStateChanged.addError(e, s);
onInitStateChanged?.call(InitState.error);
final clientInitException = ClientInitException( final clientInitException = ClientInitException(
e, e,
homeserver: homeserver, homeserver: homeserver,
@ -3728,6 +3751,7 @@ class Client extends MatrixApi {
} }
Future<void> _migrateFromLegacyDatabase({ Future<void> _migrateFromLegacyDatabase({
void Function(InitState)? onInitStateChanged,
void Function()? onMigration, void Function()? onMigration,
}) async { }) async {
Logs().i('Check legacy database for migration data...'); Logs().i('Check legacy database for migration data...');
@ -3741,6 +3765,7 @@ class Client extends MatrixApi {
return; return;
} }
Logs().i('Found data in the legacy database!'); Logs().i('Found data in the legacy database!');
onInitStateChanged?.call(InitState.migratingDatabase);
onMigration?.call(); onMigration?.call();
_id = migrateClient['client_id']; _id = migrateClient['client_id'];
final tokenExpiresAtMs = final tokenExpiresAtMs =
@ -3858,6 +3883,7 @@ class Client extends MatrixApi {
return init( return init(
waitForFirstSync: false, waitForFirstSync: false,
waitUntilLoadCompletedLoaded: false, waitUntilLoadCompletedLoaded: false,
onInitStateChanged: onInitStateChanged,
); );
} }
} }
@ -3958,3 +3984,30 @@ class _EventPendingDecryption {
_EventPendingDecryption(this.event); _EventPendingDecryption(this.event);
} }
enum InitState {
/// Initialization has been started. Client fetches information from the database.
initializing,
/// The database has been updated. A migration is in progress.
migratingDatabase,
/// The encryption module will be set up now. For the first login this also
/// includes uploading keys to the server.
settingUpEncryption,
/// The client is loading rooms, device keys and account data from the
/// database.
loadingData,
/// The client waits now for the first sync before procceeding. Get more
/// information from `Client.onSyncUpdate`.
waitingForFirstSync,
/// Initialization is complete without errors. The client is now either
/// logged in or no active session was found.
finished,
/// Initialization has been completed with an error.
error,
}

View File

@ -82,15 +82,26 @@ void main() {
databaseBuilder: getDatabase, databaseBuilder: getDatabase,
); );
expect(client.isLogged(), false); expect(client.isLogged(), false);
await client.init(); final Set<InitState> initStates = {};
await client.init(onInitStateChanged: initStates.add);
expect(client.isLogged(), false); expect(client.isLogged(), false);
expect(initStates, {InitState.initializing, InitState.finished});
initStates.clear();
await client.login( await client.login(
LoginType.mLoginPassword, LoginType.mLoginPassword,
token: 'abcd', token: 'abcd',
identifier: identifier:
AuthenticationUserIdentifier(user: '@test:fakeServer.notExisting'), AuthenticationUserIdentifier(user: '@test:fakeServer.notExisting'),
deviceId: 'GHTYAJCE', deviceId: 'GHTYAJCE',
onInitStateChanged: initStates.add,
); );
expect(initStates, {
InitState.initializing,
InitState.settingUpEncryption,
InitState.loadingData,
InitState.waitingForFirstSync,
InitState.finished,
});
expect(client.isLogged(), true); expect(client.isLogged(), true);
@ -1439,7 +1450,14 @@ void main() {
databaseBuilder: getDatabase, databaseBuilder: getDatabase,
legacyDatabaseBuilder: (_) => database, legacyDatabaseBuilder: (_) => database,
); );
await hiveClient.init(); final Set<InitState> initStates = {};
await hiveClient.init(onInitStateChanged: initStates.add);
expect(initStates, {
InitState.initializing,
InitState.migratingDatabase,
InitState.settingUpEncryption,
InitState.finished,
});
await Future.delayed(Duration(milliseconds: 200)); await Future.delayed(Duration(milliseconds: 200));
expect(hiveClient.isLogged(), true); expect(hiveClient.isLogged(), true);
await hiveClient.dispose(closeDatabase: false); await hiveClient.dispose(closeDatabase: false);