init method

Future<void> init({
  1. String? newToken,
  2. Uri? newNode,
  3. String? newUserID,
  4. String? newDeviceName,
  5. String? newDeviceID,
  6. String? newOlmAccount,
  7. bool waitForFirstSync = true,
  8. bool waitUntilLoadCompletedLoaded = true,
  9. void onMigration()?,
})

Sets the user credentials and starts the synchronisation.

Before you can connect you need at least an accessToken, a node, a userID, a deviceID, and a deviceName.

Usually you don't need to call this method yourself because login(), register() and even the constructor calls it.

Sends LoginState.loggedIn to onLoginStateChanged.

If one of newToken, newUserID, newDeviceID, newDeviceName is set then all of them must be set! If you don't set them, this method will try to get them from the database.

Set waitForFirstSync and waitUntilLoadCompletedLoaded to false to speed this up. You can then wait for roomsLoading, _accountDataLoading and userDeviceKeysLoading where it is necessary.

Implementation

Future<void> init({
  String? newToken,
  Uri? newNode,
  String? newUserID,
  String? newDeviceName,
  String? newDeviceID,
  String? newOlmAccount,
  bool waitForFirstSync = true,
  bool waitUntilLoadCompletedLoaded = true,

  /// Will be called if the app performs a migration task from the [legacyDatabaseBuilder]
  void Function()? onMigration,
}) async {
  if ((newToken != null ||
          newUserID != null ||
          newDeviceID != null /*||
          newDeviceName != null*/
      ) &&
      (newToken == null ||
          newUserID == null ||
          newDeviceID == null /*||
          newDeviceName == null*/
      )) {
    throw Exception(
        'If one of [newToken, newUserID, newDeviceID] is set then all of them must be set!');
  }
  print('init newNode :$newNode');

  if (_initLock) throw Exception('[init()] has been called multiple times!');
  _initLock = true;
  try {
    Logs().i('Initialize client $clientName');
    if (isLogged()) {
      throw Exception('User is already logged in! Call [logout()] first!');
    }

    final databaseBuilder = this.databaseBuilder;
    if (databaseBuilder != null) {
      _database ??= await runBenchmarked<DatabaseApi>(
        'Build database',
        () async => await databaseBuilder(this),
      );
    }

    _groupCallSessionId = randomAlpha(12);

    String? olmAccount;
    String? accessToken;
    String? userID;
    final account = await this.database?.getClient(clientName);
    if (account != null) {
      _id = account['client_id'];
      node = Uri.parse(account['node_url']);
      accessToken = this.accessToken = account['token'];
      userID = _userID = account['user_id'];
      _deviceID = account['device_id'];
      _deviceName = account['device_name'];
      syncFilterId = account['sync_filter_id'];
      prevBatch = account['prev_batch'];
      olmAccount = account['olm_account'];
    }
    if (newToken != null) {
      accessToken = this.accessToken = newToken;
      node = newNode;
      userID = _userID = newUserID;
      _deviceID = newDeviceID;
      _deviceName = newDeviceName;
      olmAccount = newOlmAccount;
    } else {
      accessToken = this.accessToken = newToken ?? accessToken;
      node = newNode ?? node;
      userID = _userID = newUserID ?? userID;
      _deviceID = newDeviceID ?? _deviceID;
      _deviceName = newDeviceName ?? _deviceName;
      olmAccount = newOlmAccount ?? olmAccount;
    }

    if (accessToken == null || node == null || userID == null) {
      if (legacyDatabaseBuilder != null) {
        await _migrateFromLegacyDatabase(onMigration: onMigration);
        if (isLogged()) return;
      }
      // we aren't logged in
      await encryption?.dispose();
      encryption = null;
      onLoginStateChanged.add(LoginState.loggedOut);
      Logs().i('User is not logged in.');
      _initLock = false;
      return;
    }

    await encryption?.dispose();
    try {
      // make sure to throw an exception if libolm doesn't exist
      await olm.init();
      olm.get_library_version();
      encryption = Encryption(client: this);
    } catch (e) {
      Logs().e('Error initializing encryption $e');
      await encryption?.dispose();
      encryption = null;
    }
    await encryption?.init(olmAccount);

    final database = this.database;
    if (database != null) {
      if (id != null) {
        await database.updateClient(
          node.toString(),
          accessToken,
          userID,
          _deviceID,
          _deviceName,
          prevBatch,
          encryption?.pickledOlmAccount,
        );
      } else {
        _id = await database.insertClient(
          clientName,
          node.toString(),
          accessToken,
          userID,
          _deviceID,
          _deviceName,
          prevBatch,
          encryption?.pickledOlmAccount,
        );
      }
      userDeviceKeysLoading = database
          .getUserDeviceKeys(this)
          .then((keys) => _userDeviceKeys = keys);
      roomsLoading = database.getRoomList(this).then((rooms) {
        _rooms = rooms;
        _sortRooms();
      });
      _accountDataLoading = database.getAccountData().then((data) {
        _accountData = data;
        _updatePushrules();
      });
      presences.clear();
      if (waitUntilLoadCompletedLoaded) {
        await userDeviceKeysLoading;
        await roomsLoading;
        await _accountDataLoading;
      }
    }
    _initLock = false;
    onLoginStateChanged.add(LoginState.loggedIn);
    Logs().i(
      'Successfully connected as ${userID.localpart} with ${node.toString()}',
    );

    /// Timeout of 0, so that we don't see a spinner for 30 seconds.
    final syncFuture = _sync(timeout: Duration.zero);
    if (waitForFirstSync) {
      await syncFuture;
    }
    return;
  } catch (e, s) {
    Logs().e('Initialization failed', e, s);
    await logout().catchError((_) => null);
    onLoginStateChanged.addError(e, s);
    _initLock = false;
    rethrow;
  }
}