From 29aa28eecf7bd81ceba2fdfbaf488040421c8e4d Mon Sep 17 00:00:00 2001 From: Krille Date: Mon, 2 Dec 2024 13:19:47 +0100 Subject: [PATCH] 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. --- lib/src/client.dart | 57 +++++++++++++++++++++++++++++++++++++++++-- test/client_test.dart | 22 +++++++++++++++-- 2 files changed, 75 insertions(+), 4 deletions(-) diff --git a/lib/src/client.dart b/lib/src/client.dart index c4988093..744dde94 100644 --- a/lib/src/client.dart +++ b/lib/src/client.dart @@ -597,6 +597,7 @@ class Client extends MatrixApi { bool? refreshToken, AuthenticationData? auth, AccountKind? kind, + void Function(InitState)? onInitStateChanged, }) async { final response = await super.register( kind: kind, @@ -632,6 +633,7 @@ class Client extends MatrixApi { newHomeserver: homeserver, newDeviceName: initialDeviceDisplayName ?? '', newDeviceID: deviceId_, + onInitStateChanged: onInitStateChanged, ); 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? medium, @Deprecated('Deprecated in favour of identifier.') String? address, + void Function(InitState)? onInitStateChanged, }) async { if (homeserver == null) { final domain = identifier is AuthenticationUserIdentifier @@ -704,6 +707,7 @@ class Client extends MatrixApi { newHomeserver: homeserver_, newDeviceName: initialDeviceDisplayName ?? '', newDeviceID: deviceId_, + onInitStateChanged: onInitStateChanged, ); return response; } @@ -1931,7 +1935,11 @@ class Client extends MatrixApi { bool waitUntilLoadCompletedLoaded = true, /// Will be called if the app performs a migration task from the [legacyDatabaseBuilder] + @Deprecated('Use onInitStateChanged and listen to `InitState.migration`.') void Function()? onMigration, + + /// To track what actually happens you can set a callback here. + void Function(InitState)? onInitStateChanged, }) async { if ((newToken != null || newUserID != null || @@ -1956,6 +1964,7 @@ class Client extends MatrixApi { String? accessToken; String? userID; try { + onInitStateChanged?.call(InitState.initializing); Logs().i('Initialize client $clientName'); if (onLoginStateChanged.value == LoginState.loggedIn) { throw ClientInitPreconditionError( @@ -2036,14 +2045,21 @@ class Client extends MatrixApi { encryption?.pickledOlmAccount, ); } + onInitStateChanged?.call(InitState.finished); onLoginStateChanged.add(LoginState.loggedIn); return; } if (accessToken == null || homeserver == null || userID == null) { if (legacyDatabaseBuilder != null) { - await _migrateFromLegacyDatabase(onMigration: onMigration); - if (isLogged()) return; + await _migrateFromLegacyDatabase( + onInitStateChanged: onInitStateChanged, + onMigration: onMigration, + ); + if (isLogged()) { + onInitStateChanged?.call(InitState.finished); + return; + } } // we aren't logged in await encryption?.dispose(); @@ -2051,6 +2067,7 @@ class Client extends MatrixApi { onLoginStateChanged.add(LoginState.loggedOut); Logs().i('User is not logged in.'); _initLock = false; + onInitStateChanged?.call(InitState.finished); return; } @@ -2065,6 +2082,7 @@ class Client extends MatrixApi { await encryption?.dispose(); _encryption = null; } + onInitStateChanged?.call(InitState.settingUpEncryption); await encryption?.init(olmAccount); final database = this.database; @@ -2112,6 +2130,7 @@ class Client extends MatrixApi { // ignore: deprecated_member_use_from_same_package presences.clear(); if (waitUntilLoadCompletedLoaded) { + onInitStateChanged?.call(InitState.loadingData); await userDeviceKeysLoading; await roomsLoading; await _accountDataLoading; @@ -2127,14 +2146,18 @@ class Client extends MatrixApi { /// Timeout of 0, so that we don't see a spinner for 30 seconds. firstSyncReceived = _sync(timeout: Duration.zero); if (waitForFirstSync) { + onInitStateChanged?.call(InitState.waitingForFirstSync); await firstSyncReceived; } + onInitStateChanged?.call(InitState.finished); return; } on ClientInitPreconditionError { + onInitStateChanged?.call(InitState.error); rethrow; } catch (e, s) { Logs().wtf('Client initialization failed', e, s); onLoginStateChanged.addError(e, s); + onInitStateChanged?.call(InitState.error); final clientInitException = ClientInitException( e, homeserver: homeserver, @@ -3728,6 +3751,7 @@ class Client extends MatrixApi { } Future _migrateFromLegacyDatabase({ + void Function(InitState)? onInitStateChanged, void Function()? onMigration, }) async { Logs().i('Check legacy database for migration data...'); @@ -3741,6 +3765,7 @@ class Client extends MatrixApi { return; } Logs().i('Found data in the legacy database!'); + onInitStateChanged?.call(InitState.migratingDatabase); onMigration?.call(); _id = migrateClient['client_id']; final tokenExpiresAtMs = @@ -3858,6 +3883,7 @@ class Client extends MatrixApi { return init( waitForFirstSync: false, waitUntilLoadCompletedLoaded: false, + onInitStateChanged: onInitStateChanged, ); } } @@ -3958,3 +3984,30 @@ class _EventPendingDecryption { _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, +} diff --git a/test/client_test.dart b/test/client_test.dart index dedabb09..c5677e09 100644 --- a/test/client_test.dart +++ b/test/client_test.dart @@ -82,15 +82,26 @@ void main() { databaseBuilder: getDatabase, ); expect(client.isLogged(), false); - await client.init(); + final Set initStates = {}; + await client.init(onInitStateChanged: initStates.add); expect(client.isLogged(), false); + expect(initStates, {InitState.initializing, InitState.finished}); + initStates.clear(); await client.login( LoginType.mLoginPassword, token: 'abcd', identifier: AuthenticationUserIdentifier(user: '@test:fakeServer.notExisting'), deviceId: 'GHTYAJCE', + onInitStateChanged: initStates.add, ); + expect(initStates, { + InitState.initializing, + InitState.settingUpEncryption, + InitState.loadingData, + InitState.waitingForFirstSync, + InitState.finished, + }); expect(client.isLogged(), true); @@ -1439,7 +1450,14 @@ void main() { databaseBuilder: getDatabase, legacyDatabaseBuilder: (_) => database, ); - await hiveClient.init(); + final Set initStates = {}; + await hiveClient.init(onInitStateChanged: initStates.add); + expect(initStates, { + InitState.initializing, + InitState.migratingDatabase, + InitState.settingUpEncryption, + InitState.finished, + }); await Future.delayed(Duration(milliseconds: 200)); expect(hiveClient.isLogged(), true); await hiveClient.dispose(closeDatabase: false);