refactor: (BREAKING) Make database required

This commit is contained in:
Christian Kußowski 2025-05-14 14:35:19 +02:00
parent 9af1c563f1
commit c618baae70
No known key found for this signature in database
GPG Key ID: E067ECD60F1A0652
32 changed files with 522 additions and 419 deletions

View File

@ -16,11 +16,30 @@ In your `pubspec.yaml` file add the following dependencies:
## Step 2: Create the client
```dart
import 'package:sqflite_common_ffi/sqflite_ffi.dart';
import 'package:matrix/matrix.dart';
final client = Client(
'<Your Client Name>',
databaseBuilder: (client) => MatrixSdkDatabase(
'<Client Name>',
database: await MatrixSdkDatabase.init(
'<Database Name>',
database: await openDatabase('<path-to-store-database>'),
database: await databaseFactoryFfi.openDatabase(':memory:'),
sqfliteFactory: databaseFactoryFfi,
),
);
```
### Alternative: Create a persistent database with SQFlite:
```dart
import 'package:sqflite/sqflite.dart';
import 'package:matrix/matrix.dart';
final client = Client(
'<Client Name>',
database: await MatrixSdkDatabase.init(
'<Database Name>',
database: await openDatabase('/path/to/database.sqlite'),
),
);
```

View File

@ -7,18 +7,18 @@ import 'package:sqflite/sqflite.dart' as sqlite;
void main() async {
WidgetsFlutterBinding.ensureInitialized();
// Build the database
final dbDirectory = await getApplicationSupportDirectory();
final client = Client(
'Matrix Example Chat',
databaseBuilder: (_) async {
final dir = await getApplicationSupportDirectory();
final db = MatrixSdkDatabase(
c.name,
await sqlite.openDatabase(dir.toString() + '/database.sqlite'),
);
await db.open();
return db;
},
database: await MatrixSdkDatabase.init(
c.name,
database:
await sqlite.openDatabase(directory.toString() + '/database.sqlite'),
),
);
await client.init();
runApp(MatrixExampleChat(client: client));
}

View File

@ -246,7 +246,7 @@ class Encryption {
// it un-awaited here, nothing happens, which is exactly the result we want
client.database
// ignore: discarded_futures
?.updateInboundGroupSessionIndexes(
.updateInboundGroupSessionIndexes(
json.encode(inboundGroupSession.indexes),
event.room.id,
sessionId,
@ -319,8 +319,7 @@ class Encryption {
}
final content = event.parsedRoomEncryptedContent;
final sessionId = content.sessionId;
if (client.database != null &&
sessionId != null &&
if (sessionId != null &&
!(keyManager
.getInboundGroupSession(
event.room.id,
@ -347,7 +346,7 @@ class Encryption {
if (updateType != EventUpdateType.history) {
event.room.setState(event);
}
await client.database?.storeEventUpdate(
await client.database.storeEventUpdate(
event.room.id,
event,
updateType,
@ -429,8 +428,7 @@ class Encryption {
// check if we can set our own master key as verified, if it isn't yet
final userId = client.userID;
final masterKey = client.userDeviceKeys[userId]?.masterKey;
if (client.database != null &&
masterKey != null &&
if (masterKey != null &&
userId != null &&
!masterKey.directVerified &&
masterKey.hasValidSignatureChain(onlyValidateUserIds: {userId})) {

View File

@ -166,7 +166,7 @@ class KeyManager {
}
final storeFuture = client.database
?.storeInboundGroupSession(
.storeInboundGroupSession(
roomId,
sessionId,
inboundGroupSession.pickle(userId),
@ -182,7 +182,7 @@ class KeyManager {
}
if (uploaded) {
await client.database
?.markInboundGroupSessionAsUploaded(roomId, sessionId);
.markInboundGroupSessionAsUploaded(roomId, sessionId);
}
});
final room = client.getRoomById(roomId);
@ -198,7 +198,7 @@ class KeyManager {
room.lastEvent = decrypted;
// To persist it in database and trigger UI updates:
await client.database?.transaction(() async {
await client.database.transaction(() async {
await client.handleSync(
SyncUpdate(
nextBatch: '',
@ -222,7 +222,7 @@ class KeyManager {
room.onSessionKeyReceived.add(sessionId);
}
return storeFuture ?? Future.value();
return storeFuture;
}
SessionKey? getInboundGroupSession(String roomId, String sessionId) {
@ -277,7 +277,7 @@ class KeyManager {
return sess; // nothing to do
}
final session =
await client.database?.getInboundGroupSession(roomId, sessionId);
await client.database.getInboundGroupSession(roomId, sessionId);
if (session == null) return null;
final userID = client.userID;
if (userID == null) return null;
@ -455,7 +455,7 @@ class KeyManager {
sess.outboundGroupSession!.message_index();
}
}
await client.database?.updateInboundGroupSessionAllowedAtIndex(
await client.database.updateInboundGroupSessionAllowedAtIndex(
json.encode(inboundSess!.allowedAtIndex),
room.id,
sess.outboundGroupSession!.session_id(),
@ -479,7 +479,7 @@ class KeyManager {
}
sess.dispose();
_outboundGroupSessions.remove(roomId);
await client.database?.removeOutboundGroupSession(roomId);
await client.database.removeOutboundGroupSession(roomId);
return true;
}
@ -490,7 +490,7 @@ class KeyManager {
) async {
final userID = client.userID;
if (userID == null) return;
await client.database?.storeOutboundGroupSession(
await client.database.storeOutboundGroupSession(
roomId,
sess.outboundGroupSession!.pickle(userID),
json.encode(sess.devices),
@ -621,7 +621,6 @@ class KeyManager {
final userID = client.userID;
if (_loadedOutboundGroupSessions.contains(roomId) ||
_outboundGroupSessions.containsKey(roomId) ||
database == null ||
userID == null) {
return; // nothing to do
}
@ -847,7 +846,7 @@ class KeyManager {
}) async {
final database = client.database;
final userID = client.userID;
if (database == null || userID == null) {
if (userID == null) {
return;
}

View File

@ -68,7 +68,7 @@ class SSSS {
// for testing
Future<void> clearCache() async {
await client.database?.clearSSSSCache();
await client.database.clearSSSSCache();
_cache.clear();
}
@ -301,10 +301,6 @@ class SSSS {
client.accountData[type]?.content['encrypted'] is Map;
Future<String?> getCached(String type) async {
if (client.database == null) {
return null;
}
// check if it is still valid
final keys = keyIdsFromType(type);
if (keys == null) {
return null;
@ -323,7 +319,7 @@ class SSSS {
if (fromCache != null && isValid(fromCache)) {
return fromCache.content;
}
final ret = await client.database?.getSSSSCache(type);
final ret = await client.database.getSSSSCache(type);
if (ret == null) {
return null;
}
@ -361,7 +357,7 @@ class SSSS {
);
final decrypted = await decryptAes(encryptInfo, key, type);
final db = client.database;
if (cacheTypes.contains(type) && db != null) {
if (cacheTypes.contains(type)) {
// cache the thing
await db.storeSSSSCache(type, keyId, ciphertext, decrypted);
onSecretStored.add(keyId);
@ -398,7 +394,7 @@ class SSSS {
// store the thing in your account data
await client.setAccountData(client.userID!, type, content);
final db = client.database;
if (cacheTypes.contains(type) && db != null) {
if (cacheTypes.contains(type)) {
// cache the thing
await db.storeSSSSCache(type, keyId, encrypted.ciphertext, secret);
onSecretStored.add(keyId);
@ -444,7 +440,7 @@ class SSSS {
if (ciphertext == null) {
throw Exception('Wrong type for ciphertext!');
}
await client.database?.storeSSSSCache(type, keyId, ciphertext, secret);
await client.database.storeSSSSCache(type, keyId, ciphertext, secret);
onSecretStored.add(keyId);
}
}
@ -611,23 +607,21 @@ class SSSS {
}
Logs().i('[SSSS] Secret for type ${request.type} is ok, storing it');
final db = client.database;
if (db != null) {
final keyId = keyIdFromType(request.type);
if (keyId != null) {
final ciphertext = (client.accountData[request.type]!.content
.tryGetMap<String, Object?>('encrypted'))
?.tryGetMap<String, Object?>(keyId)
?.tryGet<String>('ciphertext');
if (ciphertext == null) {
Logs().i('[SSSS] Ciphertext is empty or not a String');
return;
}
await db.storeSSSSCache(request.type, keyId, ciphertext, secret);
if (_cacheCallbacks.containsKey(request.type)) {
_cacheCallbacks[request.type]!(secret);
}
onSecretStored.add(keyId);
final keyId = keyIdFromType(request.type);
if (keyId != null) {
final ciphertext = (client.accountData[request.type]!.content
.tryGetMap<String, Object?>('encrypted'))
?.tryGetMap<String, Object?>(keyId)
?.tryGet<String>('ciphertext');
if (ciphertext == null) {
Logs().i('[SSSS] Ciphertext is empty or not a String');
return;
}
await db.storeSSSSCache(request.type, keyId, ciphertext, secret);
if (_cacheCallbacks.containsKey(request.type)) {
_cacheCallbacks[request.type]!(secret);
}
onSecretStored.add(keyId);
}
}
}

View File

@ -583,7 +583,7 @@ class Bootstrap {
Logs().v(
'And finally set all megolm keys as needing to be uploaded again...',
);
await client.database?.markInboundGroupSessionsAsNeedingUpload();
await client.database.markInboundGroupSessionsAsNeedingUpload();
Logs().v('And uploading keys...');
await client.encryption?.keyManager.uploadInboundGroupSessions();
} catch (e, s) {

View File

@ -225,7 +225,7 @@ class FakeMatrixApi extends BaseClient {
accountData: [sdk.BasicEvent(content: decodeJson(data), type: type)],
);
if (_client?.database != null) {
await _client?.database?.transaction(() async {
await _client?.database.transaction(() async {
await _client?.handleSync(syncUpdate);
});
} else {
@ -255,7 +255,7 @@ class FakeMatrixApi extends BaseClient {
),
);
if (_client?.database != null) {
await _client?.database?.transaction(() async {
await _client?.database.transaction(() async {
await _client?.handleSync(syncUpdate);
});
} else {

View File

@ -110,9 +110,8 @@ extension TimelineExportExtension on Timeline {
}
// From the database
final eventsFromStore = await room.client.database
?.getEventList(room, start: events.length) ??
[];
final eventsFromStore =
await room.client.database.getEventList(room, start: events.length);
if (eventsFromStore.isNotEmpty) {
if (until == null ||
eventsFromStore.last.originServerTs.isBefore(until)) {

View File

@ -67,11 +67,9 @@ class Client extends MatrixApi {
int? get id => _id;
final FutureOr<DatabaseApi> Function(Client)? databaseBuilder;
final FutureOr<DatabaseApi> Function(Client)? legacyDatabaseBuilder;
DatabaseApi? _database;
DatabaseApi? get database => _database;
final DatabaseApi database;
Encryption? get encryption => _encryption;
Encryption? _encryption;
@ -141,7 +139,6 @@ class Client extends MatrixApi {
set homeserver(Uri? homeserver) {
if (this.homeserver != null && homeserver?.host != this.homeserver?.host) {
_wellKnown = null;
unawaited(database?.storeWellKnown(null));
}
super.homeserver = homeserver;
}
@ -192,7 +189,7 @@ class Client extends MatrixApi {
/// Set [enableDehydratedDevices] to enable experimental support for enabling MSC3814 dehydrated devices.
Client(
this.clientName, {
this.databaseBuilder,
required this.database,
this.legacyDatabaseBuilder,
Set<KeyVerificationMethod>? verificationMethods,
http.Client? httpClient,
@ -295,7 +292,7 @@ class Client extends MatrixApi {
/// Throws an Exception if there is no refresh token available or the
/// client is not logged in.
Future<void> refreshAccessToken() async {
final storedClient = await database?.getClient(clientName);
final storedClient = await database.getClient(clientName);
final refreshToken = storedClient?.tryGet<String>('refresh_token');
if (refreshToken == null) {
throw Exception('No refresh token available');
@ -318,7 +315,7 @@ class Client extends MatrixApi {
? null
: DateTime.now().add(Duration(milliseconds: expiresInMs));
_accessTokenExpiresAt = tokenExpiresAt;
await database?.updateClient(
await database.updateClient(
homeserverUrl,
tokenResponse.accessToken,
tokenExpiresAt,
@ -596,7 +593,7 @@ class Client extends MatrixApi {
// do not reset the well known here, so super call
super.homeserver = wellKnown.mHomeserver.baseUrl.stripTrailingSlash();
_wellKnown = wellKnown;
await database?.storeWellKnown(wellKnown);
await database.storeWellKnown(wellKnown);
return wellKnown;
}
@ -1060,7 +1057,7 @@ class Client extends MatrixApi {
Duration timeout = const Duration(seconds: 30),
Duration maxCacheAge = const Duration(days: 1),
}) async {
final cachedProfile = await database?.getUserProfile(userId);
final cachedProfile = await database.getUserProfile(userId);
if (cachedProfile != null &&
!cachedProfile.outdated &&
DateTime.now().difference(cachedProfile.updated) < maxCacheAge) {
@ -1085,7 +1082,7 @@ class Client extends MatrixApi {
updated: DateTime.now(),
);
await database?.storeUserProfile(userId, newCachedProfile);
await database.storeUserProfile(userId, newCachedProfile);
return newCachedProfile;
}
@ -1540,7 +1537,7 @@ class Client extends MatrixApi {
.uploadContent(file, filename: filename, contentType: contentType);
final database = this.database;
if (database != null && file.length <= database.maxFileSize) {
if (file.length <= database.maxFileSize) {
await database.storeFile(
mxc,
file,
@ -1572,16 +1569,13 @@ class Client extends MatrixApi {
///
/// This can be useful to migrate a session from one device to a future one.
Future<String?> exportDump() async {
if (database != null) {
await abortSync();
await dispose(closeDatabase: false);
await abortSync();
await dispose(closeDatabase: false);
final export = await database!.exportDump();
final export = await database.exportDump();
await clear();
return export;
}
return null;
await clear();
return export;
}
/// imports a dumped session
@ -1595,9 +1589,7 @@ class Client extends MatrixApi {
// Client was probably not initialized yet.
}
_database ??= await databaseBuilder!.call(this);
final success = await database!.importDump(export);
final success = await database.importDump(export);
if (success) {
// closing including DB
@ -1790,13 +1782,7 @@ class Client extends MatrixApi {
bool returnNullIfSeen = true,
}) async {
// Get access token if necessary:
final database = _database ??= await databaseBuilder?.call(this);
if (!isLogged()) {
if (database == null) {
throw Exception(
'Can not execute getEventByPushNotification() without a database',
);
}
final clientInfoMap = await database.getClient(clientName);
final token = clientInfoMap?.tryGet<String>('token');
if (token == null) {
@ -1814,7 +1800,7 @@ class Client extends MatrixApi {
// Create the room object:
final room = getRoomById(roomId) ??
await database?.getSingleRoom(this, roomId) ??
await database.getSingleRoom(this, roomId) ??
Room(
id: roomId,
client: this,
@ -1864,7 +1850,7 @@ class Client extends MatrixApi {
);
}
matrixEvent ??= await database
?.getEventById(eventId, room)
.getEventById(eventId, room)
.timeout(timeoutForServerRequests);
try {
@ -1893,7 +1879,7 @@ class Client extends MatrixApi {
return null;
}
final readMarkerEvent = await database
?.getEventById(room.fullyRead, room)
.getEventById(room.fullyRead, room)
.timeout(timeoutForServerRequests);
if (readMarkerEvent != null &&
readMarkerEvent.originServerTs.isAfter(
@ -1940,7 +1926,7 @@ class Client extends MatrixApi {
}
if (storeInDatabase) {
await database?.transaction(() async {
await database.transaction(() async {
await database.storeEventUpdate(
roomId,
event,
@ -2020,14 +2006,6 @@ class Client extends MatrixApi {
);
}
final databaseBuilder = this.databaseBuilder;
if (databaseBuilder != null) {
_database ??= await runBenchmarked<DatabaseApi>(
'Build database',
() async => await databaseBuilder(this),
);
}
_groupCallSessionId = randomAlpha(12);
/// while I would like to move these to a onLoginStateChanged stream listener
@ -2036,7 +2014,7 @@ class Client extends MatrixApi {
_serverConfigCache.invalidate();
_versionsCache.invalidate();
final account = await this.database?.getClient(clientName);
final account = await database.getClient(clientName);
newRefreshToken ??= account?.tryGet<String>('refresh_token');
// can have discovery_information so make sure it also has the proper
// account creds
@ -2081,7 +2059,7 @@ class Client extends MatrixApi {
if (onLoginStateChanged.value == LoginState.softLoggedOut) {
if (newRefreshToken != null && accessToken != null && userID != null) {
// Store the new tokens:
await _database?.updateClient(
await database.updateClient(
homeserver.toString(),
accessToken,
accessTokenExpiresAt,
@ -2133,58 +2111,56 @@ class Client extends MatrixApi {
onInitStateChanged?.call(InitState.settingUpEncryption);
await encryption?.init(olmAccount);
final database = this.database;
if (database != null) {
if (id != null) {
await database.updateClient(
homeserver.toString(),
accessToken,
accessTokenExpiresAt,
newRefreshToken,
userID,
_deviceID,
_deviceName,
prevBatch,
encryption?.pickledOlmAccount,
);
} else {
_id = await database.insertClient(
clientName,
homeserver.toString(),
accessToken,
accessTokenExpiresAt,
newRefreshToken,
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();
});
_discoveryDataLoading = database.getWellKnown().then((data) {
_wellKnown = data;
});
// ignore: deprecated_member_use_from_same_package
presences.clear();
if (waitUntilLoadCompletedLoaded) {
onInitStateChanged?.call(InitState.loadingData);
await userDeviceKeysLoading;
await roomsLoading;
await _accountDataLoading;
await _discoveryDataLoading;
}
if (id != null) {
await database.updateClient(
homeserver.toString(),
accessToken,
accessTokenExpiresAt,
newRefreshToken,
userID,
_deviceID,
_deviceName,
prevBatch,
encryption?.pickledOlmAccount,
);
} else {
_id = await database.insertClient(
clientName,
homeserver.toString(),
accessToken,
accessTokenExpiresAt,
newRefreshToken,
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();
});
_discoveryDataLoading = database.getWellKnown().then((data) {
_wellKnown = data;
});
// ignore: deprecated_member_use_from_same_package
presences.clear();
if (waitUntilLoadCompletedLoaded) {
onInitStateChanged?.call(InitState.loadingData);
await userDeviceKeysLoading;
await roomsLoading;
await _accountDataLoading;
await _discoveryDataLoading;
}
_initLock = false;
onLoginStateChanged.add(LoginState.loggedIn);
Logs().i(
@ -2238,15 +2214,15 @@ class Client extends MatrixApi {
}
try {
await abortSync();
await database?.clear();
await database.clear();
await legacyDatabase?.clear();
_backgroundSync = true;
} catch (e, s) {
Logs().e('Unable to clear database', e, s);
} finally {
await database?.delete();
await database.delete();
await legacyDatabase?.delete();
_database = null;
await dispose();
}
_id = accessToken = _syncFilterId =
@ -2303,7 +2279,7 @@ class Client extends MatrixApi {
if (syncFilterId == null && userID != null) {
final syncFilterId =
_syncFilterId = await defineFilter(userID, syncFilter);
await database?.storeSyncFilterId(syncFilterId);
await database.storeSyncFilterId(syncFilterId);
}
return;
}
@ -2405,29 +2381,25 @@ class Client extends MatrixApi {
}
final database = this.database;
if (database != null) {
await userDeviceKeysLoading;
await roomsLoading;
await _accountDataLoading;
_currentTransaction = database.transaction(() async {
await _handleSync(syncResp, direction: Direction.f);
if (prevBatch != syncResp.nextBatch) {
await database.storePrevBatch(syncResp.nextBatch);
}
});
await runBenchmarked(
'Process sync',
() async => await _currentTransaction,
syncResp.itemCount,
);
} else {
await userDeviceKeysLoading;
await roomsLoading;
await _accountDataLoading;
_currentTransaction = database.transaction(() async {
await _handleSync(syncResp, direction: Direction.f);
}
if (prevBatch != syncResp.nextBatch) {
await database.storePrevBatch(syncResp.nextBatch);
}
});
await runBenchmarked(
'Process sync',
() async => await _currentTransaction,
syncResp.itemCount,
);
if (_disposed || _aborted) return;
_prevBatch = syncResp.nextBatch;
onSyncStatus.add(SyncStatusUpdate(SyncStatus.cleaningUp));
// ignore: unawaited_futures
database?.deleteOldFiles(
database.deleteOldFiles(
DateTime.now().subtract(Duration(days: 30)).millisecondsSinceEpoch,
);
await updateUserDeviceKeys();
@ -2523,10 +2495,10 @@ class Client extends MatrixApi {
// ignore: deprecated_member_use_from_same_package
onPresence.add(newPresence);
onPresenceChanged.add(cachedPresence);
await database?.storePresence(newPresence.senderId, cachedPresence);
await database.storePresence(newPresence.senderId, cachedPresence);
}
for (final newAccountData in sync.accountData ?? <BasicEvent>[]) {
await database?.storeAccountData(
await database.storeAccountData(
newAccountData.type,
newAccountData.content,
);
@ -2559,7 +2531,7 @@ class Client extends MatrixApi {
final userKeys = _userDeviceKeys[userId];
if (userKeys != null) {
userKeys.outdated = true;
await database?.storeUserDeviceKeysInfo(userId, true);
await database.storeUserDeviceKeysInfo(userId, true);
}
}
for (final userId in deviceLists.left ?? []) {
@ -2663,7 +2635,7 @@ class Client extends MatrixApi {
// removed from the database!
if (syncRoomUpdate is JoinedRoomUpdate &&
syncRoomUpdate.timeline?.limited == true) {
await database?.deleteTimelineForRoom(id);
await database.deleteTimelineForRoom(id);
}
final room = await _updateRoomsByRoomUpdate(id, syncRoomUpdate);
@ -2722,7 +2694,7 @@ class Client extends MatrixApi {
final accountData = syncRoomUpdate.accountData;
if (accountData != null && accountData.isNotEmpty) {
for (final event in accountData) {
await database?.storeRoomAccountData(room.id, event);
await database.storeRoomAccountData(room.id, event);
room.roomAccountData[event.type] = event;
}
}
@ -2761,7 +2733,7 @@ class Client extends MatrixApi {
await _handleRoomEvents(room, state, EventUpdateType.inviteState);
}
}
await database?.storeRoomUpdate(id, syncRoomUpdate, room.lastEvent, this);
await database.storeRoomUpdate(id, syncRoomUpdate, room.lastEvent, this);
}
}
@ -2790,7 +2762,7 @@ class Client extends MatrixApi {
type: LatestReceiptState.eventType,
content: receiptStateContent.toJson(),
);
await database?.storeRoomAccountData(room.id, event);
await database.storeRoomAccountData(room.id, event);
room.roomAccountData[event.type] = event;
}
}
@ -2844,25 +2816,24 @@ class Client extends MatrixApi {
// We do not re-request the profile here as this would lead to
// an unknown amount of network requests as we never know how many
// member change events can come down in a single sync update.
await database?.markUserProfileAsOutdated(userId);
await database.markUserProfileAsOutdated(userId);
onUserProfileUpdate.add(userId);
}
}
if (event.type == EventTypes.Message &&
!room.isDirectChat &&
database != null &&
event is MatrixEvent &&
room.getState(EventTypes.RoomMember, event.senderId) == null) {
// In order to correctly render room list previews we need to fetch the member from the database
final user = await database?.getUser(event.senderId, room);
final user = await database.getUser(event.senderId, room);
if (user != null) {
room.setState(user);
}
}
await _updateRoomsByEventUpdate(room, event, type);
if (store) {
await database?.storeEventUpdate(room.id, event, type, this);
await database.storeEventUpdate(room.id, event, type, this);
}
if (event is MatrixEvent && encryptionEnabled) {
await encryption?.handleEventUpdate(
@ -3080,7 +3051,7 @@ class Client extends MatrixApi {
(event.redacts ?? event.content.tryGet<String>('redacts')) &&
event.type == EventTypes.Redaction &&
room.lastEvent?.relationshipType == RelationshipTypes.edit) {
final originalEvent = await database?.getEventById(
final originalEvent = await database.getEventById(
relationshipEventId,
room,
) ??
@ -3216,7 +3187,7 @@ class Client extends MatrixApi {
Future<void> updateUserDeviceKeys({Set<String>? additionalUsers}) async {
try {
final database = this.database;
if (!isLogged() || database == null) return;
if (!isLogged()) return;
final dbActions = <Future<dynamic> Function()>[];
final trackedUserIds = await _getUserIdsInEncryptedRooms();
if (!isLogged()) return;
@ -3464,7 +3435,7 @@ class Client extends MatrixApi {
/// proccessed all the way.
Future<void> processToDeviceQueue() async {
final database = this.database;
if (database == null || !_toDeviceQueueNeedsProcessing) {
if (!_toDeviceQueueNeedsProcessing) {
return;
}
final entries = await database.getToDeviceEventQueue();
@ -3518,14 +3489,12 @@ class Client extends MatrixApi {
s,
);
final database = this.database;
if (database != null) {
_toDeviceQueueNeedsProcessing = true;
await database.insertIntoToDeviceQueue(
eventType,
txnId,
json.encode(messages),
);
}
_toDeviceQueueNeedsProcessing = true;
await database.insertIntoToDeviceQueue(
eventType,
txnId,
json.encode(messages),
);
rethrow;
}
}
@ -3762,7 +3731,7 @@ class Client extends MatrixApi {
await abortSync();
_prevBatch = null;
rooms.clear();
await database?.clearCache();
await database.clearCache();
encryption?.keyManager.clearOutboundGroupSessions();
_eventsPendingDecryption.clear();
onCacheCleared.add(true);
@ -3824,7 +3793,7 @@ class Client extends MatrixApi {
return cachedPresence;
}
final dbPresence = await database?.getPresence(userId);
final dbPresence = await database.getPresence(userId);
// ignore: deprecated_member_use_from_same_package
if (dbPresence != null) return presences[userId] = dbPresence;
@ -3833,12 +3802,12 @@ class Client extends MatrixApi {
try {
final result = await getPresence(userId);
final presence = CachedPresence.fromPresenceResponse(result, userId);
await database?.storePresence(userId, presence);
await database.storePresence(userId, presence);
// ignore: deprecated_member_use_from_same_package
return presences[userId] = presence;
} catch (e) {
final presence = CachedPresence.neverSeen(userId);
await database?.storePresence(userId, presence);
await database.storePresence(userId, presence);
// ignore: deprecated_member_use_from_same_package
return presences[userId] = presence;
}
@ -3873,10 +3842,8 @@ class Client extends MatrixApi {
_encryption = null;
try {
if (closeDatabase) {
final database = _database;
_database = null;
await database
?.close()
.close()
.catchError((e, s) => Logs().w('Failed to close database: ', e, s));
}
} catch (error, stacktrace) {
@ -3894,7 +3861,7 @@ class Client extends MatrixApi {
final migrateClient = await legacyDatabase?.getClient(clientName);
final database = this.database;
if (migrateClient == null || legacyDatabase == null || database == null) {
if (migrateClient == null || legacyDatabase == null) {
await legacyDatabase?.close();
_initLock = false;
return;

View File

@ -176,23 +176,38 @@ class MatrixSdkDatabase extends DatabaseApi with DatabaseFileStorage {
/// like delete. Set it if you want to use sqlite FFI.
final DatabaseFactory? sqfliteFactory;
MatrixSdkDatabase(
static Future<MatrixSdkDatabase> init(
String name, {
Database? database,
dynamic idbFactory,
DatabaseFactory? sqfliteFactory,
int maxFileSize = 0,
Uri? fileStorageLocation,
Duration? deleteFilesAfterDuration,
}) async {
final matrixSdkDatabase = MatrixSdkDatabase._(
name,
database: database,
idbFactory: idbFactory,
sqfliteFactory: sqfliteFactory,
maxFileSize: maxFileSize,
fileStorageLocation: fileStorageLocation,
deleteFilesAfterDuration: deleteFilesAfterDuration,
);
await matrixSdkDatabase.open();
return matrixSdkDatabase;
}
MatrixSdkDatabase._(
this.name, {
this.database,
this.idbFactory,
this.sqfliteFactory,
this.maxFileSize = 0,
// TODO : remove deprecated member migration on next major release
@Deprecated(
'Breaks support for web standalone. Use [fileStorageLocation] instead.',
)
dynamic fileStoragePath,
Uri? fileStorageLocation,
Duration? deleteFilesAfterDuration,
}) {
final legacyPath = fileStoragePath?.path;
this.fileStorageLocation = fileStorageLocation ??
(legacyPath is String ? Uri.tryParse(legacyPath) : null);
this.fileStorageLocation = fileStorageLocation;
this.deleteFilesAfterDuration = deleteFilesAfterDuration;
}

View File

@ -121,7 +121,7 @@ class Event extends MatrixEvent {
// Mark event as failed to send if status is `sending` and event is older
// than the timeout. This should not happen with the deprecated Moor
// database!
if (status.isSending && room.client.database != null) {
if (status.isSending) {
// Age of this event in milliseconds
final age = DateTime.now().millisecondsSinceEpoch -
originServerTs.millisecondsSinceEpoch;
@ -402,7 +402,7 @@ class Event extends MatrixEvent {
throw Exception('Can only delete events which are not sent yet!');
}
await room.client.database?.removeEvent(eventId, room.id);
await room.client.database.removeEvent(eventId, room.id);
if (room.lastEvent != null && room.lastEvent!.eventId == eventId) {
final redactedBecause = Event.fromMatrixEvent(
@ -726,9 +726,6 @@ class Event extends MatrixEvent {
// Is this file storeable?
final thisInfoMap = getThumbnail ? thumbnailInfoMap : infoMap;
final database = room.client.database;
if (database == null) {
return false;
}
final storeable = thisInfoMap['size'] is int &&
thisInfoMap['size'] <= database.maxFileSize;
@ -772,13 +769,12 @@ class Event extends MatrixEvent {
// Is this file storeable?
final thisInfoMap = getThumbnail ? thumbnailInfoMap : infoMap;
var storeable = database != null &&
thisInfoMap['size'] is int &&
var storeable = thisInfoMap['size'] is int &&
thisInfoMap['size'] <= database.maxFileSize;
Uint8List? uint8list;
if (storeable) {
uint8list = await room.client.database?.getFile(mxcUrl);
uint8list = await room.client.database.getFile(mxcUrl);
}
// Download the file
@ -792,9 +788,7 @@ class Event extends MatrixEvent {
.bodyBytes;
uint8list =
await downloadCallback(await mxcUrl.getDownloadUri(room.client));
storeable = database != null &&
storeable &&
uint8list.lengthInBytes < database.maxFileSize;
storeable = storeable && uint8list.lengthInBytes < database.maxFileSize;
if (storeable) {
await database.storeFile(
mxcUrl,

View File

@ -284,7 +284,7 @@ class LatestReceiptState {
// set the latest receipt to the one furthest down in the timeline, or if we don't know that, the newest ts.
if (updatedTimelines.isEmpty) return;
final eventOrder = await room.client.database?.getEventIdList(room) ?? [];
final eventOrder = await room.client.database.getEventIdList(room);
for (final timeline in updatedTimelines) {
if (timeline.ownPrivate?.eventId == timeline.ownPublic?.eventId) {

View File

@ -121,15 +121,13 @@ class Room {
return;
}
final allStates =
await client.database?.getUnimportantRoomEventStatesForRoom(
await client.database.getUnimportantRoomEventStatesForRoom(
client.importantStateEvents.toList(),
this,
);
if (allStates != null) {
for (final state in allStates) {
setState(state);
}
for (final state in allStates) {
setState(state);
}
partial = false;
}
@ -1237,7 +1235,7 @@ class Room {
/// Call the Matrix API to forget this room if you already left it.
Future<void> forget() async {
await client.database?.forgetRoom(id);
await client.database.forgetRoom(id);
await client.forgetRoom(id);
// Update archived rooms, otherwise an archived room may still be in the
// list after a forget room call
@ -1377,17 +1375,13 @@ class Room {
);
}
if (client.database != null) {
await client.database?.transaction(() async {
if (storeInDatabase && direction == Direction.b) {
this.prev_batch = resp.end;
await client.database?.setRoomPrevBatch(resp.end, id, client);
}
await loadFn();
});
} else {
await client.database.transaction(() async {
if (storeInDatabase && direction == Direction.b) {
this.prev_batch = resp.end;
await client.database.setRoomPrevBatch(resp.end, id, client);
}
await loadFn();
}
});
return resp.chunk.length;
}
@ -1478,7 +1472,7 @@ class Room {
].map((e) => Event.fromMatrixEvent(e, this)).toList();
// Try again to decrypt encrypted events but don't update the database.
if (encrypted && client.database != null && client.encryptionEnabled) {
if (encrypted && client.encryptionEnabled) {
for (var i = 0; i < events.length; i++) {
if (events[i].type == EventTypes.Encrypted &&
events[i].content['can_request_session'] == true) {
@ -1537,12 +1531,11 @@ class Room {
var events = <Event>[];
if (!isArchived) {
await client.database?.transaction(() async {
events = await client.database?.getEventList(
this,
limit: limit,
) ??
<Event>[];
await client.database.transaction(() async {
events = await client.database.getEventList(
this,
limit: limit,
);
});
} else {
final archive = client.getArchiveRoomFromCache(id);
@ -1581,7 +1574,7 @@ class Room {
final userIds = events.map((event) => event.senderId).toSet();
for (final userId in userIds) {
if (getState(EventTypes.RoomMember, userId) != null) continue;
final dbUser = await client.database?.getUser(userId, this);
final dbUser = await client.database.getUser(userId, this);
if (dbUser != null) setState(dbUser);
}
}
@ -1597,9 +1590,9 @@ class Room {
chunk.events[i] = await client.encryption!.decryptRoomEvent(
chunk.events[i],
);
} else if (client.database != null) {
} else {
// else, we need the database
await client.database?.transaction(() async {
await client.database.transaction(() async {
for (var i = 0; i < chunk.events.length; i++) {
if (chunk.events[i].content['can_request_session'] == true) {
chunk.events[i] = await client.encryption!.decryptRoomEvent(
@ -1666,7 +1659,7 @@ class Room {
// events won't get written to memory in this case and someone new could
// have joined, while someone else left, which might lead to the same
// count in the completeness check.
final users = await client.database?.getUsers(this) ?? [];
final users = await client.database.getUsers(this);
for (final user in users) {
setState(user);
}
@ -1697,7 +1690,7 @@ class Room {
if (cache) {
for (final user in users) {
setState(user); // at *least* cache this in-memory
await client.database?.storeEventUpdate(
await client.database.storeEventUpdate(
id,
user,
EventUpdateType.state,
@ -1775,8 +1768,8 @@ class Room {
);
// Store user in database:
await client.database?.transaction(() async {
await client.database?.storeEventUpdate(
await client.database.transaction(() async {
await client.database.storeEventUpdate(
id,
foundUser,
EventUpdateType.state,
@ -1812,7 +1805,7 @@ class Room {
// If the room is not postloaded, check the database
if (partial && foundUser == null) {
foundUser = await client.database?.getUser(mxID, this);
foundUser = await client.database.getUser(mxID, this);
}
// If not in the database, try fetching the member from the server
@ -1920,7 +1913,7 @@ class Room {
/// found. Returns null if not found anywhere.
Future<Event?> getEventById(String eventID) async {
try {
final dbEvent = await client.database?.getEventById(eventID, this);
final dbEvent = await client.database.getEventById(eventID, this);
if (dbEvent != null) return dbEvent;
final matrixEvent = await client.getOneRoomEvent(id, eventID);
final event = Event.fromMatrixEvent(matrixEvent, this);
@ -2393,13 +2386,9 @@ class Room {
SyncUpdate syncUpdate, {
Direction? direction,
}) async {
if (client.database != null) {
await client.database?.transaction(() async {
await client.handleSync(syncUpdate, direction: direction);
});
} else {
await client.database.transaction(() async {
await client.handleSync(syncUpdate, direction: direction);
}
});
}
/// Whether this is an extinct room which has been archived in favor of a new

View File

@ -145,7 +145,7 @@ class Timeline {
// Look up for events in the database first. With fragmented view, we should delete the database cache
final eventsFromStore = isFragmentedTimeline
? null
: await room.client.database?.getEventList(
: await room.client.database.getEventList(
room,
start: events.length,
limit: historyCount,
@ -161,7 +161,7 @@ class Timeline {
continue;
}
final dbUser =
await room.client.database?.getUser(event.senderId, room);
await room.client.database.getUser(event.senderId, room);
if (dbUser != null) room.setState(dbUser);
}
@ -274,8 +274,7 @@ class Timeline {
if (allowNewEvent) {
Logs().d('We now allow sync update into the timeline.');
newEvents.addAll(
await room.client.database?.getEventList(room, onlySending: true) ??
[],
await room.client.database.getEventList(room, onlySending: true),
);
}
}
@ -419,11 +418,7 @@ class Timeline {
}
}
if (room.client.database != null) {
await room.client.database?.transaction(decryptFn);
} else {
await decryptFn();
}
await room.client.database.transaction(decryptFn);
if (decryptAtLeastOneEvent) onUpdate?.call();
}
@ -664,12 +659,11 @@ class Timeline {
// Search in database
var start = events.length;
while (true) {
final eventsFromStore = await room.client.database?.getEventList(
room,
start: start,
limit: requestHistoryCount,
) ??
[];
final eventsFromStore = await room.client.database.getEventList(
room,
start: start,
limit: requestHistoryCount,
);
if (eventsFromStore.isEmpty) break;
start += eventsFromStore.length;
for (final event in eventsFromStore) {

View File

@ -426,7 +426,7 @@ class CrossSigningKey extends SignableKey {
}
await super.setVerified(newVerified, sign);
await client.database
?.setVerifiedUserCrossSigningKey(newVerified, userId, publicKey!);
.setVerifiedUserCrossSigningKey(newVerified, userId, publicKey!);
}
@override
@ -436,7 +436,7 @@ class CrossSigningKey extends SignableKey {
}
_blocked = newBlocked;
await client.database
?.setBlockedUserCrossSigningKey(newBlocked, userId, publicKey!);
.setBlockedUserCrossSigningKey(newBlocked, userId, publicKey!);
}
CrossSigningKey.fromMatrixCrossSigningKey(
@ -513,7 +513,7 @@ class DeviceKeys extends SignableKey {
}
await super.setVerified(newVerified, sign);
await client.database
?.setVerifiedUserDeviceKey(newVerified, userId, deviceId!);
.setVerifiedUserDeviceKey(newVerified, userId, deviceId!);
}
@override
@ -524,7 +524,7 @@ class DeviceKeys extends SignableKey {
}
_blocked = newBlocked;
await client.database
?.setBlockedUserDeviceKey(newBlocked, userId, deviceId!);
.setBlockedUserDeviceKey(newBlocked, userId, deviceId!);
}
DeviceKeys.fromMatrixDeviceKeys(

View File

@ -79,7 +79,7 @@ void main() {
final client = Client(
'testclient',
httpClient: FakeMatrixApi(),
databaseBuilder: getDatabase,
database: await getDatabase(),
);
expect(client.isLogged(), false);
final Set<InitState> initStates = {};
@ -112,7 +112,7 @@ void main() {
matrix = Client(
'testclient',
httpClient: FakeMatrixApi(),
databaseBuilder: getDatabase,
database: await getDatabase(),
);
final eventUpdateListFuture = matrix.onTimelineEvent.stream.toList();
final toDeviceUpdateListFuture = matrix.onToDeviceEvent.stream.toList();
@ -369,10 +369,10 @@ void main() {
final key = 'abc def!/_-';
await matrix.setAccountData(matrix.userID!, key, content);
final dbContent = await matrix.database?.getAccountData();
final dbContent = await matrix.database.getAccountData();
expect(matrix.accountData[key]?.content, content);
expect(dbContent?[key]?.content, content);
expect(dbContent[key]?.content, content);
});
test('roomAccountData', () async {
@ -383,15 +383,15 @@ void main() {
final key = 'abc def!/_-';
final roomId = '!726s6s6q:example.com';
await matrix.setAccountDataPerRoom(matrix.userID!, roomId, key, content);
final roomFromList = (await matrix.database?.getRoomList(matrix))
?.firstWhere((room) => room.id == roomId);
final roomFromDb = await matrix.database?.getSingleRoom(matrix, roomId);
final roomFromList = (await matrix.database.getRoomList(matrix))
.firstWhere((room) => room.id == roomId);
final roomFromDb = await matrix.database.getSingleRoom(matrix, roomId);
expect(
matrix.getRoomById(roomId)?.roomAccountData[key]?.content,
content,
);
expect(roomFromList?.roomAccountData[key]?.content, content);
expect(roomFromList.roomAccountData[key]?.content, content);
expect(
roomFromDb?.roomAccountData[key]?.content,
content,
@ -418,7 +418,7 @@ void main() {
matrix = Client(
'testclient',
httpClient: FakeMatrixApi(),
databaseBuilder: getDatabase,
database: await getDatabase(),
);
expect(matrix.homeserver, null);
@ -504,7 +504,7 @@ void main() {
matrix = Client(
'testclient',
httpClient: FakeMatrixApi(),
databaseBuilder: getDatabase,
database: await getDatabase(),
);
await matrix.checkHomeserver(
@ -912,14 +912,14 @@ void main() {
);
expect(matrix.onUserProfileUpdate.value, '@alice:example.com');
final cachedProfileFromDb =
await matrix.database?.getUserProfile('@alice:example.com');
await matrix.database.getUserProfile('@alice:example.com');
expect(cachedProfileFromDb?.outdated, true);
});
test('joinAfterInviteMembership', () async {
final client = await getClient();
await client.abortSync();
client.rooms.clear();
await client.database?.clearCache();
await client.database.clearCache();
await client.handleSync(
SyncUpdate.fromJson(
@ -955,7 +955,7 @@ void main() {
await client.abortSync();
client.rooms.clear();
await client.database?.clearCache();
await client.database.clearCache();
await client.dispose(closeDatabase: true);
});
test('leaveThenInvite should be invited', () async {
@ -967,7 +967,7 @@ void main() {
final client = await getClient();
await client.abortSync();
client.rooms.clear();
await client.database?.clearCache();
await client.database.clearCache();
final roomId = '!inviteLeaveRoom:example.com';
await client.handleSync(
@ -1015,14 +1015,14 @@ void main() {
await client.abortSync();
client.rooms.clear();
await client.database?.clearCache();
await client.database.clearCache();
await client.dispose(closeDatabase: true);
});
test('ownProfile', () async {
final client = await getClient();
await client.abortSync();
client.rooms.clear();
await client.database?.clearCache();
await client.database.clearCache();
await client.handleSync(
SyncUpdate.fromJson(
jsonDecode(
@ -1394,11 +1394,11 @@ void main() {
);
});
test('Test the fake store api', () async {
final database = await getDatabase(null);
final database = await getDatabase();
final client1 = Client(
'testclient',
httpClient: FakeMatrixApi(),
databaseBuilder: (_) => database,
database: database,
);
await client1.init(
@ -1418,7 +1418,7 @@ void main() {
final client2 = Client(
'testclient',
httpClient: FakeMatrixApi(),
databaseBuilder: (_) => database,
database: database,
);
await client2.init();
@ -1470,8 +1470,8 @@ void main() {
await client.uploadContent(Uint8List(0), filename: 'file.jpeg');
expect(response.toString(), 'mxc://example.com/AQwafuaFswefuhsfAFAgsw');
expect(
await client.database?.getFile(response) != null,
client.database?.supportsFileStoring,
await client.database.getFile(response) != null,
client.database.supportsFileStoring,
);
await client.dispose(closeDatabase: true);
});
@ -1509,7 +1509,7 @@ void main() {
FakeMatrixApi.expectedAccessToken = null;
expect(client.accessToken, 'a_new_token');
expect(softLoggedOut, 1);
final storedClient = await client.database?.getClient(client.clientName);
final storedClient = await client.database.getClient(client.clientName);
expect(storedClient?.tryGet<String>('token'), 'a_new_token');
expect(
storedClient?.tryGet<String>('refresh_token'),
@ -1580,11 +1580,11 @@ void main() {
});
test('Database Migration', () async {
final firstDatabase = await getDatabase(null);
final firstDatabase = await getDatabase();
final firstClient = Client(
'testclient',
httpClient: FakeMatrixApi(),
databaseBuilder: (_) => firstDatabase,
database: firstDatabase,
);
FakeMatrixApi.client = firstClient;
await firstClient.checkHomeserver(
@ -1605,7 +1605,7 @@ void main() {
final newClient = Client(
'testclient',
httpClient: FakeMatrixApi(),
databaseBuilder: getDatabase,
database: await getDatabase(),
legacyDatabaseBuilder: (_) => firstDatabase,
);
final Set<InitState> initStates = {};
@ -1621,7 +1621,7 @@ void main() {
await newClient.dispose(closeDatabase: false);
await firstDatabase.close();
final sameOldFirstDatabase = await getDatabase(null);
final sameOldFirstDatabase = await getDatabase();
expect(await sameOldFirstDatabase.getClient('testclient'), null);
});
@ -1629,7 +1629,7 @@ void main() {
final client = Client(
'testclient',
httpClient: FakeMatrixApi(),
databaseBuilder: getDatabase,
database: await getDatabase(),
)
..accessToken = '1234'
..baseUri = Uri.parse('https://fakeserver.notexisting');
@ -1664,8 +1664,8 @@ void main() {
expect(event?.room.name, 'TestRoomName');
expect(event?.room.canonicalAlias, '#testalias:blaaa');
final storedEvent =
await client.database?.getEventById('123', event!.room);
expect(storedEvent?.eventId, event?.eventId);
await client.database.getEventById('123', event!.room);
expect(storedEvent?.eventId, event.eventId);
event = await client.getEventByPushNotification(
PushNotification(
@ -1680,8 +1680,8 @@ void main() {
expect(event?.messageType, 'm.text');
expect(event?.type, 'm.room.message');
final storedEvent2 = await client.database
?.getEventById('143273582443PhrSn:example.org', event!.room);
expect(storedEvent2?.eventId, event?.eventId);
.getEventById('143273582443PhrSn:example.org', event!.room);
expect(storedEvent2?.eventId, event.eventId);
});
test('Rooms and archived rooms getter', () async {
@ -1738,7 +1738,7 @@ void main() {
() async {
final customClient = Client(
'failclient',
databaseBuilder: getMatrixSdkDatabase,
database: await getMatrixSdkDatabase(),
);
try {
await customClient.init(

View File

@ -44,7 +44,7 @@ void main() {
late int toDeviceQueueIndex;
test('Setup', () async {
database = await databaseBuilder.value(null);
database = await databaseBuilder.value();
});
test('transaction', () async {
var counter = 0;
@ -105,29 +105,50 @@ void main() {
'limited_timeline': false,
'membership': Membership.join,
});
final client = Client('testclient');
final client = Client(
'testclient',
database: await getMatrixSdkDatabase(),
);
await database.storeRoomUpdate('!testroom', roomUpdate, null, client);
final rooms = await database.getRoomList(client);
expect(rooms.single.id, '!testroom');
});
test('getRoomList', () async {
final room =
await database.getSingleRoom(Client('testclient'), '!testroom');
final room = await database.getSingleRoom(
Client(
'testclient',
database: await getMatrixSdkDatabase(),
),
'!testroom',
);
expect(room?.id, '!testroom');
});
test('getRoomList', () async {
final list = await database.getRoomList(Client('testclient'));
final list = await database.getRoomList(
Client(
'testclient',
database: await getMatrixSdkDatabase(),
),
);
expect(list.single.id, '!testroom');
});
test('setRoomPrevBatch', () async {
final client = Client('testclient');
final client = Client(
'testclient',
database: await getMatrixSdkDatabase(),
);
await database.setRoomPrevBatch('1234', '!testroom', client);
final rooms = await database.getRoomList(client);
expect(rooms.single.prev_batch, '1234');
});
test('forgetRoom', () async {
await database.forgetRoom('!testroom');
final rooms = await database.getRoomList(Client('testclient'));
final rooms = await database.getRoomList(
Client(
'testclient',
database: await getMatrixSdkDatabase(),
),
);
expect(rooms.isEmpty, true);
});
test('getClient', () async {
@ -238,12 +259,18 @@ void main() {
},
),
EventUpdateType.timeline,
Client('testclient'),
Client(
'testclient',
database: await getMatrixSdkDatabase(),
),
);
});
test('storeEventUpdate (state)', () async {
final roomid = '!testrooma:example.com';
final client = Client('testclient');
final client = Client(
'testclient',
database: await getMatrixSdkDatabase(),
);
await database.storeRoomUpdate(
roomid,
@ -376,26 +403,50 @@ void main() {
test('getEventById', () async {
final event = await database.getEventById(
'\$event:example.com',
Room(id: '!testroom:example.com', client: Client('testclient')),
Room(
id: '!testroom:example.com',
client: Client(
'testclient',
database: await getMatrixSdkDatabase(),
),
),
);
expect(event?.type, EventTypes.Message);
});
test('getEventList', () async {
final events = await database.getEventList(
Room(id: '!testroom:example.com', client: Client('testclient')),
Room(
id: '!testroom:example.com',
client: Client(
'testclient',
database: await getMatrixSdkDatabase(),
),
),
);
expect(events.single.type, EventTypes.Message);
});
test('getUser', () async {
final user = await database.getUser(
'@bob:example.org',
Room(id: '!testroom:example.com', client: Client('testclient')),
Room(
id: '!testroom:example.com',
client: Client(
'testclient',
database: await getMatrixSdkDatabase(),
),
),
);
expect(user, null);
});
test('getUsers', () async {
final users = await database.getUsers(
Room(id: '!testroom:example.com', client: Client('testclient')),
Room(
id: '!testroom:example.com',
client: Client(
'testclient',
database: await getMatrixSdkDatabase(),
),
),
);
expect(users.isEmpty, true);
});
@ -406,7 +457,13 @@ void main() {
);
final event = await database.getEventById(
'\$event:example.com',
Room(id: '!testroom:example.com', client: Client('testclient')),
Room(
id: '!testroom:example.com',
client: Client(
'testclient',
database: await getMatrixSdkDatabase(),
),
),
);
expect(event, null);
});
@ -568,12 +625,23 @@ void main() {
test('getUnimportantRoomEventStatesForRoom', () async {
final events = await database.getUnimportantRoomEventStatesForRoom(
['events'],
Room(id: '!mep', client: Client('testclient')),
Room(
id: '!mep',
client: Client(
'testclient',
database: await getMatrixSdkDatabase(),
),
),
);
expect(events.isEmpty, true);
});
test('getUserDeviceKeys', () async {
await database.getUserDeviceKeys(Client('testclient'));
await database.getUserDeviceKeys(
Client(
'testclient',
database: await getMatrixSdkDatabase(),
),
);
});
test('storeUserCrossSigningKey', () async {
await database.storeUserCrossSigningKey(
@ -695,7 +763,7 @@ void main() {
await database.close();
});
test('Delete', () async {
final database = await getMatrixSdkDatabase(null);
final database = await getMatrixSdkDatabase();
await database.storeAccountData(
'm.test.data',
{'foo': 'bar'},
@ -703,7 +771,7 @@ void main() {
await database.delete();
// Check if previously stored data is gone:
final reopenedDatabase = await getMatrixSdkDatabase(null);
final reopenedDatabase = await getMatrixSdkDatabase();
final dump = await reopenedDatabase.getAccountData();
expect(dump.isEmpty, true);
});

View File

@ -23,11 +23,11 @@ import 'package:matrix/matrix.dart';
import '../fake_client.dart';
import '../fake_database.dart';
void main() {
void main() async {
// key @othertest:fakeServer.notExisting
const otherPickledOlmAccount =
'VWhVApbkcilKAEGppsPDf9nNVjaK8/IxT3asSR0sYg0S5KgbfE8vXEPwoiKBX2cEvwX3OessOBOkk+ZE7TTbjlrh/KEd31p8Wo+47qj0AP+Ky+pabnhi+/rTBvZy+gfzTqUfCxZrkzfXI9Op4JnP6gYmy7dVX2lMYIIs9WCO1jcmIXiXum5jnfXu1WLfc7PZtO2hH+k9CDKosOFaXRBmsu8k/BGXPSoWqUpvu6WpEG9t5STk4FeAzA';
final database = await getDatabase();
group('Encrypt/Decrypt to-device messages', tags: 'olm', () {
Logs().level = Level.error;
@ -35,7 +35,7 @@ void main() {
final otherClient = Client(
'othertestclient',
httpClient: FakeMatrixApi(),
databaseBuilder: getDatabase,
database: database,
);
late DeviceKeys device;
late Map<String, dynamic> payload;

View File

@ -67,7 +67,7 @@ void main() async {
client2 = Client(
'othertestclient',
httpClient: FakeMatrixApi.currentApi!,
databaseBuilder: getDatabase,
database: await getDatabase(),
);
await client2.checkHomeserver(
Uri.parse('https://fakeserver.notexisting'),

View File

@ -157,7 +157,7 @@ void main() {
final deviceId = 'JLAFKJWSCS';
final senderKey = 'L+4+JCl8MD63dgo8z5Ta+9QAHXiANyOVSfgbHA5d3H8';
FakeMatrixApi.calledEndpoints.clear();
await client.database!.setLastSentMessageUserDeviceKey(
await client.database.setLastSentMessageUserDeviceKey(
json.encode({
'type': 'm.foxies',
'content': {
@ -187,7 +187,7 @@ void main() {
// not encrypted
FakeMatrixApi.calledEndpoints.clear();
await client.database!.setLastSentMessageUserDeviceKey(
await client.database.setLastSentMessageUserDeviceKey(
json.encode({
'type': 'm.foxies',
'content': {
@ -213,7 +213,7 @@ void main() {
// device not found
FakeMatrixApi.calledEndpoints.clear();
await client.database!.setLastSentMessageUserDeviceKey(
await client.database.setLastSentMessageUserDeviceKey(
json.encode({
'type': 'm.foxies',
'content': {
@ -241,7 +241,7 @@ void main() {
// don't replay if the last event is m.dummy itself
FakeMatrixApi.calledEndpoints.clear();
await client.database!.setLastSentMessageUserDeviceKey(
await client.database.setLastSentMessageUserDeviceKey(
json.encode({
'type': 'm.dummy',
'content': {},

View File

@ -111,7 +111,7 @@ void main() {
sessionPayload,
forwarded: true,
);
var dbSessions = await client.database!.getInboundGroupSessionsToUpload();
var dbSessions = await client.database.getInboundGroupSessionsToUpload();
expect(dbSessions.isNotEmpty, true);
await client.encryption!.keyManager.uploadInboundGroupSessions();
await FakeMatrixApi.firstWhereValue(
@ -119,7 +119,7 @@ void main() {
);
final payload = FakeMatrixApi
.calledEndpoints['/client/v3/room_keys/keys?version=5']!.first;
dbSessions = await client.database!.getInboundGroupSessionsToUpload();
dbSessions = await client.database.getInboundGroupSessionsToUpload();
expect(dbSessions.isEmpty, true);
final onlineKeys = RoomKeys.fromJson(json.decode(payload));

View File

@ -25,8 +25,15 @@ import 'package:matrix/encryption.dart';
import 'package:matrix/matrix.dart';
import 'package:matrix/src/models/timeline_chunk.dart';
import 'fake_client.dart';
import 'fake_database.dart';
void main() async {
final client = Client(
'testclient',
httpClient: FakeMatrixApi(),
database: await getDatabase(),
);
void main() {
/// All Tests related to the Event
group('Event', () {
Logs().level = Level.error;
@ -51,7 +58,6 @@ void main() {
'status': EventStatus.synced.intValue,
'content': contentJson,
};
final client = Client('testclient', httpClient: FakeMatrixApi());
final room = Room(id: '!testroom:example.abc', client: client);
final event = Event.fromJson(
jsonObj,
@ -221,7 +227,13 @@ void main() {
];
for (final testType in testTypes) {
redactJsonObj['type'] = testType;
final room = Room(id: '1234', client: Client('testclient'));
final room = Room(
id: '1234',
client: Client(
'testclient',
database: await getDatabase(),
),
);
final redactionEventJson = {
'content': {'reason': 'Spamming'},
'event_id': '143273582443PhrSn:example.org',
@ -248,7 +260,13 @@ void main() {
test('remove', () async {
final event = Event.fromJson(
jsonObj,
Room(id: '1234', client: Client('testclient')),
Room(
id: '1234',
client: Client(
'testclient',
database: await getDatabase(),
),
),
);
expect(() async => await event.cancelSend(), throwsException);
event.status = EventStatus.sending;
@ -258,7 +276,11 @@ void main() {
});
test('sendAgain', () async {
final matrix = Client('testclient', httpClient: FakeMatrixApi());
final matrix = Client(
'testclient',
httpClient: FakeMatrixApi(),
database: await getDatabase(),
);
await matrix.checkHomeserver(
Uri.parse('https://fakeserver.notexisting'),
checkWellKnown: false,
@ -283,7 +305,11 @@ void main() {
});
test('requestKey', tags: 'olm', () async {
final matrix = Client('testclient', httpClient: FakeMatrixApi());
final matrix = Client(
'testclient',
httpClient: FakeMatrixApi(),
database: await getDatabase(),
);
await matrix.checkHomeserver(
Uri.parse('https://fakeserver.notexisting'),
checkWellKnown: false,
@ -333,6 +359,7 @@ void main() {
await matrix.dispose(closeDatabase: true);
});
test('requestKey', tags: 'olm', () async {
final client = await getClient();
jsonObj['state_key'] = '@alice:example.com';
final event = Event.fromJson(
jsonObj,
@ -355,7 +382,11 @@ void main() {
await client.dispose();
});
test('getLocalizedBody, isEventKnown', () async {
final matrix = Client('testclient', httpClient: FakeMatrixApi());
final matrix = Client(
'testclient',
httpClient: FakeMatrixApi(),
database: await getDatabase(),
);
final room = Room(id: '!1234:example.com', client: matrix);
var event = Event.fromJson(
{
@ -1167,7 +1198,11 @@ void main() {
});
test('getLocalizedBody, parameters', () async {
final matrix = Client('testclient', httpClient: FakeMatrixApi());
final matrix = Client(
'testclient',
httpClient: FakeMatrixApi(),
database: await getDatabase(),
);
final room = Room(id: '!1234:example.com', client: matrix);
var event = Event.fromJson(
{
@ -2477,7 +2512,7 @@ void main() {
);
expect(
await event.isAttachmentInLocalStore(),
event.room.client.database?.supportsFileStoring,
event.room.client.database.supportsFileStoring,
);
expect(buffer.bytes, FILE_BUFF);
expect(serverHits, 1);
@ -2487,7 +2522,7 @@ void main() {
expect(buffer.bytes, FILE_BUFF);
expect(
serverHits,
event.room.client.database!.supportsFileStoring ? 1 : 2,
event.room.client.database.supportsFileStoring ? 1 : 2,
);
await room.client.dispose(closeDatabase: true);
@ -2530,12 +2565,12 @@ void main() {
);
expect(
await event.isAttachmentInLocalStore(),
event.room.client.database?.supportsFileStoring,
event.room.client.database.supportsFileStoring,
);
expect(buffer.bytes, FILE_BUFF);
expect(serverHits, 1);
if (event.room.client.database?.supportsFileStoring == true) {
if (event.room.client.database.supportsFileStoring == true) {
buffer = await event.downloadAndDecryptAttachment(
downloadCallback: downloadCallback,
fromLocalStoreOnly: true,

View File

@ -35,8 +35,7 @@ Future<Client> getClient({
logLevel: Level.verbose,
'testclient',
httpClient: FakeMatrixApi(),
databaseBuilder: (client) =>
getDatabase(client, databasePath: databasePath),
database: await getDatabase(databasePath: databasePath),
onSoftLogout: (client) => client.refreshAccessToken(),
sendTimelineEventTimeout: sendTimelineEventTimeout,
);
@ -63,7 +62,7 @@ Future<Client> getOtherClient() async {
final client = Client(
'othertestclient',
httpClient: FakeMatrixApi(),
databaseBuilder: getDatabase,
database: await getDatabase(),
);
FakeMatrixApi.client = client;
await client.checkHomeserver(

View File

@ -20,23 +20,18 @@ import 'package:sqflite_common_ffi/sqflite_ffi.dart';
import 'package:matrix/matrix.dart';
Future<DatabaseApi> getDatabase(Client? c, {String? databasePath}) =>
getMatrixSdkDatabase(c, path: databasePath);
Future<DatabaseApi> getDatabase({String? databasePath}) =>
getMatrixSdkDatabase(path: databasePath);
// ignore: deprecated_member_use_from_same_package
Future<MatrixSdkDatabase> getMatrixSdkDatabase(
Client? c, {
Future<MatrixSdkDatabase> getMatrixSdkDatabase({
String? path,
}) async {
final database = await databaseFactoryFfi.openDatabase(
path ?? ':memory:',
options: OpenDatabaseOptions(singleInstance: false),
);
final db = MatrixSdkDatabase(
'unit_test.${c?.hashCode}',
database: database,
sqfliteFactory: databaseFactoryFfi,
);
await db.open();
return db;
}
}) async =>
MatrixSdkDatabase.init(
'unit_test.${DateTime.now().millisecondsSinceEpoch}',
database: await databaseFactoryFfi.openDatabase(
path ?? ':memory:',
options: OpenDatabaseOptions(singleInstance: false),
),
sqfliteFactory: databaseFactoryFfi,
);

View File

@ -25,9 +25,16 @@ import 'fake_database.dart';
void main() {
group('Databse', () {
Logs().level = Level.error;
final room = Room(id: '!room:blubb', client: Client('testclient'));
late final Room room;
test('setupDatabase', () async {
final database = await getDatabase(null);
final database = await getDatabase();
room = Room(
id: '!room:blubb',
client: Client(
'testclient',
database: database,
),
);
await database.insertClient(
'testclient',
'https://example.org',
@ -43,8 +50,8 @@ void main() {
});
test('storeEventUpdate', () async {
final client = Client('testclient');
final database = await getDatabase(client);
final database = await getDatabase();
final client = Client('testclient', database: database);
// store a simple update
await database.storeEventUpdate(
room.id,

View File

@ -17,7 +17,7 @@ class MockClient extends Client {
this.serverEvents = const [],
this.dbEvents = const [],
this.throwError = false,
});
}) : super(database: MockDatabase(dbEvents));
@override
Future<GetRoomEventsResponse> getRoomEvents(
@ -47,7 +47,7 @@ class MockClient extends Client {
}
@override
DatabaseApi? get database => MockDatabase(dbEvents);
DatabaseApi get database => MockDatabase(dbEvents);
}
// MockDatabase: Simulates database access for the `TimelineExportExtension.export`

View File

@ -19,13 +19,18 @@
import 'package:test/test.dart';
import 'package:matrix/matrix.dart';
import 'fake_database.dart';
void main() {
/// All Tests related to the MxContent
group('MxContent', () {
Logs().level = Level.error;
test('Formatting', () async {
final client = Client('testclient', httpClient: FakeMatrixApi());
final client = Client(
'testclient',
httpClient: FakeMatrixApi(),
database: await getDatabase(),
);
await client.checkHomeserver(
Uri.parse('https://fakeserver.notexisting'),
checkWellKnown: false,
@ -56,7 +61,11 @@ void main() {
);
});
test('other port', () async {
final client = Client('testclient', httpClient: FakeMatrixApi());
final client = Client(
'testclient',
httpClient: FakeMatrixApi(),
database: await getDatabase(),
);
await client.checkHomeserver(
Uri.parse('https://fakeserver.notexisting'),
checkWellKnown: false,
@ -88,7 +97,11 @@ void main() {
);
});
test('other remote port', () async {
final client = Client('testclient', httpClient: FakeMatrixApi());
final client = Client(
'testclient',
httpClient: FakeMatrixApi(),
database: await getDatabase(),
);
await client.checkHomeserver(
Uri.parse('https://fakeserver.notexisting'),
checkWellKnown: false,
@ -108,7 +121,11 @@ void main() {
);
});
test('Wrong scheme throw exception', () async {
final client = Client('testclient', httpClient: FakeMatrixApi());
final client = Client(
'testclient',
httpClient: FakeMatrixApi(),
database: await getDatabase(),
);
await client.checkHomeserver(
Uri.parse('https://fakeserver.notexisting'),
checkWellKnown: false,
@ -119,7 +136,11 @@ void main() {
});
test('auth media fallback', () async {
final client = Client('testclient', httpClient: FakeMatrixApi());
final client = Client(
'testclient',
httpClient: FakeMatrixApi(),
database: await getDatabase(),
);
await client.checkHomeserver(
Uri.parse('https://fakeserverpriortoauthmedia.notexisting'),
checkWellKnown: false,

View File

@ -98,12 +98,12 @@ void main() async {
final archiveRoom = client.getRoomById('!5345234234:example.com');
expect(archiveRoom != null, true);
final eventsFromStore = await client.database?.getEventList(
final eventsFromStore = await client.database.getEventList(
archiveRoom!,
start: 0,
limit: Room.defaultHistoryCount,
);
expect(eventsFromStore?.isEmpty, true);
expect(eventsFromStore.isEmpty, true);
});
test('discard room from archives when membership change', () async {

View File

@ -1677,11 +1677,11 @@ void main() {
// check if persisted in db
final sentEventFromDB =
await matrix.database?.getEventById('older_event', room);
await matrix.database.getEventById('older_event', room);
expect(sentEventFromDB?.eventId, 'older_event');
Room? roomFromDB;
roomFromDB = await matrix.database?.getSingleRoom(matrix, room.id);
roomFromDB = await matrix.database.getSingleRoom(matrix, room.id);
expect(roomFromDB?.lastEvent?.eventId, 'older_event');
expect(room.lastEvent?.body, 'older_event');
@ -1702,7 +1702,7 @@ void main() {
expect(room.lastEvent?.eventId, 'event_too_large');
expect(room.lastEvent?.status, EventStatus.error);
roomFromDB = await matrix.database?.getSingleRoom(matrix, room.id);
roomFromDB = await matrix.database.getSingleRoom(matrix, room.id);
expect(roomFromDB?.lastEvent?.eventId, 'event_too_large');
// force null because except would have caught it anyway
@ -1718,12 +1718,12 @@ void main() {
// check if persisted in db
final lastEventFromDB =
await matrix.database?.getEventById('event_too_large', room);
await matrix.database.getEventById('event_too_large', room);
// null here because cancelSend removes event.
expect(lastEventFromDB, null);
roomFromDB = await matrix.database?.getSingleRoom(matrix, room.id);
roomFromDB = await matrix.database.getSingleRoom(matrix, room.id);
expect(roomFromDB?.partial, true);
@ -1733,7 +1733,7 @@ void main() {
'Cancelled sending message',
);
roomFromDB = await matrix.database?.getSingleRoom(matrix, room.id);
roomFromDB = await matrix.database.getSingleRoom(matrix, room.id);
await roomFromDB?.postLoad();
expect(roomFromDB?.partial, false);

View File

@ -19,30 +19,37 @@
import 'package:test/test.dart';
import 'package:matrix/matrix.dart';
import 'fake_database.dart';
void main() {
void main() async {
/// All Tests related to the Event
group('User', () {
Logs().level = Level.error;
final client = Client('testclient', httpClient: FakeMatrixApi());
final room = Room(id: '!localpart:server.abc', client: client);
final user1 = User(
'@alice:example.com',
membership: 'join',
displayName: 'Alice M',
avatarUrl: 'mxc://bla',
room: room,
);
final user2 = User(
'@bob:example.com',
membership: 'join',
displayName: 'Bob',
avatarUrl: 'mxc://bla',
room: room,
);
room.setState(user1);
room.setState(user2);
late Client client;
late Room room;
late User user1, user2;
setUp(() async {
client = Client(
'testclient',
httpClient: FakeMatrixApi(),
database: await getDatabase(),
);
room = Room(id: '!localpart:server.abc', client: client);
user1 = User(
'@alice:example.com',
membership: 'join',
displayName: 'Alice M',
avatarUrl: 'mxc://bla',
room: room,
);
user2 = User(
'@bob:example.com',
membership: 'join',
displayName: 'Bob',
avatarUrl: 'mxc://bla',
room: room,
);
room.setState(user1);
room.setState(user2);
await client.checkHomeserver(
Uri.parse('https://fakeserver.notexisting'),
checkWellKnown: false,

View File

@ -47,7 +47,7 @@ void main() => group(
Logs().i('++++ Using homeserver $homeserverUri ++++');
Logs().i('++++ Login Alice at ++++');
testClientA = Client('TestClientA', databaseBuilder: getDatabase);
testClientA = Client('TestClientA', database: await getDatabase());
await testClientA.checkHomeserver(homeserverUri);
await testClientA.login(
LoginType.mLoginPassword,
@ -57,7 +57,7 @@ void main() => group(
expect(testClientA.encryptionEnabled, true);
Logs().i('++++ Login Bob ++++');
testClientB = Client('TestClientB', databaseBuilder: getDatabase);
testClientB = Client('TestClientB', database: await getDatabase());
await testClientB.checkHomeserver(homeserverUri);
await testClientB.login(
LoginType.mLoginPassword,
@ -341,7 +341,7 @@ void main() => group(
Logs().i('++++ Login Bob in another client ++++');
final testClientC =
Client('TestClientC', databaseBuilder: getDatabase);
Client('TestClientC', database: await getDatabase());
await testClientC.checkHomeserver(homeserverUri);
// We can't sign in using the displayname, since that breaks e2ee on dendrite: https://github.com/matrix-org/dendrite/issues/2914
await testClientC.login(
@ -493,7 +493,7 @@ void main() => group(
Logs().i('++++ Using homeserver $homeserverUri ++++');
Logs().i('++++ Login Alice at ++++');
testClientA = Client('TestClientA', databaseBuilder: getDatabase);
testClientA = Client('TestClientA', database: await getDatabase());
await testClientA.checkHomeserver(homeserverUri);
await testClientA.login(
LoginType.mLoginPassword,
@ -503,7 +503,7 @@ void main() => group(
expect(testClientA.encryptionEnabled, true);
Logs().i('++++ Login Bob ++++');
testClientB = Client('TestClientB', databaseBuilder: getDatabase);
testClientB = Client('TestClientB', database: await getDatabase());
await testClientB.checkHomeserver(homeserverUri);
await testClientB.login(
LoginType.mLoginPassword,

View File

@ -1,6 +1,9 @@
import 'package:matrix/matrix.dart';
Future<void> main() async {
final client = Client('web_test');
final client = Client(
'web_test',
database: await MatrixSdkDatabase.init('web_test'),
);
await client.init();
}