diff --git a/.gitignore b/.gitignore index 7989428..a36ea6d 100644 --- a/.gitignore +++ b/.gitignore @@ -23,6 +23,7 @@ native/ **/doc/api/ .dart_tool/ .flutter-plugins +.flutter-plugins-dependencies .packages .pub-cache/ .pub/ diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index bcbfafa..4afcbbb 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -17,7 +17,7 @@ coverage: - curl https://storage.googleapis.com/dart-archive/channels/stable/release/2.7.2/linux_packages/dart_2.7.2-1_amd64.deb > dart.deb - apt install -y ./dart.deb - apt update - - apt install -y chromium lcov libolm3 + - apt install -y chromium lcov libolm3 sqlite3 libsqlite3-dev - ln -s /usr/lib/dart/bin/pub /usr/bin/ - useradd -m test - chown -R 'test:' '.' @@ -33,7 +33,7 @@ coverage_without_olm: dependencies: [] script: - apt update - - apt install -y curl git + - apt install -y curl git sqlite3 libsqlite3-dev - curl https://storage.googleapis.com/dart-archive/channels/stable/release/2.7.2/linux_packages/dart_2.7.2-1_amd64.deb > dart.deb - apt install -y ./dart.deb - ln -s /usr/lib/dart/bin/pub /usr/bin/ diff --git a/lib/famedlysdk.dart b/lib/famedlysdk.dart index ae5cf13..2379b21 100644 --- a/lib/famedlysdk.dart +++ b/lib/famedlysdk.dart @@ -48,6 +48,6 @@ export 'package:famedlysdk/src/event.dart'; export 'package:famedlysdk/src/presence.dart'; export 'package:famedlysdk/src/room.dart'; export 'package:famedlysdk/src/room_account_data.dart'; -export 'package:famedlysdk/src/store_api.dart'; export 'package:famedlysdk/src/timeline.dart'; export 'package:famedlysdk/src/user.dart'; +export 'package:famedlysdk/src/database/database.dart' show Database; diff --git a/lib/src/account_data.dart b/lib/src/account_data.dart index 1a0208b..7b74162 100644 --- a/lib/src/account_data.dart +++ b/lib/src/account_data.dart @@ -22,6 +22,7 @@ */ import 'package:famedlysdk/famedlysdk.dart'; +import './database/database.dart' show DbAccountData; /// The global private data created by this user. class AccountData { @@ -38,4 +39,10 @@ class AccountData { final content = Event.getMapFromPayload(jsonPayload['content']); return AccountData(content: content, typeKey: jsonPayload['type']); } + + /// Get account data from DbAccountData + factory AccountData.fromDb(DbAccountData dbEntry) { + final content = Event.getMapFromPayload(dbEntry.content); + return AccountData(content: content, typeKey: dbEntry.type); + } } diff --git a/lib/src/client.dart b/lib/src/client.dart index d8be4c6..ccea37b 100644 --- a/lib/src/client.dart +++ b/lib/src/client.dart @@ -30,7 +30,6 @@ import 'package:famedlysdk/famedlysdk.dart'; import 'package:famedlysdk/src/account_data.dart'; import 'package:famedlysdk/src/presence.dart'; import 'package:famedlysdk/src/room.dart'; -import 'package:famedlysdk/src/store_api.dart'; import 'package:famedlysdk/src/sync/user_update.dart'; import 'package:famedlysdk/src/utils/device_keys_list.dart'; import 'package:famedlysdk/src/utils/matrix_file.dart'; @@ -54,6 +53,7 @@ import 'sync/user_update.dart'; import 'user.dart'; import 'utils/matrix_exception.dart'; import 'utils/profile.dart'; +import 'database/database.dart' show Database; import 'utils/pusher.dart'; typedef RoomSorter = int Function(Room a, Room b); @@ -70,12 +70,12 @@ class Client { @deprecated Client get connection => this; - /// Optional persistent store for all data. - ExtendedStoreAPI get store => (storeAPI?.extended ?? false) ? storeAPI : null; + int _id; + int get id => _id; - StoreAPI storeAPI; + Database database; - Client(this.clientName, {this.debug = false, this.storeAPI}) { + Client(this.clientName, {this.debug = false, this.database}) { onLoginStateChanged.stream.listen((loginState) { print('LoginState: ${loginState.toString()}'); }); @@ -111,10 +111,6 @@ class Client { String get deviceName => _deviceName; String _deviceName; - /// Which version of the matrix specification does this server support? - List get matrixVersions => _matrixVersions; - List _matrixVersions; - /// Returns the current login state. bool isLogged() => accessToken != null; @@ -208,7 +204,7 @@ class Client { /// Checks the supported versions of the Matrix protocol and the supported /// login types. Returns false if the server is not compatible with the - /// client. Automatically sets [matrixVersions]. + /// client. /// Throws FormatException, TimeoutException and MatrixException on error. Future checkServer(serverUrl) async { try { @@ -226,8 +222,6 @@ class Client { } } - _matrixVersions = versions; - final loginResp = await jsonRequest(type: HTTPType.GET, action: '/client/r0/login'); @@ -243,7 +237,7 @@ class Client { } return true; } catch (_) { - _homeserver = _matrixVersions = null; + _homeserver = null; rethrow; } } @@ -292,8 +286,7 @@ class Client { newUserID: response['user_id'], newHomeserver: homeserver, newDeviceName: initialDeviceDisplayName ?? '', - newDeviceID: response['device_id'], - newMatrixVersions: matrixVersions); + newDeviceID: response['device_id']); } return response; } @@ -334,7 +327,6 @@ class Client { newHomeserver: homeserver, newDeviceName: initialDeviceDisplayName ?? '', newDeviceID: loginResp['device_id'], - newMatrixVersions: matrixVersions, ); return true; } @@ -676,20 +668,39 @@ class Client { String newUserID, String newDeviceName, String newDeviceID, - List newMatrixVersions, String newPrevBatch, String newOlmAccount, }) async { - _accessToken = newToken; - _homeserver = newHomeserver; - _userID = newUserID; - _deviceID = newDeviceID; - _deviceName = newDeviceName; - _matrixVersions = newMatrixVersions; - prevBatch = newPrevBatch; + String olmAccount; + if (database != null) { + final account = await database.getClient(clientName); + if (account != null) { + _id = account.clientId; + _homeserver = account.homeserverUrl; + _accessToken = account.token; + _userID = account.userId; + _deviceID = account.deviceId; + _deviceName = account.deviceName; + prevBatch = account.prevBatch; + olmAccount = account.olmAccount; + } + } + _accessToken = newToken ?? _accessToken; + _homeserver = newHomeserver ?? _homeserver; + _userID = newUserID ?? _userID; + _deviceID = newDeviceID ?? _deviceID; + _deviceName = newDeviceName ?? _deviceName; + prevBatch = newPrevBatch ?? prevBatch; + olmAccount = newOlmAccount ?? olmAccount; + + if (_accessToken == null || _homeserver == null || _userID == null) { + // we aren't logged in + onLoginStateChanged.add(LoginState.logged); + return; + } // Try to create a new olm account or restore a previous one. - if (newOlmAccount == null) { + if (olmAccount == null) { try { await olm.init(); _olmAccount = olm.Account(); @@ -704,39 +715,30 @@ class Client { try { await olm.init(); _olmAccount = olm.Account(); - _olmAccount.unpickle(userID, newOlmAccount); + _olmAccount.unpickle(userID, olmAccount); } catch (_) { _olmAccount = null; } } - if (storeAPI != null) { - await storeAPI.storeClient(); - _userDeviceKeys = await storeAPI.getUserDeviceKeys(); - final String olmSessionPickleString = - await storeAPI.getItem('/clients/$userID/olm-sessions'); - if (olmSessionPickleString != null) { - final Map pickleMap = - json.decode(olmSessionPickleString); - for (var entry in pickleMap.entries) { - for (String pickle in entry.value) { - _olmSessions[entry.key] = []; - try { - var session = olm.Session(); - session.unpickle(userID, pickle); - _olmSessions[entry.key].add(session); - } catch (e) { - print('[LibOlm] Could not unpickle olm session: ' + e.toString()); - } - } - } - } - if (store != null) { - _rooms = await store.getRoomList(onlyLeft: false); - _sortRooms(); - accountData = await store.getAccountData(); - presences = await store.getPresences(); + if (database != null) { + if (id != null) { + await database.updateClient( + _homeserver, _accessToken, _userID, _deviceID, + _deviceName, prevBatch, pickledOlmAccount, id, + ); + } else { + _id = await database.insertClient( + clientName, _homeserver, _accessToken, _userID, _deviceID, + _deviceName, prevBatch, pickledOlmAccount, + ); } + _userDeviceKeys = await database.getUserDeviceKeys(id); + _olmSessions = await database.getOlmSessions(id, _userID); + _rooms = await database.getRoomList(this, onlyLeft: false); + _sortRooms(); + accountData = await database.getAccountData(id); + presences = await database.getPresences(id); } _userEventSub ??= onUserEvent.stream.listen(handleUserUpdate); @@ -755,14 +757,14 @@ class Client { }); rooms.forEach((Room room) { room.clearOutboundGroupSession(wipe: true); - room.sessionKeys.values.forEach((SessionKey sessionKey) { + room.inboundGroupSessions.values.forEach((SessionKey sessionKey) { sessionKey.inboundGroupSession?.free(); }); }); _olmAccount?.free(); - storeAPI?.clear(); - _accessToken = _homeserver = - _userID = _deviceID = _deviceName = _matrixVersions = prevBatch = null; + database?.clear(id); + _id = _accessToken = _homeserver = + _userID = _deviceID = _deviceName = prevBatch = null; _rooms = []; onLoginStateChanged.add(LoginState.loggedOut); } @@ -858,6 +860,7 @@ class Client { var exception = MatrixException(resp); if (exception.error == MatrixError.M_UNKNOWN_TOKEN) { // The token is no longer valid. Need to sign off.... + // TODO: add a way to export keys prior logout? onError.add(exception); clear(); } @@ -926,14 +929,15 @@ class Client { final syncResp = await _syncRequest; if (hash != _syncRequest.hashCode) return; _timeoutFactor = 1; - if (store != null) { - await store.transaction(() { - handleSync(syncResp); - store.storePrevBatch(syncResp['next_batch']); - }); - } else { - await handleSync(syncResp); - } + final futures = handleSync(syncResp); + await database?.transaction(() async { + for (final f in futures) { + await f(); + } + if (prevBatch != syncResp['next_batch']) { + await database.storePrevBatch(syncResp['next_batch'], id); + } + }); if (prevBatch == null) { onFirstSync.add(true); prevBatch = syncResp['next_batch']; @@ -946,37 +950,39 @@ class Client { onError.add(exception); await Future.delayed(Duration(seconds: syncErrorTimeoutSec), _sync); } catch (exception) { + print('Error during processing events: ' + exception.toString()); await Future.delayed(Duration(seconds: syncErrorTimeoutSec), _sync); } } /// Use this method only for testing utilities! - void handleSync(dynamic sync) { + List Function()> handleSync(dynamic sync) { + final dbActions = Function()>[]; if (sync['to_device'] is Map && sync['to_device']['events'] is List) { _handleToDeviceEvents(sync['to_device']['events']); } if (sync['rooms'] is Map) { if (sync['rooms']['join'] is Map) { - _handleRooms(sync['rooms']['join'], Membership.join); + _handleRooms(sync['rooms']['join'], Membership.join, dbActions); } if (sync['rooms']['invite'] is Map) { - _handleRooms(sync['rooms']['invite'], Membership.invite); + _handleRooms(sync['rooms']['invite'], Membership.invite, dbActions); } if (sync['rooms']['leave'] is Map) { - _handleRooms(sync['rooms']['leave'], Membership.leave); + _handleRooms(sync['rooms']['leave'], Membership.leave, dbActions); } } if (sync['presence'] is Map && sync['presence']['events'] is List) { - _handleGlobalEvents(sync['presence']['events'], 'presence'); + _handleGlobalEvents(sync['presence']['events'], 'presence', dbActions); } if (sync['account_data'] is Map && sync['account_data']['events'] is List) { - _handleGlobalEvents(sync['account_data']['events'], 'account_data'); + _handleGlobalEvents(sync['account_data']['events'], 'account_data', dbActions); } if (sync['device_lists'] is Map) { - _handleDeviceListsEvents(sync['device_lists']); + _handleDeviceListsEvents(sync['device_lists'], dbActions); } if (sync['device_one_time_keys_count'] is Map) { _handleDeviceOneTimeKeysCount(sync['device_one_time_keys_count']); @@ -988,6 +994,7 @@ class Client { ); } onSync.add(sync); + return dbActions; } void _handleDeviceOneTimeKeysCount( @@ -1004,11 +1011,14 @@ class Client { } } - void _handleDeviceListsEvents(Map deviceLists) { + void _handleDeviceListsEvents(Map deviceLists, List Function()> dbActions) { if (deviceLists['changed'] is List) { for (final userId in deviceLists['changed']) { if (_userDeviceKeys.containsKey(userId)) { _userDeviceKeys[userId].outdated = true; + if (database != null) { + dbActions.add(() => database.storeUserDeviceKeysInfo(id, userId, true)); + } } } for (final userId in deviceLists['left']) { @@ -1046,8 +1056,8 @@ class Client { } } - void _handleRooms(Map rooms, Membership membership) { - rooms.forEach((String id, dynamic room) async { + void _handleRooms(Map rooms, Membership membership, List Function()> dbActions) { + rooms.forEach((String id, dynamic room) { // calculate the notification counts, the limitedTimeline and prevbatch num highlight_count = 0; num notification_count = 0; @@ -1089,40 +1099,55 @@ class Client { summary: summary, ); _updateRoomsByRoomUpdate(update); - unawaited(store?.storeRoomUpdate(update)); + final roomObj = getRoomById(id); + if (limitedTimeline && roomObj != null) { + roomObj.resetSortOrder(); + } + if (database != null) { + dbActions.add(() => database.storeRoomUpdate(this.id, update, getRoomById(id))); + } onRoomUpdate.add(update); + var handledEvents = false; /// Handle now all room events and save them in the database if (room['state'] is Map && - room['state']['events'] is List) { - _handleRoomEvents(id, room['state']['events'], 'state'); + room['state']['events'] is List && + room['state']['events'].isNotEmpty) { + _handleRoomEvents(id, room['state']['events'], 'state', dbActions); + handledEvents = true; } if (room['invite_state'] is Map && room['invite_state']['events'] is List) { - _handleRoomEvents(id, room['invite_state']['events'], 'invite_state'); + _handleRoomEvents(id, room['invite_state']['events'], 'invite_state', dbActions); } if (room['timeline'] is Map && - room['timeline']['events'] is List) { - _handleRoomEvents(id, room['timeline']['events'], 'timeline'); + room['timeline']['events'] is List && + room['timeline']['events'].isNotEmpty) { + _handleRoomEvents(id, room['timeline']['events'], 'timeline', dbActions); + handledEvents = true; } if (room['ephemeral'] is Map && room['ephemeral']['events'] is List) { - _handleEphemerals(id, room['ephemeral']['events']); + _handleEphemerals(id, room['ephemeral']['events'], dbActions); } if (room['account_data'] is Map && room['account_data']['events'] is List) { - _handleRoomEvents(id, room['account_data']['events'], 'account_data'); + _handleRoomEvents(id, room['account_data']['events'], 'account_data', dbActions); + } + + if (handledEvents && database != null && roomObj != null) { + dbActions.add(() => roomObj.updateSortOrder()); } }); } - void _handleEphemerals(String id, List events) { + void _handleEphemerals(String id, List events, List Function()> dbActions) { for (num i = 0; i < events.length; i++) { - _handleEvent(events[i], id, 'ephemeral'); + _handleEvent(events[i], id, 'ephemeral', dbActions); // Receipt events are deltas between two states. We will create a // fake room account data event for this and store the difference @@ -1142,12 +1167,12 @@ class Client { final mxid = userTimestampMapEntry.key; // Remove previous receipt event from this user - for (var entry in receiptStateContent.entries) { - if (entry.value['m.read'] is Map && - entry.value['m.read'].containsKey(mxid)) { - entry.value['m.read'].remove(mxid); - break; - } + if ( + receiptStateContent[eventID] is Map && + receiptStateContent[eventID]['m.read'] is Map && + receiptStateContent[eventID]['m.read'].containsKey(mxid) + ) { + receiptStateContent[eventID]['m.read'].remove(mxid); } if (userTimestampMap[mxid] is Map && userTimestampMap[mxid].containsKey('ts')) { @@ -1160,18 +1185,18 @@ class Client { } } events[i]['content'] = receiptStateContent; - _handleEvent(events[i], id, 'account_data'); + _handleEvent(events[i], id, 'account_data', dbActions); } } } - void _handleRoomEvents(String chat_id, List events, String type) { + void _handleRoomEvents(String chat_id, List events, String type, List Function()> dbActions) { for (num i = 0; i < events.length; i++) { - _handleEvent(events[i], chat_id, type); + _handleEvent(events[i], chat_id, type, dbActions); } } - void _handleGlobalEvents(List events, String type) { + void _handleGlobalEvents(List events, String type, List Function()> dbActions) { for (var i = 0; i < events.length; i++) { if (events[i]['type'] is String && events[i]['content'] is Map) { @@ -1180,42 +1205,51 @@ class Client { type: type, content: events[i], ); - store?.storeUserEventUpdate(update); + if (database != null) { + dbActions.add(() => database.storeUserEventUpdate(id, update)); + } onUserEvent.add(update); } } } - void _handleEvent(Map event, String roomID, String type) { + void _handleEvent(Map event, String roomID, String type, List Function()> dbActions) { if (event['type'] is String && event['content'] is Map) { // The client must ignore any new m.room.encryption event to prevent // man-in-the-middle attacks! - if (event['type'] == 'm.room.encryption' && - getRoomById(roomID).encrypted) { + final room = getRoomById(roomID); + if (room == null || (event['type'] == 'm.room.encryption' && + room.encrypted)) { return; } + // ephemeral events aren't persisted and don't need a sort order - they are + // expected to be processed as soon as they come in + final sortOrder = type != 'ephemeral' ? room.newSortOrder : 0.0; var update = EventUpdate( eventType: event['type'], roomID: roomID, type: type, content: event, + sortOrder: sortOrder, ); if (event['type'] == 'm.room.encrypted') { - update = update.decrypt(getRoomById(update.roomID)); + update = update.decrypt(room); + } + if (type != 'ephemeral' && database != null) { + dbActions.add(() => database.storeEventUpdate(id, update)); } - store?.storeEventUpdate(update); _updateRoomsByEventUpdate(update); onEvent.add(update); if (event['type'] == 'm.call.invite') { - onCallInvite.add(Event.fromJson(event, getRoomById(roomID))); + onCallInvite.add(Event.fromJson(event, room, sortOrder)); } else if (event['type'] == 'm.call.hangup') { - onCallHangup.add(Event.fromJson(event, getRoomById(roomID))); + onCallHangup.add(Event.fromJson(event, room, sortOrder)); } else if (event['type'] == 'm.call.answer') { - onCallAnswer.add(Event.fromJson(event, getRoomById(roomID))); + onCallAnswer.add(Event.fromJson(event, room, sortOrder)); } else if (event['type'] == 'm.call.candidates') { - onCallCandidates.add(Event.fromJson(event, getRoomById(roomID))); + onCallCandidates.add(Event.fromJson(event, room, sortOrder)); } } } @@ -1294,7 +1328,7 @@ class Client { if (eventUpdate.type == 'timeline' || eventUpdate.type == 'state' || eventUpdate.type == 'invite_state') { - var stateEvent = Event.fromJson(eventUpdate.content, rooms[j]); + var stateEvent = Event.fromJson(eventUpdate.content, rooms[j], eventUpdate.sortOrder); if (stateEvent.type == EventTypes.Redaction) { final String redacts = eventUpdate.content['redacts']; rooms[j].states.states.forEach( @@ -1337,6 +1371,7 @@ class Client { var room = getRoomById(roomId); if (room == null && addToPendingIfNotFound) { _pendingToDeviceEvents.add(toDeviceEvent); + break; } final String sessionId = toDeviceEvent.content['session_id']; if (toDeviceEvent.type == 'm.room_key' && @@ -1349,7 +1384,7 @@ class Client { .deviceKeys[toDeviceEvent.content['requesting_device_id']] .ed25519Key; } - room.setSessionKey( + room.setInboundGroupSession( sessionId, toDeviceEvent.content, forwarded: toDeviceEvent.type == 'm.forwarded_room_key', @@ -1379,7 +1414,7 @@ class Client { .containsKey(toDeviceEvent.content['requesting_device_id'])) { deviceKeys = userDeviceKeys[toDeviceEvent.sender] .deviceKeys[toDeviceEvent.content['requesting_device_id']]; - if (room.sessionKeys.containsKey(sessionId)) { + if (room.inboundGroupSessions.containsKey(sessionId)) { final roomKeyRequest = RoomKeyRequest.fromToDeviceEvent(toDeviceEvent, this); if (deviceKeys.userId == userID && @@ -1446,6 +1481,7 @@ class Client { Future _updateUserDeviceKeys() async { try { if (!isLogged()) return; + final dbActions = Function()>[]; var trackedUserIds = await _getUserIdsInEncryptedRooms(); trackedUserIds.add(userID); @@ -1481,6 +1517,7 @@ class Client { final String deviceId = rawDeviceKeyEntry.key; // Set the new device key for this device + if (!oldKeys.containsKey(deviceId)) { _userDeviceKeys[userId].deviceKeys[deviceId] = DeviceKeys.fromJson(rawDeviceKeyEntry.value); @@ -1493,11 +1530,34 @@ class Client { } else { _userDeviceKeys[userId].deviceKeys[deviceId] = oldKeys[deviceId]; } + if (database != null) { + dbActions.add(() => database.storeUserDeviceKey(id, userId, deviceId, + json.encode(_userDeviceKeys[userId].deviceKeys[deviceId].toJson()), + _userDeviceKeys[userId].deviceKeys[deviceId].verified, + _userDeviceKeys[userId].deviceKeys[deviceId].blocked, + )); + } + } + if (database != null) { + for (final oldDeviceKeyEntry in oldKeys.entries) { + final deviceId = oldDeviceKeyEntry.key; + if (!_userDeviceKeys[userId].deviceKeys.containsKey(deviceId)) { + // we need to remove an old key + dbActions.add(() => database.removeUserDeviceKey(id, userId, deviceId)); + } + } } _userDeviceKeys[userId].outdated = false; + if (database != null) { + dbActions.add(() => database.storeUserDeviceKeysInfo(id, userId, false)); + } } } - await storeAPI?.storeUserDeviceKeys(userDeviceKeys); + await database?.transaction(() async { + for (final f in dbActions) { + await f(); + } + }); rooms.forEach((Room room) { if (room.encrypted) { room.clearOutboundGroupSession(); @@ -1616,7 +1676,7 @@ class Client { oneTimeKeysCount) { return false; } - await storeAPI?.storeClient(); + await database?.updateClientKeys(pickledOlmAccount, id); lastTimeKeysUploaded = DateTime.now(); return true; } @@ -1668,7 +1728,7 @@ class Client { var newSession = olm.Session(); newSession.create_inbound_from(_olmAccount, senderKey, body); _olmAccount.remove_one_time_keys(newSession); - storeAPI?.storeClient(); + database?.updateClientKeys(pickledOlmAccount, id); plaintext = newSession.decrypt(type, body); storeOlmSession(senderKey, newSession); } @@ -1695,29 +1755,24 @@ class Client { /// A map from Curve25519 identity keys to existing olm sessions. Map> get olmSessions => _olmSessions; - final Map> _olmSessions = {}; + Map> _olmSessions = {}; void storeOlmSession(String curve25519IdentityKey, olm.Session session) { if (!_olmSessions.containsKey(curve25519IdentityKey)) { _olmSessions[curve25519IdentityKey] = []; } - if (_olmSessions[curve25519IdentityKey] - .indexWhere((s) => s.session_id() == session.session_id()) == + final ix = _olmSessions[curve25519IdentityKey] + .indexWhere((s) => s.session_id() == session.session_id()); + if (ix == -1) { + // add a new session _olmSessions[curve25519IdentityKey].add(session); + } else { + // update an existing session + _olmSessions[curve25519IdentityKey][ix] = session; } - var pickleMap = >{}; - for (var entry in olmSessions.entries) { - pickleMap[entry.key] = []; - for (var session in entry.value) { - try { - pickleMap[entry.key].add(session.pickle(userID)); - } catch (e) { - print('[LibOlm] Could not pickle olm session: ' + e.toString()); - } - } - } - storeAPI?.setItem('/clients/$userID/olm-sessions', json.encode(pickleMap)); + final pickle = session.pickle(userID); + database?.storeOlmSession(id, curve25519IdentityKey, session.session_id(), pickle); } /// Sends an encrypted [message] of this [type] to these [deviceKeys]. To send diff --git a/lib/src/database/database.dart b/lib/src/database/database.dart new file mode 100644 index 0000000..710c36e --- /dev/null +++ b/lib/src/database/database.dart @@ -0,0 +1,368 @@ +import 'package:moor/moor.dart'; +import 'dart:convert'; + +import 'package:famedlysdk/famedlysdk.dart' as sdk; +import 'package:olm/olm.dart' as olm; + +part 'database.g.dart'; + +@UseMoor( + include: {'database.moor'}, +) +class Database extends _$Database { + Database(QueryExecutor e) : super(e); + + @override + int get schemaVersion => 2; + + int get maxFileSize => 1 * 1024 * 1024; + + @override + MigrationStrategy get migration => MigrationStrategy( + onCreate: (Migrator m) { + return m.createAll(); + }, + onUpgrade: (Migrator m, int from, int to) async { + // this appears to be only called once, so multiple consecutive upgrades have to be handled appropriately in here + if (from == 1) { + await m.createIndex(userDeviceKeysIndex); + await m.createIndex(userDeviceKeysKeyIndex); + await m.createIndex(olmSessionsIndex); + await m.createIndex(outboundGroupSessionsIndex); + await m.createIndex(inboundGroupSessionsIndex); + await m.createIndex(roomsIndex); + await m.createIndex(eventsIndex); + await m.createIndex(roomStatesIndex); + await m.createIndex(accountDataIndex); + await m.createIndex(roomAccountDataIndex); + await m.createIndex(presencesIndex); + } + }, + ); + + Future getClient(String name) async { + final res = await dbGetClient(name).get(); + if (res.isEmpty) return null; + return res.first; + } + + Future> getUserDeviceKeys(int clientId) async { + final deviceKeys = await getAllUserDeviceKeys(clientId).get(); + if (deviceKeys.isEmpty) { + return {}; + } + final deviceKeysKeys = await getAllUserDeviceKeysKeys(clientId).get(); + final res = {}; + for (final entry in deviceKeys) { + res[entry.userId] = sdk.DeviceKeysList.fromDb(entry, deviceKeysKeys.where((k) => k.userId == entry.userId).toList()); + } + return res; + } + + Future>> getOlmSessions(int clientId, String userId) async { + final raw = await getAllOlmSessions(clientId).get(); + if (raw.isEmpty) { + return {}; + } + final res = >{}; + for (final row in raw) { + if (!res.containsKey(row.identityKey)) { + res[row.identityKey] = []; + } + try { + var session = olm.Session(); + session.unpickle(userId, row.pickle); + res[row.identityKey].add(session); + } catch (e) { + print('[LibOlm] Could not unpickle olm session: ' + e.toString()); + } + } + return res; + } + + Future getDbOutboundGroupSession(int clientId, String roomId) async { + final res = await dbGetOutboundGroupSession(clientId, roomId).get(); + if (res.isEmpty) { + return null; + } + return res.first; + } + + Future> getDbInboundGroupSessions(int clientId, String roomId) async { + return await dbGetInboundGroupSessionKeys(clientId, roomId).get(); + } + + Future> getRoomList(sdk.Client client, {bool onlyLeft = false}) async { + final res = await (select(rooms)..where((t) => onlyLeft + ? t.membership.equals('leave') + : t.membership.equals('leave').not())).get(); + final resStates = await getAllRoomStates(client.id).get(); + final resAccountData = await getAllRoomAccountData(client.id).get(); + final resOutboundGroupSessions = await getAllOutboundGroupSessions(client.id).get(); + final resInboundGroupSessions = await getAllInboundGroupSessions(client.id).get(); + final roomList = []; + for (final r in res) { + final outboundGroupSession = resOutboundGroupSessions.where((rs) => rs.roomId == r.roomId); + final room = await sdk.Room.getRoomFromTableRow( + r, + client, + states: resStates.where((rs) => rs.roomId == r.roomId), + roomAccountData: resAccountData.where((rs) => rs.roomId == r.roomId), + outboundGroupSession: outboundGroupSession.isEmpty ? false : outboundGroupSession.first, + inboundGroupSessions: resInboundGroupSessions.where((rs) => rs.roomId == r.roomId), + ); + roomList.add(room); + } + return roomList; + } + + Future> getAccountData(int clientId) async { + final newAccountData = {}; + final rawAccountData = await getAllAccountData(clientId).get(); + for (final d in rawAccountData) { + newAccountData[d.type] = sdk.AccountData.fromDb(d); + } + return newAccountData; + } + + Future> getPresences(int clientId) async { + final newPresences = {}; + final rawPresences = await getAllPresences(clientId).get(); + for (final d in rawPresences) { + newPresences[d.sender] = sdk.Presence.fromDb(d); + } + return newPresences; + } + + /// Stores a RoomUpdate object in the database. Must be called inside of + /// [transaction]. + final Set _ensuredRooms = {}; + Future storeRoomUpdate(int clientId, sdk.RoomUpdate roomUpdate, [sdk.Room oldRoom]) async { + final setKey = '${clientId};${roomUpdate.id}'; + if (roomUpdate.membership != sdk.Membership.leave) { + if (!_ensuredRooms.contains(setKey)) { + await ensureRoomExists(clientId, roomUpdate.id, roomUpdate.membership.toString().split('.').last); + _ensuredRooms.add(setKey); + } + } else { + _ensuredRooms.remove(setKey); + await removeRoom(clientId, roomUpdate.id); + return; + } + + var doUpdate = oldRoom == null; + if (!doUpdate) { + doUpdate = roomUpdate.highlight_count != oldRoom.highlightCount || + roomUpdate.notification_count != oldRoom.notificationCount || + roomUpdate.membership.toString().split('.').last != oldRoom.membership.toString().split('.').last || + (roomUpdate.summary?.mJoinedMemberCount != null && + roomUpdate.summary.mJoinedMemberCount != oldRoom.mInvitedMemberCount) || + (roomUpdate.summary?.mInvitedMemberCount != null && + roomUpdate.summary.mJoinedMemberCount != oldRoom.mJoinedMemberCount) || + (roomUpdate.summary?.mHeroes != null && + roomUpdate.summary.mHeroes.join(',') != oldRoom.mHeroes.join(',')); + } + + if (doUpdate) { + await (update(rooms)..where((r) => r.roomId.equals(roomUpdate.id) & r.clientId.equals(clientId))).write(RoomsCompanion( + highlightCount: Value(roomUpdate.highlight_count), + notificationCount: Value(roomUpdate.notification_count), + membership: Value(roomUpdate.membership.toString().split('.').last), + joinedMemberCount: roomUpdate.summary?.mJoinedMemberCount != null ? Value(roomUpdate.summary.mJoinedMemberCount) : Value.absent(), + invitedMemberCount: roomUpdate.summary?.mInvitedMemberCount != null ? Value(roomUpdate.summary.mInvitedMemberCount) : Value.absent(), + heroes: roomUpdate.summary?.mHeroes != null ? Value(roomUpdate.summary.mHeroes.join(',')) : Value.absent(), + )); + } + + // Is the timeline limited? Then all previous messages should be + // removed from the database! + if (roomUpdate.limitedTimeline) { + await removeRoomEvents(clientId, roomUpdate.id); + await updateRoomSortOrder(0.0, 0.0, clientId, roomUpdate.id); + await setRoomPrevBatch(roomUpdate.prev_batch, clientId, roomUpdate.id); + } + } + + /// Stores an UserUpdate object in the database. Must be called inside of + /// [transaction]. + Future storeUserEventUpdate(int clientId, sdk.UserUpdate userUpdate) async { + if (userUpdate.type == 'account_data') { + await storeAccountData(clientId, userUpdate.eventType, json.encode(userUpdate.content['content'])); + } else if (userUpdate.type == 'presence') { + await storePresence(clientId, userUpdate.eventType, userUpdate.content['sender'], json.encode(userUpdate.content['content'])); + } + } + + /// Stores an EventUpdate object in the database. Must be called inside of + /// [transaction]. + Future storeEventUpdate(int clientId, sdk.EventUpdate eventUpdate) async { + if (eventUpdate.type == 'ephemeral') return; + final eventContent = eventUpdate.content; + final type = eventUpdate.type; + final chatId = eventUpdate.roomID; + + // Get the state_key for state events + var stateKey = ''; + if (eventContent['state_key'] is String) { + stateKey = eventContent['state_key']; + } + + if (eventUpdate.eventType == 'm.room.redaction') { + await redactMessage(clientId, eventUpdate); + } + + if (type == 'timeline' || type == 'history') { + // calculate the status + var status = 2; + if (eventContent['status'] is num) status = eventContent['status']; + if ((status == 1 || status == -1) && + eventContent['unsigned'] is Map && + eventContent['unsigned']['transaction_id'] is String) { + // status changed and we have an old transaction id --> update event id and stuffs + await updateEventStatus(status, eventContent['event_id'], clientId, eventContent['unsigned']['transaction_id'], chatId); + } else { + DbEvent oldEvent; + if (type == 'history') { + final allOldEvents = await getEvent(clientId, eventContent['event_id'], chatId).get(); + if (allOldEvents.isNotEmpty) { + oldEvent = allOldEvents.first; + } + } + await storeEvent( + clientId, + eventContent['event_id'], + chatId, + oldEvent?.sortOrder ?? eventUpdate.sortOrder, + eventContent['origin_server_ts'] != null ? DateTime.fromMillisecondsSinceEpoch(eventContent['origin_server_ts']) : DateTime.now(), + eventContent['sender'], + eventContent['type'], + json.encode(eventContent['unsigned'] ?? ''), + json.encode(eventContent['content']), + json.encode(eventContent['prevContent']), + eventContent['state_key'], + status, + ); + } + + // is there a transaction id? Then delete the event with this id. + if (status != -1 && + eventUpdate.content.containsKey('unsigned') && + eventUpdate.content['unsigned']['transaction_id'] is String) { + await removeEvent(clientId, eventUpdate.content['unsigned']['transaction_id'], chatId); + } + } + + if (type == 'history') return; + + if (type != 'account_data') { + final now = DateTime.now(); + await storeRoomState( + clientId, + eventContent['event_id'] ?? now.millisecondsSinceEpoch.toString(), + chatId, + eventUpdate.sortOrder ?? 0.0, + eventContent['origin_server_ts'] != null ? DateTime.fromMillisecondsSinceEpoch(eventContent['origin_server_ts']) : now, + eventContent['sender'], + eventContent['type'], + json.encode(eventContent['unsigned'] ?? ''), + json.encode(eventContent['content']), + json.encode(eventContent['prev_content'] ?? ''), + stateKey, + ); + } else if (type == 'account_data') { + await storeRoomAccountData( + clientId, + eventContent['type'], + chatId, + json.encode(eventContent['content']), + ); + } + } + + Future getEventById(int clientId, String eventId, sdk.Room room) async { + final event = await getEvent(clientId, eventId, room.id).get(); + if (event.isEmpty) { + return null; + } + return sdk.Event.fromDb(event.first, room); + } + + Future redactMessage(int clientId, sdk.EventUpdate eventUpdate) async { + final events = await getEvent(clientId, eventUpdate.content['redacts'], eventUpdate.roomID).get(); + var success = false; + for (final dbEvent in events) { + final event = sdk.Event.fromDb(dbEvent, null); + event.setRedactionEvent(sdk.Event.fromJson(eventUpdate.content, null)); + final changes1 = await updateEvent( + json.encode(event.unsigned ?? ''), + json.encode(event.content ?? ''), + json.encode(event.prevContent ?? ''), + clientId, + event.eventId, + eventUpdate.roomID, + ); + final changes2 = await updateEvent( + json.encode(event.unsigned ?? ''), + json.encode(event.content ?? ''), + json.encode(event.prevContent ?? ''), + clientId, + event.eventId, + eventUpdate.roomID, + ); + if (changes1 == 1 && changes2 == 1) success = true; + } + return success; + } + + Future forgetRoom(int clientId, String roomId) async { + final setKey = '${clientId};${roomId}'; + _ensuredRooms.remove(setKey); + await (delete(rooms)..where((r) => r.roomId.equals(roomId) & r.clientId.equals(clientId))).go(); + await (delete(events)..where((r) => r.roomId.equals(roomId) & r.clientId.equals(clientId))).go(); + await (delete(roomStates)..where((r) => r.roomId.equals(roomId) & r.clientId.equals(clientId))).go(); + await (delete(roomAccountData)..where((r) => r.roomId.equals(roomId) & r.clientId.equals(clientId))).go(); + } + + Future clearCache(int clientId) async { + await (delete(presences)..where((r) => r.clientId.equals(clientId))).go(); + await (delete(roomAccountData)..where((r) => r.clientId.equals(clientId))).go(); + await (delete(accountData)..where((r) => r.clientId.equals(clientId))).go(); + await (delete(roomStates)..where((r) => r.clientId.equals(clientId))).go(); + await (delete(events)..where((r) => r.clientId.equals(clientId))).go(); + await (delete(rooms)..where((r) => r.clientId.equals(clientId))).go(); + await (delete(outboundGroupSessions)..where((r) => r.clientId.equals(clientId))).go(); + await storePrevBatch(null, clientId); + } + + Future clear(int clientId) async { + await clearCache(clientId); + await (delete(inboundGroupSessions)..where((r) => r.clientId.equals(clientId))).go(); + await (delete(olmSessions)..where((r) => r.clientId.equals(clientId))).go(); + await (delete(userDeviceKeysKey)..where((r) => r.clientId.equals(clientId))).go(); + await (delete(userDeviceKeys)..where((r) => r.clientId.equals(clientId))).go(); + await (delete(clients)..where((r) => r.clientId.equals(clientId))).go(); + } + + Future getUser(int clientId, String userId, sdk.Room room) async { + final res = await dbGetUser(clientId, userId, room.id).get(); + if (res.isEmpty) { + return null; + } + return sdk.Event.fromDb(res.first, room).asUser; + } + + Future> getEventList(int clientId, sdk.Room room) async { + final res = await dbGetEventList(clientId, room.id).get(); + final eventList = []; + for (final r in res) { + eventList.add(sdk.Event.fromDb(r, room)); + } + return eventList; + } + + Future getFile(String mxcUri) async { + final res = await dbGetFile(mxcUri).get(); + if (res.isEmpty) return null; + return res.first.bytes; + } +} diff --git a/lib/src/database/database.g.dart b/lib/src/database/database.g.dart new file mode 100644 index 0000000..b87edcf --- /dev/null +++ b/lib/src/database/database.g.dart @@ -0,0 +1,5364 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND + +part of 'database.dart'; + +// ************************************************************************** +// MoorGenerator +// ************************************************************************** + +// ignore_for_file: unnecessary_brace_in_string_interps, unnecessary_this +class DbClient extends DataClass implements Insertable { + final int clientId; + final String name; + final String homeserverUrl; + final String token; + final String userId; + final String deviceId; + final String deviceName; + final String prevBatch; + final String olmAccount; + DbClient( + {@required this.clientId, + @required this.name, + @required this.homeserverUrl, + @required this.token, + @required this.userId, + this.deviceId, + this.deviceName, + this.prevBatch, + this.olmAccount}); + factory DbClient.fromData(Map data, GeneratedDatabase db, + {String prefix}) { + final effectivePrefix = prefix ?? ''; + final intType = db.typeSystem.forDartType(); + final stringType = db.typeSystem.forDartType(); + return DbClient( + clientId: + intType.mapFromDatabaseResponse(data['${effectivePrefix}client_id']), + name: stringType.mapFromDatabaseResponse(data['${effectivePrefix}name']), + homeserverUrl: stringType + .mapFromDatabaseResponse(data['${effectivePrefix}homeserver_url']), + token: + stringType.mapFromDatabaseResponse(data['${effectivePrefix}token']), + userId: + stringType.mapFromDatabaseResponse(data['${effectivePrefix}user_id']), + deviceId: stringType + .mapFromDatabaseResponse(data['${effectivePrefix}device_id']), + deviceName: stringType + .mapFromDatabaseResponse(data['${effectivePrefix}device_name']), + prevBatch: stringType + .mapFromDatabaseResponse(data['${effectivePrefix}prev_batch']), + olmAccount: stringType + .mapFromDatabaseResponse(data['${effectivePrefix}olm_account']), + ); + } + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (!nullToAbsent || clientId != null) { + map['client_id'] = Variable(clientId); + } + if (!nullToAbsent || name != null) { + map['name'] = Variable(name); + } + if (!nullToAbsent || homeserverUrl != null) { + map['homeserver_url'] = Variable(homeserverUrl); + } + if (!nullToAbsent || token != null) { + map['token'] = Variable(token); + } + if (!nullToAbsent || userId != null) { + map['user_id'] = Variable(userId); + } + if (!nullToAbsent || deviceId != null) { + map['device_id'] = Variable(deviceId); + } + if (!nullToAbsent || deviceName != null) { + map['device_name'] = Variable(deviceName); + } + if (!nullToAbsent || prevBatch != null) { + map['prev_batch'] = Variable(prevBatch); + } + if (!nullToAbsent || olmAccount != null) { + map['olm_account'] = Variable(olmAccount); + } + return map; + } + + factory DbClient.fromJson(Map json, + {ValueSerializer serializer}) { + serializer ??= moorRuntimeOptions.defaultSerializer; + return DbClient( + clientId: serializer.fromJson(json['client_id']), + name: serializer.fromJson(json['name']), + homeserverUrl: serializer.fromJson(json['homeserver_url']), + token: serializer.fromJson(json['token']), + userId: serializer.fromJson(json['user_id']), + deviceId: serializer.fromJson(json['device_id']), + deviceName: serializer.fromJson(json['device_name']), + prevBatch: serializer.fromJson(json['prev_batch']), + olmAccount: serializer.fromJson(json['olm_account']), + ); + } + @override + Map toJson({ValueSerializer serializer}) { + serializer ??= moorRuntimeOptions.defaultSerializer; + return { + 'client_id': serializer.toJson(clientId), + 'name': serializer.toJson(name), + 'homeserver_url': serializer.toJson(homeserverUrl), + 'token': serializer.toJson(token), + 'user_id': serializer.toJson(userId), + 'device_id': serializer.toJson(deviceId), + 'device_name': serializer.toJson(deviceName), + 'prev_batch': serializer.toJson(prevBatch), + 'olm_account': serializer.toJson(olmAccount), + }; + } + + DbClient copyWith( + {int clientId, + String name, + String homeserverUrl, + String token, + String userId, + String deviceId, + String deviceName, + String prevBatch, + String olmAccount}) => + DbClient( + clientId: clientId ?? this.clientId, + name: name ?? this.name, + homeserverUrl: homeserverUrl ?? this.homeserverUrl, + token: token ?? this.token, + userId: userId ?? this.userId, + deviceId: deviceId ?? this.deviceId, + deviceName: deviceName ?? this.deviceName, + prevBatch: prevBatch ?? this.prevBatch, + olmAccount: olmAccount ?? this.olmAccount, + ); + @override + String toString() { + return (StringBuffer('DbClient(') + ..write('clientId: $clientId, ') + ..write('name: $name, ') + ..write('homeserverUrl: $homeserverUrl, ') + ..write('token: $token, ') + ..write('userId: $userId, ') + ..write('deviceId: $deviceId, ') + ..write('deviceName: $deviceName, ') + ..write('prevBatch: $prevBatch, ') + ..write('olmAccount: $olmAccount') + ..write(')')) + .toString(); + } + + @override + int get hashCode => $mrjf($mrjc( + clientId.hashCode, + $mrjc( + name.hashCode, + $mrjc( + homeserverUrl.hashCode, + $mrjc( + token.hashCode, + $mrjc( + userId.hashCode, + $mrjc( + deviceId.hashCode, + $mrjc( + deviceName.hashCode, + $mrjc(prevBatch.hashCode, + olmAccount.hashCode))))))))); + @override + bool operator ==(dynamic other) => + identical(this, other) || + (other is DbClient && + other.clientId == this.clientId && + other.name == this.name && + other.homeserverUrl == this.homeserverUrl && + other.token == this.token && + other.userId == this.userId && + other.deviceId == this.deviceId && + other.deviceName == this.deviceName && + other.prevBatch == this.prevBatch && + other.olmAccount == this.olmAccount); +} + +class ClientsCompanion extends UpdateCompanion { + final Value clientId; + final Value name; + final Value homeserverUrl; + final Value token; + final Value userId; + final Value deviceId; + final Value deviceName; + final Value prevBatch; + final Value olmAccount; + const ClientsCompanion({ + this.clientId = const Value.absent(), + this.name = const Value.absent(), + this.homeserverUrl = const Value.absent(), + this.token = const Value.absent(), + this.userId = const Value.absent(), + this.deviceId = const Value.absent(), + this.deviceName = const Value.absent(), + this.prevBatch = const Value.absent(), + this.olmAccount = const Value.absent(), + }); + ClientsCompanion.insert({ + this.clientId = const Value.absent(), + @required String name, + @required String homeserverUrl, + @required String token, + @required String userId, + this.deviceId = const Value.absent(), + this.deviceName = const Value.absent(), + this.prevBatch = const Value.absent(), + this.olmAccount = const Value.absent(), + }) : name = Value(name), + homeserverUrl = Value(homeserverUrl), + token = Value(token), + userId = Value(userId); + static Insertable custom({ + Expression clientId, + Expression name, + Expression homeserverUrl, + Expression token, + Expression userId, + Expression deviceId, + Expression deviceName, + Expression prevBatch, + Expression olmAccount, + }) { + return RawValuesInsertable({ + if (clientId != null) 'client_id': clientId, + if (name != null) 'name': name, + if (homeserverUrl != null) 'homeserver_url': homeserverUrl, + if (token != null) 'token': token, + if (userId != null) 'user_id': userId, + if (deviceId != null) 'device_id': deviceId, + if (deviceName != null) 'device_name': deviceName, + if (prevBatch != null) 'prev_batch': prevBatch, + if (olmAccount != null) 'olm_account': olmAccount, + }); + } + + ClientsCompanion copyWith( + {Value clientId, + Value name, + Value homeserverUrl, + Value token, + Value userId, + Value deviceId, + Value deviceName, + Value prevBatch, + Value olmAccount}) { + return ClientsCompanion( + clientId: clientId ?? this.clientId, + name: name ?? this.name, + homeserverUrl: homeserverUrl ?? this.homeserverUrl, + token: token ?? this.token, + userId: userId ?? this.userId, + deviceId: deviceId ?? this.deviceId, + deviceName: deviceName ?? this.deviceName, + prevBatch: prevBatch ?? this.prevBatch, + olmAccount: olmAccount ?? this.olmAccount, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (clientId.present) { + map['client_id'] = Variable(clientId.value); + } + if (name.present) { + map['name'] = Variable(name.value); + } + if (homeserverUrl.present) { + map['homeserver_url'] = Variable(homeserverUrl.value); + } + if (token.present) { + map['token'] = Variable(token.value); + } + if (userId.present) { + map['user_id'] = Variable(userId.value); + } + if (deviceId.present) { + map['device_id'] = Variable(deviceId.value); + } + if (deviceName.present) { + map['device_name'] = Variable(deviceName.value); + } + if (prevBatch.present) { + map['prev_batch'] = Variable(prevBatch.value); + } + if (olmAccount.present) { + map['olm_account'] = Variable(olmAccount.value); + } + return map; + } +} + +class Clients extends Table with TableInfo { + final GeneratedDatabase _db; + final String _alias; + Clients(this._db, [this._alias]); + final VerificationMeta _clientIdMeta = const VerificationMeta('clientId'); + GeneratedIntColumn _clientId; + GeneratedIntColumn get clientId => _clientId ??= _constructClientId(); + GeneratedIntColumn _constructClientId() { + return GeneratedIntColumn('client_id', $tableName, false, + declaredAsPrimaryKey: true, + hasAutoIncrement: true, + $customConstraints: 'NOT NULL PRIMARY KEY AUTOINCREMENT'); + } + + final VerificationMeta _nameMeta = const VerificationMeta('name'); + GeneratedTextColumn _name; + GeneratedTextColumn get name => _name ??= _constructName(); + GeneratedTextColumn _constructName() { + return GeneratedTextColumn('name', $tableName, false, + $customConstraints: 'NOT NULL'); + } + + final VerificationMeta _homeserverUrlMeta = + const VerificationMeta('homeserverUrl'); + GeneratedTextColumn _homeserverUrl; + GeneratedTextColumn get homeserverUrl => + _homeserverUrl ??= _constructHomeserverUrl(); + GeneratedTextColumn _constructHomeserverUrl() { + return GeneratedTextColumn('homeserver_url', $tableName, false, + $customConstraints: 'NOT NULL'); + } + + final VerificationMeta _tokenMeta = const VerificationMeta('token'); + GeneratedTextColumn _token; + GeneratedTextColumn get token => _token ??= _constructToken(); + GeneratedTextColumn _constructToken() { + return GeneratedTextColumn('token', $tableName, false, + $customConstraints: 'NOT NULL'); + } + + final VerificationMeta _userIdMeta = const VerificationMeta('userId'); + GeneratedTextColumn _userId; + GeneratedTextColumn get userId => _userId ??= _constructUserId(); + GeneratedTextColumn _constructUserId() { + return GeneratedTextColumn('user_id', $tableName, false, + $customConstraints: 'NOT NULL'); + } + + final VerificationMeta _deviceIdMeta = const VerificationMeta('deviceId'); + GeneratedTextColumn _deviceId; + GeneratedTextColumn get deviceId => _deviceId ??= _constructDeviceId(); + GeneratedTextColumn _constructDeviceId() { + return GeneratedTextColumn('device_id', $tableName, true, + $customConstraints: ''); + } + + final VerificationMeta _deviceNameMeta = const VerificationMeta('deviceName'); + GeneratedTextColumn _deviceName; + GeneratedTextColumn get deviceName => _deviceName ??= _constructDeviceName(); + GeneratedTextColumn _constructDeviceName() { + return GeneratedTextColumn('device_name', $tableName, true, + $customConstraints: ''); + } + + final VerificationMeta _prevBatchMeta = const VerificationMeta('prevBatch'); + GeneratedTextColumn _prevBatch; + GeneratedTextColumn get prevBatch => _prevBatch ??= _constructPrevBatch(); + GeneratedTextColumn _constructPrevBatch() { + return GeneratedTextColumn('prev_batch', $tableName, true, + $customConstraints: ''); + } + + final VerificationMeta _olmAccountMeta = const VerificationMeta('olmAccount'); + GeneratedTextColumn _olmAccount; + GeneratedTextColumn get olmAccount => _olmAccount ??= _constructOlmAccount(); + GeneratedTextColumn _constructOlmAccount() { + return GeneratedTextColumn('olm_account', $tableName, true, + $customConstraints: ''); + } + + @override + List get $columns => [ + clientId, + name, + homeserverUrl, + token, + userId, + deviceId, + deviceName, + prevBatch, + olmAccount + ]; + @override + Clients get asDslTable => this; + @override + String get $tableName => _alias ?? 'clients'; + @override + final String actualTableName = 'clients'; + @override + VerificationContext validateIntegrity(Insertable instance, + {bool isInserting = false}) { + final context = VerificationContext(); + final data = instance.toColumns(true); + if (data.containsKey('client_id')) { + context.handle(_clientIdMeta, + clientId.isAcceptableOrUnknown(data['client_id'], _clientIdMeta)); + } + if (data.containsKey('name')) { + context.handle( + _nameMeta, name.isAcceptableOrUnknown(data['name'], _nameMeta)); + } else if (isInserting) { + context.missing(_nameMeta); + } + if (data.containsKey('homeserver_url')) { + context.handle( + _homeserverUrlMeta, + homeserverUrl.isAcceptableOrUnknown( + data['homeserver_url'], _homeserverUrlMeta)); + } else if (isInserting) { + context.missing(_homeserverUrlMeta); + } + if (data.containsKey('token')) { + context.handle( + _tokenMeta, token.isAcceptableOrUnknown(data['token'], _tokenMeta)); + } else if (isInserting) { + context.missing(_tokenMeta); + } + if (data.containsKey('user_id')) { + context.handle(_userIdMeta, + userId.isAcceptableOrUnknown(data['user_id'], _userIdMeta)); + } else if (isInserting) { + context.missing(_userIdMeta); + } + if (data.containsKey('device_id')) { + context.handle(_deviceIdMeta, + deviceId.isAcceptableOrUnknown(data['device_id'], _deviceIdMeta)); + } + if (data.containsKey('device_name')) { + context.handle( + _deviceNameMeta, + deviceName.isAcceptableOrUnknown( + data['device_name'], _deviceNameMeta)); + } + if (data.containsKey('prev_batch')) { + context.handle(_prevBatchMeta, + prevBatch.isAcceptableOrUnknown(data['prev_batch'], _prevBatchMeta)); + } + if (data.containsKey('olm_account')) { + context.handle( + _olmAccountMeta, + olmAccount.isAcceptableOrUnknown( + data['olm_account'], _olmAccountMeta)); + } + return context; + } + + @override + Set get $primaryKey => {clientId}; + @override + DbClient map(Map data, {String tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : null; + return DbClient.fromData(data, _db, prefix: effectivePrefix); + } + + @override + Clients createAlias(String alias) { + return Clients(_db, alias); + } + + @override + List get customConstraints => const ['UNIQUE(name)']; + @override + bool get dontWriteConstraints => true; +} + +class DbUserDeviceKey extends DataClass implements Insertable { + final int clientId; + final String userId; + final bool outdated; + DbUserDeviceKey( + {@required this.clientId, @required this.userId, this.outdated}); + factory DbUserDeviceKey.fromData( + Map data, GeneratedDatabase db, + {String prefix}) { + final effectivePrefix = prefix ?? ''; + final intType = db.typeSystem.forDartType(); + final stringType = db.typeSystem.forDartType(); + final boolType = db.typeSystem.forDartType(); + return DbUserDeviceKey( + clientId: + intType.mapFromDatabaseResponse(data['${effectivePrefix}client_id']), + userId: + stringType.mapFromDatabaseResponse(data['${effectivePrefix}user_id']), + outdated: + boolType.mapFromDatabaseResponse(data['${effectivePrefix}outdated']), + ); + } + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (!nullToAbsent || clientId != null) { + map['client_id'] = Variable(clientId); + } + if (!nullToAbsent || userId != null) { + map['user_id'] = Variable(userId); + } + if (!nullToAbsent || outdated != null) { + map['outdated'] = Variable(outdated); + } + return map; + } + + factory DbUserDeviceKey.fromJson(Map json, + {ValueSerializer serializer}) { + serializer ??= moorRuntimeOptions.defaultSerializer; + return DbUserDeviceKey( + clientId: serializer.fromJson(json['client_id']), + userId: serializer.fromJson(json['user_id']), + outdated: serializer.fromJson(json['outdated']), + ); + } + @override + Map toJson({ValueSerializer serializer}) { + serializer ??= moorRuntimeOptions.defaultSerializer; + return { + 'client_id': serializer.toJson(clientId), + 'user_id': serializer.toJson(userId), + 'outdated': serializer.toJson(outdated), + }; + } + + DbUserDeviceKey copyWith({int clientId, String userId, bool outdated}) => + DbUserDeviceKey( + clientId: clientId ?? this.clientId, + userId: userId ?? this.userId, + outdated: outdated ?? this.outdated, + ); + @override + String toString() { + return (StringBuffer('DbUserDeviceKey(') + ..write('clientId: $clientId, ') + ..write('userId: $userId, ') + ..write('outdated: $outdated') + ..write(')')) + .toString(); + } + + @override + int get hashCode => $mrjf( + $mrjc(clientId.hashCode, $mrjc(userId.hashCode, outdated.hashCode))); + @override + bool operator ==(dynamic other) => + identical(this, other) || + (other is DbUserDeviceKey && + other.clientId == this.clientId && + other.userId == this.userId && + other.outdated == this.outdated); +} + +class UserDeviceKeysCompanion extends UpdateCompanion { + final Value clientId; + final Value userId; + final Value outdated; + const UserDeviceKeysCompanion({ + this.clientId = const Value.absent(), + this.userId = const Value.absent(), + this.outdated = const Value.absent(), + }); + UserDeviceKeysCompanion.insert({ + @required int clientId, + @required String userId, + this.outdated = const Value.absent(), + }) : clientId = Value(clientId), + userId = Value(userId); + static Insertable custom({ + Expression clientId, + Expression userId, + Expression outdated, + }) { + return RawValuesInsertable({ + if (clientId != null) 'client_id': clientId, + if (userId != null) 'user_id': userId, + if (outdated != null) 'outdated': outdated, + }); + } + + UserDeviceKeysCompanion copyWith( + {Value clientId, Value userId, Value outdated}) { + return UserDeviceKeysCompanion( + clientId: clientId ?? this.clientId, + userId: userId ?? this.userId, + outdated: outdated ?? this.outdated, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (clientId.present) { + map['client_id'] = Variable(clientId.value); + } + if (userId.present) { + map['user_id'] = Variable(userId.value); + } + if (outdated.present) { + map['outdated'] = Variable(outdated.value); + } + return map; + } +} + +class UserDeviceKeys extends Table + with TableInfo { + final GeneratedDatabase _db; + final String _alias; + UserDeviceKeys(this._db, [this._alias]); + final VerificationMeta _clientIdMeta = const VerificationMeta('clientId'); + GeneratedIntColumn _clientId; + GeneratedIntColumn get clientId => _clientId ??= _constructClientId(); + GeneratedIntColumn _constructClientId() { + return GeneratedIntColumn('client_id', $tableName, false, + $customConstraints: 'NOT NULL REFERENCES clients(client_id)'); + } + + final VerificationMeta _userIdMeta = const VerificationMeta('userId'); + GeneratedTextColumn _userId; + GeneratedTextColumn get userId => _userId ??= _constructUserId(); + GeneratedTextColumn _constructUserId() { + return GeneratedTextColumn('user_id', $tableName, false, + $customConstraints: 'NOT NULL'); + } + + final VerificationMeta _outdatedMeta = const VerificationMeta('outdated'); + GeneratedBoolColumn _outdated; + GeneratedBoolColumn get outdated => _outdated ??= _constructOutdated(); + GeneratedBoolColumn _constructOutdated() { + return GeneratedBoolColumn('outdated', $tableName, true, + $customConstraints: 'DEFAULT true', + defaultValue: const CustomExpression('true')); + } + + @override + List get $columns => [clientId, userId, outdated]; + @override + UserDeviceKeys get asDslTable => this; + @override + String get $tableName => _alias ?? 'user_device_keys'; + @override + final String actualTableName = 'user_device_keys'; + @override + VerificationContext validateIntegrity(Insertable instance, + {bool isInserting = false}) { + final context = VerificationContext(); + final data = instance.toColumns(true); + if (data.containsKey('client_id')) { + context.handle(_clientIdMeta, + clientId.isAcceptableOrUnknown(data['client_id'], _clientIdMeta)); + } else if (isInserting) { + context.missing(_clientIdMeta); + } + if (data.containsKey('user_id')) { + context.handle(_userIdMeta, + userId.isAcceptableOrUnknown(data['user_id'], _userIdMeta)); + } else if (isInserting) { + context.missing(_userIdMeta); + } + if (data.containsKey('outdated')) { + context.handle(_outdatedMeta, + outdated.isAcceptableOrUnknown(data['outdated'], _outdatedMeta)); + } + return context; + } + + @override + Set get $primaryKey => {}; + @override + DbUserDeviceKey map(Map data, {String tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : null; + return DbUserDeviceKey.fromData(data, _db, prefix: effectivePrefix); + } + + @override + UserDeviceKeys createAlias(String alias) { + return UserDeviceKeys(_db, alias); + } + + @override + List get customConstraints => const ['UNIQUE(client_id, user_id)']; + @override + bool get dontWriteConstraints => true; +} + +class DbUserDeviceKeysKey extends DataClass + implements Insertable { + final int clientId; + final String userId; + final String deviceId; + final String content; + final bool verified; + final bool blocked; + DbUserDeviceKeysKey( + {@required this.clientId, + @required this.userId, + @required this.deviceId, + @required this.content, + this.verified, + this.blocked}); + factory DbUserDeviceKeysKey.fromData( + Map data, GeneratedDatabase db, + {String prefix}) { + final effectivePrefix = prefix ?? ''; + final intType = db.typeSystem.forDartType(); + final stringType = db.typeSystem.forDartType(); + final boolType = db.typeSystem.forDartType(); + return DbUserDeviceKeysKey( + clientId: + intType.mapFromDatabaseResponse(data['${effectivePrefix}client_id']), + userId: + stringType.mapFromDatabaseResponse(data['${effectivePrefix}user_id']), + deviceId: stringType + .mapFromDatabaseResponse(data['${effectivePrefix}device_id']), + content: + stringType.mapFromDatabaseResponse(data['${effectivePrefix}content']), + verified: + boolType.mapFromDatabaseResponse(data['${effectivePrefix}verified']), + blocked: + boolType.mapFromDatabaseResponse(data['${effectivePrefix}blocked']), + ); + } + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (!nullToAbsent || clientId != null) { + map['client_id'] = Variable(clientId); + } + if (!nullToAbsent || userId != null) { + map['user_id'] = Variable(userId); + } + if (!nullToAbsent || deviceId != null) { + map['device_id'] = Variable(deviceId); + } + if (!nullToAbsent || content != null) { + map['content'] = Variable(content); + } + if (!nullToAbsent || verified != null) { + map['verified'] = Variable(verified); + } + if (!nullToAbsent || blocked != null) { + map['blocked'] = Variable(blocked); + } + return map; + } + + factory DbUserDeviceKeysKey.fromJson(Map json, + {ValueSerializer serializer}) { + serializer ??= moorRuntimeOptions.defaultSerializer; + return DbUserDeviceKeysKey( + clientId: serializer.fromJson(json['client_id']), + userId: serializer.fromJson(json['user_id']), + deviceId: serializer.fromJson(json['device_id']), + content: serializer.fromJson(json['content']), + verified: serializer.fromJson(json['verified']), + blocked: serializer.fromJson(json['blocked']), + ); + } + @override + Map toJson({ValueSerializer serializer}) { + serializer ??= moorRuntimeOptions.defaultSerializer; + return { + 'client_id': serializer.toJson(clientId), + 'user_id': serializer.toJson(userId), + 'device_id': serializer.toJson(deviceId), + 'content': serializer.toJson(content), + 'verified': serializer.toJson(verified), + 'blocked': serializer.toJson(blocked), + }; + } + + DbUserDeviceKeysKey copyWith( + {int clientId, + String userId, + String deviceId, + String content, + bool verified, + bool blocked}) => + DbUserDeviceKeysKey( + clientId: clientId ?? this.clientId, + userId: userId ?? this.userId, + deviceId: deviceId ?? this.deviceId, + content: content ?? this.content, + verified: verified ?? this.verified, + blocked: blocked ?? this.blocked, + ); + @override + String toString() { + return (StringBuffer('DbUserDeviceKeysKey(') + ..write('clientId: $clientId, ') + ..write('userId: $userId, ') + ..write('deviceId: $deviceId, ') + ..write('content: $content, ') + ..write('verified: $verified, ') + ..write('blocked: $blocked') + ..write(')')) + .toString(); + } + + @override + int get hashCode => $mrjf($mrjc( + clientId.hashCode, + $mrjc( + userId.hashCode, + $mrjc( + deviceId.hashCode, + $mrjc(content.hashCode, + $mrjc(verified.hashCode, blocked.hashCode)))))); + @override + bool operator ==(dynamic other) => + identical(this, other) || + (other is DbUserDeviceKeysKey && + other.clientId == this.clientId && + other.userId == this.userId && + other.deviceId == this.deviceId && + other.content == this.content && + other.verified == this.verified && + other.blocked == this.blocked); +} + +class UserDeviceKeysKeyCompanion extends UpdateCompanion { + final Value clientId; + final Value userId; + final Value deviceId; + final Value content; + final Value verified; + final Value blocked; + const UserDeviceKeysKeyCompanion({ + this.clientId = const Value.absent(), + this.userId = const Value.absent(), + this.deviceId = const Value.absent(), + this.content = const Value.absent(), + this.verified = const Value.absent(), + this.blocked = const Value.absent(), + }); + UserDeviceKeysKeyCompanion.insert({ + @required int clientId, + @required String userId, + @required String deviceId, + @required String content, + this.verified = const Value.absent(), + this.blocked = const Value.absent(), + }) : clientId = Value(clientId), + userId = Value(userId), + deviceId = Value(deviceId), + content = Value(content); + static Insertable custom({ + Expression clientId, + Expression userId, + Expression deviceId, + Expression content, + Expression verified, + Expression blocked, + }) { + return RawValuesInsertable({ + if (clientId != null) 'client_id': clientId, + if (userId != null) 'user_id': userId, + if (deviceId != null) 'device_id': deviceId, + if (content != null) 'content': content, + if (verified != null) 'verified': verified, + if (blocked != null) 'blocked': blocked, + }); + } + + UserDeviceKeysKeyCompanion copyWith( + {Value clientId, + Value userId, + Value deviceId, + Value content, + Value verified, + Value blocked}) { + return UserDeviceKeysKeyCompanion( + clientId: clientId ?? this.clientId, + userId: userId ?? this.userId, + deviceId: deviceId ?? this.deviceId, + content: content ?? this.content, + verified: verified ?? this.verified, + blocked: blocked ?? this.blocked, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (clientId.present) { + map['client_id'] = Variable(clientId.value); + } + if (userId.present) { + map['user_id'] = Variable(userId.value); + } + if (deviceId.present) { + map['device_id'] = Variable(deviceId.value); + } + if (content.present) { + map['content'] = Variable(content.value); + } + if (verified.present) { + map['verified'] = Variable(verified.value); + } + if (blocked.present) { + map['blocked'] = Variable(blocked.value); + } + return map; + } +} + +class UserDeviceKeysKey extends Table + with TableInfo { + final GeneratedDatabase _db; + final String _alias; + UserDeviceKeysKey(this._db, [this._alias]); + final VerificationMeta _clientIdMeta = const VerificationMeta('clientId'); + GeneratedIntColumn _clientId; + GeneratedIntColumn get clientId => _clientId ??= _constructClientId(); + GeneratedIntColumn _constructClientId() { + return GeneratedIntColumn('client_id', $tableName, false, + $customConstraints: 'NOT NULL REFERENCES clients(client_id)'); + } + + final VerificationMeta _userIdMeta = const VerificationMeta('userId'); + GeneratedTextColumn _userId; + GeneratedTextColumn get userId => _userId ??= _constructUserId(); + GeneratedTextColumn _constructUserId() { + return GeneratedTextColumn('user_id', $tableName, false, + $customConstraints: 'NOT NULL'); + } + + final VerificationMeta _deviceIdMeta = const VerificationMeta('deviceId'); + GeneratedTextColumn _deviceId; + GeneratedTextColumn get deviceId => _deviceId ??= _constructDeviceId(); + GeneratedTextColumn _constructDeviceId() { + return GeneratedTextColumn('device_id', $tableName, false, + $customConstraints: 'NOT NULL'); + } + + final VerificationMeta _contentMeta = const VerificationMeta('content'); + GeneratedTextColumn _content; + GeneratedTextColumn get content => _content ??= _constructContent(); + GeneratedTextColumn _constructContent() { + return GeneratedTextColumn('content', $tableName, false, + $customConstraints: 'NOT NULL'); + } + + final VerificationMeta _verifiedMeta = const VerificationMeta('verified'); + GeneratedBoolColumn _verified; + GeneratedBoolColumn get verified => _verified ??= _constructVerified(); + GeneratedBoolColumn _constructVerified() { + return GeneratedBoolColumn('verified', $tableName, true, + $customConstraints: 'DEFAULT false', + defaultValue: const CustomExpression('false')); + } + + final VerificationMeta _blockedMeta = const VerificationMeta('blocked'); + GeneratedBoolColumn _blocked; + GeneratedBoolColumn get blocked => _blocked ??= _constructBlocked(); + GeneratedBoolColumn _constructBlocked() { + return GeneratedBoolColumn('blocked', $tableName, true, + $customConstraints: 'DEFAULT false', + defaultValue: const CustomExpression('false')); + } + + @override + List get $columns => + [clientId, userId, deviceId, content, verified, blocked]; + @override + UserDeviceKeysKey get asDslTable => this; + @override + String get $tableName => _alias ?? 'user_device_keys_key'; + @override + final String actualTableName = 'user_device_keys_key'; + @override + VerificationContext validateIntegrity( + Insertable instance, + {bool isInserting = false}) { + final context = VerificationContext(); + final data = instance.toColumns(true); + if (data.containsKey('client_id')) { + context.handle(_clientIdMeta, + clientId.isAcceptableOrUnknown(data['client_id'], _clientIdMeta)); + } else if (isInserting) { + context.missing(_clientIdMeta); + } + if (data.containsKey('user_id')) { + context.handle(_userIdMeta, + userId.isAcceptableOrUnknown(data['user_id'], _userIdMeta)); + } else if (isInserting) { + context.missing(_userIdMeta); + } + if (data.containsKey('device_id')) { + context.handle(_deviceIdMeta, + deviceId.isAcceptableOrUnknown(data['device_id'], _deviceIdMeta)); + } else if (isInserting) { + context.missing(_deviceIdMeta); + } + if (data.containsKey('content')) { + context.handle(_contentMeta, + content.isAcceptableOrUnknown(data['content'], _contentMeta)); + } else if (isInserting) { + context.missing(_contentMeta); + } + if (data.containsKey('verified')) { + context.handle(_verifiedMeta, + verified.isAcceptableOrUnknown(data['verified'], _verifiedMeta)); + } + if (data.containsKey('blocked')) { + context.handle(_blockedMeta, + blocked.isAcceptableOrUnknown(data['blocked'], _blockedMeta)); + } + return context; + } + + @override + Set get $primaryKey => {}; + @override + DbUserDeviceKeysKey map(Map data, {String tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : null; + return DbUserDeviceKeysKey.fromData(data, _db, prefix: effectivePrefix); + } + + @override + UserDeviceKeysKey createAlias(String alias) { + return UserDeviceKeysKey(_db, alias); + } + + @override + List get customConstraints => + const ['UNIQUE(client_id, user_id, device_id)']; + @override + bool get dontWriteConstraints => true; +} + +class DbOlmSessions extends DataClass implements Insertable { + final int clientId; + final String identityKey; + final String sessionId; + final String pickle; + DbOlmSessions( + {@required this.clientId, + @required this.identityKey, + @required this.sessionId, + @required this.pickle}); + factory DbOlmSessions.fromData( + Map data, GeneratedDatabase db, + {String prefix}) { + final effectivePrefix = prefix ?? ''; + final intType = db.typeSystem.forDartType(); + final stringType = db.typeSystem.forDartType(); + return DbOlmSessions( + clientId: + intType.mapFromDatabaseResponse(data['${effectivePrefix}client_id']), + identityKey: stringType + .mapFromDatabaseResponse(data['${effectivePrefix}identity_key']), + sessionId: stringType + .mapFromDatabaseResponse(data['${effectivePrefix}session_id']), + pickle: + stringType.mapFromDatabaseResponse(data['${effectivePrefix}pickle']), + ); + } + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (!nullToAbsent || clientId != null) { + map['client_id'] = Variable(clientId); + } + if (!nullToAbsent || identityKey != null) { + map['identity_key'] = Variable(identityKey); + } + if (!nullToAbsent || sessionId != null) { + map['session_id'] = Variable(sessionId); + } + if (!nullToAbsent || pickle != null) { + map['pickle'] = Variable(pickle); + } + return map; + } + + factory DbOlmSessions.fromJson(Map json, + {ValueSerializer serializer}) { + serializer ??= moorRuntimeOptions.defaultSerializer; + return DbOlmSessions( + clientId: serializer.fromJson(json['client_id']), + identityKey: serializer.fromJson(json['identity_key']), + sessionId: serializer.fromJson(json['session_id']), + pickle: serializer.fromJson(json['pickle']), + ); + } + @override + Map toJson({ValueSerializer serializer}) { + serializer ??= moorRuntimeOptions.defaultSerializer; + return { + 'client_id': serializer.toJson(clientId), + 'identity_key': serializer.toJson(identityKey), + 'session_id': serializer.toJson(sessionId), + 'pickle': serializer.toJson(pickle), + }; + } + + DbOlmSessions copyWith( + {int clientId, + String identityKey, + String sessionId, + String pickle}) => + DbOlmSessions( + clientId: clientId ?? this.clientId, + identityKey: identityKey ?? this.identityKey, + sessionId: sessionId ?? this.sessionId, + pickle: pickle ?? this.pickle, + ); + @override + String toString() { + return (StringBuffer('DbOlmSessions(') + ..write('clientId: $clientId, ') + ..write('identityKey: $identityKey, ') + ..write('sessionId: $sessionId, ') + ..write('pickle: $pickle') + ..write(')')) + .toString(); + } + + @override + int get hashCode => $mrjf($mrjc(clientId.hashCode, + $mrjc(identityKey.hashCode, $mrjc(sessionId.hashCode, pickle.hashCode)))); + @override + bool operator ==(dynamic other) => + identical(this, other) || + (other is DbOlmSessions && + other.clientId == this.clientId && + other.identityKey == this.identityKey && + other.sessionId == this.sessionId && + other.pickle == this.pickle); +} + +class OlmSessionsCompanion extends UpdateCompanion { + final Value clientId; + final Value identityKey; + final Value sessionId; + final Value pickle; + const OlmSessionsCompanion({ + this.clientId = const Value.absent(), + this.identityKey = const Value.absent(), + this.sessionId = const Value.absent(), + this.pickle = const Value.absent(), + }); + OlmSessionsCompanion.insert({ + @required int clientId, + @required String identityKey, + @required String sessionId, + @required String pickle, + }) : clientId = Value(clientId), + identityKey = Value(identityKey), + sessionId = Value(sessionId), + pickle = Value(pickle); + static Insertable custom({ + Expression clientId, + Expression identityKey, + Expression sessionId, + Expression pickle, + }) { + return RawValuesInsertable({ + if (clientId != null) 'client_id': clientId, + if (identityKey != null) 'identity_key': identityKey, + if (sessionId != null) 'session_id': sessionId, + if (pickle != null) 'pickle': pickle, + }); + } + + OlmSessionsCompanion copyWith( + {Value clientId, + Value identityKey, + Value sessionId, + Value pickle}) { + return OlmSessionsCompanion( + clientId: clientId ?? this.clientId, + identityKey: identityKey ?? this.identityKey, + sessionId: sessionId ?? this.sessionId, + pickle: pickle ?? this.pickle, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (clientId.present) { + map['client_id'] = Variable(clientId.value); + } + if (identityKey.present) { + map['identity_key'] = Variable(identityKey.value); + } + if (sessionId.present) { + map['session_id'] = Variable(sessionId.value); + } + if (pickle.present) { + map['pickle'] = Variable(pickle.value); + } + return map; + } +} + +class OlmSessions extends Table with TableInfo { + final GeneratedDatabase _db; + final String _alias; + OlmSessions(this._db, [this._alias]); + final VerificationMeta _clientIdMeta = const VerificationMeta('clientId'); + GeneratedIntColumn _clientId; + GeneratedIntColumn get clientId => _clientId ??= _constructClientId(); + GeneratedIntColumn _constructClientId() { + return GeneratedIntColumn('client_id', $tableName, false, + $customConstraints: 'NOT NULL REFERENCES clients(client_id)'); + } + + final VerificationMeta _identityKeyMeta = + const VerificationMeta('identityKey'); + GeneratedTextColumn _identityKey; + GeneratedTextColumn get identityKey => + _identityKey ??= _constructIdentityKey(); + GeneratedTextColumn _constructIdentityKey() { + return GeneratedTextColumn('identity_key', $tableName, false, + $customConstraints: 'NOT NULL'); + } + + final VerificationMeta _sessionIdMeta = const VerificationMeta('sessionId'); + GeneratedTextColumn _sessionId; + GeneratedTextColumn get sessionId => _sessionId ??= _constructSessionId(); + GeneratedTextColumn _constructSessionId() { + return GeneratedTextColumn('session_id', $tableName, false, + $customConstraints: 'NOT NULL'); + } + + final VerificationMeta _pickleMeta = const VerificationMeta('pickle'); + GeneratedTextColumn _pickle; + GeneratedTextColumn get pickle => _pickle ??= _constructPickle(); + GeneratedTextColumn _constructPickle() { + return GeneratedTextColumn('pickle', $tableName, false, + $customConstraints: 'NOT NULL'); + } + + @override + List get $columns => + [clientId, identityKey, sessionId, pickle]; + @override + OlmSessions get asDslTable => this; + @override + String get $tableName => _alias ?? 'olm_sessions'; + @override + final String actualTableName = 'olm_sessions'; + @override + VerificationContext validateIntegrity(Insertable instance, + {bool isInserting = false}) { + final context = VerificationContext(); + final data = instance.toColumns(true); + if (data.containsKey('client_id')) { + context.handle(_clientIdMeta, + clientId.isAcceptableOrUnknown(data['client_id'], _clientIdMeta)); + } else if (isInserting) { + context.missing(_clientIdMeta); + } + if (data.containsKey('identity_key')) { + context.handle( + _identityKeyMeta, + identityKey.isAcceptableOrUnknown( + data['identity_key'], _identityKeyMeta)); + } else if (isInserting) { + context.missing(_identityKeyMeta); + } + if (data.containsKey('session_id')) { + context.handle(_sessionIdMeta, + sessionId.isAcceptableOrUnknown(data['session_id'], _sessionIdMeta)); + } else if (isInserting) { + context.missing(_sessionIdMeta); + } + if (data.containsKey('pickle')) { + context.handle(_pickleMeta, + pickle.isAcceptableOrUnknown(data['pickle'], _pickleMeta)); + } else if (isInserting) { + context.missing(_pickleMeta); + } + return context; + } + + @override + Set get $primaryKey => {}; + @override + DbOlmSessions map(Map data, {String tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : null; + return DbOlmSessions.fromData(data, _db, prefix: effectivePrefix); + } + + @override + OlmSessions createAlias(String alias) { + return OlmSessions(_db, alias); + } + + @override + List get customConstraints => + const ['UNIQUE(client_id, identity_key, session_id)']; + @override + bool get dontWriteConstraints => true; +} + +class DbOutboundGroupSession extends DataClass + implements Insertable { + final int clientId; + final String roomId; + final String pickle; + final String deviceIds; + DbOutboundGroupSession( + {@required this.clientId, + @required this.roomId, + @required this.pickle, + @required this.deviceIds}); + factory DbOutboundGroupSession.fromData( + Map data, GeneratedDatabase db, + {String prefix}) { + final effectivePrefix = prefix ?? ''; + final intType = db.typeSystem.forDartType(); + final stringType = db.typeSystem.forDartType(); + return DbOutboundGroupSession( + clientId: + intType.mapFromDatabaseResponse(data['${effectivePrefix}client_id']), + roomId: + stringType.mapFromDatabaseResponse(data['${effectivePrefix}room_id']), + pickle: + stringType.mapFromDatabaseResponse(data['${effectivePrefix}pickle']), + deviceIds: stringType + .mapFromDatabaseResponse(data['${effectivePrefix}device_ids']), + ); + } + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (!nullToAbsent || clientId != null) { + map['client_id'] = Variable(clientId); + } + if (!nullToAbsent || roomId != null) { + map['room_id'] = Variable(roomId); + } + if (!nullToAbsent || pickle != null) { + map['pickle'] = Variable(pickle); + } + if (!nullToAbsent || deviceIds != null) { + map['device_ids'] = Variable(deviceIds); + } + return map; + } + + factory DbOutboundGroupSession.fromJson(Map json, + {ValueSerializer serializer}) { + serializer ??= moorRuntimeOptions.defaultSerializer; + return DbOutboundGroupSession( + clientId: serializer.fromJson(json['client_id']), + roomId: serializer.fromJson(json['room_id']), + pickle: serializer.fromJson(json['pickle']), + deviceIds: serializer.fromJson(json['device_ids']), + ); + } + @override + Map toJson({ValueSerializer serializer}) { + serializer ??= moorRuntimeOptions.defaultSerializer; + return { + 'client_id': serializer.toJson(clientId), + 'room_id': serializer.toJson(roomId), + 'pickle': serializer.toJson(pickle), + 'device_ids': serializer.toJson(deviceIds), + }; + } + + DbOutboundGroupSession copyWith( + {int clientId, String roomId, String pickle, String deviceIds}) => + DbOutboundGroupSession( + clientId: clientId ?? this.clientId, + roomId: roomId ?? this.roomId, + pickle: pickle ?? this.pickle, + deviceIds: deviceIds ?? this.deviceIds, + ); + @override + String toString() { + return (StringBuffer('DbOutboundGroupSession(') + ..write('clientId: $clientId, ') + ..write('roomId: $roomId, ') + ..write('pickle: $pickle, ') + ..write('deviceIds: $deviceIds') + ..write(')')) + .toString(); + } + + @override + int get hashCode => $mrjf($mrjc(clientId.hashCode, + $mrjc(roomId.hashCode, $mrjc(pickle.hashCode, deviceIds.hashCode)))); + @override + bool operator ==(dynamic other) => + identical(this, other) || + (other is DbOutboundGroupSession && + other.clientId == this.clientId && + other.roomId == this.roomId && + other.pickle == this.pickle && + other.deviceIds == this.deviceIds); +} + +class OutboundGroupSessionsCompanion + extends UpdateCompanion { + final Value clientId; + final Value roomId; + final Value pickle; + final Value deviceIds; + const OutboundGroupSessionsCompanion({ + this.clientId = const Value.absent(), + this.roomId = const Value.absent(), + this.pickle = const Value.absent(), + this.deviceIds = const Value.absent(), + }); + OutboundGroupSessionsCompanion.insert({ + @required int clientId, + @required String roomId, + @required String pickle, + @required String deviceIds, + }) : clientId = Value(clientId), + roomId = Value(roomId), + pickle = Value(pickle), + deviceIds = Value(deviceIds); + static Insertable custom({ + Expression clientId, + Expression roomId, + Expression pickle, + Expression deviceIds, + }) { + return RawValuesInsertable({ + if (clientId != null) 'client_id': clientId, + if (roomId != null) 'room_id': roomId, + if (pickle != null) 'pickle': pickle, + if (deviceIds != null) 'device_ids': deviceIds, + }); + } + + OutboundGroupSessionsCompanion copyWith( + {Value clientId, + Value roomId, + Value pickle, + Value deviceIds}) { + return OutboundGroupSessionsCompanion( + clientId: clientId ?? this.clientId, + roomId: roomId ?? this.roomId, + pickle: pickle ?? this.pickle, + deviceIds: deviceIds ?? this.deviceIds, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (clientId.present) { + map['client_id'] = Variable(clientId.value); + } + if (roomId.present) { + map['room_id'] = Variable(roomId.value); + } + if (pickle.present) { + map['pickle'] = Variable(pickle.value); + } + if (deviceIds.present) { + map['device_ids'] = Variable(deviceIds.value); + } + return map; + } +} + +class OutboundGroupSessions extends Table + with TableInfo { + final GeneratedDatabase _db; + final String _alias; + OutboundGroupSessions(this._db, [this._alias]); + final VerificationMeta _clientIdMeta = const VerificationMeta('clientId'); + GeneratedIntColumn _clientId; + GeneratedIntColumn get clientId => _clientId ??= _constructClientId(); + GeneratedIntColumn _constructClientId() { + return GeneratedIntColumn('client_id', $tableName, false, + $customConstraints: 'NOT NULL REFERENCES clients(client_id)'); + } + + final VerificationMeta _roomIdMeta = const VerificationMeta('roomId'); + GeneratedTextColumn _roomId; + GeneratedTextColumn get roomId => _roomId ??= _constructRoomId(); + GeneratedTextColumn _constructRoomId() { + return GeneratedTextColumn('room_id', $tableName, false, + $customConstraints: 'NOT NULL'); + } + + final VerificationMeta _pickleMeta = const VerificationMeta('pickle'); + GeneratedTextColumn _pickle; + GeneratedTextColumn get pickle => _pickle ??= _constructPickle(); + GeneratedTextColumn _constructPickle() { + return GeneratedTextColumn('pickle', $tableName, false, + $customConstraints: 'NOT NULL'); + } + + final VerificationMeta _deviceIdsMeta = const VerificationMeta('deviceIds'); + GeneratedTextColumn _deviceIds; + GeneratedTextColumn get deviceIds => _deviceIds ??= _constructDeviceIds(); + GeneratedTextColumn _constructDeviceIds() { + return GeneratedTextColumn('device_ids', $tableName, false, + $customConstraints: 'NOT NULL'); + } + + @override + List get $columns => [clientId, roomId, pickle, deviceIds]; + @override + OutboundGroupSessions get asDslTable => this; + @override + String get $tableName => _alias ?? 'outbound_group_sessions'; + @override + final String actualTableName = 'outbound_group_sessions'; + @override + VerificationContext validateIntegrity( + Insertable instance, + {bool isInserting = false}) { + final context = VerificationContext(); + final data = instance.toColumns(true); + if (data.containsKey('client_id')) { + context.handle(_clientIdMeta, + clientId.isAcceptableOrUnknown(data['client_id'], _clientIdMeta)); + } else if (isInserting) { + context.missing(_clientIdMeta); + } + if (data.containsKey('room_id')) { + context.handle(_roomIdMeta, + roomId.isAcceptableOrUnknown(data['room_id'], _roomIdMeta)); + } else if (isInserting) { + context.missing(_roomIdMeta); + } + if (data.containsKey('pickle')) { + context.handle(_pickleMeta, + pickle.isAcceptableOrUnknown(data['pickle'], _pickleMeta)); + } else if (isInserting) { + context.missing(_pickleMeta); + } + if (data.containsKey('device_ids')) { + context.handle(_deviceIdsMeta, + deviceIds.isAcceptableOrUnknown(data['device_ids'], _deviceIdsMeta)); + } else if (isInserting) { + context.missing(_deviceIdsMeta); + } + return context; + } + + @override + Set get $primaryKey => {}; + @override + DbOutboundGroupSession map(Map data, {String tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : null; + return DbOutboundGroupSession.fromData(data, _db, prefix: effectivePrefix); + } + + @override + OutboundGroupSessions createAlias(String alias) { + return OutboundGroupSessions(_db, alias); + } + + @override + List get customConstraints => const ['UNIQUE(client_id, room_id)']; + @override + bool get dontWriteConstraints => true; +} + +class DbInboundGroupSession extends DataClass + implements Insertable { + final int clientId; + final String roomId; + final String sessionId; + final String pickle; + final String content; + final String indexes; + DbInboundGroupSession( + {@required this.clientId, + @required this.roomId, + @required this.sessionId, + @required this.pickle, + this.content, + this.indexes}); + factory DbInboundGroupSession.fromData( + Map data, GeneratedDatabase db, + {String prefix}) { + final effectivePrefix = prefix ?? ''; + final intType = db.typeSystem.forDartType(); + final stringType = db.typeSystem.forDartType(); + return DbInboundGroupSession( + clientId: + intType.mapFromDatabaseResponse(data['${effectivePrefix}client_id']), + roomId: + stringType.mapFromDatabaseResponse(data['${effectivePrefix}room_id']), + sessionId: stringType + .mapFromDatabaseResponse(data['${effectivePrefix}session_id']), + pickle: + stringType.mapFromDatabaseResponse(data['${effectivePrefix}pickle']), + content: + stringType.mapFromDatabaseResponse(data['${effectivePrefix}content']), + indexes: + stringType.mapFromDatabaseResponse(data['${effectivePrefix}indexes']), + ); + } + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (!nullToAbsent || clientId != null) { + map['client_id'] = Variable(clientId); + } + if (!nullToAbsent || roomId != null) { + map['room_id'] = Variable(roomId); + } + if (!nullToAbsent || sessionId != null) { + map['session_id'] = Variable(sessionId); + } + if (!nullToAbsent || pickle != null) { + map['pickle'] = Variable(pickle); + } + if (!nullToAbsent || content != null) { + map['content'] = Variable(content); + } + if (!nullToAbsent || indexes != null) { + map['indexes'] = Variable(indexes); + } + return map; + } + + factory DbInboundGroupSession.fromJson(Map json, + {ValueSerializer serializer}) { + serializer ??= moorRuntimeOptions.defaultSerializer; + return DbInboundGroupSession( + clientId: serializer.fromJson(json['client_id']), + roomId: serializer.fromJson(json['room_id']), + sessionId: serializer.fromJson(json['session_id']), + pickle: serializer.fromJson(json['pickle']), + content: serializer.fromJson(json['content']), + indexes: serializer.fromJson(json['indexes']), + ); + } + @override + Map toJson({ValueSerializer serializer}) { + serializer ??= moorRuntimeOptions.defaultSerializer; + return { + 'client_id': serializer.toJson(clientId), + 'room_id': serializer.toJson(roomId), + 'session_id': serializer.toJson(sessionId), + 'pickle': serializer.toJson(pickle), + 'content': serializer.toJson(content), + 'indexes': serializer.toJson(indexes), + }; + } + + DbInboundGroupSession copyWith( + {int clientId, + String roomId, + String sessionId, + String pickle, + String content, + String indexes}) => + DbInboundGroupSession( + clientId: clientId ?? this.clientId, + roomId: roomId ?? this.roomId, + sessionId: sessionId ?? this.sessionId, + pickle: pickle ?? this.pickle, + content: content ?? this.content, + indexes: indexes ?? this.indexes, + ); + @override + String toString() { + return (StringBuffer('DbInboundGroupSession(') + ..write('clientId: $clientId, ') + ..write('roomId: $roomId, ') + ..write('sessionId: $sessionId, ') + ..write('pickle: $pickle, ') + ..write('content: $content, ') + ..write('indexes: $indexes') + ..write(')')) + .toString(); + } + + @override + int get hashCode => $mrjf($mrjc( + clientId.hashCode, + $mrjc( + roomId.hashCode, + $mrjc( + sessionId.hashCode, + $mrjc(pickle.hashCode, + $mrjc(content.hashCode, indexes.hashCode)))))); + @override + bool operator ==(dynamic other) => + identical(this, other) || + (other is DbInboundGroupSession && + other.clientId == this.clientId && + other.roomId == this.roomId && + other.sessionId == this.sessionId && + other.pickle == this.pickle && + other.content == this.content && + other.indexes == this.indexes); +} + +class InboundGroupSessionsCompanion + extends UpdateCompanion { + final Value clientId; + final Value roomId; + final Value sessionId; + final Value pickle; + final Value content; + final Value indexes; + const InboundGroupSessionsCompanion({ + this.clientId = const Value.absent(), + this.roomId = const Value.absent(), + this.sessionId = const Value.absent(), + this.pickle = const Value.absent(), + this.content = const Value.absent(), + this.indexes = const Value.absent(), + }); + InboundGroupSessionsCompanion.insert({ + @required int clientId, + @required String roomId, + @required String sessionId, + @required String pickle, + this.content = const Value.absent(), + this.indexes = const Value.absent(), + }) : clientId = Value(clientId), + roomId = Value(roomId), + sessionId = Value(sessionId), + pickle = Value(pickle); + static Insertable custom({ + Expression clientId, + Expression roomId, + Expression sessionId, + Expression pickle, + Expression content, + Expression indexes, + }) { + return RawValuesInsertable({ + if (clientId != null) 'client_id': clientId, + if (roomId != null) 'room_id': roomId, + if (sessionId != null) 'session_id': sessionId, + if (pickle != null) 'pickle': pickle, + if (content != null) 'content': content, + if (indexes != null) 'indexes': indexes, + }); + } + + InboundGroupSessionsCompanion copyWith( + {Value clientId, + Value roomId, + Value sessionId, + Value pickle, + Value content, + Value indexes}) { + return InboundGroupSessionsCompanion( + clientId: clientId ?? this.clientId, + roomId: roomId ?? this.roomId, + sessionId: sessionId ?? this.sessionId, + pickle: pickle ?? this.pickle, + content: content ?? this.content, + indexes: indexes ?? this.indexes, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (clientId.present) { + map['client_id'] = Variable(clientId.value); + } + if (roomId.present) { + map['room_id'] = Variable(roomId.value); + } + if (sessionId.present) { + map['session_id'] = Variable(sessionId.value); + } + if (pickle.present) { + map['pickle'] = Variable(pickle.value); + } + if (content.present) { + map['content'] = Variable(content.value); + } + if (indexes.present) { + map['indexes'] = Variable(indexes.value); + } + return map; + } +} + +class InboundGroupSessions extends Table + with TableInfo { + final GeneratedDatabase _db; + final String _alias; + InboundGroupSessions(this._db, [this._alias]); + final VerificationMeta _clientIdMeta = const VerificationMeta('clientId'); + GeneratedIntColumn _clientId; + GeneratedIntColumn get clientId => _clientId ??= _constructClientId(); + GeneratedIntColumn _constructClientId() { + return GeneratedIntColumn('client_id', $tableName, false, + $customConstraints: 'NOT NULL REFERENCES clients(client_id)'); + } + + final VerificationMeta _roomIdMeta = const VerificationMeta('roomId'); + GeneratedTextColumn _roomId; + GeneratedTextColumn get roomId => _roomId ??= _constructRoomId(); + GeneratedTextColumn _constructRoomId() { + return GeneratedTextColumn('room_id', $tableName, false, + $customConstraints: 'NOT NULL'); + } + + final VerificationMeta _sessionIdMeta = const VerificationMeta('sessionId'); + GeneratedTextColumn _sessionId; + GeneratedTextColumn get sessionId => _sessionId ??= _constructSessionId(); + GeneratedTextColumn _constructSessionId() { + return GeneratedTextColumn('session_id', $tableName, false, + $customConstraints: 'NOT NULL'); + } + + final VerificationMeta _pickleMeta = const VerificationMeta('pickle'); + GeneratedTextColumn _pickle; + GeneratedTextColumn get pickle => _pickle ??= _constructPickle(); + GeneratedTextColumn _constructPickle() { + return GeneratedTextColumn('pickle', $tableName, false, + $customConstraints: 'NOT NULL'); + } + + final VerificationMeta _contentMeta = const VerificationMeta('content'); + GeneratedTextColumn _content; + GeneratedTextColumn get content => _content ??= _constructContent(); + GeneratedTextColumn _constructContent() { + return GeneratedTextColumn('content', $tableName, true, + $customConstraints: ''); + } + + final VerificationMeta _indexesMeta = const VerificationMeta('indexes'); + GeneratedTextColumn _indexes; + GeneratedTextColumn get indexes => _indexes ??= _constructIndexes(); + GeneratedTextColumn _constructIndexes() { + return GeneratedTextColumn('indexes', $tableName, true, + $customConstraints: ''); + } + + @override + List get $columns => + [clientId, roomId, sessionId, pickle, content, indexes]; + @override + InboundGroupSessions get asDslTable => this; + @override + String get $tableName => _alias ?? 'inbound_group_sessions'; + @override + final String actualTableName = 'inbound_group_sessions'; + @override + VerificationContext validateIntegrity( + Insertable instance, + {bool isInserting = false}) { + final context = VerificationContext(); + final data = instance.toColumns(true); + if (data.containsKey('client_id')) { + context.handle(_clientIdMeta, + clientId.isAcceptableOrUnknown(data['client_id'], _clientIdMeta)); + } else if (isInserting) { + context.missing(_clientIdMeta); + } + if (data.containsKey('room_id')) { + context.handle(_roomIdMeta, + roomId.isAcceptableOrUnknown(data['room_id'], _roomIdMeta)); + } else if (isInserting) { + context.missing(_roomIdMeta); + } + if (data.containsKey('session_id')) { + context.handle(_sessionIdMeta, + sessionId.isAcceptableOrUnknown(data['session_id'], _sessionIdMeta)); + } else if (isInserting) { + context.missing(_sessionIdMeta); + } + if (data.containsKey('pickle')) { + context.handle(_pickleMeta, + pickle.isAcceptableOrUnknown(data['pickle'], _pickleMeta)); + } else if (isInserting) { + context.missing(_pickleMeta); + } + if (data.containsKey('content')) { + context.handle(_contentMeta, + content.isAcceptableOrUnknown(data['content'], _contentMeta)); + } + if (data.containsKey('indexes')) { + context.handle(_indexesMeta, + indexes.isAcceptableOrUnknown(data['indexes'], _indexesMeta)); + } + return context; + } + + @override + Set get $primaryKey => {}; + @override + DbInboundGroupSession map(Map data, {String tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : null; + return DbInboundGroupSession.fromData(data, _db, prefix: effectivePrefix); + } + + @override + InboundGroupSessions createAlias(String alias) { + return InboundGroupSessions(_db, alias); + } + + @override + List get customConstraints => + const ['UNIQUE(client_id, room_id, session_id)']; + @override + bool get dontWriteConstraints => true; +} + +class DbRoom extends DataClass implements Insertable { + final int clientId; + final String roomId; + final String membership; + final int highlightCount; + final int notificationCount; + final String prevBatch; + final int joinedMemberCount; + final int invitedMemberCount; + final double newestSortOrder; + final double oldestSortOrder; + final String heroes; + DbRoom( + {@required this.clientId, + @required this.roomId, + @required this.membership, + @required this.highlightCount, + @required this.notificationCount, + this.prevBatch, + @required this.joinedMemberCount, + @required this.invitedMemberCount, + @required this.newestSortOrder, + @required this.oldestSortOrder, + this.heroes}); + factory DbRoom.fromData(Map data, GeneratedDatabase db, + {String prefix}) { + final effectivePrefix = prefix ?? ''; + final intType = db.typeSystem.forDartType(); + final stringType = db.typeSystem.forDartType(); + final doubleType = db.typeSystem.forDartType(); + return DbRoom( + clientId: + intType.mapFromDatabaseResponse(data['${effectivePrefix}client_id']), + roomId: + stringType.mapFromDatabaseResponse(data['${effectivePrefix}room_id']), + membership: stringType + .mapFromDatabaseResponse(data['${effectivePrefix}membership']), + highlightCount: intType + .mapFromDatabaseResponse(data['${effectivePrefix}highlight_count']), + notificationCount: intType.mapFromDatabaseResponse( + data['${effectivePrefix}notification_count']), + prevBatch: stringType + .mapFromDatabaseResponse(data['${effectivePrefix}prev_batch']), + joinedMemberCount: intType.mapFromDatabaseResponse( + data['${effectivePrefix}joined_member_count']), + invitedMemberCount: intType.mapFromDatabaseResponse( + data['${effectivePrefix}invited_member_count']), + newestSortOrder: doubleType + .mapFromDatabaseResponse(data['${effectivePrefix}newest_sort_order']), + oldestSortOrder: doubleType + .mapFromDatabaseResponse(data['${effectivePrefix}oldest_sort_order']), + heroes: + stringType.mapFromDatabaseResponse(data['${effectivePrefix}heroes']), + ); + } + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (!nullToAbsent || clientId != null) { + map['client_id'] = Variable(clientId); + } + if (!nullToAbsent || roomId != null) { + map['room_id'] = Variable(roomId); + } + if (!nullToAbsent || membership != null) { + map['membership'] = Variable(membership); + } + if (!nullToAbsent || highlightCount != null) { + map['highlight_count'] = Variable(highlightCount); + } + if (!nullToAbsent || notificationCount != null) { + map['notification_count'] = Variable(notificationCount); + } + if (!nullToAbsent || prevBatch != null) { + map['prev_batch'] = Variable(prevBatch); + } + if (!nullToAbsent || joinedMemberCount != null) { + map['joined_member_count'] = Variable(joinedMemberCount); + } + if (!nullToAbsent || invitedMemberCount != null) { + map['invited_member_count'] = Variable(invitedMemberCount); + } + if (!nullToAbsent || newestSortOrder != null) { + map['newest_sort_order'] = Variable(newestSortOrder); + } + if (!nullToAbsent || oldestSortOrder != null) { + map['oldest_sort_order'] = Variable(oldestSortOrder); + } + if (!nullToAbsent || heroes != null) { + map['heroes'] = Variable(heroes); + } + return map; + } + + factory DbRoom.fromJson(Map json, + {ValueSerializer serializer}) { + serializer ??= moorRuntimeOptions.defaultSerializer; + return DbRoom( + clientId: serializer.fromJson(json['client_id']), + roomId: serializer.fromJson(json['room_id']), + membership: serializer.fromJson(json['membership']), + highlightCount: serializer.fromJson(json['highlight_count']), + notificationCount: serializer.fromJson(json['notification_count']), + prevBatch: serializer.fromJson(json['prev_batch']), + joinedMemberCount: serializer.fromJson(json['joined_member_count']), + invitedMemberCount: + serializer.fromJson(json['invited_member_count']), + newestSortOrder: serializer.fromJson(json['newest_sort_order']), + oldestSortOrder: serializer.fromJson(json['oldest_sort_order']), + heroes: serializer.fromJson(json['heroes']), + ); + } + @override + Map toJson({ValueSerializer serializer}) { + serializer ??= moorRuntimeOptions.defaultSerializer; + return { + 'client_id': serializer.toJson(clientId), + 'room_id': serializer.toJson(roomId), + 'membership': serializer.toJson(membership), + 'highlight_count': serializer.toJson(highlightCount), + 'notification_count': serializer.toJson(notificationCount), + 'prev_batch': serializer.toJson(prevBatch), + 'joined_member_count': serializer.toJson(joinedMemberCount), + 'invited_member_count': serializer.toJson(invitedMemberCount), + 'newest_sort_order': serializer.toJson(newestSortOrder), + 'oldest_sort_order': serializer.toJson(oldestSortOrder), + 'heroes': serializer.toJson(heroes), + }; + } + + DbRoom copyWith( + {int clientId, + String roomId, + String membership, + int highlightCount, + int notificationCount, + String prevBatch, + int joinedMemberCount, + int invitedMemberCount, + double newestSortOrder, + double oldestSortOrder, + String heroes}) => + DbRoom( + clientId: clientId ?? this.clientId, + roomId: roomId ?? this.roomId, + membership: membership ?? this.membership, + highlightCount: highlightCount ?? this.highlightCount, + notificationCount: notificationCount ?? this.notificationCount, + prevBatch: prevBatch ?? this.prevBatch, + joinedMemberCount: joinedMemberCount ?? this.joinedMemberCount, + invitedMemberCount: invitedMemberCount ?? this.invitedMemberCount, + newestSortOrder: newestSortOrder ?? this.newestSortOrder, + oldestSortOrder: oldestSortOrder ?? this.oldestSortOrder, + heroes: heroes ?? this.heroes, + ); + @override + String toString() { + return (StringBuffer('DbRoom(') + ..write('clientId: $clientId, ') + ..write('roomId: $roomId, ') + ..write('membership: $membership, ') + ..write('highlightCount: $highlightCount, ') + ..write('notificationCount: $notificationCount, ') + ..write('prevBatch: $prevBatch, ') + ..write('joinedMemberCount: $joinedMemberCount, ') + ..write('invitedMemberCount: $invitedMemberCount, ') + ..write('newestSortOrder: $newestSortOrder, ') + ..write('oldestSortOrder: $oldestSortOrder, ') + ..write('heroes: $heroes') + ..write(')')) + .toString(); + } + + @override + int get hashCode => $mrjf($mrjc( + clientId.hashCode, + $mrjc( + roomId.hashCode, + $mrjc( + membership.hashCode, + $mrjc( + highlightCount.hashCode, + $mrjc( + notificationCount.hashCode, + $mrjc( + prevBatch.hashCode, + $mrjc( + joinedMemberCount.hashCode, + $mrjc( + invitedMemberCount.hashCode, + $mrjc( + newestSortOrder.hashCode, + $mrjc(oldestSortOrder.hashCode, + heroes.hashCode))))))))))); + @override + bool operator ==(dynamic other) => + identical(this, other) || + (other is DbRoom && + other.clientId == this.clientId && + other.roomId == this.roomId && + other.membership == this.membership && + other.highlightCount == this.highlightCount && + other.notificationCount == this.notificationCount && + other.prevBatch == this.prevBatch && + other.joinedMemberCount == this.joinedMemberCount && + other.invitedMemberCount == this.invitedMemberCount && + other.newestSortOrder == this.newestSortOrder && + other.oldestSortOrder == this.oldestSortOrder && + other.heroes == this.heroes); +} + +class RoomsCompanion extends UpdateCompanion { + final Value clientId; + final Value roomId; + final Value membership; + final Value highlightCount; + final Value notificationCount; + final Value prevBatch; + final Value joinedMemberCount; + final Value invitedMemberCount; + final Value newestSortOrder; + final Value oldestSortOrder; + final Value heroes; + const RoomsCompanion({ + this.clientId = const Value.absent(), + this.roomId = const Value.absent(), + this.membership = const Value.absent(), + this.highlightCount = const Value.absent(), + this.notificationCount = const Value.absent(), + this.prevBatch = const Value.absent(), + this.joinedMemberCount = const Value.absent(), + this.invitedMemberCount = const Value.absent(), + this.newestSortOrder = const Value.absent(), + this.oldestSortOrder = const Value.absent(), + this.heroes = const Value.absent(), + }); + RoomsCompanion.insert({ + @required int clientId, + @required String roomId, + @required String membership, + this.highlightCount = const Value.absent(), + this.notificationCount = const Value.absent(), + this.prevBatch = const Value.absent(), + this.joinedMemberCount = const Value.absent(), + this.invitedMemberCount = const Value.absent(), + this.newestSortOrder = const Value.absent(), + this.oldestSortOrder = const Value.absent(), + this.heroes = const Value.absent(), + }) : clientId = Value(clientId), + roomId = Value(roomId), + membership = Value(membership); + static Insertable custom({ + Expression clientId, + Expression roomId, + Expression membership, + Expression highlightCount, + Expression notificationCount, + Expression prevBatch, + Expression joinedMemberCount, + Expression invitedMemberCount, + Expression newestSortOrder, + Expression oldestSortOrder, + Expression heroes, + }) { + return RawValuesInsertable({ + if (clientId != null) 'client_id': clientId, + if (roomId != null) 'room_id': roomId, + if (membership != null) 'membership': membership, + if (highlightCount != null) 'highlight_count': highlightCount, + if (notificationCount != null) 'notification_count': notificationCount, + if (prevBatch != null) 'prev_batch': prevBatch, + if (joinedMemberCount != null) 'joined_member_count': joinedMemberCount, + if (invitedMemberCount != null) + 'invited_member_count': invitedMemberCount, + if (newestSortOrder != null) 'newest_sort_order': newestSortOrder, + if (oldestSortOrder != null) 'oldest_sort_order': oldestSortOrder, + if (heroes != null) 'heroes': heroes, + }); + } + + RoomsCompanion copyWith( + {Value clientId, + Value roomId, + Value membership, + Value highlightCount, + Value notificationCount, + Value prevBatch, + Value joinedMemberCount, + Value invitedMemberCount, + Value newestSortOrder, + Value oldestSortOrder, + Value heroes}) { + return RoomsCompanion( + clientId: clientId ?? this.clientId, + roomId: roomId ?? this.roomId, + membership: membership ?? this.membership, + highlightCount: highlightCount ?? this.highlightCount, + notificationCount: notificationCount ?? this.notificationCount, + prevBatch: prevBatch ?? this.prevBatch, + joinedMemberCount: joinedMemberCount ?? this.joinedMemberCount, + invitedMemberCount: invitedMemberCount ?? this.invitedMemberCount, + newestSortOrder: newestSortOrder ?? this.newestSortOrder, + oldestSortOrder: oldestSortOrder ?? this.oldestSortOrder, + heroes: heroes ?? this.heroes, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (clientId.present) { + map['client_id'] = Variable(clientId.value); + } + if (roomId.present) { + map['room_id'] = Variable(roomId.value); + } + if (membership.present) { + map['membership'] = Variable(membership.value); + } + if (highlightCount.present) { + map['highlight_count'] = Variable(highlightCount.value); + } + if (notificationCount.present) { + map['notification_count'] = Variable(notificationCount.value); + } + if (prevBatch.present) { + map['prev_batch'] = Variable(prevBatch.value); + } + if (joinedMemberCount.present) { + map['joined_member_count'] = Variable(joinedMemberCount.value); + } + if (invitedMemberCount.present) { + map['invited_member_count'] = Variable(invitedMemberCount.value); + } + if (newestSortOrder.present) { + map['newest_sort_order'] = Variable(newestSortOrder.value); + } + if (oldestSortOrder.present) { + map['oldest_sort_order'] = Variable(oldestSortOrder.value); + } + if (heroes.present) { + map['heroes'] = Variable(heroes.value); + } + return map; + } +} + +class Rooms extends Table with TableInfo { + final GeneratedDatabase _db; + final String _alias; + Rooms(this._db, [this._alias]); + final VerificationMeta _clientIdMeta = const VerificationMeta('clientId'); + GeneratedIntColumn _clientId; + GeneratedIntColumn get clientId => _clientId ??= _constructClientId(); + GeneratedIntColumn _constructClientId() { + return GeneratedIntColumn('client_id', $tableName, false, + $customConstraints: 'NOT NULL REFERENCES clients(client_id)'); + } + + final VerificationMeta _roomIdMeta = const VerificationMeta('roomId'); + GeneratedTextColumn _roomId; + GeneratedTextColumn get roomId => _roomId ??= _constructRoomId(); + GeneratedTextColumn _constructRoomId() { + return GeneratedTextColumn('room_id', $tableName, false, + $customConstraints: 'NOT NULL'); + } + + final VerificationMeta _membershipMeta = const VerificationMeta('membership'); + GeneratedTextColumn _membership; + GeneratedTextColumn get membership => _membership ??= _constructMembership(); + GeneratedTextColumn _constructMembership() { + return GeneratedTextColumn('membership', $tableName, false, + $customConstraints: 'NOT NULL'); + } + + final VerificationMeta _highlightCountMeta = + const VerificationMeta('highlightCount'); + GeneratedIntColumn _highlightCount; + GeneratedIntColumn get highlightCount => + _highlightCount ??= _constructHighlightCount(); + GeneratedIntColumn _constructHighlightCount() { + return GeneratedIntColumn('highlight_count', $tableName, false, + $customConstraints: 'NOT NULL DEFAULT \'0\'', + defaultValue: const CustomExpression('\'0\'')); + } + + final VerificationMeta _notificationCountMeta = + const VerificationMeta('notificationCount'); + GeneratedIntColumn _notificationCount; + GeneratedIntColumn get notificationCount => + _notificationCount ??= _constructNotificationCount(); + GeneratedIntColumn _constructNotificationCount() { + return GeneratedIntColumn('notification_count', $tableName, false, + $customConstraints: 'NOT NULL DEFAULT \'0\'', + defaultValue: const CustomExpression('\'0\'')); + } + + final VerificationMeta _prevBatchMeta = const VerificationMeta('prevBatch'); + GeneratedTextColumn _prevBatch; + GeneratedTextColumn get prevBatch => _prevBatch ??= _constructPrevBatch(); + GeneratedTextColumn _constructPrevBatch() { + return GeneratedTextColumn('prev_batch', $tableName, true, + $customConstraints: 'DEFAULT \'\'', + defaultValue: const CustomExpression('\'\'')); + } + + final VerificationMeta _joinedMemberCountMeta = + const VerificationMeta('joinedMemberCount'); + GeneratedIntColumn _joinedMemberCount; + GeneratedIntColumn get joinedMemberCount => + _joinedMemberCount ??= _constructJoinedMemberCount(); + GeneratedIntColumn _constructJoinedMemberCount() { + return GeneratedIntColumn('joined_member_count', $tableName, false, + $customConstraints: 'NOT NULL DEFAULT \'0\'', + defaultValue: const CustomExpression('\'0\'')); + } + + final VerificationMeta _invitedMemberCountMeta = + const VerificationMeta('invitedMemberCount'); + GeneratedIntColumn _invitedMemberCount; + GeneratedIntColumn get invitedMemberCount => + _invitedMemberCount ??= _constructInvitedMemberCount(); + GeneratedIntColumn _constructInvitedMemberCount() { + return GeneratedIntColumn('invited_member_count', $tableName, false, + $customConstraints: 'NOT NULL DEFAULT \'0\'', + defaultValue: const CustomExpression('\'0\'')); + } + + final VerificationMeta _newestSortOrderMeta = + const VerificationMeta('newestSortOrder'); + GeneratedRealColumn _newestSortOrder; + GeneratedRealColumn get newestSortOrder => + _newestSortOrder ??= _constructNewestSortOrder(); + GeneratedRealColumn _constructNewestSortOrder() { + return GeneratedRealColumn('newest_sort_order', $tableName, false, + $customConstraints: 'NOT NULL DEFAULT \'0\'', + defaultValue: const CustomExpression('\'0\'')); + } + + final VerificationMeta _oldestSortOrderMeta = + const VerificationMeta('oldestSortOrder'); + GeneratedRealColumn _oldestSortOrder; + GeneratedRealColumn get oldestSortOrder => + _oldestSortOrder ??= _constructOldestSortOrder(); + GeneratedRealColumn _constructOldestSortOrder() { + return GeneratedRealColumn('oldest_sort_order', $tableName, false, + $customConstraints: 'NOT NULL DEFAULT \'0\'', + defaultValue: const CustomExpression('\'0\'')); + } + + final VerificationMeta _heroesMeta = const VerificationMeta('heroes'); + GeneratedTextColumn _heroes; + GeneratedTextColumn get heroes => _heroes ??= _constructHeroes(); + GeneratedTextColumn _constructHeroes() { + return GeneratedTextColumn('heroes', $tableName, true, + $customConstraints: 'DEFAULT \'\'', + defaultValue: const CustomExpression('\'\'')); + } + + @override + List get $columns => [ + clientId, + roomId, + membership, + highlightCount, + notificationCount, + prevBatch, + joinedMemberCount, + invitedMemberCount, + newestSortOrder, + oldestSortOrder, + heroes + ]; + @override + Rooms get asDslTable => this; + @override + String get $tableName => _alias ?? 'rooms'; + @override + final String actualTableName = 'rooms'; + @override + VerificationContext validateIntegrity(Insertable instance, + {bool isInserting = false}) { + final context = VerificationContext(); + final data = instance.toColumns(true); + if (data.containsKey('client_id')) { + context.handle(_clientIdMeta, + clientId.isAcceptableOrUnknown(data['client_id'], _clientIdMeta)); + } else if (isInserting) { + context.missing(_clientIdMeta); + } + if (data.containsKey('room_id')) { + context.handle(_roomIdMeta, + roomId.isAcceptableOrUnknown(data['room_id'], _roomIdMeta)); + } else if (isInserting) { + context.missing(_roomIdMeta); + } + if (data.containsKey('membership')) { + context.handle( + _membershipMeta, + membership.isAcceptableOrUnknown( + data['membership'], _membershipMeta)); + } else if (isInserting) { + context.missing(_membershipMeta); + } + if (data.containsKey('highlight_count')) { + context.handle( + _highlightCountMeta, + highlightCount.isAcceptableOrUnknown( + data['highlight_count'], _highlightCountMeta)); + } + if (data.containsKey('notification_count')) { + context.handle( + _notificationCountMeta, + notificationCount.isAcceptableOrUnknown( + data['notification_count'], _notificationCountMeta)); + } + if (data.containsKey('prev_batch')) { + context.handle(_prevBatchMeta, + prevBatch.isAcceptableOrUnknown(data['prev_batch'], _prevBatchMeta)); + } + if (data.containsKey('joined_member_count')) { + context.handle( + _joinedMemberCountMeta, + joinedMemberCount.isAcceptableOrUnknown( + data['joined_member_count'], _joinedMemberCountMeta)); + } + if (data.containsKey('invited_member_count')) { + context.handle( + _invitedMemberCountMeta, + invitedMemberCount.isAcceptableOrUnknown( + data['invited_member_count'], _invitedMemberCountMeta)); + } + if (data.containsKey('newest_sort_order')) { + context.handle( + _newestSortOrderMeta, + newestSortOrder.isAcceptableOrUnknown( + data['newest_sort_order'], _newestSortOrderMeta)); + } + if (data.containsKey('oldest_sort_order')) { + context.handle( + _oldestSortOrderMeta, + oldestSortOrder.isAcceptableOrUnknown( + data['oldest_sort_order'], _oldestSortOrderMeta)); + } + if (data.containsKey('heroes')) { + context.handle(_heroesMeta, + heroes.isAcceptableOrUnknown(data['heroes'], _heroesMeta)); + } + return context; + } + + @override + Set get $primaryKey => {}; + @override + DbRoom map(Map data, {String tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : null; + return DbRoom.fromData(data, _db, prefix: effectivePrefix); + } + + @override + Rooms createAlias(String alias) { + return Rooms(_db, alias); + } + + @override + List get customConstraints => const ['UNIQUE(client_id, room_id)']; + @override + bool get dontWriteConstraints => true; +} + +class DbEvent extends DataClass implements Insertable { + final int clientId; + final String eventId; + final String roomId; + final double sortOrder; + final DateTime originServerTs; + final String sender; + final String type; + final String unsigned; + final String content; + final String prevContent; + final String stateKey; + final int status; + DbEvent( + {@required this.clientId, + @required this.eventId, + @required this.roomId, + @required this.sortOrder, + @required this.originServerTs, + @required this.sender, + @required this.type, + this.unsigned, + this.content, + this.prevContent, + this.stateKey, + this.status}); + factory DbEvent.fromData(Map data, GeneratedDatabase db, + {String prefix}) { + final effectivePrefix = prefix ?? ''; + final intType = db.typeSystem.forDartType(); + final stringType = db.typeSystem.forDartType(); + final doubleType = db.typeSystem.forDartType(); + final dateTimeType = db.typeSystem.forDartType(); + return DbEvent( + clientId: + intType.mapFromDatabaseResponse(data['${effectivePrefix}client_id']), + eventId: stringType + .mapFromDatabaseResponse(data['${effectivePrefix}event_id']), + roomId: + stringType.mapFromDatabaseResponse(data['${effectivePrefix}room_id']), + sortOrder: doubleType + .mapFromDatabaseResponse(data['${effectivePrefix}sort_order']), + originServerTs: dateTimeType + .mapFromDatabaseResponse(data['${effectivePrefix}origin_server_ts']), + sender: + stringType.mapFromDatabaseResponse(data['${effectivePrefix}sender']), + type: stringType.mapFromDatabaseResponse(data['${effectivePrefix}type']), + unsigned: stringType + .mapFromDatabaseResponse(data['${effectivePrefix}unsigned']), + content: + stringType.mapFromDatabaseResponse(data['${effectivePrefix}content']), + prevContent: stringType + .mapFromDatabaseResponse(data['${effectivePrefix}prev_content']), + stateKey: stringType + .mapFromDatabaseResponse(data['${effectivePrefix}state_key']), + status: intType.mapFromDatabaseResponse(data['${effectivePrefix}status']), + ); + } + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (!nullToAbsent || clientId != null) { + map['client_id'] = Variable(clientId); + } + if (!nullToAbsent || eventId != null) { + map['event_id'] = Variable(eventId); + } + if (!nullToAbsent || roomId != null) { + map['room_id'] = Variable(roomId); + } + if (!nullToAbsent || sortOrder != null) { + map['sort_order'] = Variable(sortOrder); + } + if (!nullToAbsent || originServerTs != null) { + map['origin_server_ts'] = Variable(originServerTs); + } + if (!nullToAbsent || sender != null) { + map['sender'] = Variable(sender); + } + if (!nullToAbsent || type != null) { + map['type'] = Variable(type); + } + if (!nullToAbsent || unsigned != null) { + map['unsigned'] = Variable(unsigned); + } + if (!nullToAbsent || content != null) { + map['content'] = Variable(content); + } + if (!nullToAbsent || prevContent != null) { + map['prev_content'] = Variable(prevContent); + } + if (!nullToAbsent || stateKey != null) { + map['state_key'] = Variable(stateKey); + } + if (!nullToAbsent || status != null) { + map['status'] = Variable(status); + } + return map; + } + + factory DbEvent.fromJson(Map json, + {ValueSerializer serializer}) { + serializer ??= moorRuntimeOptions.defaultSerializer; + return DbEvent( + clientId: serializer.fromJson(json['client_id']), + eventId: serializer.fromJson(json['event_id']), + roomId: serializer.fromJson(json['room_id']), + sortOrder: serializer.fromJson(json['sort_order']), + originServerTs: serializer.fromJson(json['origin_server_ts']), + sender: serializer.fromJson(json['sender']), + type: serializer.fromJson(json['type']), + unsigned: serializer.fromJson(json['unsigned']), + content: serializer.fromJson(json['content']), + prevContent: serializer.fromJson(json['prev_content']), + stateKey: serializer.fromJson(json['state_key']), + status: serializer.fromJson(json['status']), + ); + } + @override + Map toJson({ValueSerializer serializer}) { + serializer ??= moorRuntimeOptions.defaultSerializer; + return { + 'client_id': serializer.toJson(clientId), + 'event_id': serializer.toJson(eventId), + 'room_id': serializer.toJson(roomId), + 'sort_order': serializer.toJson(sortOrder), + 'origin_server_ts': serializer.toJson(originServerTs), + 'sender': serializer.toJson(sender), + 'type': serializer.toJson(type), + 'unsigned': serializer.toJson(unsigned), + 'content': serializer.toJson(content), + 'prev_content': serializer.toJson(prevContent), + 'state_key': serializer.toJson(stateKey), + 'status': serializer.toJson(status), + }; + } + + DbEvent copyWith( + {int clientId, + String eventId, + String roomId, + double sortOrder, + DateTime originServerTs, + String sender, + String type, + String unsigned, + String content, + String prevContent, + String stateKey, + int status}) => + DbEvent( + clientId: clientId ?? this.clientId, + eventId: eventId ?? this.eventId, + roomId: roomId ?? this.roomId, + sortOrder: sortOrder ?? this.sortOrder, + originServerTs: originServerTs ?? this.originServerTs, + sender: sender ?? this.sender, + type: type ?? this.type, + unsigned: unsigned ?? this.unsigned, + content: content ?? this.content, + prevContent: prevContent ?? this.prevContent, + stateKey: stateKey ?? this.stateKey, + status: status ?? this.status, + ); + @override + String toString() { + return (StringBuffer('DbEvent(') + ..write('clientId: $clientId, ') + ..write('eventId: $eventId, ') + ..write('roomId: $roomId, ') + ..write('sortOrder: $sortOrder, ') + ..write('originServerTs: $originServerTs, ') + ..write('sender: $sender, ') + ..write('type: $type, ') + ..write('unsigned: $unsigned, ') + ..write('content: $content, ') + ..write('prevContent: $prevContent, ') + ..write('stateKey: $stateKey, ') + ..write('status: $status') + ..write(')')) + .toString(); + } + + @override + int get hashCode => $mrjf($mrjc( + clientId.hashCode, + $mrjc( + eventId.hashCode, + $mrjc( + roomId.hashCode, + $mrjc( + sortOrder.hashCode, + $mrjc( + originServerTs.hashCode, + $mrjc( + sender.hashCode, + $mrjc( + type.hashCode, + $mrjc( + unsigned.hashCode, + $mrjc( + content.hashCode, + $mrjc( + prevContent.hashCode, + $mrjc(stateKey.hashCode, + status.hashCode)))))))))))); + @override + bool operator ==(dynamic other) => + identical(this, other) || + (other is DbEvent && + other.clientId == this.clientId && + other.eventId == this.eventId && + other.roomId == this.roomId && + other.sortOrder == this.sortOrder && + other.originServerTs == this.originServerTs && + other.sender == this.sender && + other.type == this.type && + other.unsigned == this.unsigned && + other.content == this.content && + other.prevContent == this.prevContent && + other.stateKey == this.stateKey && + other.status == this.status); +} + +class EventsCompanion extends UpdateCompanion { + final Value clientId; + final Value eventId; + final Value roomId; + final Value sortOrder; + final Value originServerTs; + final Value sender; + final Value type; + final Value unsigned; + final Value content; + final Value prevContent; + final Value stateKey; + final Value status; + const EventsCompanion({ + this.clientId = const Value.absent(), + this.eventId = const Value.absent(), + this.roomId = const Value.absent(), + this.sortOrder = const Value.absent(), + this.originServerTs = const Value.absent(), + this.sender = const Value.absent(), + this.type = const Value.absent(), + this.unsigned = const Value.absent(), + this.content = const Value.absent(), + this.prevContent = const Value.absent(), + this.stateKey = const Value.absent(), + this.status = const Value.absent(), + }); + EventsCompanion.insert({ + @required int clientId, + @required String eventId, + @required String roomId, + @required double sortOrder, + @required DateTime originServerTs, + @required String sender, + @required String type, + this.unsigned = const Value.absent(), + this.content = const Value.absent(), + this.prevContent = const Value.absent(), + this.stateKey = const Value.absent(), + this.status = const Value.absent(), + }) : clientId = Value(clientId), + eventId = Value(eventId), + roomId = Value(roomId), + sortOrder = Value(sortOrder), + originServerTs = Value(originServerTs), + sender = Value(sender), + type = Value(type); + static Insertable custom({ + Expression clientId, + Expression eventId, + Expression roomId, + Expression sortOrder, + Expression originServerTs, + Expression sender, + Expression type, + Expression unsigned, + Expression content, + Expression prevContent, + Expression stateKey, + Expression status, + }) { + return RawValuesInsertable({ + if (clientId != null) 'client_id': clientId, + if (eventId != null) 'event_id': eventId, + if (roomId != null) 'room_id': roomId, + if (sortOrder != null) 'sort_order': sortOrder, + if (originServerTs != null) 'origin_server_ts': originServerTs, + if (sender != null) 'sender': sender, + if (type != null) 'type': type, + if (unsigned != null) 'unsigned': unsigned, + if (content != null) 'content': content, + if (prevContent != null) 'prev_content': prevContent, + if (stateKey != null) 'state_key': stateKey, + if (status != null) 'status': status, + }); + } + + EventsCompanion copyWith( + {Value clientId, + Value eventId, + Value roomId, + Value sortOrder, + Value originServerTs, + Value sender, + Value type, + Value unsigned, + Value content, + Value prevContent, + Value stateKey, + Value status}) { + return EventsCompanion( + clientId: clientId ?? this.clientId, + eventId: eventId ?? this.eventId, + roomId: roomId ?? this.roomId, + sortOrder: sortOrder ?? this.sortOrder, + originServerTs: originServerTs ?? this.originServerTs, + sender: sender ?? this.sender, + type: type ?? this.type, + unsigned: unsigned ?? this.unsigned, + content: content ?? this.content, + prevContent: prevContent ?? this.prevContent, + stateKey: stateKey ?? this.stateKey, + status: status ?? this.status, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (clientId.present) { + map['client_id'] = Variable(clientId.value); + } + if (eventId.present) { + map['event_id'] = Variable(eventId.value); + } + if (roomId.present) { + map['room_id'] = Variable(roomId.value); + } + if (sortOrder.present) { + map['sort_order'] = Variable(sortOrder.value); + } + if (originServerTs.present) { + map['origin_server_ts'] = Variable(originServerTs.value); + } + if (sender.present) { + map['sender'] = Variable(sender.value); + } + if (type.present) { + map['type'] = Variable(type.value); + } + if (unsigned.present) { + map['unsigned'] = Variable(unsigned.value); + } + if (content.present) { + map['content'] = Variable(content.value); + } + if (prevContent.present) { + map['prev_content'] = Variable(prevContent.value); + } + if (stateKey.present) { + map['state_key'] = Variable(stateKey.value); + } + if (status.present) { + map['status'] = Variable(status.value); + } + return map; + } +} + +class Events extends Table with TableInfo { + final GeneratedDatabase _db; + final String _alias; + Events(this._db, [this._alias]); + final VerificationMeta _clientIdMeta = const VerificationMeta('clientId'); + GeneratedIntColumn _clientId; + GeneratedIntColumn get clientId => _clientId ??= _constructClientId(); + GeneratedIntColumn _constructClientId() { + return GeneratedIntColumn('client_id', $tableName, false, + $customConstraints: 'NOT NULL REFERENCES clients(client_id)'); + } + + final VerificationMeta _eventIdMeta = const VerificationMeta('eventId'); + GeneratedTextColumn _eventId; + GeneratedTextColumn get eventId => _eventId ??= _constructEventId(); + GeneratedTextColumn _constructEventId() { + return GeneratedTextColumn('event_id', $tableName, false, + $customConstraints: 'NOT NULL'); + } + + final VerificationMeta _roomIdMeta = const VerificationMeta('roomId'); + GeneratedTextColumn _roomId; + GeneratedTextColumn get roomId => _roomId ??= _constructRoomId(); + GeneratedTextColumn _constructRoomId() { + return GeneratedTextColumn('room_id', $tableName, false, + $customConstraints: 'NOT NULL'); + } + + final VerificationMeta _sortOrderMeta = const VerificationMeta('sortOrder'); + GeneratedRealColumn _sortOrder; + GeneratedRealColumn get sortOrder => _sortOrder ??= _constructSortOrder(); + GeneratedRealColumn _constructSortOrder() { + return GeneratedRealColumn('sort_order', $tableName, false, + $customConstraints: 'NOT NULL'); + } + + final VerificationMeta _originServerTsMeta = + const VerificationMeta('originServerTs'); + GeneratedDateTimeColumn _originServerTs; + GeneratedDateTimeColumn get originServerTs => + _originServerTs ??= _constructOriginServerTs(); + GeneratedDateTimeColumn _constructOriginServerTs() { + return GeneratedDateTimeColumn('origin_server_ts', $tableName, false, + $customConstraints: 'NOT NULL'); + } + + final VerificationMeta _senderMeta = const VerificationMeta('sender'); + GeneratedTextColumn _sender; + GeneratedTextColumn get sender => _sender ??= _constructSender(); + GeneratedTextColumn _constructSender() { + return GeneratedTextColumn('sender', $tableName, false, + $customConstraints: 'NOT NULL'); + } + + final VerificationMeta _typeMeta = const VerificationMeta('type'); + GeneratedTextColumn _type; + GeneratedTextColumn get type => _type ??= _constructType(); + GeneratedTextColumn _constructType() { + return GeneratedTextColumn('type', $tableName, false, + $customConstraints: 'NOT NULL'); + } + + final VerificationMeta _unsignedMeta = const VerificationMeta('unsigned'); + GeneratedTextColumn _unsigned; + GeneratedTextColumn get unsigned => _unsigned ??= _constructUnsigned(); + GeneratedTextColumn _constructUnsigned() { + return GeneratedTextColumn('unsigned', $tableName, true, + $customConstraints: ''); + } + + final VerificationMeta _contentMeta = const VerificationMeta('content'); + GeneratedTextColumn _content; + GeneratedTextColumn get content => _content ??= _constructContent(); + GeneratedTextColumn _constructContent() { + return GeneratedTextColumn('content', $tableName, true, + $customConstraints: ''); + } + + final VerificationMeta _prevContentMeta = + const VerificationMeta('prevContent'); + GeneratedTextColumn _prevContent; + GeneratedTextColumn get prevContent => + _prevContent ??= _constructPrevContent(); + GeneratedTextColumn _constructPrevContent() { + return GeneratedTextColumn('prev_content', $tableName, true, + $customConstraints: ''); + } + + final VerificationMeta _stateKeyMeta = const VerificationMeta('stateKey'); + GeneratedTextColumn _stateKey; + GeneratedTextColumn get stateKey => _stateKey ??= _constructStateKey(); + GeneratedTextColumn _constructStateKey() { + return GeneratedTextColumn('state_key', $tableName, true, + $customConstraints: ''); + } + + final VerificationMeta _statusMeta = const VerificationMeta('status'); + GeneratedIntColumn _status; + GeneratedIntColumn get status => _status ??= _constructStatus(); + GeneratedIntColumn _constructStatus() { + return GeneratedIntColumn('status', $tableName, true, + $customConstraints: ''); + } + + @override + List get $columns => [ + clientId, + eventId, + roomId, + sortOrder, + originServerTs, + sender, + type, + unsigned, + content, + prevContent, + stateKey, + status + ]; + @override + Events get asDslTable => this; + @override + String get $tableName => _alias ?? 'events'; + @override + final String actualTableName = 'events'; + @override + VerificationContext validateIntegrity(Insertable instance, + {bool isInserting = false}) { + final context = VerificationContext(); + final data = instance.toColumns(true); + if (data.containsKey('client_id')) { + context.handle(_clientIdMeta, + clientId.isAcceptableOrUnknown(data['client_id'], _clientIdMeta)); + } else if (isInserting) { + context.missing(_clientIdMeta); + } + if (data.containsKey('event_id')) { + context.handle(_eventIdMeta, + eventId.isAcceptableOrUnknown(data['event_id'], _eventIdMeta)); + } else if (isInserting) { + context.missing(_eventIdMeta); + } + if (data.containsKey('room_id')) { + context.handle(_roomIdMeta, + roomId.isAcceptableOrUnknown(data['room_id'], _roomIdMeta)); + } else if (isInserting) { + context.missing(_roomIdMeta); + } + if (data.containsKey('sort_order')) { + context.handle(_sortOrderMeta, + sortOrder.isAcceptableOrUnknown(data['sort_order'], _sortOrderMeta)); + } else if (isInserting) { + context.missing(_sortOrderMeta); + } + if (data.containsKey('origin_server_ts')) { + context.handle( + _originServerTsMeta, + originServerTs.isAcceptableOrUnknown( + data['origin_server_ts'], _originServerTsMeta)); + } else if (isInserting) { + context.missing(_originServerTsMeta); + } + if (data.containsKey('sender')) { + context.handle(_senderMeta, + sender.isAcceptableOrUnknown(data['sender'], _senderMeta)); + } else if (isInserting) { + context.missing(_senderMeta); + } + if (data.containsKey('type')) { + context.handle( + _typeMeta, type.isAcceptableOrUnknown(data['type'], _typeMeta)); + } else if (isInserting) { + context.missing(_typeMeta); + } + if (data.containsKey('unsigned')) { + context.handle(_unsignedMeta, + unsigned.isAcceptableOrUnknown(data['unsigned'], _unsignedMeta)); + } + if (data.containsKey('content')) { + context.handle(_contentMeta, + content.isAcceptableOrUnknown(data['content'], _contentMeta)); + } + if (data.containsKey('prev_content')) { + context.handle( + _prevContentMeta, + prevContent.isAcceptableOrUnknown( + data['prev_content'], _prevContentMeta)); + } + if (data.containsKey('state_key')) { + context.handle(_stateKeyMeta, + stateKey.isAcceptableOrUnknown(data['state_key'], _stateKeyMeta)); + } + if (data.containsKey('status')) { + context.handle(_statusMeta, + status.isAcceptableOrUnknown(data['status'], _statusMeta)); + } + return context; + } + + @override + Set get $primaryKey => {}; + @override + DbEvent map(Map data, {String tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : null; + return DbEvent.fromData(data, _db, prefix: effectivePrefix); + } + + @override + Events createAlias(String alias) { + return Events(_db, alias); + } + + @override + List get customConstraints => + const ['UNIQUE(client_id, event_id, room_id)']; + @override + bool get dontWriteConstraints => true; +} + +class DbRoomState extends DataClass implements Insertable { + final int clientId; + final String eventId; + final String roomId; + final double sortOrder; + final DateTime originServerTs; + final String sender; + final String type; + final String unsigned; + final String content; + final String prevContent; + final String stateKey; + DbRoomState( + {@required this.clientId, + @required this.eventId, + @required this.roomId, + @required this.sortOrder, + @required this.originServerTs, + @required this.sender, + @required this.type, + this.unsigned, + this.content, + this.prevContent, + @required this.stateKey}); + factory DbRoomState.fromData(Map data, GeneratedDatabase db, + {String prefix}) { + final effectivePrefix = prefix ?? ''; + final intType = db.typeSystem.forDartType(); + final stringType = db.typeSystem.forDartType(); + final doubleType = db.typeSystem.forDartType(); + final dateTimeType = db.typeSystem.forDartType(); + return DbRoomState( + clientId: + intType.mapFromDatabaseResponse(data['${effectivePrefix}client_id']), + eventId: stringType + .mapFromDatabaseResponse(data['${effectivePrefix}event_id']), + roomId: + stringType.mapFromDatabaseResponse(data['${effectivePrefix}room_id']), + sortOrder: doubleType + .mapFromDatabaseResponse(data['${effectivePrefix}sort_order']), + originServerTs: dateTimeType + .mapFromDatabaseResponse(data['${effectivePrefix}origin_server_ts']), + sender: + stringType.mapFromDatabaseResponse(data['${effectivePrefix}sender']), + type: stringType.mapFromDatabaseResponse(data['${effectivePrefix}type']), + unsigned: stringType + .mapFromDatabaseResponse(data['${effectivePrefix}unsigned']), + content: + stringType.mapFromDatabaseResponse(data['${effectivePrefix}content']), + prevContent: stringType + .mapFromDatabaseResponse(data['${effectivePrefix}prev_content']), + stateKey: stringType + .mapFromDatabaseResponse(data['${effectivePrefix}state_key']), + ); + } + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (!nullToAbsent || clientId != null) { + map['client_id'] = Variable(clientId); + } + if (!nullToAbsent || eventId != null) { + map['event_id'] = Variable(eventId); + } + if (!nullToAbsent || roomId != null) { + map['room_id'] = Variable(roomId); + } + if (!nullToAbsent || sortOrder != null) { + map['sort_order'] = Variable(sortOrder); + } + if (!nullToAbsent || originServerTs != null) { + map['origin_server_ts'] = Variable(originServerTs); + } + if (!nullToAbsent || sender != null) { + map['sender'] = Variable(sender); + } + if (!nullToAbsent || type != null) { + map['type'] = Variable(type); + } + if (!nullToAbsent || unsigned != null) { + map['unsigned'] = Variable(unsigned); + } + if (!nullToAbsent || content != null) { + map['content'] = Variable(content); + } + if (!nullToAbsent || prevContent != null) { + map['prev_content'] = Variable(prevContent); + } + if (!nullToAbsent || stateKey != null) { + map['state_key'] = Variable(stateKey); + } + return map; + } + + factory DbRoomState.fromJson(Map json, + {ValueSerializer serializer}) { + serializer ??= moorRuntimeOptions.defaultSerializer; + return DbRoomState( + clientId: serializer.fromJson(json['client_id']), + eventId: serializer.fromJson(json['event_id']), + roomId: serializer.fromJson(json['room_id']), + sortOrder: serializer.fromJson(json['sort_order']), + originServerTs: serializer.fromJson(json['origin_server_ts']), + sender: serializer.fromJson(json['sender']), + type: serializer.fromJson(json['type']), + unsigned: serializer.fromJson(json['unsigned']), + content: serializer.fromJson(json['content']), + prevContent: serializer.fromJson(json['prev_content']), + stateKey: serializer.fromJson(json['state_key']), + ); + } + @override + Map toJson({ValueSerializer serializer}) { + serializer ??= moorRuntimeOptions.defaultSerializer; + return { + 'client_id': serializer.toJson(clientId), + 'event_id': serializer.toJson(eventId), + 'room_id': serializer.toJson(roomId), + 'sort_order': serializer.toJson(sortOrder), + 'origin_server_ts': serializer.toJson(originServerTs), + 'sender': serializer.toJson(sender), + 'type': serializer.toJson(type), + 'unsigned': serializer.toJson(unsigned), + 'content': serializer.toJson(content), + 'prev_content': serializer.toJson(prevContent), + 'state_key': serializer.toJson(stateKey), + }; + } + + DbRoomState copyWith( + {int clientId, + String eventId, + String roomId, + double sortOrder, + DateTime originServerTs, + String sender, + String type, + String unsigned, + String content, + String prevContent, + String stateKey}) => + DbRoomState( + clientId: clientId ?? this.clientId, + eventId: eventId ?? this.eventId, + roomId: roomId ?? this.roomId, + sortOrder: sortOrder ?? this.sortOrder, + originServerTs: originServerTs ?? this.originServerTs, + sender: sender ?? this.sender, + type: type ?? this.type, + unsigned: unsigned ?? this.unsigned, + content: content ?? this.content, + prevContent: prevContent ?? this.prevContent, + stateKey: stateKey ?? this.stateKey, + ); + @override + String toString() { + return (StringBuffer('DbRoomState(') + ..write('clientId: $clientId, ') + ..write('eventId: $eventId, ') + ..write('roomId: $roomId, ') + ..write('sortOrder: $sortOrder, ') + ..write('originServerTs: $originServerTs, ') + ..write('sender: $sender, ') + ..write('type: $type, ') + ..write('unsigned: $unsigned, ') + ..write('content: $content, ') + ..write('prevContent: $prevContent, ') + ..write('stateKey: $stateKey') + ..write(')')) + .toString(); + } + + @override + int get hashCode => $mrjf($mrjc( + clientId.hashCode, + $mrjc( + eventId.hashCode, + $mrjc( + roomId.hashCode, + $mrjc( + sortOrder.hashCode, + $mrjc( + originServerTs.hashCode, + $mrjc( + sender.hashCode, + $mrjc( + type.hashCode, + $mrjc( + unsigned.hashCode, + $mrjc( + content.hashCode, + $mrjc(prevContent.hashCode, + stateKey.hashCode))))))))))); + @override + bool operator ==(dynamic other) => + identical(this, other) || + (other is DbRoomState && + other.clientId == this.clientId && + other.eventId == this.eventId && + other.roomId == this.roomId && + other.sortOrder == this.sortOrder && + other.originServerTs == this.originServerTs && + other.sender == this.sender && + other.type == this.type && + other.unsigned == this.unsigned && + other.content == this.content && + other.prevContent == this.prevContent && + other.stateKey == this.stateKey); +} + +class RoomStatesCompanion extends UpdateCompanion { + final Value clientId; + final Value eventId; + final Value roomId; + final Value sortOrder; + final Value originServerTs; + final Value sender; + final Value type; + final Value unsigned; + final Value content; + final Value prevContent; + final Value stateKey; + const RoomStatesCompanion({ + this.clientId = const Value.absent(), + this.eventId = const Value.absent(), + this.roomId = const Value.absent(), + this.sortOrder = const Value.absent(), + this.originServerTs = const Value.absent(), + this.sender = const Value.absent(), + this.type = const Value.absent(), + this.unsigned = const Value.absent(), + this.content = const Value.absent(), + this.prevContent = const Value.absent(), + this.stateKey = const Value.absent(), + }); + RoomStatesCompanion.insert({ + @required int clientId, + @required String eventId, + @required String roomId, + @required double sortOrder, + @required DateTime originServerTs, + @required String sender, + @required String type, + this.unsigned = const Value.absent(), + this.content = const Value.absent(), + this.prevContent = const Value.absent(), + @required String stateKey, + }) : clientId = Value(clientId), + eventId = Value(eventId), + roomId = Value(roomId), + sortOrder = Value(sortOrder), + originServerTs = Value(originServerTs), + sender = Value(sender), + type = Value(type), + stateKey = Value(stateKey); + static Insertable custom({ + Expression clientId, + Expression eventId, + Expression roomId, + Expression sortOrder, + Expression originServerTs, + Expression sender, + Expression type, + Expression unsigned, + Expression content, + Expression prevContent, + Expression stateKey, + }) { + return RawValuesInsertable({ + if (clientId != null) 'client_id': clientId, + if (eventId != null) 'event_id': eventId, + if (roomId != null) 'room_id': roomId, + if (sortOrder != null) 'sort_order': sortOrder, + if (originServerTs != null) 'origin_server_ts': originServerTs, + if (sender != null) 'sender': sender, + if (type != null) 'type': type, + if (unsigned != null) 'unsigned': unsigned, + if (content != null) 'content': content, + if (prevContent != null) 'prev_content': prevContent, + if (stateKey != null) 'state_key': stateKey, + }); + } + + RoomStatesCompanion copyWith( + {Value clientId, + Value eventId, + Value roomId, + Value sortOrder, + Value originServerTs, + Value sender, + Value type, + Value unsigned, + Value content, + Value prevContent, + Value stateKey}) { + return RoomStatesCompanion( + clientId: clientId ?? this.clientId, + eventId: eventId ?? this.eventId, + roomId: roomId ?? this.roomId, + sortOrder: sortOrder ?? this.sortOrder, + originServerTs: originServerTs ?? this.originServerTs, + sender: sender ?? this.sender, + type: type ?? this.type, + unsigned: unsigned ?? this.unsigned, + content: content ?? this.content, + prevContent: prevContent ?? this.prevContent, + stateKey: stateKey ?? this.stateKey, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (clientId.present) { + map['client_id'] = Variable(clientId.value); + } + if (eventId.present) { + map['event_id'] = Variable(eventId.value); + } + if (roomId.present) { + map['room_id'] = Variable(roomId.value); + } + if (sortOrder.present) { + map['sort_order'] = Variable(sortOrder.value); + } + if (originServerTs.present) { + map['origin_server_ts'] = Variable(originServerTs.value); + } + if (sender.present) { + map['sender'] = Variable(sender.value); + } + if (type.present) { + map['type'] = Variable(type.value); + } + if (unsigned.present) { + map['unsigned'] = Variable(unsigned.value); + } + if (content.present) { + map['content'] = Variable(content.value); + } + if (prevContent.present) { + map['prev_content'] = Variable(prevContent.value); + } + if (stateKey.present) { + map['state_key'] = Variable(stateKey.value); + } + return map; + } +} + +class RoomStates extends Table with TableInfo { + final GeneratedDatabase _db; + final String _alias; + RoomStates(this._db, [this._alias]); + final VerificationMeta _clientIdMeta = const VerificationMeta('clientId'); + GeneratedIntColumn _clientId; + GeneratedIntColumn get clientId => _clientId ??= _constructClientId(); + GeneratedIntColumn _constructClientId() { + return GeneratedIntColumn('client_id', $tableName, false, + $customConstraints: 'NOT NULL REFERENCES clients(client_id)'); + } + + final VerificationMeta _eventIdMeta = const VerificationMeta('eventId'); + GeneratedTextColumn _eventId; + GeneratedTextColumn get eventId => _eventId ??= _constructEventId(); + GeneratedTextColumn _constructEventId() { + return GeneratedTextColumn('event_id', $tableName, false, + $customConstraints: 'NOT NULL'); + } + + final VerificationMeta _roomIdMeta = const VerificationMeta('roomId'); + GeneratedTextColumn _roomId; + GeneratedTextColumn get roomId => _roomId ??= _constructRoomId(); + GeneratedTextColumn _constructRoomId() { + return GeneratedTextColumn('room_id', $tableName, false, + $customConstraints: 'NOT NULL'); + } + + final VerificationMeta _sortOrderMeta = const VerificationMeta('sortOrder'); + GeneratedRealColumn _sortOrder; + GeneratedRealColumn get sortOrder => _sortOrder ??= _constructSortOrder(); + GeneratedRealColumn _constructSortOrder() { + return GeneratedRealColumn('sort_order', $tableName, false, + $customConstraints: 'NOT NULL'); + } + + final VerificationMeta _originServerTsMeta = + const VerificationMeta('originServerTs'); + GeneratedDateTimeColumn _originServerTs; + GeneratedDateTimeColumn get originServerTs => + _originServerTs ??= _constructOriginServerTs(); + GeneratedDateTimeColumn _constructOriginServerTs() { + return GeneratedDateTimeColumn('origin_server_ts', $tableName, false, + $customConstraints: 'NOT NULL'); + } + + final VerificationMeta _senderMeta = const VerificationMeta('sender'); + GeneratedTextColumn _sender; + GeneratedTextColumn get sender => _sender ??= _constructSender(); + GeneratedTextColumn _constructSender() { + return GeneratedTextColumn('sender', $tableName, false, + $customConstraints: 'NOT NULL'); + } + + final VerificationMeta _typeMeta = const VerificationMeta('type'); + GeneratedTextColumn _type; + GeneratedTextColumn get type => _type ??= _constructType(); + GeneratedTextColumn _constructType() { + return GeneratedTextColumn('type', $tableName, false, + $customConstraints: 'NOT NULL'); + } + + final VerificationMeta _unsignedMeta = const VerificationMeta('unsigned'); + GeneratedTextColumn _unsigned; + GeneratedTextColumn get unsigned => _unsigned ??= _constructUnsigned(); + GeneratedTextColumn _constructUnsigned() { + return GeneratedTextColumn('unsigned', $tableName, true, + $customConstraints: ''); + } + + final VerificationMeta _contentMeta = const VerificationMeta('content'); + GeneratedTextColumn _content; + GeneratedTextColumn get content => _content ??= _constructContent(); + GeneratedTextColumn _constructContent() { + return GeneratedTextColumn('content', $tableName, true, + $customConstraints: ''); + } + + final VerificationMeta _prevContentMeta = + const VerificationMeta('prevContent'); + GeneratedTextColumn _prevContent; + GeneratedTextColumn get prevContent => + _prevContent ??= _constructPrevContent(); + GeneratedTextColumn _constructPrevContent() { + return GeneratedTextColumn('prev_content', $tableName, true, + $customConstraints: ''); + } + + final VerificationMeta _stateKeyMeta = const VerificationMeta('stateKey'); + GeneratedTextColumn _stateKey; + GeneratedTextColumn get stateKey => _stateKey ??= _constructStateKey(); + GeneratedTextColumn _constructStateKey() { + return GeneratedTextColumn('state_key', $tableName, false, + $customConstraints: 'NOT NULL'); + } + + @override + List get $columns => [ + clientId, + eventId, + roomId, + sortOrder, + originServerTs, + sender, + type, + unsigned, + content, + prevContent, + stateKey + ]; + @override + RoomStates get asDslTable => this; + @override + String get $tableName => _alias ?? 'room_states'; + @override + final String actualTableName = 'room_states'; + @override + VerificationContext validateIntegrity(Insertable instance, + {bool isInserting = false}) { + final context = VerificationContext(); + final data = instance.toColumns(true); + if (data.containsKey('client_id')) { + context.handle(_clientIdMeta, + clientId.isAcceptableOrUnknown(data['client_id'], _clientIdMeta)); + } else if (isInserting) { + context.missing(_clientIdMeta); + } + if (data.containsKey('event_id')) { + context.handle(_eventIdMeta, + eventId.isAcceptableOrUnknown(data['event_id'], _eventIdMeta)); + } else if (isInserting) { + context.missing(_eventIdMeta); + } + if (data.containsKey('room_id')) { + context.handle(_roomIdMeta, + roomId.isAcceptableOrUnknown(data['room_id'], _roomIdMeta)); + } else if (isInserting) { + context.missing(_roomIdMeta); + } + if (data.containsKey('sort_order')) { + context.handle(_sortOrderMeta, + sortOrder.isAcceptableOrUnknown(data['sort_order'], _sortOrderMeta)); + } else if (isInserting) { + context.missing(_sortOrderMeta); + } + if (data.containsKey('origin_server_ts')) { + context.handle( + _originServerTsMeta, + originServerTs.isAcceptableOrUnknown( + data['origin_server_ts'], _originServerTsMeta)); + } else if (isInserting) { + context.missing(_originServerTsMeta); + } + if (data.containsKey('sender')) { + context.handle(_senderMeta, + sender.isAcceptableOrUnknown(data['sender'], _senderMeta)); + } else if (isInserting) { + context.missing(_senderMeta); + } + if (data.containsKey('type')) { + context.handle( + _typeMeta, type.isAcceptableOrUnknown(data['type'], _typeMeta)); + } else if (isInserting) { + context.missing(_typeMeta); + } + if (data.containsKey('unsigned')) { + context.handle(_unsignedMeta, + unsigned.isAcceptableOrUnknown(data['unsigned'], _unsignedMeta)); + } + if (data.containsKey('content')) { + context.handle(_contentMeta, + content.isAcceptableOrUnknown(data['content'], _contentMeta)); + } + if (data.containsKey('prev_content')) { + context.handle( + _prevContentMeta, + prevContent.isAcceptableOrUnknown( + data['prev_content'], _prevContentMeta)); + } + if (data.containsKey('state_key')) { + context.handle(_stateKeyMeta, + stateKey.isAcceptableOrUnknown(data['state_key'], _stateKeyMeta)); + } else if (isInserting) { + context.missing(_stateKeyMeta); + } + return context; + } + + @override + Set get $primaryKey => {}; + @override + DbRoomState map(Map data, {String tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : null; + return DbRoomState.fromData(data, _db, prefix: effectivePrefix); + } + + @override + RoomStates createAlias(String alias) { + return RoomStates(_db, alias); + } + + @override + List get customConstraints => const [ + 'UNIQUE(client_id, event_id, room_id)', + 'UNIQUE(client_id, room_id, state_key, type)' + ]; + @override + bool get dontWriteConstraints => true; +} + +class DbAccountData extends DataClass implements Insertable { + final int clientId; + final String type; + final String content; + DbAccountData({@required this.clientId, @required this.type, this.content}); + factory DbAccountData.fromData( + Map data, GeneratedDatabase db, + {String prefix}) { + final effectivePrefix = prefix ?? ''; + final intType = db.typeSystem.forDartType(); + final stringType = db.typeSystem.forDartType(); + return DbAccountData( + clientId: + intType.mapFromDatabaseResponse(data['${effectivePrefix}client_id']), + type: stringType.mapFromDatabaseResponse(data['${effectivePrefix}type']), + content: + stringType.mapFromDatabaseResponse(data['${effectivePrefix}content']), + ); + } + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (!nullToAbsent || clientId != null) { + map['client_id'] = Variable(clientId); + } + if (!nullToAbsent || type != null) { + map['type'] = Variable(type); + } + if (!nullToAbsent || content != null) { + map['content'] = Variable(content); + } + return map; + } + + factory DbAccountData.fromJson(Map json, + {ValueSerializer serializer}) { + serializer ??= moorRuntimeOptions.defaultSerializer; + return DbAccountData( + clientId: serializer.fromJson(json['client_id']), + type: serializer.fromJson(json['type']), + content: serializer.fromJson(json['content']), + ); + } + @override + Map toJson({ValueSerializer serializer}) { + serializer ??= moorRuntimeOptions.defaultSerializer; + return { + 'client_id': serializer.toJson(clientId), + 'type': serializer.toJson(type), + 'content': serializer.toJson(content), + }; + } + + DbAccountData copyWith({int clientId, String type, String content}) => + DbAccountData( + clientId: clientId ?? this.clientId, + type: type ?? this.type, + content: content ?? this.content, + ); + @override + String toString() { + return (StringBuffer('DbAccountData(') + ..write('clientId: $clientId, ') + ..write('type: $type, ') + ..write('content: $content') + ..write(')')) + .toString(); + } + + @override + int get hashCode => + $mrjf($mrjc(clientId.hashCode, $mrjc(type.hashCode, content.hashCode))); + @override + bool operator ==(dynamic other) => + identical(this, other) || + (other is DbAccountData && + other.clientId == this.clientId && + other.type == this.type && + other.content == this.content); +} + +class AccountDataCompanion extends UpdateCompanion { + final Value clientId; + final Value type; + final Value content; + const AccountDataCompanion({ + this.clientId = const Value.absent(), + this.type = const Value.absent(), + this.content = const Value.absent(), + }); + AccountDataCompanion.insert({ + @required int clientId, + @required String type, + this.content = const Value.absent(), + }) : clientId = Value(clientId), + type = Value(type); + static Insertable custom({ + Expression clientId, + Expression type, + Expression content, + }) { + return RawValuesInsertable({ + if (clientId != null) 'client_id': clientId, + if (type != null) 'type': type, + if (content != null) 'content': content, + }); + } + + AccountDataCompanion copyWith( + {Value clientId, Value type, Value content}) { + return AccountDataCompanion( + clientId: clientId ?? this.clientId, + type: type ?? this.type, + content: content ?? this.content, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (clientId.present) { + map['client_id'] = Variable(clientId.value); + } + if (type.present) { + map['type'] = Variable(type.value); + } + if (content.present) { + map['content'] = Variable(content.value); + } + return map; + } +} + +class AccountData extends Table with TableInfo { + final GeneratedDatabase _db; + final String _alias; + AccountData(this._db, [this._alias]); + final VerificationMeta _clientIdMeta = const VerificationMeta('clientId'); + GeneratedIntColumn _clientId; + GeneratedIntColumn get clientId => _clientId ??= _constructClientId(); + GeneratedIntColumn _constructClientId() { + return GeneratedIntColumn('client_id', $tableName, false, + $customConstraints: 'NOT NULL REFERENCES clients(client_id)'); + } + + final VerificationMeta _typeMeta = const VerificationMeta('type'); + GeneratedTextColumn _type; + GeneratedTextColumn get type => _type ??= _constructType(); + GeneratedTextColumn _constructType() { + return GeneratedTextColumn('type', $tableName, false, + $customConstraints: 'NOT NULL'); + } + + final VerificationMeta _contentMeta = const VerificationMeta('content'); + GeneratedTextColumn _content; + GeneratedTextColumn get content => _content ??= _constructContent(); + GeneratedTextColumn _constructContent() { + return GeneratedTextColumn('content', $tableName, true, + $customConstraints: ''); + } + + @override + List get $columns => [clientId, type, content]; + @override + AccountData get asDslTable => this; + @override + String get $tableName => _alias ?? 'account_data'; + @override + final String actualTableName = 'account_data'; + @override + VerificationContext validateIntegrity(Insertable instance, + {bool isInserting = false}) { + final context = VerificationContext(); + final data = instance.toColumns(true); + if (data.containsKey('client_id')) { + context.handle(_clientIdMeta, + clientId.isAcceptableOrUnknown(data['client_id'], _clientIdMeta)); + } else if (isInserting) { + context.missing(_clientIdMeta); + } + if (data.containsKey('type')) { + context.handle( + _typeMeta, type.isAcceptableOrUnknown(data['type'], _typeMeta)); + } else if (isInserting) { + context.missing(_typeMeta); + } + if (data.containsKey('content')) { + context.handle(_contentMeta, + content.isAcceptableOrUnknown(data['content'], _contentMeta)); + } + return context; + } + + @override + Set get $primaryKey => {}; + @override + DbAccountData map(Map data, {String tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : null; + return DbAccountData.fromData(data, _db, prefix: effectivePrefix); + } + + @override + AccountData createAlias(String alias) { + return AccountData(_db, alias); + } + + @override + List get customConstraints => const ['UNIQUE(client_id, type)']; + @override + bool get dontWriteConstraints => true; +} + +class DbRoomAccountData extends DataClass + implements Insertable { + final int clientId; + final String type; + final String roomId; + final String content; + DbRoomAccountData( + {@required this.clientId, + @required this.type, + @required this.roomId, + this.content}); + factory DbRoomAccountData.fromData( + Map data, GeneratedDatabase db, + {String prefix}) { + final effectivePrefix = prefix ?? ''; + final intType = db.typeSystem.forDartType(); + final stringType = db.typeSystem.forDartType(); + return DbRoomAccountData( + clientId: + intType.mapFromDatabaseResponse(data['${effectivePrefix}client_id']), + type: stringType.mapFromDatabaseResponse(data['${effectivePrefix}type']), + roomId: + stringType.mapFromDatabaseResponse(data['${effectivePrefix}room_id']), + content: + stringType.mapFromDatabaseResponse(data['${effectivePrefix}content']), + ); + } + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (!nullToAbsent || clientId != null) { + map['client_id'] = Variable(clientId); + } + if (!nullToAbsent || type != null) { + map['type'] = Variable(type); + } + if (!nullToAbsent || roomId != null) { + map['room_id'] = Variable(roomId); + } + if (!nullToAbsent || content != null) { + map['content'] = Variable(content); + } + return map; + } + + factory DbRoomAccountData.fromJson(Map json, + {ValueSerializer serializer}) { + serializer ??= moorRuntimeOptions.defaultSerializer; + return DbRoomAccountData( + clientId: serializer.fromJson(json['client_id']), + type: serializer.fromJson(json['type']), + roomId: serializer.fromJson(json['room_id']), + content: serializer.fromJson(json['content']), + ); + } + @override + Map toJson({ValueSerializer serializer}) { + serializer ??= moorRuntimeOptions.defaultSerializer; + return { + 'client_id': serializer.toJson(clientId), + 'type': serializer.toJson(type), + 'room_id': serializer.toJson(roomId), + 'content': serializer.toJson(content), + }; + } + + DbRoomAccountData copyWith( + {int clientId, String type, String roomId, String content}) => + DbRoomAccountData( + clientId: clientId ?? this.clientId, + type: type ?? this.type, + roomId: roomId ?? this.roomId, + content: content ?? this.content, + ); + @override + String toString() { + return (StringBuffer('DbRoomAccountData(') + ..write('clientId: $clientId, ') + ..write('type: $type, ') + ..write('roomId: $roomId, ') + ..write('content: $content') + ..write(')')) + .toString(); + } + + @override + int get hashCode => $mrjf($mrjc(clientId.hashCode, + $mrjc(type.hashCode, $mrjc(roomId.hashCode, content.hashCode)))); + @override + bool operator ==(dynamic other) => + identical(this, other) || + (other is DbRoomAccountData && + other.clientId == this.clientId && + other.type == this.type && + other.roomId == this.roomId && + other.content == this.content); +} + +class RoomAccountDataCompanion extends UpdateCompanion { + final Value clientId; + final Value type; + final Value roomId; + final Value content; + const RoomAccountDataCompanion({ + this.clientId = const Value.absent(), + this.type = const Value.absent(), + this.roomId = const Value.absent(), + this.content = const Value.absent(), + }); + RoomAccountDataCompanion.insert({ + @required int clientId, + @required String type, + @required String roomId, + this.content = const Value.absent(), + }) : clientId = Value(clientId), + type = Value(type), + roomId = Value(roomId); + static Insertable custom({ + Expression clientId, + Expression type, + Expression roomId, + Expression content, + }) { + return RawValuesInsertable({ + if (clientId != null) 'client_id': clientId, + if (type != null) 'type': type, + if (roomId != null) 'room_id': roomId, + if (content != null) 'content': content, + }); + } + + RoomAccountDataCompanion copyWith( + {Value clientId, + Value type, + Value roomId, + Value content}) { + return RoomAccountDataCompanion( + clientId: clientId ?? this.clientId, + type: type ?? this.type, + roomId: roomId ?? this.roomId, + content: content ?? this.content, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (clientId.present) { + map['client_id'] = Variable(clientId.value); + } + if (type.present) { + map['type'] = Variable(type.value); + } + if (roomId.present) { + map['room_id'] = Variable(roomId.value); + } + if (content.present) { + map['content'] = Variable(content.value); + } + return map; + } +} + +class RoomAccountData extends Table + with TableInfo { + final GeneratedDatabase _db; + final String _alias; + RoomAccountData(this._db, [this._alias]); + final VerificationMeta _clientIdMeta = const VerificationMeta('clientId'); + GeneratedIntColumn _clientId; + GeneratedIntColumn get clientId => _clientId ??= _constructClientId(); + GeneratedIntColumn _constructClientId() { + return GeneratedIntColumn('client_id', $tableName, false, + $customConstraints: 'NOT NULL REFERENCES clients(client_id)'); + } + + final VerificationMeta _typeMeta = const VerificationMeta('type'); + GeneratedTextColumn _type; + GeneratedTextColumn get type => _type ??= _constructType(); + GeneratedTextColumn _constructType() { + return GeneratedTextColumn('type', $tableName, false, + $customConstraints: 'NOT NULL'); + } + + final VerificationMeta _roomIdMeta = const VerificationMeta('roomId'); + GeneratedTextColumn _roomId; + GeneratedTextColumn get roomId => _roomId ??= _constructRoomId(); + GeneratedTextColumn _constructRoomId() { + return GeneratedTextColumn('room_id', $tableName, false, + $customConstraints: 'NOT NULL'); + } + + final VerificationMeta _contentMeta = const VerificationMeta('content'); + GeneratedTextColumn _content; + GeneratedTextColumn get content => _content ??= _constructContent(); + GeneratedTextColumn _constructContent() { + return GeneratedTextColumn('content', $tableName, true, + $customConstraints: ''); + } + + @override + List get $columns => [clientId, type, roomId, content]; + @override + RoomAccountData get asDslTable => this; + @override + String get $tableName => _alias ?? 'room_account_data'; + @override + final String actualTableName = 'room_account_data'; + @override + VerificationContext validateIntegrity(Insertable instance, + {bool isInserting = false}) { + final context = VerificationContext(); + final data = instance.toColumns(true); + if (data.containsKey('client_id')) { + context.handle(_clientIdMeta, + clientId.isAcceptableOrUnknown(data['client_id'], _clientIdMeta)); + } else if (isInserting) { + context.missing(_clientIdMeta); + } + if (data.containsKey('type')) { + context.handle( + _typeMeta, type.isAcceptableOrUnknown(data['type'], _typeMeta)); + } else if (isInserting) { + context.missing(_typeMeta); + } + if (data.containsKey('room_id')) { + context.handle(_roomIdMeta, + roomId.isAcceptableOrUnknown(data['room_id'], _roomIdMeta)); + } else if (isInserting) { + context.missing(_roomIdMeta); + } + if (data.containsKey('content')) { + context.handle(_contentMeta, + content.isAcceptableOrUnknown(data['content'], _contentMeta)); + } + return context; + } + + @override + Set get $primaryKey => {}; + @override + DbRoomAccountData map(Map data, {String tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : null; + return DbRoomAccountData.fromData(data, _db, prefix: effectivePrefix); + } + + @override + RoomAccountData createAlias(String alias) { + return RoomAccountData(_db, alias); + } + + @override + List get customConstraints => + const ['UNIQUE(client_id, type, room_id)']; + @override + bool get dontWriteConstraints => true; +} + +class DbPresence extends DataClass implements Insertable { + final int clientId; + final String type; + final String sender; + final String content; + DbPresence( + {@required this.clientId, + @required this.type, + @required this.sender, + this.content}); + factory DbPresence.fromData(Map data, GeneratedDatabase db, + {String prefix}) { + final effectivePrefix = prefix ?? ''; + final intType = db.typeSystem.forDartType(); + final stringType = db.typeSystem.forDartType(); + return DbPresence( + clientId: + intType.mapFromDatabaseResponse(data['${effectivePrefix}client_id']), + type: stringType.mapFromDatabaseResponse(data['${effectivePrefix}type']), + sender: + stringType.mapFromDatabaseResponse(data['${effectivePrefix}sender']), + content: + stringType.mapFromDatabaseResponse(data['${effectivePrefix}content']), + ); + } + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (!nullToAbsent || clientId != null) { + map['client_id'] = Variable(clientId); + } + if (!nullToAbsent || type != null) { + map['type'] = Variable(type); + } + if (!nullToAbsent || sender != null) { + map['sender'] = Variable(sender); + } + if (!nullToAbsent || content != null) { + map['content'] = Variable(content); + } + return map; + } + + factory DbPresence.fromJson(Map json, + {ValueSerializer serializer}) { + serializer ??= moorRuntimeOptions.defaultSerializer; + return DbPresence( + clientId: serializer.fromJson(json['client_id']), + type: serializer.fromJson(json['type']), + sender: serializer.fromJson(json['sender']), + content: serializer.fromJson(json['content']), + ); + } + @override + Map toJson({ValueSerializer serializer}) { + serializer ??= moorRuntimeOptions.defaultSerializer; + return { + 'client_id': serializer.toJson(clientId), + 'type': serializer.toJson(type), + 'sender': serializer.toJson(sender), + 'content': serializer.toJson(content), + }; + } + + DbPresence copyWith( + {int clientId, String type, String sender, String content}) => + DbPresence( + clientId: clientId ?? this.clientId, + type: type ?? this.type, + sender: sender ?? this.sender, + content: content ?? this.content, + ); + @override + String toString() { + return (StringBuffer('DbPresence(') + ..write('clientId: $clientId, ') + ..write('type: $type, ') + ..write('sender: $sender, ') + ..write('content: $content') + ..write(')')) + .toString(); + } + + @override + int get hashCode => $mrjf($mrjc(clientId.hashCode, + $mrjc(type.hashCode, $mrjc(sender.hashCode, content.hashCode)))); + @override + bool operator ==(dynamic other) => + identical(this, other) || + (other is DbPresence && + other.clientId == this.clientId && + other.type == this.type && + other.sender == this.sender && + other.content == this.content); +} + +class PresencesCompanion extends UpdateCompanion { + final Value clientId; + final Value type; + final Value sender; + final Value content; + const PresencesCompanion({ + this.clientId = const Value.absent(), + this.type = const Value.absent(), + this.sender = const Value.absent(), + this.content = const Value.absent(), + }); + PresencesCompanion.insert({ + @required int clientId, + @required String type, + @required String sender, + this.content = const Value.absent(), + }) : clientId = Value(clientId), + type = Value(type), + sender = Value(sender); + static Insertable custom({ + Expression clientId, + Expression type, + Expression sender, + Expression content, + }) { + return RawValuesInsertable({ + if (clientId != null) 'client_id': clientId, + if (type != null) 'type': type, + if (sender != null) 'sender': sender, + if (content != null) 'content': content, + }); + } + + PresencesCompanion copyWith( + {Value clientId, + Value type, + Value sender, + Value content}) { + return PresencesCompanion( + clientId: clientId ?? this.clientId, + type: type ?? this.type, + sender: sender ?? this.sender, + content: content ?? this.content, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (clientId.present) { + map['client_id'] = Variable(clientId.value); + } + if (type.present) { + map['type'] = Variable(type.value); + } + if (sender.present) { + map['sender'] = Variable(sender.value); + } + if (content.present) { + map['content'] = Variable(content.value); + } + return map; + } +} + +class Presences extends Table with TableInfo { + final GeneratedDatabase _db; + final String _alias; + Presences(this._db, [this._alias]); + final VerificationMeta _clientIdMeta = const VerificationMeta('clientId'); + GeneratedIntColumn _clientId; + GeneratedIntColumn get clientId => _clientId ??= _constructClientId(); + GeneratedIntColumn _constructClientId() { + return GeneratedIntColumn('client_id', $tableName, false, + $customConstraints: 'NOT NULL REFERENCES clients(client_id)'); + } + + final VerificationMeta _typeMeta = const VerificationMeta('type'); + GeneratedTextColumn _type; + GeneratedTextColumn get type => _type ??= _constructType(); + GeneratedTextColumn _constructType() { + return GeneratedTextColumn('type', $tableName, false, + $customConstraints: 'NOT NULL'); + } + + final VerificationMeta _senderMeta = const VerificationMeta('sender'); + GeneratedTextColumn _sender; + GeneratedTextColumn get sender => _sender ??= _constructSender(); + GeneratedTextColumn _constructSender() { + return GeneratedTextColumn('sender', $tableName, false, + $customConstraints: 'NOT NULL'); + } + + final VerificationMeta _contentMeta = const VerificationMeta('content'); + GeneratedTextColumn _content; + GeneratedTextColumn get content => _content ??= _constructContent(); + GeneratedTextColumn _constructContent() { + return GeneratedTextColumn('content', $tableName, true, + $customConstraints: ''); + } + + @override + List get $columns => [clientId, type, sender, content]; + @override + Presences get asDslTable => this; + @override + String get $tableName => _alias ?? 'presences'; + @override + final String actualTableName = 'presences'; + @override + VerificationContext validateIntegrity(Insertable instance, + {bool isInserting = false}) { + final context = VerificationContext(); + final data = instance.toColumns(true); + if (data.containsKey('client_id')) { + context.handle(_clientIdMeta, + clientId.isAcceptableOrUnknown(data['client_id'], _clientIdMeta)); + } else if (isInserting) { + context.missing(_clientIdMeta); + } + if (data.containsKey('type')) { + context.handle( + _typeMeta, type.isAcceptableOrUnknown(data['type'], _typeMeta)); + } else if (isInserting) { + context.missing(_typeMeta); + } + if (data.containsKey('sender')) { + context.handle(_senderMeta, + sender.isAcceptableOrUnknown(data['sender'], _senderMeta)); + } else if (isInserting) { + context.missing(_senderMeta); + } + if (data.containsKey('content')) { + context.handle(_contentMeta, + content.isAcceptableOrUnknown(data['content'], _contentMeta)); + } + return context; + } + + @override + Set get $primaryKey => {}; + @override + DbPresence map(Map data, {String tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : null; + return DbPresence.fromData(data, _db, prefix: effectivePrefix); + } + + @override + Presences createAlias(String alias) { + return Presences(_db, alias); + } + + @override + List get customConstraints => + const ['UNIQUE(client_id, type, sender)']; + @override + bool get dontWriteConstraints => true; +} + +class DbFile extends DataClass implements Insertable { + final String mxcUri; + final Uint8List bytes; + final DateTime savedAt; + DbFile({@required this.mxcUri, this.bytes, this.savedAt}); + factory DbFile.fromData(Map data, GeneratedDatabase db, + {String prefix}) { + final effectivePrefix = prefix ?? ''; + final stringType = db.typeSystem.forDartType(); + final uint8ListType = db.typeSystem.forDartType(); + final dateTimeType = db.typeSystem.forDartType(); + return DbFile( + mxcUri: + stringType.mapFromDatabaseResponse(data['${effectivePrefix}mxc_uri']), + bytes: uint8ListType + .mapFromDatabaseResponse(data['${effectivePrefix}bytes']), + savedAt: dateTimeType + .mapFromDatabaseResponse(data['${effectivePrefix}saved_at']), + ); + } + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (!nullToAbsent || mxcUri != null) { + map['mxc_uri'] = Variable(mxcUri); + } + if (!nullToAbsent || bytes != null) { + map['bytes'] = Variable(bytes); + } + if (!nullToAbsent || savedAt != null) { + map['saved_at'] = Variable(savedAt); + } + return map; + } + + factory DbFile.fromJson(Map json, + {ValueSerializer serializer}) { + serializer ??= moorRuntimeOptions.defaultSerializer; + return DbFile( + mxcUri: serializer.fromJson(json['mxc_uri']), + bytes: serializer.fromJson(json['bytes']), + savedAt: serializer.fromJson(json['saved_at']), + ); + } + @override + Map toJson({ValueSerializer serializer}) { + serializer ??= moorRuntimeOptions.defaultSerializer; + return { + 'mxc_uri': serializer.toJson(mxcUri), + 'bytes': serializer.toJson(bytes), + 'saved_at': serializer.toJson(savedAt), + }; + } + + DbFile copyWith({String mxcUri, Uint8List bytes, DateTime savedAt}) => DbFile( + mxcUri: mxcUri ?? this.mxcUri, + bytes: bytes ?? this.bytes, + savedAt: savedAt ?? this.savedAt, + ); + @override + String toString() { + return (StringBuffer('DbFile(') + ..write('mxcUri: $mxcUri, ') + ..write('bytes: $bytes, ') + ..write('savedAt: $savedAt') + ..write(')')) + .toString(); + } + + @override + int get hashCode => + $mrjf($mrjc(mxcUri.hashCode, $mrjc(bytes.hashCode, savedAt.hashCode))); + @override + bool operator ==(dynamic other) => + identical(this, other) || + (other is DbFile && + other.mxcUri == this.mxcUri && + other.bytes == this.bytes && + other.savedAt == this.savedAt); +} + +class FilesCompanion extends UpdateCompanion { + final Value mxcUri; + final Value bytes; + final Value savedAt; + const FilesCompanion({ + this.mxcUri = const Value.absent(), + this.bytes = const Value.absent(), + this.savedAt = const Value.absent(), + }); + FilesCompanion.insert({ + @required String mxcUri, + this.bytes = const Value.absent(), + this.savedAt = const Value.absent(), + }) : mxcUri = Value(mxcUri); + static Insertable custom({ + Expression mxcUri, + Expression bytes, + Expression savedAt, + }) { + return RawValuesInsertable({ + if (mxcUri != null) 'mxc_uri': mxcUri, + if (bytes != null) 'bytes': bytes, + if (savedAt != null) 'saved_at': savedAt, + }); + } + + FilesCompanion copyWith( + {Value mxcUri, Value bytes, Value savedAt}) { + return FilesCompanion( + mxcUri: mxcUri ?? this.mxcUri, + bytes: bytes ?? this.bytes, + savedAt: savedAt ?? this.savedAt, + ); + } + + @override + Map toColumns(bool nullToAbsent) { + final map = {}; + if (mxcUri.present) { + map['mxc_uri'] = Variable(mxcUri.value); + } + if (bytes.present) { + map['bytes'] = Variable(bytes.value); + } + if (savedAt.present) { + map['saved_at'] = Variable(savedAt.value); + } + return map; + } +} + +class Files extends Table with TableInfo { + final GeneratedDatabase _db; + final String _alias; + Files(this._db, [this._alias]); + final VerificationMeta _mxcUriMeta = const VerificationMeta('mxcUri'); + GeneratedTextColumn _mxcUri; + GeneratedTextColumn get mxcUri => _mxcUri ??= _constructMxcUri(); + GeneratedTextColumn _constructMxcUri() { + return GeneratedTextColumn('mxc_uri', $tableName, false, + $customConstraints: 'NOT NULL PRIMARY KEY'); + } + + final VerificationMeta _bytesMeta = const VerificationMeta('bytes'); + GeneratedBlobColumn _bytes; + GeneratedBlobColumn get bytes => _bytes ??= _constructBytes(); + GeneratedBlobColumn _constructBytes() { + return GeneratedBlobColumn('bytes', $tableName, true, + $customConstraints: ''); + } + + final VerificationMeta _savedAtMeta = const VerificationMeta('savedAt'); + GeneratedDateTimeColumn _savedAt; + GeneratedDateTimeColumn get savedAt => _savedAt ??= _constructSavedAt(); + GeneratedDateTimeColumn _constructSavedAt() { + return GeneratedDateTimeColumn('saved_at', $tableName, true, + $customConstraints: ''); + } + + @override + List get $columns => [mxcUri, bytes, savedAt]; + @override + Files get asDslTable => this; + @override + String get $tableName => _alias ?? 'files'; + @override + final String actualTableName = 'files'; + @override + VerificationContext validateIntegrity(Insertable instance, + {bool isInserting = false}) { + final context = VerificationContext(); + final data = instance.toColumns(true); + if (data.containsKey('mxc_uri')) { + context.handle(_mxcUriMeta, + mxcUri.isAcceptableOrUnknown(data['mxc_uri'], _mxcUriMeta)); + } else if (isInserting) { + context.missing(_mxcUriMeta); + } + if (data.containsKey('bytes')) { + context.handle( + _bytesMeta, bytes.isAcceptableOrUnknown(data['bytes'], _bytesMeta)); + } + if (data.containsKey('saved_at')) { + context.handle(_savedAtMeta, + savedAt.isAcceptableOrUnknown(data['saved_at'], _savedAtMeta)); + } + return context; + } + + @override + Set get $primaryKey => {mxcUri}; + @override + DbFile map(Map data, {String tablePrefix}) { + final effectivePrefix = tablePrefix != null ? '$tablePrefix.' : null; + return DbFile.fromData(data, _db, prefix: effectivePrefix); + } + + @override + Files createAlias(String alias) { + return Files(_db, alias); + } + + @override + List get customConstraints => const ['UNIQUE(mxc_uri)']; + @override + bool get dontWriteConstraints => true; +} + +abstract class _$Database extends GeneratedDatabase { + _$Database(QueryExecutor e) : super(SqlTypeSystem.defaultInstance, e); + Clients _clients; + Clients get clients => _clients ??= Clients(this); + UserDeviceKeys _userDeviceKeys; + UserDeviceKeys get userDeviceKeys => _userDeviceKeys ??= UserDeviceKeys(this); + Index _userDeviceKeysIndex; + Index get userDeviceKeysIndex => _userDeviceKeysIndex ??= Index( + 'user_device_keys_index', + 'CREATE INDEX user_device_keys_index ON user_device_keys(client_id);'); + UserDeviceKeysKey _userDeviceKeysKey; + UserDeviceKeysKey get userDeviceKeysKey => + _userDeviceKeysKey ??= UserDeviceKeysKey(this); + Index _userDeviceKeysKeyIndex; + Index get userDeviceKeysKeyIndex => _userDeviceKeysKeyIndex ??= Index( + 'user_device_keys_key_index', + 'CREATE INDEX user_device_keys_key_index ON user_device_keys_key(client_id);'); + OlmSessions _olmSessions; + OlmSessions get olmSessions => _olmSessions ??= OlmSessions(this); + Index _olmSessionsIndex; + Index get olmSessionsIndex => _olmSessionsIndex ??= Index( + 'olm_sessions_index', + 'CREATE INDEX olm_sessions_index ON olm_sessions(client_id);'); + OutboundGroupSessions _outboundGroupSessions; + OutboundGroupSessions get outboundGroupSessions => + _outboundGroupSessions ??= OutboundGroupSessions(this); + Index _outboundGroupSessionsIndex; + Index get outboundGroupSessionsIndex => _outboundGroupSessionsIndex ??= Index( + 'outbound_group_sessions_index', + 'CREATE INDEX outbound_group_sessions_index ON outbound_group_sessions(client_id);'); + InboundGroupSessions _inboundGroupSessions; + InboundGroupSessions get inboundGroupSessions => + _inboundGroupSessions ??= InboundGroupSessions(this); + Index _inboundGroupSessionsIndex; + Index get inboundGroupSessionsIndex => _inboundGroupSessionsIndex ??= Index( + 'inbound_group_sessions_index', + 'CREATE INDEX inbound_group_sessions_index ON inbound_group_sessions(client_id);'); + Rooms _rooms; + Rooms get rooms => _rooms ??= Rooms(this); + Index _roomsIndex; + Index get roomsIndex => _roomsIndex ??= + Index('rooms_index', 'CREATE INDEX rooms_index ON rooms(client_id);'); + Events _events; + Events get events => _events ??= Events(this); + Index _eventsIndex; + Index get eventsIndex => _eventsIndex ??= Index('events_index', + 'CREATE INDEX events_index ON events(client_id, room_id);'); + RoomStates _roomStates; + RoomStates get roomStates => _roomStates ??= RoomStates(this); + Index _roomStatesIndex; + Index get roomStatesIndex => _roomStatesIndex ??= Index('room_states_index', + 'CREATE INDEX room_states_index ON room_states(client_id);'); + AccountData _accountData; + AccountData get accountData => _accountData ??= AccountData(this); + Index _accountDataIndex; + Index get accountDataIndex => _accountDataIndex ??= Index( + 'account_data_index', + 'CREATE INDEX account_data_index ON account_data(client_id);'); + RoomAccountData _roomAccountData; + RoomAccountData get roomAccountData => + _roomAccountData ??= RoomAccountData(this); + Index _roomAccountDataIndex; + Index get roomAccountDataIndex => _roomAccountDataIndex ??= Index( + 'room_account_data_index', + 'CREATE INDEX room_account_data_index ON room_account_data(client_id);'); + Presences _presences; + Presences get presences => _presences ??= Presences(this); + Index _presencesIndex; + Index get presencesIndex => _presencesIndex ??= Index('presences_index', + 'CREATE INDEX presences_index ON presences(client_id);'); + Files _files; + Files get files => _files ??= Files(this); + DbClient _rowToDbClient(QueryRow row) { + return DbClient( + clientId: row.readInt('client_id'), + name: row.readString('name'), + homeserverUrl: row.readString('homeserver_url'), + token: row.readString('token'), + userId: row.readString('user_id'), + deviceId: row.readString('device_id'), + deviceName: row.readString('device_name'), + prevBatch: row.readString('prev_batch'), + olmAccount: row.readString('olm_account'), + ); + } + + Selectable dbGetClient(String name) { + return customSelect('SELECT * FROM clients WHERE name = :name', + variables: [Variable.withString(name)], + readsFrom: {clients}).map(_rowToDbClient); + } + + Future updateClient( + String homeserver_url, + String token, + String user_id, + String device_id, + String device_name, + String prev_batch, + String olm_account, + int client_id) { + return customUpdate( + 'UPDATE clients SET homeserver_url = :homeserver_url, token = :token, user_id = :user_id, device_id = :device_id, device_name = :device_name, prev_batch = :prev_batch, olm_account = :olm_account WHERE client_id = :client_id', + variables: [ + Variable.withString(homeserver_url), + Variable.withString(token), + Variable.withString(user_id), + Variable.withString(device_id), + Variable.withString(device_name), + Variable.withString(prev_batch), + Variable.withString(olm_account), + Variable.withInt(client_id) + ], + updates: {clients}, + updateKind: UpdateKind.update, + ); + } + + Future updateClientKeys(String olm_account, int client_id) { + return customUpdate( + 'UPDATE clients SET olm_account = :olm_account WHERE client_id = :client_id', + variables: [ + Variable.withString(olm_account), + Variable.withInt(client_id) + ], + updates: {clients}, + updateKind: UpdateKind.update, + ); + } + + Future storePrevBatch(String prev_batch, int client_id) { + return customUpdate( + 'UPDATE clients SET prev_batch = :prev_batch WHERE client_id = :client_id', + variables: [Variable.withString(prev_batch), Variable.withInt(client_id)], + updates: {clients}, + updateKind: UpdateKind.update, + ); + } + + DbUserDeviceKey _rowToDbUserDeviceKey(QueryRow row) { + return DbUserDeviceKey( + clientId: row.readInt('client_id'), + userId: row.readString('user_id'), + outdated: row.readBool('outdated'), + ); + } + + Selectable getAllUserDeviceKeys(int client_id) { + return customSelect( + 'SELECT * FROM user_device_keys WHERE client_id = :client_id', + variables: [Variable.withInt(client_id)], + readsFrom: {userDeviceKeys}).map(_rowToDbUserDeviceKey); + } + + DbUserDeviceKeysKey _rowToDbUserDeviceKeysKey(QueryRow row) { + return DbUserDeviceKeysKey( + clientId: row.readInt('client_id'), + userId: row.readString('user_id'), + deviceId: row.readString('device_id'), + content: row.readString('content'), + verified: row.readBool('verified'), + blocked: row.readBool('blocked'), + ); + } + + Selectable getAllUserDeviceKeysKeys(int client_id) { + return customSelect( + 'SELECT * FROM user_device_keys_key WHERE client_id = :client_id', + variables: [Variable.withInt(client_id)], + readsFrom: {userDeviceKeysKey}).map(_rowToDbUserDeviceKeysKey); + } + + DbOlmSessions _rowToDbOlmSessions(QueryRow row) { + return DbOlmSessions( + clientId: row.readInt('client_id'), + identityKey: row.readString('identity_key'), + sessionId: row.readString('session_id'), + pickle: row.readString('pickle'), + ); + } + + Selectable getAllOlmSessions(int client_id) { + return customSelect( + 'SELECT * FROM olm_sessions WHERE client_id = :client_id', + variables: [Variable.withInt(client_id)], + readsFrom: {olmSessions}).map(_rowToDbOlmSessions); + } + + Future storeOlmSession( + int client_id, String identitiy_key, String session_id, String pickle) { + return customInsert( + 'INSERT OR REPLACE INTO olm_sessions (client_id, identity_key, session_id, pickle) VALUES (:client_id, :identitiy_key, :session_id, :pickle)', + variables: [ + Variable.withInt(client_id), + Variable.withString(identitiy_key), + Variable.withString(session_id), + Variable.withString(pickle) + ], + updates: {olmSessions}, + ); + } + + DbOutboundGroupSession _rowToDbOutboundGroupSession(QueryRow row) { + return DbOutboundGroupSession( + clientId: row.readInt('client_id'), + roomId: row.readString('room_id'), + pickle: row.readString('pickle'), + deviceIds: row.readString('device_ids'), + ); + } + + Selectable getAllOutboundGroupSessions( + int client_id) { + return customSelect( + 'SELECT * FROM outbound_group_sessions WHERE client_id = :client_id', + variables: [Variable.withInt(client_id)], + readsFrom: {outboundGroupSessions}).map(_rowToDbOutboundGroupSession); + } + + Selectable dbGetOutboundGroupSession( + int client_id, String room_id) { + return customSelect( + 'SELECT * FROM outbound_group_sessions WHERE client_id = :client_id AND room_id = :room_id', + variables: [Variable.withInt(client_id), Variable.withString(room_id)], + readsFrom: {outboundGroupSessions}).map(_rowToDbOutboundGroupSession); + } + + Future storeOutboundGroupSession( + int client_id, String room_id, String pickle, String device_ids) { + return customInsert( + 'INSERT OR REPLACE INTO outbound_group_sessions (client_id, room_id, pickle, device_ids) VALUES (:client_id, :room_id, :pickle, :device_ids)', + variables: [ + Variable.withInt(client_id), + Variable.withString(room_id), + Variable.withString(pickle), + Variable.withString(device_ids) + ], + updates: {outboundGroupSessions}, + ); + } + + Future removeOutboundGroupSession(int client_id, String room_id) { + return customUpdate( + 'DELETE FROM outbound_group_sessions WHERE client_id = :client_id AND room_id = :room_id', + variables: [Variable.withInt(client_id), Variable.withString(room_id)], + updates: {outboundGroupSessions}, + updateKind: UpdateKind.delete, + ); + } + + DbInboundGroupSession _rowToDbInboundGroupSession(QueryRow row) { + return DbInboundGroupSession( + clientId: row.readInt('client_id'), + roomId: row.readString('room_id'), + sessionId: row.readString('session_id'), + pickle: row.readString('pickle'), + content: row.readString('content'), + indexes: row.readString('indexes'), + ); + } + + Selectable dbGetInboundGroupSessionKeys( + int client_id, String room_id) { + return customSelect( + 'SELECT * FROM inbound_group_sessions WHERE client_id = :client_id AND room_id = :room_id', + variables: [Variable.withInt(client_id), Variable.withString(room_id)], + readsFrom: {inboundGroupSessions}).map(_rowToDbInboundGroupSession); + } + + Selectable getAllInboundGroupSessions(int client_id) { + return customSelect( + 'SELECT * FROM inbound_group_sessions WHERE client_id = :client_id', + variables: [Variable.withInt(client_id)], + readsFrom: {inboundGroupSessions}).map(_rowToDbInboundGroupSession); + } + + Future storeInboundGroupSession(int client_id, String room_id, + String session_id, String pickle, String content, String indexes) { + return customInsert( + 'INSERT OR REPLACE INTO inbound_group_sessions (client_id, room_id, session_id, pickle, content, indexes) VALUES (:client_id, :room_id, :session_id, :pickle, :content, :indexes)', + variables: [ + Variable.withInt(client_id), + Variable.withString(room_id), + Variable.withString(session_id), + Variable.withString(pickle), + Variable.withString(content), + Variable.withString(indexes) + ], + updates: {inboundGroupSessions}, + ); + } + + Future storeUserDeviceKeysInfo( + int client_id, String user_id, bool outdated) { + return customInsert( + 'INSERT OR REPLACE INTO user_device_keys (client_id, user_id, outdated) VALUES (:client_id, :user_id, :outdated)', + variables: [ + Variable.withInt(client_id), + Variable.withString(user_id), + Variable.withBool(outdated) + ], + updates: {userDeviceKeys}, + ); + } + + Future setVerifiedUserDeviceKey( + bool verified, int client_id, String user_id, String device_id) { + return customUpdate( + 'UPDATE user_device_keys_key SET verified = :verified WHERE client_id = :client_id AND user_id = :user_id AND device_id = :device_id', + variables: [ + Variable.withBool(verified), + Variable.withInt(client_id), + Variable.withString(user_id), + Variable.withString(device_id) + ], + updates: {userDeviceKeysKey}, + updateKind: UpdateKind.update, + ); + } + + Future setBlockedUserDeviceKey( + bool blocked, int client_id, String user_id, String device_id) { + return customUpdate( + 'UPDATE user_device_keys_key SET blocked = :blocked WHERE client_id = :client_id AND user_id = :user_id AND device_id = :device_id', + variables: [ + Variable.withBool(blocked), + Variable.withInt(client_id), + Variable.withString(user_id), + Variable.withString(device_id) + ], + updates: {userDeviceKeysKey}, + updateKind: UpdateKind.update, + ); + } + + Future storeUserDeviceKey(int client_id, String user_id, + String device_id, String content, bool verified, bool blocked) { + return customInsert( + 'INSERT OR REPLACE INTO user_device_keys_key (client_id, user_id, device_id, content, verified, blocked) VALUES (:client_id, :user_id, :device_id, :content, :verified, :blocked)', + variables: [ + Variable.withInt(client_id), + Variable.withString(user_id), + Variable.withString(device_id), + Variable.withString(content), + Variable.withBool(verified), + Variable.withBool(blocked) + ], + updates: {userDeviceKeysKey}, + ); + } + + Future removeUserDeviceKey( + int client_id, String user_id, String device_id) { + return customUpdate( + 'DELETE FROM user_device_keys_key WHERE client_id = :client_id AND user_id = :user_id AND device_id = :device_id', + variables: [ + Variable.withInt(client_id), + Variable.withString(user_id), + Variable.withString(device_id) + ], + updates: {userDeviceKeysKey}, + updateKind: UpdateKind.delete, + ); + } + + Future insertClient( + String name, + String homeserver_url, + String token, + String user_id, + String device_id, + String device_name, + String prev_batch, + String olm_account) { + return customInsert( + 'INSERT INTO clients (name, homeserver_url, token, user_id, device_id, device_name, prev_batch, olm_account) VALUES (:name, :homeserver_url, :token, :user_id, :device_id, :device_name, :prev_batch, :olm_account)', + variables: [ + Variable.withString(name), + Variable.withString(homeserver_url), + Variable.withString(token), + Variable.withString(user_id), + Variable.withString(device_id), + Variable.withString(device_name), + Variable.withString(prev_batch), + Variable.withString(olm_account) + ], + updates: {clients}, + ); + } + + Future ensureRoomExists( + int client_id, String room_id, String membership) { + return customInsert( + 'INSERT OR IGNORE INTO rooms (client_id, room_id, membership) VALUES (:client_id, :room_id, :membership)', + variables: [ + Variable.withInt(client_id), + Variable.withString(room_id), + Variable.withString(membership) + ], + updates: {rooms}, + ); + } + + Future setRoomPrevBatch( + String prev_batch, int client_id, String room_id) { + return customUpdate( + 'UPDATE rooms SET prev_batch = :prev_batch WHERE client_id = :client_id AND room_id = :room_id', + variables: [ + Variable.withString(prev_batch), + Variable.withInt(client_id), + Variable.withString(room_id) + ], + updates: {rooms}, + updateKind: UpdateKind.update, + ); + } + + Future updateRoomSortOrder(double oldest_sort_order, + double newest_sort_order, int client_id, String room_id) { + return customUpdate( + 'UPDATE rooms SET oldest_sort_order = :oldest_sort_order, newest_sort_order = :newest_sort_order WHERE client_id = :client_id AND room_id = :room_id', + variables: [ + Variable.withReal(oldest_sort_order), + Variable.withReal(newest_sort_order), + Variable.withInt(client_id), + Variable.withString(room_id) + ], + updates: {rooms}, + updateKind: UpdateKind.update, + ); + } + + DbAccountData _rowToDbAccountData(QueryRow row) { + return DbAccountData( + clientId: row.readInt('client_id'), + type: row.readString('type'), + content: row.readString('content'), + ); + } + + Selectable getAllAccountData(int client_id) { + return customSelect( + 'SELECT * FROM account_data WHERE client_id = :client_id', + variables: [Variable.withInt(client_id)], + readsFrom: {accountData}).map(_rowToDbAccountData); + } + + Future storeAccountData(int client_id, String type, String content) { + return customInsert( + 'INSERT OR REPLACE INTO account_data (client_id, type, content) VALUES (:client_id, :type, :content)', + variables: [ + Variable.withInt(client_id), + Variable.withString(type), + Variable.withString(content) + ], + updates: {accountData}, + ); + } + + DbPresence _rowToDbPresence(QueryRow row) { + return DbPresence( + clientId: row.readInt('client_id'), + type: row.readString('type'), + sender: row.readString('sender'), + content: row.readString('content'), + ); + } + + Selectable getAllPresences(int client_id) { + return customSelect('SELECT * FROM presences WHERE client_id = :client_id', + variables: [Variable.withInt(client_id)], + readsFrom: {presences}).map(_rowToDbPresence); + } + + Future storePresence( + int client_id, String type, String sender, String content) { + return customInsert( + 'INSERT OR REPLACE INTO presences (client_id, type, sender, content) VALUES (:client_id, :type, :sender, :content)', + variables: [ + Variable.withInt(client_id), + Variable.withString(type), + Variable.withString(sender), + Variable.withString(content) + ], + updates: {presences}, + ); + } + + Future updateEvent(String unsigned, String content, String prev_content, + int client_id, String event_id, String room_id) { + return customUpdate( + 'UPDATE events SET unsigned = :unsigned, content = :content, prev_content = :prev_content WHERE client_id = :client_id AND event_id = :event_id AND room_id = :room_id', + variables: [ + Variable.withString(unsigned), + Variable.withString(content), + Variable.withString(prev_content), + Variable.withInt(client_id), + Variable.withString(event_id), + Variable.withString(room_id) + ], + updates: {events}, + updateKind: UpdateKind.update, + ); + } + + Future updateEventStatus(int status, String new_event_id, int client_id, + String old_event_id, String room_id) { + return customUpdate( + 'UPDATE events SET status = :status, event_id = :new_event_id WHERE client_id = :client_id AND event_id = :old_event_id AND room_id = :room_id', + variables: [ + Variable.withInt(status), + Variable.withString(new_event_id), + Variable.withInt(client_id), + Variable.withString(old_event_id), + Variable.withString(room_id) + ], + updates: {events}, + updateKind: UpdateKind.update, + ); + } + + DbRoomState _rowToDbRoomState(QueryRow row) { + return DbRoomState( + clientId: row.readInt('client_id'), + eventId: row.readString('event_id'), + roomId: row.readString('room_id'), + sortOrder: row.readDouble('sort_order'), + originServerTs: row.readDateTime('origin_server_ts'), + sender: row.readString('sender'), + type: row.readString('type'), + unsigned: row.readString('unsigned'), + content: row.readString('content'), + prevContent: row.readString('prev_content'), + stateKey: row.readString('state_key'), + ); + } + + Selectable getAllRoomStates(int client_id) { + return customSelect( + 'SELECT * FROM room_states WHERE client_id = :client_id', + variables: [Variable.withInt(client_id)], + readsFrom: {roomStates}).map(_rowToDbRoomState); + } + + Future storeEvent( + int client_id, + String event_id, + String room_id, + double sort_order, + DateTime origin_server_ts, + String sender, + String type, + String unsigned, + String content, + String prev_content, + String state_key, + int status) { + return customInsert( + 'INSERT OR REPLACE INTO events (client_id, event_id, room_id, sort_order, origin_server_ts, sender, type, unsigned, content, prev_content, state_key, status) VALUES (:client_id, :event_id, :room_id, :sort_order, :origin_server_ts, :sender, :type, :unsigned, :content, :prev_content, :state_key, :status)', + variables: [ + Variable.withInt(client_id), + Variable.withString(event_id), + Variable.withString(room_id), + Variable.withReal(sort_order), + Variable.withDateTime(origin_server_ts), + Variable.withString(sender), + Variable.withString(type), + Variable.withString(unsigned), + Variable.withString(content), + Variable.withString(prev_content), + Variable.withString(state_key), + Variable.withInt(status) + ], + updates: {events}, + ); + } + + Future storeRoomState( + int client_id, + String event_id, + String room_id, + double sort_order, + DateTime origin_server_ts, + String sender, + String type, + String unsigned, + String content, + String prev_content, + String state_key) { + return customInsert( + 'INSERT OR REPLACE INTO room_states (client_id, event_id, room_id, sort_order, origin_server_ts, sender, type, unsigned, content, prev_content, state_key) VALUES (:client_id, :event_id, :room_id, :sort_order, :origin_server_ts, :sender, :type, :unsigned, :content, :prev_content, :state_key)', + variables: [ + Variable.withInt(client_id), + Variable.withString(event_id), + Variable.withString(room_id), + Variable.withReal(sort_order), + Variable.withDateTime(origin_server_ts), + Variable.withString(sender), + Variable.withString(type), + Variable.withString(unsigned), + Variable.withString(content), + Variable.withString(prev_content), + Variable.withString(state_key) + ], + updates: {roomStates}, + ); + } + + DbRoomAccountData _rowToDbRoomAccountData(QueryRow row) { + return DbRoomAccountData( + clientId: row.readInt('client_id'), + type: row.readString('type'), + roomId: row.readString('room_id'), + content: row.readString('content'), + ); + } + + Selectable getAllRoomAccountData(int client_id) { + return customSelect( + 'SELECT * FROM room_account_data WHERE client_id = :client_id', + variables: [Variable.withInt(client_id)], + readsFrom: {roomAccountData}).map(_rowToDbRoomAccountData); + } + + Future storeRoomAccountData( + int client_id, String type, String room_id, String content) { + return customInsert( + 'INSERT OR REPLACE INTO room_account_data (client_id, type, room_id, content) VALUES (:client_id, :type, :room_id, :content)', + variables: [ + Variable.withInt(client_id), + Variable.withString(type), + Variable.withString(room_id), + Variable.withString(content) + ], + updates: {roomAccountData}, + ); + } + + Selectable dbGetUser( + int client_id, String state_key, String room_id) { + return customSelect( + 'SELECT * FROM room_states WHERE client_id = :client_id AND type = \'m.room.member\' AND state_key = :state_key AND room_id = :room_id', + variables: [ + Variable.withInt(client_id), + Variable.withString(state_key), + Variable.withString(room_id) + ], + readsFrom: { + roomStates + }).map(_rowToDbRoomState); + } + + DbEvent _rowToDbEvent(QueryRow row) { + return DbEvent( + clientId: row.readInt('client_id'), + eventId: row.readString('event_id'), + roomId: row.readString('room_id'), + sortOrder: row.readDouble('sort_order'), + originServerTs: row.readDateTime('origin_server_ts'), + sender: row.readString('sender'), + type: row.readString('type'), + unsigned: row.readString('unsigned'), + content: row.readString('content'), + prevContent: row.readString('prev_content'), + stateKey: row.readString('state_key'), + status: row.readInt('status'), + ); + } + + Selectable dbGetEventList(int client_id, String room_id) { + return customSelect( + 'SELECT * FROM events WHERE client_id = :client_id AND room_id = :room_id GROUP BY event_id ORDER BY sort_order DESC', + variables: [Variable.withInt(client_id), Variable.withString(room_id)], + readsFrom: {events}).map(_rowToDbEvent); + } + + Selectable getStates(int client_id, String room_id) { + return customSelect( + 'SELECT * FROM room_states WHERE client_id = :client_id AND room_id = :room_id', + variables: [Variable.withInt(client_id), Variable.withString(room_id)], + readsFrom: {roomStates}).map(_rowToDbRoomState); + } + + Future resetNotificationCount(int client_id, String room_id) { + return customUpdate( + 'UPDATE rooms SET notification_count = 0, highlight_count = 0 WHERE client_id = :client_id AND room_id = :room_id', + variables: [Variable.withInt(client_id), Variable.withString(room_id)], + updates: {rooms}, + updateKind: UpdateKind.update, + ); + } + + Selectable getEvent(int client_id, String event_id, String room_id) { + return customSelect( + 'SELECT * FROM events WHERE client_id = :client_id AND event_id = :event_id AND room_id = :room_id', + variables: [ + Variable.withInt(client_id), + Variable.withString(event_id), + Variable.withString(room_id) + ], + readsFrom: { + events + }).map(_rowToDbEvent); + } + + Future removeEvent(int client_id, String event_id, String room_id) { + return customUpdate( + 'DELETE FROM events WHERE client_id = :client_id AND event_id = :event_id AND room_id = :room_id', + variables: [ + Variable.withInt(client_id), + Variable.withString(event_id), + Variable.withString(room_id) + ], + updates: {events}, + updateKind: UpdateKind.delete, + ); + } + + Future removeRoom(int client_id, String room_id) { + return customUpdate( + 'DELETE FROM rooms WHERE client_id = :client_id AND room_id = :room_id', + variables: [Variable.withInt(client_id), Variable.withString(room_id)], + updates: {rooms}, + updateKind: UpdateKind.delete, + ); + } + + Future removeRoomEvents(int client_id, String room_id) { + return customUpdate( + 'DELETE FROM events WHERE client_id = :client_id AND room_id = :room_id', + variables: [Variable.withInt(client_id), Variable.withString(room_id)], + updates: {events}, + updateKind: UpdateKind.delete, + ); + } + + Future storeFile(String mxc_uri, Uint8List bytes, DateTime time) { + return customInsert( + 'INSERT OR REPLACE INTO files (mxc_uri, bytes, saved_at) VALUES (:mxc_uri, :bytes, :time)', + variables: [ + Variable.withString(mxc_uri), + Variable.withBlob(bytes), + Variable.withDateTime(time) + ], + updates: {files}, + ); + } + + DbFile _rowToDbFile(QueryRow row) { + return DbFile( + mxcUri: row.readString('mxc_uri'), + bytes: row.readBlob('bytes'), + savedAt: row.readDateTime('saved_at'), + ); + } + + Selectable dbGetFile(String mxc_uri) { + return customSelect('SELECT * FROM files WHERE mxc_uri = :mxc_uri', + variables: [Variable.withString(mxc_uri)], + readsFrom: {files}).map(_rowToDbFile); + } + + @override + Iterable get allTables => allSchemaEntities.whereType(); + @override + List get allSchemaEntities => [ + clients, + userDeviceKeys, + userDeviceKeysIndex, + userDeviceKeysKey, + userDeviceKeysKeyIndex, + olmSessions, + olmSessionsIndex, + outboundGroupSessions, + outboundGroupSessionsIndex, + inboundGroupSessions, + inboundGroupSessionsIndex, + rooms, + roomsIndex, + events, + eventsIndex, + roomStates, + roomStatesIndex, + accountData, + accountDataIndex, + roomAccountData, + roomAccountDataIndex, + presences, + presencesIndex, + files + ]; +} diff --git a/lib/src/database/database.moor b/lib/src/database/database.moor new file mode 100644 index 0000000..ecd185c --- /dev/null +++ b/lib/src/database/database.moor @@ -0,0 +1,193 @@ +-- Table definitions + +CREATE TABLE clients ( + client_id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, + name TEXT NOT NULL, + homeserver_url TEXT NOT NULL, + token TEXT NOT NULL, + user_id TEXT NOT NULL, + device_id TEXT, + device_name TEXT, + prev_batch TEXT, + olm_account TEXT, + UNIQUE(name) +) AS DbClient; + +CREATE TABLE user_device_keys ( + client_id INTEGER NOT NULL REFERENCES clients(client_id), + user_id TEXT NOT NULL, + outdated BOOLEAN DEFAULT true, + UNIQUE(client_id, user_id) +) as DbUserDeviceKey; +CREATE INDEX user_device_keys_index ON user_device_keys(client_id); + +CREATE TABLE user_device_keys_key ( + client_id INTEGER NOT NULL REFERENCES clients(client_id), + user_id TEXT NOT NULL, + device_id TEXT NOT NULL, + content TEXT NOT NULL, + verified BOOLEAN DEFAULT false, + blocked BOOLEAN DEFAULT false, + UNIQUE(client_id, user_id, device_id) +) as DbUserDeviceKeysKey; +CREATE INDEX user_device_keys_key_index ON user_device_keys_key(client_id); + +CREATE TABLE olm_sessions ( + client_id INTEGER NOT NULL REFERENCES clients(client_id), + identity_key TEXT NOT NULL, + session_id TEXT NOT NULL, + pickle TEXT NOT NULL, + UNIQUE(client_id, identity_key, session_id) +) AS DbOlmSessions; +CREATE INDEX olm_sessions_index ON olm_sessions(client_id); + +CREATE TABLE outbound_group_sessions ( + client_id INTEGER NOT NULL REFERENCES clients(client_id), + room_id TEXT NOT NULL, + pickle TEXT NOT NULL, + device_ids TEXT NOT NULL, + UNIQUE(client_id, room_id) +) AS DbOutboundGroupSession; +CREATE INDEX outbound_group_sessions_index ON outbound_group_sessions(client_id); + +CREATE TABLE inbound_group_sessions ( + client_id INTEGER NOT NULL REFERENCES clients(client_id), + room_id TEXT NOT NULL, + session_id TEXT NOT NULL, + pickle TEXT NOT NULL, + content TEXT, + indexes TEXT, + UNIQUE(client_id, room_id, session_id) +) AS DbInboundGroupSession; +CREATE INDEX inbound_group_sessions_index ON inbound_group_sessions(client_id); + +CREATE TABLE rooms ( + client_id INTEGER NOT NULL REFERENCES clients(client_id), + room_id TEXT NOT NULL, + membership TEXT NOT NULL, + highlight_count INTEGER NOT NULL DEFAULT '0', + notification_count INTEGER NOT NULL DEFAULT '0', + prev_batch TEXT DEFAULT '', + joined_member_count INTEGER NOT NULL DEFAULT '0', + invited_member_count INTEGER NOT NULL DEFAULT '0', + newest_sort_order DOUBLE NOT NULL DEFAULT '0', + oldest_sort_order DOUBLE NOT NULL DEFAULT '0', + heroes TEXT DEFAULT '', + UNIQUE(client_id, room_id) +) AS DbRoom; +CREATE INDEX rooms_index ON rooms(client_id); + +CREATE TABLE events ( + client_id INTEGER NOT NULL REFERENCES clients(client_id), + event_id TEXT NOT NULL, + room_id TEXT NOT NULL, + sort_order DOUBLE NOT NULL, + origin_server_ts DATETIME NOT NULL, + sender TEXT NOT NULL, + type TEXT NOT NULL, + unsigned TEXT, + content TEXT, + prev_content TEXT, + state_key TEXT, + status INTEGER, + UNIQUE(client_id, event_id, room_id) +) AS DbEvent; +CREATE INDEX events_index ON events(client_id, room_id); + +CREATE TABLE room_states ( + client_id INTEGER NOT NULL REFERENCES clients(client_id), + event_id TEXT NOT NULL, + room_id TEXT NOT NULL, + sort_order DOUBLE NOT NULL, + origin_server_ts DATETIME NOT NULL, + sender TEXT NOT NULL, + type TEXT NOT NULL, + unsigned TEXT, + content TEXT, + prev_content TEXT, + state_key TEXT NOT NULL, + UNIQUE(client_id, event_id, room_id), + UNIQUE(client_id, room_id, state_key, type) +) AS DbRoomState; +CREATE INDEX room_states_index ON room_states(client_id); + +CREATE TABLE account_data ( + client_id INTEGER NOT NULL REFERENCES clients(client_id), + type TEXT NOT NULL, + content TEXT, + UNIQUE(client_id, type) +) AS DbAccountData; +CREATE INDEX account_data_index ON account_data(client_id); + +CREATE TABLE room_account_data ( + client_id INTEGER NOT NULL REFERENCES clients(client_id), + type TEXT NOT NULL, + room_id TEXT NOT NULL, + content TEXT, + UNIQUE(client_id, type, room_id) +) AS DbRoomAccountData; +CREATE INDEX room_account_data_index ON room_account_data(client_id); + +CREATE TABLE presences ( + client_id INTEGER NOT NULL REFERENCES clients(client_id), + type TEXT NOT NULL, + sender TEXT NOT NULL, + content TEXT, + UNIQUE(client_id, type, sender) +) AS DbPresence; +CREATE INDEX presences_index ON presences(client_id); + +CREATE TABLE files ( + mxc_uri TEXT NOT NULL PRIMARY KEY, + bytes BLOB, + saved_at DATETIME, + UNIQUE(mxc_uri) +) AS DbFile; + +-- named queries + +dbGetClient: SELECT * FROM clients WHERE name = :name; +updateClient: UPDATE clients SET homeserver_url = :homeserver_url, token = :token, user_id = :user_id, device_id = :device_id, device_name = :device_name, prev_batch = :prev_batch, olm_account = :olm_account WHERE client_id = :client_id; +updateClientKeys: UPDATE clients SET olm_account = :olm_account WHERE client_id = :client_id; +storePrevBatch: UPDATE clients SET prev_batch = :prev_batch WHERE client_id = :client_id; +getAllUserDeviceKeys: SELECT * FROM user_device_keys WHERE client_id = :client_id; +getAllUserDeviceKeysKeys: SELECT * FROM user_device_keys_key WHERE client_id = :client_id; +getAllOlmSessions: SELECT * FROM olm_sessions WHERE client_id = :client_id; +storeOlmSession: INSERT OR REPLACE INTO olm_sessions (client_id, identity_key, session_id, pickle) VALUES (:client_id, :identitiy_key, :session_id, :pickle); +getAllOutboundGroupSessions: SELECT * FROM outbound_group_sessions WHERE client_id = :client_id; +dbGetOutboundGroupSession: SELECT * FROM outbound_group_sessions WHERE client_id = :client_id AND room_id = :room_id; +storeOutboundGroupSession: INSERT OR REPLACE INTO outbound_group_sessions (client_id, room_id, pickle, device_ids) VALUES (:client_id, :room_id, :pickle, :device_ids); +removeOutboundGroupSession: DELETE FROM outbound_group_sessions WHERE client_id = :client_id AND room_id = :room_id; +dbGetInboundGroupSessionKeys: SELECT * FROM inbound_group_sessions WHERE client_id = :client_id AND room_id = :room_id; +getAllInboundGroupSessions: SELECT * FROM inbound_group_sessions WHERE client_id = :client_id; +storeInboundGroupSession: INSERT OR REPLACE INTO inbound_group_sessions (client_id, room_id, session_id, pickle, content, indexes) VALUES (:client_id, :room_id, :session_id, :pickle, :content, :indexes); +storeUserDeviceKeysInfo: INSERT OR REPLACE INTO user_device_keys (client_id, user_id, outdated) VALUES (:client_id, :user_id, :outdated); +setVerifiedUserDeviceKey: UPDATE user_device_keys_key SET verified = :verified WHERE client_id = :client_id AND user_id = :user_id AND device_id = :device_id; +setBlockedUserDeviceKey: UPDATE user_device_keys_key SET blocked = :blocked WHERE client_id = :client_id AND user_id = :user_id AND device_id = :device_id; +storeUserDeviceKey: INSERT OR REPLACE INTO user_device_keys_key (client_id, user_id, device_id, content, verified, blocked) VALUES (:client_id, :user_id, :device_id, :content, :verified, :blocked); +removeUserDeviceKey: DELETE FROM user_device_keys_key WHERE client_id = :client_id AND user_id = :user_id AND device_id = :device_id; +insertClient: INSERT INTO clients (name, homeserver_url, token, user_id, device_id, device_name, prev_batch, olm_account) VALUES (:name, :homeserver_url, :token, :user_id, :device_id, :device_name, :prev_batch, :olm_account); +ensureRoomExists: INSERT OR IGNORE INTO rooms (client_id, room_id, membership) VALUES (:client_id, :room_id, :membership); +setRoomPrevBatch: UPDATE rooms SET prev_batch = :prev_batch WHERE client_id = :client_id AND room_id = :room_id; +updateRoomSortOrder: UPDATE rooms SET oldest_sort_order = :oldest_sort_order, newest_sort_order = :newest_sort_order WHERE client_id = :client_id AND room_id = :room_id; +getAllAccountData: SELECT * FROM account_data WHERE client_id = :client_id; +storeAccountData: INSERT OR REPLACE INTO account_data (client_id, type, content) VALUES (:client_id, :type, :content); +getAllPresences: SELECT * FROM presences WHERE client_id = :client_id; +storePresence: INSERT OR REPLACE INTO presences (client_id, type, sender, content) VALUES (:client_id, :type, :sender, :content); +updateEvent: UPDATE events SET unsigned = :unsigned, content = :content, prev_content = :prev_content WHERE client_id = :client_id AND event_id = :event_id AND room_id = :room_id; +updateEventStatus: UPDATE events SET status = :status, event_id = :new_event_id WHERE client_id = :client_id AND event_id = :old_event_id AND room_id = :room_id; +getAllRoomStates: SELECT * FROM room_states WHERE client_id = :client_id; +storeEvent: INSERT OR REPLACE INTO events (client_id, event_id, room_id, sort_order, origin_server_ts, sender, type, unsigned, content, prev_content, state_key, status) VALUES (:client_id, :event_id, :room_id, :sort_order, :origin_server_ts, :sender, :type, :unsigned, :content, :prev_content, :state_key, :status); +storeRoomState: INSERT OR REPLACE INTO room_states (client_id, event_id, room_id, sort_order, origin_server_ts, sender, type, unsigned, content, prev_content, state_key) VALUES (:client_id, :event_id, :room_id, :sort_order, :origin_server_ts, :sender, :type, :unsigned, :content, :prev_content, :state_key); +getAllRoomAccountData: SELECT * FROM room_account_data WHERE client_id = :client_id; +storeRoomAccountData: INSERT OR REPLACE INTO room_account_data (client_id, type, room_id, content) VALUES (:client_id, :type, :room_id, :content); +dbGetUser: SELECT * FROM room_states WHERE client_id = :client_id AND type = 'm.room.member' AND state_key = :state_key AND room_id = :room_id; +dbGetEventList: SELECT * FROM events WHERE client_id = :client_id AND room_id = :room_id GROUP BY event_id ORDER BY sort_order DESC; +getStates: SELECT * FROM room_states WHERE client_id = :client_id AND room_id = :room_id; +resetNotificationCount: UPDATE rooms SET notification_count = 0, highlight_count = 0 WHERE client_id = :client_id AND room_id = :room_id; +getEvent: SELECT * FROM events WHERE client_id = :client_id AND event_id = :event_id AND room_id = :room_id; +removeEvent: DELETE FROM events WHERE client_id = :client_id AND event_id = :event_id AND room_id = :room_id; +removeRoom: DELETE FROM rooms WHERE client_id = :client_id AND room_id = :room_id; +removeRoomEvents: DELETE FROM events WHERE client_id = :client_id AND room_id = :room_id; +storeFile: INSERT OR REPLACE INTO files (mxc_uri, bytes, saved_at) VALUES (:mxc_uri, :bytes, :time); +dbGetFile: SELECT * FROM files WHERE mxc_uri = :mxc_uri; diff --git a/lib/src/event.dart b/lib/src/event.dart index d5faf7e..16708a5 100644 --- a/lib/src/event.dart +++ b/lib/src/event.dart @@ -29,6 +29,7 @@ import 'package:http/http.dart' as http; import 'package:matrix_file_e2ee/matrix_file_e2ee.dart'; import './room.dart'; import 'utils/matrix_localizations.dart'; +import './database/database.dart' show DbRoomState, DbEvent; /// All data exchanged over Matrix is expressed as an "event". Typically each client action (e.g. sending a message) correlates with exactly one event. class Event { @@ -96,6 +97,8 @@ class Event { User get stateKeyUser => room.getUserByMXIDSync(stateKey); + double sortOrder; + Event( {this.status = defaultStatus, this.content, @@ -107,7 +110,8 @@ class Event { this.unsigned, this.prevContent, this.stateKey, - this.room}); + this.room, + this.sortOrder = 0.0}); static Map getMapFromPayload(dynamic payload) { if (payload is String) { @@ -122,7 +126,7 @@ class Event { } /// Get a State event from a table row or from the event stream. - factory Event.fromJson(Map jsonPayload, Room room) { + factory Event.fromJson(Map jsonPayload, Room room, [double sortOrder]) { final content = Event.getMapFromPayload(jsonPayload['content']); final unsigned = Event.getMapFromPayload(jsonPayload['unsigned']); final prevContent = Event.getMapFromPayload(jsonPayload['prev_content']); @@ -140,6 +144,31 @@ class Event { : DateTime.now(), unsigned: unsigned, room: room, + sortOrder: sortOrder ?? 0.0, + ); + } + + /// Get an event from either DbRoomState or DbEvent + factory Event.fromDb(dynamic dbEntry, Room room) { + if (!(dbEntry is DbRoomState || dbEntry is DbEvent)) { + throw('Unknown db type'); + } + final content = Event.getMapFromPayload(dbEntry.content); + final unsigned = Event.getMapFromPayload(dbEntry.unsigned); + final prevContent = Event.getMapFromPayload(dbEntry.prevContent); + return Event( + status: (dbEntry is DbEvent ? dbEntry.status : null) ?? defaultStatus, + stateKey: dbEntry.stateKey, + prevContent: prevContent, + content: content, + typeKey: dbEntry.type, + eventId: dbEntry.eventId, + roomId: dbEntry.roomId, + senderId: dbEntry.sender, + time: dbEntry.originServerTs ?? DateTime.now(), + unsigned: unsigned, + room: room, + sortOrder: dbEntry.sortOrder ?? 0.0, ); } @@ -337,9 +366,7 @@ class Event { /// from the database and the timelines. Returns false if not removed. Future remove() async { if (status < 1) { - if (room.client.store != null) { - await room.client.store.removeEvent(eventId); - } + await room.client.database?.removeEvent(room.client.id, eventId, room.id); room.client.onEvent.add(EventUpdate( roomID: room.id, @@ -349,7 +376,8 @@ class Event { 'event_id': eventId, 'status': -2, 'content': {'body': 'Removed...'} - })); + }, + sortOrder: sortOrder)); return true; } return false; @@ -470,13 +498,13 @@ class Event { // Is this file storeable? final infoMap = getThumbnail ? content['info']['thumbnail_info'] : content['info']; - final storeable = (room.client.storeAPI?.extended ?? false) && + final storeable = room.client.database != null && infoMap is Map && infoMap['size'] is int && - infoMap['size'] <= room.client.store.maxFileSize; + infoMap['size'] <= room.client.database.maxFileSize ; if (storeable) { - uint8list = await room.client.store.getFile(mxContent.toString()); + uint8list = await room.client.database.getFile(mxContent.toString()); } // Download the file @@ -484,7 +512,7 @@ class Event { uint8list = (await http.get(mxContent.getDownloadLink(room.client))).bodyBytes; if (storeable) { - await room.client.store.storeFile(uint8list, mxContent.toString()); + await room.client.database.storeFile(mxContent.toString(), uint8list, DateTime.now()); } } diff --git a/lib/src/presence.dart b/lib/src/presence.dart index 08b2d0b..4d1f854 100644 --- a/lib/src/presence.dart +++ b/lib/src/presence.dart @@ -21,6 +21,9 @@ * along with famedlysdk. If not, see . */ +import 'package:famedlysdk/famedlysdk.dart'; +import './database/database.dart' show DbPresence; + enum PresenceType { online, offline, unavailable } /// Informs the client of a user's presence state change. @@ -39,6 +42,8 @@ class Presence { final String statusMsg; final DateTime time; + Presence({this.sender, this.displayname, this.avatarUrl, this.currentlyActive, this.lastActiveAgo, this.presence, this.statusMsg, this.time}); + Presence.fromJson(Map json) : sender = json['sender'], displayname = json['content']['displayname'], @@ -55,4 +60,23 @@ class Presence { e.toString() == "PresenceType.${json['content']['presence']}", orElse: () => null), statusMsg = json['content']['status_msg']; + + factory Presence.fromDb(DbPresence dbEntry) { + final content = Event.getMapFromPayload(dbEntry.content); + return Presence( + sender: dbEntry.sender, + displayname: content['displayname'], + avatarUrl: content['avatar_url'] != null ? Uri.parse(content['avatar_url']) : null, + currentlyActive: content['currently_active'], + lastActiveAgo: content['last_active_ago'], + time: DateTime.fromMillisecondsSinceEpoch( + DateTime.now().millisecondsSinceEpoch - + (content['last_active_ago'] ?? 0)), + presence: PresenceType.values.firstWhere( + (e) => + e.toString() == "PresenceType.${content['presence']}", + orElse: () => null), + statusMsg: content['status_msg'], + ); + } } diff --git a/lib/src/room.dart b/lib/src/room.dart index 5be217d..f565596 100644 --- a/lib/src/room.dart +++ b/lib/src/room.dart @@ -44,6 +44,7 @@ import 'timeline.dart'; import 'utils/matrix_localizations.dart'; import 'utils/states_map.dart'; import './utils/markdown.dart'; +import './database/database.dart' show DbRoom; enum PushRuleState { notify, mentions_only, dont_notify } enum JoinRules { public, knock, invite, private } @@ -90,6 +91,27 @@ class Room { List _outboundGroupSessionDevices; + double _newestSortOrder; + double _oldestSortOrder; + + double get newSortOrder { + _newestSortOrder++; + return _newestSortOrder; + } + + double get oldSortOrder { + _oldestSortOrder--; + return _oldestSortOrder; + } + + void resetSortOrder() { + _oldestSortOrder = _newestSortOrder = 0.0; + } + + Future updateSortOrder() async { + await client.database?.updateRoomSortOrder(_oldestSortOrder, _newestSortOrder, client.id, id); + } + /// Clears the existing outboundGroupSession, tries to create a new one and /// stores it as an ingoingGroupSession in the [sessionKeys]. Then sends the /// new session encrypted with olm to all non-blocked devices using @@ -120,7 +142,7 @@ class Room { 'session_id': outboundGroupSession.session_id(), 'session_key': outboundGroupSession.session_key(), }; - setSessionKey(rawSession['session_id'], rawSession); + setInboundGroupSession(rawSession['session_id'], rawSession); try { await client.sendToDevice(deviceKeys, 'm.room_key', rawSession); _outboundGroupSession = outboundGroupSession; @@ -137,12 +159,10 @@ class Room { Future _storeOutboundGroupSession() async { if (_outboundGroupSession == null) return; - await client.storeAPI?.setItem( - '/clients/${client.deviceID}/rooms/${id}/outbound_group_session', - _outboundGroupSession.pickle(client.userID)); - await client.storeAPI?.setItem( - '/clients/${client.deviceID}/rooms/${id}/outbound_group_session_devices', - json.encode(_outboundGroupSessionDevices)); + await client.database?.storeOutboundGroupSession( + client.id, id, _outboundGroupSession.pickle(client.userID), + json.encode(_outboundGroupSessionDevices), + ); return; } @@ -162,12 +182,11 @@ class Room { return false; } } - _outboundGroupSessionDevices == null; - await client.storeAPI?.setItem( - '/clients/${client.deviceID}/rooms/${id}/outbound_group_session', null); - await client.storeAPI?.setItem( - '/clients/${client.deviceID}/rooms/${id}/outbound_group_session_devices', - null); + if (!wipe && _outboundGroupSessionDevices == null && _outboundGroupSession == null) { + return true; // let's just short-circuit out of here, no need to do DB stuff + } + _outboundGroupSessionDevices = null; + await client.database?.removeOutboundGroupSession(client.id, id); _outboundGroupSession?.free(); _outboundGroupSession = null; return true; @@ -181,13 +200,13 @@ class Room { /// "session_id": "X3lUlvLELLYxeTx4yOVu6UDpasGEVO0Jbu+QFnm0cKQ", /// "session_key": "AgAAAADxKHa9uFxcXzwYoNueL5Xqi69IkD4sni8LlfJL7qNBEY..." /// } - Map get sessionKeys => _sessionKeys; - Map _sessionKeys = {}; + Map get inboundGroupSessions => _inboundGroupSessions; + Map _inboundGroupSessions = {}; /// Add a new session key to the [sessionKeys]. - void setSessionKey(String sessionId, Map content, + void setInboundGroupSession(String sessionId, Map content, {bool forwarded = false}) { - if (sessionKeys.containsKey(sessionId)) return; + if (inboundGroupSessions.containsKey(sessionId)) return; olm.InboundGroupSession inboundGroupSession; if (content['algorithm'] == 'm.megolm.v1.aes-sha2') { try { @@ -203,16 +222,17 @@ class Room { e.toString()); } } - _sessionKeys[sessionId] = SessionKey( + _inboundGroupSessions[sessionId] = SessionKey( content: content, inboundGroupSession: inboundGroupSession, indexes: {}, key: client.userID, ); if (_fullyRestored) { - client.storeAPI?.setItem( - '/clients/${client.deviceID}/rooms/${id}/session_keys', - json.encode(sessionKeys)); + client.database?.storeInboundGroupSession(client.id, id, sessionId, + inboundGroupSession.pickle(client.userID), json.encode(content), + json.encode({}), + ); } _tryAgainDecryptLastMessage(); onSessionKeyReceived.add(sessionId); @@ -395,7 +415,9 @@ class Room { this.mInvitedMemberCount = 0, this.mJoinedMemberCount = 0, this.roomAccountData = const {}, - }); + double newestSortOrder = 0.0, + double oldestSortOrder = 0.0, + }) : _newestSortOrder = newestSortOrder, _oldestSortOrder = oldestSortOrder; /// The default count of how much events should be requested when requesting the /// history of this room. @@ -746,20 +768,22 @@ class Room { }; } + final sortOrder = newSortOrder; // Display a *sending* event and store it. - var eventUpdate = - EventUpdate(type: 'timeline', roomID: id, eventType: type, content: { - 'type': type, - 'event_id': messageID, - 'sender': client.userID, - 'status': 0, - 'origin_server_ts': now, - 'content': content - }); + var eventUpdate = EventUpdate(type: 'timeline', roomID: id, eventType: type, sortOrder: sortOrder, + content: { + 'type': type, + 'event_id': messageID, + 'sender': client.userID, + 'status': 0, + 'origin_server_ts': now, + 'content': content + }, + ); client.onEvent.add(eventUpdate); - await client.store?.transaction(() { - client.store.storeEventUpdate(eventUpdate); - return; + await client.database?.transaction(() async { + await client.database.storeEventUpdate(client.id, eventUpdate); + await updateSortOrder(); }); // Send the text and on success, store and display a *sent* event. @@ -767,7 +791,7 @@ class Room { final response = await client.jsonRequest( type: HTTPType.PUT, action: '/client/r0/rooms/${id}/send/$sendType/$messageID', - data: client.encryptionEnabled + data: encrypted && client.encryptionEnabled ? await encryptGroupMessagePayload(content) : content); final String res = response['event_id']; @@ -775,9 +799,8 @@ class Room { eventUpdate.content['unsigned'] = {'transaction_id': messageID}; eventUpdate.content['event_id'] = res; client.onEvent.add(eventUpdate); - await client.store?.transaction(() { - client.store.storeEventUpdate(eventUpdate); - return; + await client.database?.transaction(() async { + await client.database.storeEventUpdate(client.id, eventUpdate); }); return res; } catch (exception) { @@ -786,9 +809,8 @@ class Room { eventUpdate.content['status'] = -1; eventUpdate.content['unsigned'] = {'transaction_id': messageID}; client.onEvent.add(eventUpdate); - await client.store?.transaction(() { - client.store.storeEventUpdate(eventUpdate); - return; + await client.database?.transaction(() async { + await client.database.storeEventUpdate(client.id, eventUpdate); }); } return null; @@ -809,7 +831,7 @@ class Room { } } on MatrixException catch (exception) { if (exception.errorMessage == 'No known servers') { - await client.store?.forgetRoom(id); + await client.database?.forgetRoom(client.id, id); client.onRoomUpdate.add( RoomUpdate( id: id, @@ -833,7 +855,7 @@ class Room { /// Call the Matrix API to forget this room if you already left it. Future forget() async { - await client.store?.forgetRoom(id); + await client.database?.forgetRoom(client.id, id); await client.jsonRequest( type: HTTPType.POST, action: '/client/r0/rooms/${id}/forget'); return; @@ -903,65 +925,55 @@ class Room { if (onHistoryReceived != null) onHistoryReceived(); prev_batch = resp['end']; - await client.store?.storeRoomPrevBatch(this); + + final dbActions = Function()>[]; + if (client.database != null) { + dbActions.add(() => client.database.setRoomPrevBatch(prev_batch, client.id, id)); + } if (!(resp['chunk'] is List && resp['chunk'].length > 0 && resp['end'] is String)) return; if (resp['state'] is List) { - await client.store?.transaction(() { - for (var i = 0; i < resp['state'].length; i++) { - var eventUpdate = EventUpdate( - type: 'state', - roomID: id, - eventType: resp['state'][i]['type'], - content: resp['state'][i], - ).decrypt(this); - client.onEvent.add(eventUpdate); - client.store.storeEventUpdate(eventUpdate); - } - return; - }); - if (client.store == null) { - for (var i = 0; i < resp['state'].length; i++) { - var eventUpdate = EventUpdate( - type: 'state', - roomID: id, - eventType: resp['state'][i]['type'], - content: resp['state'][i], - ).decrypt(this); - client.onEvent.add(eventUpdate); + for (final state in resp['state']) { + var eventUpdate = EventUpdate( + type: 'state', + roomID: id, + eventType: state['type'], + content: state, + sortOrder: oldSortOrder, + ).decrypt(this); + client.onEvent.add(eventUpdate); + if (client.database != null) { + dbActions.add(() => client.database.storeEventUpdate(client.id, eventUpdate)); } } } List history = resp['chunk']; - await client.store?.transaction(() { - for (var i = 0; i < history.length; i++) { - var eventUpdate = EventUpdate( - type: 'history', - roomID: id, - eventType: history[i]['type'], - content: history[i], - ).decrypt(this); - client.onEvent.add(eventUpdate); - client.store.storeEventUpdate(eventUpdate); - client.store.setRoomPrevBatch(id, resp['end']); - } - return; - }); - if (client.store == null) { - for (var i = 0; i < history.length; i++) { - var eventUpdate = EventUpdate( - type: 'history', - roomID: id, - eventType: history[i]['type'], - content: history[i], - ).decrypt(this); - client.onEvent.add(eventUpdate); + for (final hist in history) { + var eventUpdate = EventUpdate( + type: 'history', + roomID: id, + eventType: hist['type'], + content: hist, + sortOrder: oldSortOrder, + ).decrypt(this); + client.onEvent.add(eventUpdate); + if (client.database != null) { + dbActions.add(() => client.database.storeEventUpdate(client.id, eventUpdate)); } } + if (client.database != null) { + dbActions.add(() => client.database.setRoomPrevBatch(resp['end'], client.id, id)); + } + await client.database?.transaction(() async { + for (final f in dbActions) { + await f(); + } + await updateSortOrder(); + }); client.onRoomUpdate.add( RoomUpdate( id: id, @@ -1013,7 +1025,7 @@ class Room { /// Sends *m.fully_read* and *m.read* for the given event ID. Future sendReadReceipt(String eventID) async { notificationCount = 0; - await client?.store?.resetNotificationCount(id); + await client.database?.resetNotificationCount(client.id, id); await client.jsonRequest( type: HTTPType.POST, action: '/client/r0/rooms/$id/read_markers', @@ -1024,48 +1036,44 @@ class Room { return; } - Future restoreGroupSessionKeys() async { + Future restoreGroupSessionKeys({ + dynamic outboundGroupSession, // DbOutboundGroupSession, optionally as future + dynamic inboundGroupSessions, // DbSessionKey, as iterator and optionally as future + }) async { // Restore the inbound and outbound session keys - if (client.encryptionEnabled && client.storeAPI != null) { - final String outboundGroupSessionPickle = await client.storeAPI.getItem( - '/clients/${client.deviceID}/rooms/${id}/outbound_group_session'); - if (outboundGroupSessionPickle != null) { + if (client.encryptionEnabled && client.database != null) { + outboundGroupSession ??= client.database.getDbOutboundGroupSession(client.id, id); + inboundGroupSessions ??= client.database.getDbInboundGroupSessions(client.id, id); + if (outboundGroupSession is Future) { + outboundGroupSession = await outboundGroupSession; + } + if (inboundGroupSessions is Future) { + inboundGroupSessions = await inboundGroupSessions; + } + if (outboundGroupSession != false && outboundGroupSession != null) { try { _outboundGroupSession = olm.OutboundGroupSession(); _outboundGroupSession.unpickle( - client.userID, outboundGroupSessionPickle); + client.userID, outboundGroupSession.pickle); } catch (e) { _outboundGroupSession = null; print('[LibOlm] Unable to unpickle outboundGroupSession: ' + e.toString()); } - } - final String outboundGroupSessionDevicesString = await client.storeAPI - .getItem( - '/clients/${client.deviceID}/rooms/${id}/outbound_group_session_devices'); - if (outboundGroupSessionDevicesString != null) { _outboundGroupSessionDevices = - List.from(json.decode(outboundGroupSessionDevicesString)); + List.from(json.decode(outboundGroupSession.deviceIds)); } - final String sessionKeysPickle = await client.storeAPI - .getItem('/clients/${client.deviceID}/rooms/${id}/session_keys'); - if (sessionKeysPickle?.isNotEmpty ?? false) { - final Map map = json.decode(sessionKeysPickle); - _sessionKeys ??= {}; - for (var entry in map.entries) { + if (inboundGroupSessions?.isNotEmpty ?? false) { + _inboundGroupSessions ??= {}; + for (final sessionKey in inboundGroupSessions) { try { - _sessionKeys[entry.key] = - SessionKey.fromJson(entry.value, client.userID); + _inboundGroupSessions[sessionKey.sessionId] = SessionKey.fromDb(sessionKey, client.userID); } catch (e) { - print('[LibOlm] Could not unpickle inboundGroupSession: ' + - e.toString()); + print('[LibOlm] Could not unpickle inboundGroupSession: ' + e.toString()); } } } } - await client.storeAPI?.setItem( - '/clients/${client.deviceID}/rooms/${id}/session_keys', - json.encode(sessionKeys)); _tryAgainDecryptLastMessage(); _fullyRestored = true; return; @@ -1076,44 +1084,64 @@ class Room { /// Returns a Room from a json String which comes normally from the store. If the /// state are also given, the method will await them. static Future getRoomFromTableRow( - Map row, Client matrix, - {Future>> states, - Future>> roomAccountData}) async { - var newRoom = Room( - id: row['room_id'], + DbRoom row, // either Map or DbRoom + Client matrix, + { + dynamic states, // DbRoomState, as iterator and optionally as future + dynamic roomAccountData, // DbRoomAccountData, as iterator and optionally as future + dynamic outboundGroupSession, // DbOutboundGroupSession, optionally as future + dynamic inboundGroupSessions, // DbSessionKey, as iterator and optionally as future + }) async { + final newRoom = Room( + id: row.roomId, membership: Membership.values - .firstWhere((e) => e.toString() == 'Membership.' + row['membership']), - notificationCount: row['notification_count'], - highlightCount: row['highlight_count'], - notificationSettings: row['notification_settings'], - prev_batch: row['prev_batch'], - mInvitedMemberCount: row['invited_member_count'], - mJoinedMemberCount: row['joined_member_count'], - mHeroes: row['heroes']?.split(',') ?? [], + .firstWhere((e) => e.toString() == 'Membership.' + row.membership), + notificationCount: row.notificationCount, + highlightCount: row.highlightCount, + notificationSettings: 'mention', // TODO: do proper things + prev_batch: row.prevBatch, + mInvitedMemberCount: row.invitedMemberCount, + mJoinedMemberCount: row.joinedMemberCount, + mHeroes: row.heroes?.split(',') ?? [], client: matrix, roomAccountData: {}, + newestSortOrder: row.newestSortOrder, + oldestSortOrder: row.oldestSortOrder, ); - // Restore the inbound and outbound session keys - await newRoom.restoreGroupSessionKeys(); - if (states != null) { - var rawStates = await states; - for (var i = 0; i < rawStates.length; i++) { - var newState = Event.fromJson(rawStates[i], newRoom); + var rawStates; + if (states is Future) { + rawStates = await states; + } else { + rawStates = states; + } + for (final rawState in rawStates) { + final newState = Event.fromDb(rawState, newRoom);; newRoom.setState(newState); } } var newRoomAccountData = {}; if (roomAccountData != null) { - var rawRoomAccountData = await roomAccountData; - for (var i = 0; i < rawRoomAccountData.length; i++) { - var newData = RoomAccountData.fromJson(rawRoomAccountData[i], newRoom); + var rawRoomAccountData; + if (roomAccountData is Future) { + rawRoomAccountData = await roomAccountData; + } else { + rawRoomAccountData = roomAccountData; + } + for (final singleAccountData in rawRoomAccountData) { + final newData = RoomAccountData.fromDb(singleAccountData, newRoom); newRoomAccountData[newData.typeKey] = newData; } - newRoom.roomAccountData = newRoomAccountData; } + newRoom.roomAccountData = newRoomAccountData; + + // Restore the inbound and outbound session keys + await newRoom.restoreGroupSessionKeys( + outboundGroupSession: outboundGroupSession, + inboundGroupSessions: inboundGroupSessions, + ); return newRoom; } @@ -1122,24 +1150,28 @@ class Room { Future getTimeline( {onTimelineUpdateCallback onUpdate, onTimelineInsertCallback onInsert}) async { - var events = client.store != null - ? await client.store.getEventList(this) - : []; + var events; + if (client.database != null) { + events = await client.database.getEventList(client.id, this); + } else { + events = []; + } // Try again to decrypt encrypted events and update the database. - if (encrypted && client.store != null) { - await client.store.transaction(() { + if (encrypted && client.database != null) { + await client.database.transaction(() async { for (var i = 0; i < events.length; i++) { if (events[i].type == EventTypes.Encrypted && events[i].content['body'] == DecryptError.UNKNOWN_SESSION) { events[i] = events[i].decrypted; if (events[i].type != EventTypes.Encrypted) { - client.store.storeEventUpdate( + await client.database.storeEventUpdate(client.id, EventUpdate( eventType: events[i].typeKey, content: events[i].toJson(), roomID: events[i].roomId, type: 'timeline', + sortOrder: events[i].sortOrder, ), ); } @@ -1154,7 +1186,7 @@ class Room { onUpdate: onUpdate, onInsert: onInsert, ); - if (client.store == null) { + if (client.database == null) { prev_batch = ''; await requestHistory(historyCount: 10); } @@ -1227,6 +1259,9 @@ class Room { /// Requests a missing [User] for this room. Important for clients using /// lazy loading. Future requestUser(String mxID, {bool ignoreErrors = false}) async { + if (getState('m.room.member', mxID) != null) { + return getState('m.room.member', mxID).asUser; + } if (mxID == null || !_requestingMatrixIds.add(mxID)) return null; Map resp; try { @@ -1237,23 +1272,30 @@ class Room { _requestingMatrixIds.remove(mxID); if (!ignoreErrors) rethrow; } + if (resp == null) { + return null; + } final user = User(mxID, displayName: resp['displayname'], avatarUrl: resp['avatar_url'], room: this); states[mxID] = user; - if (client.store != null) { - await client.store.transaction(() { - client.store.storeEventUpdate( - EventUpdate( - content: resp, - roomID: id, - type: 'state', - eventType: 'm.room.member'), - ); - return; - }); - } + await client.database?.transaction(() async { + final content = { + 'sender': mxID, + 'type': 'm.room.member', + 'content': resp, + 'state_key': mxID, + }; + await client.database.storeEventUpdate(client.id, + EventUpdate( + content: content, + roomID: id, + type: 'state', + eventType: 'm.room.member', + sortOrder: 0.0), + ); + }); if (onUpdate != null) onUpdate.add(id); _requestingMatrixIds.remove(mxID); return user; @@ -1690,12 +1732,11 @@ class Room { Future> getUserDeviceKeys() async { var deviceKeys = []; var users = await requestParticipants(); - for (final userDeviceKeyEntry in client.userDeviceKeys.entries) { - if (users.indexWhere((u) => u.id == userDeviceKeyEntry.key) == -1) { - continue; - } - for (var deviceKeyEntry in userDeviceKeyEntry.value.deviceKeys.values) { - deviceKeys.add(deviceKeyEntry); + for (final user in users) { + if (client.userDeviceKeys.containsKey(user.id)) { + for (var deviceKeyEntry in client.userDeviceKeys[user.id].deviceKeys.values) { + deviceKeys.add(deviceKeyEntry); + } } } return deviceKeys; @@ -1745,23 +1786,23 @@ class Room { throw (DecryptError.UNKNOWN_ALGORITHM); } final String sessionId = event.content['session_id']; - if (!sessionKeys.containsKey(sessionId)) { + if (!inboundGroupSessions.containsKey(sessionId)) { throw (DecryptError.UNKNOWN_SESSION); } - final decryptResult = sessionKeys[sessionId] + final decryptResult = inboundGroupSessions[sessionId] .inboundGroupSession .decrypt(event.content['ciphertext']); final messageIndexKey = event.eventId + event.time.millisecondsSinceEpoch.toString(); - if (sessionKeys[sessionId].indexes.containsKey(messageIndexKey) && - sessionKeys[sessionId].indexes[messageIndexKey] != + if (inboundGroupSessions[sessionId].indexes.containsKey(messageIndexKey) && + inboundGroupSessions[sessionId].indexes[messageIndexKey] != decryptResult.message_index) { if ((_outboundGroupSession?.session_id() ?? '') == sessionId) { clearOutboundGroupSession(); } throw (DecryptError.CHANNEL_CORRUPTED); } - sessionKeys[sessionId].indexes[messageIndexKey] = + inboundGroupSessions[sessionId].indexes[messageIndexKey] = decryptResult.message_index; _storeOutboundGroupSession(); decryptedPayload = json.decode(decryptResult.plaintext); @@ -1799,6 +1840,7 @@ class Room { stateKey: event.stateKey, prevContent: event.prevContent, status: event.status, + sortOrder: event.sortOrder, ); } } diff --git a/lib/src/room_account_data.dart b/lib/src/room_account_data.dart index dbc7386..c82c6eb 100644 --- a/lib/src/room_account_data.dart +++ b/lib/src/room_account_data.dart @@ -24,6 +24,7 @@ import 'package:famedlysdk/famedlysdk.dart'; import 'package:famedlysdk/src/account_data.dart'; import 'package:famedlysdk/src/event.dart'; +import './database/database.dart' show DbRoomAccountData; /// Stripped down events for account data and ephemrals of a room. class RoomAccountData extends AccountData { @@ -46,4 +47,14 @@ class RoomAccountData extends AccountData { roomId: jsonPayload['room_id'], room: room); } + + /// get room account data from DbRoomAccountData + factory RoomAccountData.fromDb(DbRoomAccountData dbEntry, Room room) { + final content = Event.getMapFromPayload(dbEntry.content); + return RoomAccountData( + content: content, + typeKey: dbEntry.type, + roomId: dbEntry.roomId, + room: room); + } } diff --git a/lib/src/store_api.dart b/lib/src/store_api.dart deleted file mode 100644 index dfe2ac8..0000000 --- a/lib/src/store_api.dart +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright (c) 2019 Zender & Kurtz GbR. - * - * Authors: - * Christian Pauly - * Marcel Radzio - * - * This file is part of famedlysdk. - * - * famedlysdk is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * famedlysdk is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with famedlysdk. If not, see . - */ - -import 'dart:async'; -import 'dart:core'; -import 'dart:typed_data'; -import 'package:famedlysdk/src/account_data.dart'; -import 'package:famedlysdk/src/presence.dart'; -import 'package:famedlysdk/src/utils/device_keys_list.dart'; -import 'client.dart'; -import 'event.dart'; -import 'room.dart'; -import 'user.dart'; -import 'sync/event_update.dart'; -import 'sync/room_update.dart'; -import 'sync/user_update.dart'; - -abstract class StoreAPI { - /// Whether this is a simple store which only stores the client credentials and - /// end to end encryption stuff or the whole sync payloads. - final bool extended = false; - - /// Link back to the client. - Client client; - - /// Will be automatically called when the client is logged in successfully. - Future storeClient(); - - /// Clears all tables from the database. - Future clear(); - - Future getItem(String key); - - Future setItem(String key, String value); - - Future> getUserDeviceKeys(); - - Future storeUserDeviceKeys(Map userDeviceKeys); -} - -/// Responsible to store all data persistent and to query objects from the -/// database. -abstract class ExtendedStoreAPI extends StoreAPI { - /// The maximum size of files which should be stored in bytes. - int get maxFileSize => 1 * 1024 * 1024; - - /// Whether this is a simple store which only stores the client credentials and - /// end to end encryption stuff or the whole sync payloads. - @override - final bool extended = true; - - /// The current trans - Future setRoomPrevBatch(String roomId, String prevBatch); - - /// Performs these query or queries inside of an transaction. - Future transaction(void Function() queries); - - /// Will be automatically called on every synchronisation. Must be called inside of - // /// [transaction]. - void storePrevBatch(String prevBatch); - - Future storeRoomPrevBatch(Room room); - - /// Stores a RoomUpdate object in the database. Must be called inside of - /// [transaction]. - Future storeRoomUpdate(RoomUpdate roomUpdate); - - /// Stores an UserUpdate object in the database. Must be called inside of - /// [transaction]. - Future storeUserEventUpdate(UserUpdate userUpdate); - - /// Stores an EventUpdate object in the database. Must be called inside of - /// [transaction]. - Future storeEventUpdate(EventUpdate eventUpdate); - - /// Returns a User object by a given Matrix ID and a Room. - Future getUser({String matrixID, Room room}); - - /// Returns a list of events for the given room and sets all participants. - Future> getEventList(Room room); - - /// Returns all rooms, the client is participating. Excludes left rooms. - Future> getRoomList({bool onlyLeft = false}); - - /// Deletes this room from the database. - Future forgetRoom(String roomID); - - /// Sets notification and highlight count to 0 for this room. - Future resetNotificationCount(String roomID); - - /// Searches for the event in the store. - Future getEventById(String eventID, Room room); - - /// Returns all account data for this client. - Future> getAccountData(); - - /// Returns all stored presences for this client. - Future> getPresences(); - - /// Removes this event from the store. - Future removeEvent(String eventId); - - /// Stores the bytes of this file indexed by the [mxcUri]. Throws an - /// exception if the bytes are more than [MAX_FILE_SIZE]. - Future storeFile(Uint8List bytes, String mxcUri); - - /// Returns the file bytes indexed by [mxcUri]. Returns null if not found. - Future getFile(String mxcUri); -} diff --git a/lib/src/sync/event_update.dart b/lib/src/sync/event_update.dart index 19cce63..841b290 100644 --- a/lib/src/sync/event_update.dart +++ b/lib/src/sync/event_update.dart @@ -40,7 +40,10 @@ class EventUpdate { // The json payload of the content of this event. final Map content; - EventUpdate({this.eventType, this.roomID, this.type, this.content}); + // the order where to stort this event + final double sortOrder; + + EventUpdate({this.eventType, this.roomID, this.type, this.content, this.sortOrder}); EventUpdate decrypt(Room room) { if (eventType != 'm.room.encrypted') { @@ -48,12 +51,13 @@ class EventUpdate { } try { var decrpytedEvent = - room.decryptGroupMessage(Event.fromJson(content, room)); + room.decryptGroupMessage(Event.fromJson(content, room, sortOrder)); return EventUpdate( eventType: eventType, roomID: roomID, type: type, content: decrpytedEvent.toJson(), + sortOrder: sortOrder, ); } catch (e) { print('[LibOlm] Could not decrypt megolm event: ' + e.toString()); diff --git a/lib/src/timeline.dart b/lib/src/timeline.dart index 74c66fa..9b765f8 100644 --- a/lib/src/timeline.dart +++ b/lib/src/timeline.dart @@ -122,7 +122,7 @@ class Timeline { final eventId = _findEvent(event_id: eventUpdate.content['redacts']); if (eventId != null) { events[eventId] - .setRedactionEvent(Event.fromJson(eventUpdate.content, room)); + .setRedactionEvent(Event.fromJson(eventUpdate.content, room, eventUpdate.sortOrder)); } } else if (eventUpdate.content['status'] == -2) { var i = _findEvent(event_id: eventUpdate.content['event_id']); @@ -138,18 +138,18 @@ class Timeline { : null); if (i < events.length) { - events[i] = Event.fromJson(eventUpdate.content, room); + events[i] = Event.fromJson(eventUpdate.content, room, eventUpdate.sortOrder); } } else { Event newEvent; - var senderUser = await room.client.store - ?.getUser(matrixID: eventUpdate.content['sender'], room: room); + var senderUser = room.getState('m.room.member', eventUpdate.content['sender'])?.asUser ?? await room.client.database + ?.getUser(room.client.id, eventUpdate.content['sender'], room); if (senderUser != null) { eventUpdate.content['displayname'] = senderUser.displayName; eventUpdate.content['avatar_url'] = senderUser.avatarUrl.toString(); } - newEvent = Event.fromJson(eventUpdate.content, room); + newEvent = Event.fromJson(eventUpdate.content, room, eventUpdate.sortOrder); if (eventUpdate.type == 'history' && events.indexWhere( @@ -173,8 +173,7 @@ class Timeline { void sort() { if (sortLock || events.length < 2) return; sortLock = true; - events?.sort((a, b) => - b.time.millisecondsSinceEpoch.compareTo(a.time.millisecondsSinceEpoch)); + events?.sort((a, b) => b.sortOrder - a.sortOrder > 0 ? 1 : -1); sortLock = false; } diff --git a/lib/src/utils/device_keys_list.dart b/lib/src/utils/device_keys_list.dart index f67a8e0..b4f5ba4 100644 --- a/lib/src/utils/device_keys_list.dart +++ b/lib/src/utils/device_keys_list.dart @@ -1,12 +1,23 @@ import 'dart:convert'; import '../client.dart'; +import '../database/database.dart' show DbUserDeviceKey, DbUserDeviceKeysKey; +import '../event.dart'; class DeviceKeysList { String userId; bool outdated = true; Map deviceKeys = {}; + DeviceKeysList.fromDb(DbUserDeviceKey dbEntry, List childEntries) { + userId = dbEntry.userId; + outdated = dbEntry.outdated; + deviceKeys = {}; + for (final childEntry in childEntries) { + deviceKeys[childEntry.deviceId] = DeviceKeys.fromDb(childEntry); + } + } + DeviceKeysList.fromJson(Map json) { userId = json['user_id']; outdated = json['outdated']; @@ -52,7 +63,7 @@ class DeviceKeys { Future setVerified(bool newVerified, Client client) { verified = newVerified; - return client.storeAPI.storeUserDeviceKeys(client.userDeviceKeys); + return client.database?.setVerifiedUserDeviceKey(newVerified, client.id, userId, deviceId); } Future setBlocked(bool newBlocked, Client client) { @@ -63,7 +74,7 @@ class DeviceKeys { room.clearOutboundGroupSession(); } } - return client.storeAPI.storeUserDeviceKeys(client.userDeviceKeys); + return client.database?.setBlockedUserDeviceKey(newBlocked, client.id, userId, deviceId); } DeviceKeys({ @@ -77,6 +88,22 @@ class DeviceKeys { this.blocked, }); + DeviceKeys.fromDb(DbUserDeviceKeysKey dbEntry) { + final content = Event.getMapFromPayload(dbEntry.content); + userId = dbEntry.userId; + deviceId = dbEntry.deviceId; + algorithms = content['algorithms'].cast(); + keys = content['keys'] != null ? Map.from(content['keys']) : null; + signatures = content['signatures'] != null + ? Map.from(content['signatures']) + : null; + unsigned = content['unsigned'] != null + ? Map.from(content['unsigned']) + : null; + verified = dbEntry.verified; + blocked = dbEntry.blocked; + } + DeviceKeys.fromJson(Map json) { userId = json['user_id']; deviceId = json['device_id']; diff --git a/lib/src/utils/room_key_request.dart b/lib/src/utils/room_key_request.dart index 05fbe66..501691d 100644 --- a/lib/src/utils/room_key_request.dart +++ b/lib/src/utils/room_key_request.dart @@ -16,7 +16,7 @@ class RoomKeyRequest extends ToDeviceEvent { Future forwardKey() async { var room = this.room; - final session = room.sessionKeys[content['body']['session_id']]; + final session = room.inboundGroupSessions[content['body']['session_id']]; var forwardedKeys = [client.identityKey]; for (final key in session.forwardingCurve25519KeyChain) { forwardedKeys.add(key); diff --git a/lib/src/utils/session_key.dart b/lib/src/utils/session_key.dart index 414ea85..b31707c 100644 --- a/lib/src/utils/session_key.dart +++ b/lib/src/utils/session_key.dart @@ -2,6 +2,9 @@ import 'dart:convert'; import 'package:olm/olm.dart'; +import '../database/database.dart' show DbInboundGroupSession; +import '../event.dart'; + class SessionKey { Map content; Map indexes; @@ -14,6 +17,20 @@ class SessionKey { SessionKey({this.content, this.inboundGroupSession, this.key, this.indexes}); + SessionKey.fromDb(DbInboundGroupSession dbEntry, String key) : key = key { + final parsedContent = Event.getMapFromPayload(dbEntry.content); + final parsedIndexes = Event.getMapFromPayload(dbEntry.indexes); + content = parsedContent != null + ? Map.from(parsedContent) + : null; + indexes = parsedIndexes != null + ? Map.from(parsedIndexes) + : {}; + var newInboundGroupSession = InboundGroupSession(); + newInboundGroupSession.unpickle(key, dbEntry.pickle); + inboundGroupSession = newInboundGroupSession; + } + SessionKey.fromJson(Map json, String key) : key = key { content = json['content'] != null ? Map.from(json['content']) diff --git a/pubspec.lock b/pubspec.lock index b8efd9f..d354e3e 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1,13 +1,27 @@ # Generated by pub # See https://dart.dev/tools/pub/glossary#lockfile packages: + _fe_analyzer_shared: + dependency: transitive + description: + name: _fe_analyzer_shared + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.0" analyzer: dependency: transitive description: name: analyzer url: "https://pub.dartlang.org" source: hosted - version: "0.36.3" + version: "0.39.8" + analyzer_plugin_fork: + dependency: transitive + description: + name: analyzer_plugin_fork + url: "https://pub.dartlang.org" + source: hosted + version: "0.2.2" archive: dependency: transitive description: @@ -42,42 +56,42 @@ packages: name: build url: "https://pub.dartlang.org" source: hosted - version: "1.1.4" + version: "1.2.2" build_config: dependency: transitive description: name: build_config url: "https://pub.dartlang.org" source: hosted - version: "0.4.0" + version: "0.4.2" build_daemon: dependency: transitive description: name: build_daemon url: "https://pub.dartlang.org" source: hosted - version: "1.0.0" + version: "2.1.4" build_resolvers: dependency: transitive description: name: build_resolvers url: "https://pub.dartlang.org" source: hosted - version: "1.0.5" + version: "1.3.7" build_runner: dependency: "direct dev" description: name: build_runner url: "https://pub.dartlang.org" source: hosted - version: "1.5.2" + version: "1.9.0" build_runner_core: dependency: transitive description: name: build_runner_core url: "https://pub.dartlang.org" source: hosted - version: "3.0.6" + version: "5.1.0" built_collection: dependency: transitive description: @@ -91,7 +105,7 @@ packages: name: built_value url: "https://pub.dartlang.org" source: hosted - version: "6.6.0" + version: "7.1.0" canonical_json: dependency: "direct main" description: @@ -106,13 +120,27 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.1.2" + checked_yaml: + dependency: transitive + description: + name: checked_yaml + url: "https://pub.dartlang.org" + source: hosted + version: "1.0.2" + cli_util: + dependency: transitive + description: + name: cli_util + url: "https://pub.dartlang.org" + source: hosted + version: "0.1.4" code_builder: dependency: transitive description: name: code_builder url: "https://pub.dartlang.org" source: hosted - version: "3.2.0" + version: "3.2.1" collection: dependency: transitive description: @@ -154,7 +182,7 @@ packages: name: dart_style url: "https://pub.dartlang.org" source: hosted - version: "1.2.7" + version: "1.3.6" ffi: dependency: transitive description: @@ -169,13 +197,6 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.10.9" - front_end: - dependency: transitive - description: - name: front_end - url: "https://pub.dartlang.org" - source: hosted - version: "0.1.18" glob: dependency: transitive description: @@ -252,14 +273,7 @@ packages: name: json_annotation url: "https://pub.dartlang.org" source: hosted - version: "2.4.0" - kernel: - dependency: transitive - description: - name: kernel - url: "https://pub.dartlang.org" - source: hosted - version: "0.3.18" + version: "3.0.1" logging: dependency: transitive description: @@ -311,6 +325,27 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.3.0" + moor: + dependency: "direct main" + description: + name: moor + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.2" + moor_ffi: + dependency: "direct dev" + description: + name: moor_ffi + url: "https://pub.dartlang.org" + source: hosted + version: "0.5.0" + moor_generator: + dependency: "direct dev" + description: + name: moor_generator + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.0" multi_server_socket: dependency: transitive description: @@ -396,7 +431,7 @@ packages: name: pubspec_parse url: "https://pub.dartlang.org" source: hosted - version: "0.1.4" + version: "0.1.5" quiver: dependency: transitive description: @@ -404,6 +439,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.0.5" + recase: + dependency: transitive + description: + name: recase + url: "https://pub.dartlang.org" + source: hosted + version: "3.0.0" shelf: dependency: transitive description: @@ -432,6 +474,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "0.2.3" + source_gen: + dependency: transitive + description: + name: source_gen + url: "https://pub.dartlang.org" + source: hosted + version: "0.9.5" source_map_stack_trace: dependency: transitive description: @@ -453,6 +502,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.5.5" + sqlparser: + dependency: transitive + description: + name: sqlparser + url: "https://pub.dartlang.org" + source: hosted + version: "0.8.1" stack_trace: dependency: transitive description: @@ -473,7 +529,7 @@ packages: name: stream_transform url: "https://pub.dartlang.org" source: hosted - version: "0.0.19" + version: "1.2.0" string_scanner: dependency: transitive description: @@ -481,6 +537,13 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "1.0.5" + synchronized: + dependency: transitive + description: + name: synchronized + url: "https://pub.dartlang.org" + source: hosted + version: "2.2.0" term_glyph: dependency: transitive description: @@ -515,7 +578,7 @@ packages: name: timing url: "https://pub.dartlang.org" source: hosted - version: "0.1.1+1" + version: "0.1.1+2" typed_data: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index ea95e73..576d016 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -14,6 +14,7 @@ dependencies: image: ^2.1.4 markdown: ^2.1.3 html_unescape: ^1.0.1+3 + moor: ^3.0.2 olm: git: @@ -27,5 +28,7 @@ dependencies: dev_dependencies: test: ^1.0.0 + moor_generator: ^3.0.0 build_runner: ^1.5.2 pedantic: ^1.9.0 # DO NOT UPDATE AS THIS WOULD CAUSE FLUTTER TO FAIL + moor_ffi: ^0.5.0 diff --git a/test/client_test.dart b/test/client_test.dart index 9693f4f..c59ac4a 100644 --- a/test/client_test.dart +++ b/test/client_test.dart @@ -39,7 +39,7 @@ import 'package:olm/olm.dart' as olm; import 'package:test/test.dart'; import 'fake_matrix_api.dart'; -import 'fake_store.dart'; +import 'fake_database.dart'; void main() { Client matrix; @@ -86,7 +86,6 @@ void main() { }); expect(matrix.homeserver, null); - expect(matrix.matrixVersions, null); try { await matrix.checkServer('https://fakeserver.wrongaddress'); @@ -95,8 +94,6 @@ void main() { } await matrix.checkServer('https://fakeserver.notexisting'); expect(matrix.homeserver, 'https://fakeserver.notexisting'); - expect(matrix.matrixVersions, - ['r0.0.1', 'r0.1.0', 'r0.2.0', 'r0.3.0', 'r0.4.0', 'r0.5.0']); final resp = await matrix .jsonRequest(type: HTTPType.POST, action: '/client/r0/login', data: { @@ -128,7 +125,6 @@ void main() { newHomeserver: matrix.homeserver, newDeviceName: 'Text Matrix Client', newDeviceID: resp['device_id'], - newMatrixVersions: matrix.matrixVersions, newOlmAccount: pickledOlmAccount, ); @@ -160,18 +156,18 @@ void main() { expect(matrix.directChats, matrix.accountData['m.direct'].content); expect(matrix.presences.length, 1); expect(matrix.rooms[1].ephemerals.length, 2); - expect(matrix.rooms[1].sessionKeys.length, 1); + expect(matrix.rooms[1].inboundGroupSessions.length, 1); expect( matrix .rooms[1] - .sessionKeys['ciM/JWTPrmiWPPZNkRLDPQYf9AW/I46bxyLSr+Bx5oU'] + .inboundGroupSessions['ciM/JWTPrmiWPPZNkRLDPQYf9AW/I46bxyLSr+Bx5oU'] .content['session_key'], 'AgAAAAAQcQ6XrFJk6Prm8FikZDqfry/NbDz8Xw7T6e+/9Yf/q3YHIPEQlzv7IZMNcYb51ifkRzFejVvtphS7wwG2FaXIp4XS2obla14iKISR0X74ugB2vyb1AydIHE/zbBQ1ic5s3kgjMFlWpu/S3FQCnCrv+DPFGEt3ERGWxIl3Bl5X53IjPyVkz65oljz2TZESwz0GH/QFvyOOm8ci0q/gceaF3S7Dmafg3dwTKYwcA5xkcc+BLyrLRzB6Hn+oMAqSNSscnm4mTeT5zYibIhrzqyUTMWr32spFtI9dNR/RFSzfCw'); if (olmEnabled) { expect( matrix .rooms[1] - .sessionKeys['ciM/JWTPrmiWPPZNkRLDPQYf9AW/I46bxyLSr+Bx5oU'] + .inboundGroupSessions['ciM/JWTPrmiWPPZNkRLDPQYf9AW/I46bxyLSr+Bx5oU'] .inboundGroupSession != null, true); @@ -279,7 +275,6 @@ void main() { expect(matrix.userID == null, true); expect(matrix.deviceID == null, true); expect(matrix.deviceName == null, true); - expect(matrix.matrixVersions == null, true); expect(matrix.prevBatch == null, true); var loginState = await loginStateFuture; @@ -635,8 +630,7 @@ void main() { test('Test the fake store api', () async { var client1 = Client('testclient', debug: true); client1.httpClient = FakeMatrixApi(); - var fakeStore = FakeStore(client1, {}); - client1.storeAPI = fakeStore; + client1.database = getDatabase(); client1.connect( newToken: 'abc123', @@ -644,14 +638,6 @@ void main() { newHomeserver: 'https://fakeServer.notExisting', newDeviceName: 'Text Matrix Client', newDeviceID: 'GHTYAJCE', - newMatrixVersions: [ - 'r0.0.1', - 'r0.1.0', - 'r0.2.0', - 'r0.3.0', - 'r0.4.0', - 'r0.5.0' - ], newOlmAccount: pickledOlmAccount, ); @@ -669,8 +655,9 @@ void main() { var client2 = Client('testclient', debug: true); client2.httpClient = FakeMatrixApi(); - client2.storeAPI = FakeStore(client2, fakeStore.storeMap); + client2.database = client1.database; + client2.connect(); await Future.delayed(Duration(milliseconds: 100)); expect(client2.isLogged(), true); @@ -679,11 +666,10 @@ void main() { expect(client2.homeserver, client1.homeserver); expect(client2.deviceID, client1.deviceID); expect(client2.deviceName, client1.deviceName); - expect(client2.matrixVersions, client1.matrixVersions); if (client2.encryptionEnabled) { expect(client2.pickledOlmAccount, client1.pickledOlmAccount); - expect(json.encode(client2.rooms[1].sessionKeys[sessionKey]), - json.encode(client1.rooms[1].sessionKeys[sessionKey])); + expect(json.encode(client2.rooms[1].inboundGroupSessions[sessionKey]), + json.encode(client1.rooms[1].inboundGroupSessions[sessionKey])); expect(client2.rooms[1].id, client1.rooms[1].id); expect(client2.rooms[1].outboundGroupSession.session_key(), sessionKey); } diff --git a/test/fake_database.dart b/test/fake_database.dart new file mode 100644 index 0000000..de35190 --- /dev/null +++ b/test/fake_database.dart @@ -0,0 +1,6 @@ +import 'package:famedlysdk/famedlysdk.dart'; +import 'package:moor_ffi/moor_ffi.dart' as moor; + +Database getDatabase() { + return Database(moor.VmDatabase.memory()); +} diff --git a/test/fake_store.dart b/test/fake_store.dart deleted file mode 100644 index ea8231b..0000000 --- a/test/fake_store.dart +++ /dev/null @@ -1,95 +0,0 @@ -import 'dart:convert'; - -import 'package:famedlysdk/famedlysdk.dart'; - -class FakeStore implements StoreAPI { - /// Whether this is a simple store which only stores the client credentials and - /// end to end encryption stuff or the whole sync payloads. - @override - final bool extended = false; - - Map storeMap = {}; - - /// Link back to the client. - @override - Client client; - - FakeStore(this.client, this.storeMap) { - _init(); - } - - Future _init() async { - final credentialsStr = await getItem(client.clientName); - - if (credentialsStr == null || credentialsStr.isEmpty) { - client.onLoginStateChanged.add(LoginState.loggedOut); - return; - } - print('[Matrix] Restoring account credentials'); - final Map credentials = json.decode(credentialsStr); - client.connect( - newDeviceID: credentials['deviceID'], - newDeviceName: credentials['deviceName'], - newHomeserver: credentials['homeserver'], - newMatrixVersions: List.from(credentials['matrixVersions']), - newToken: credentials['token'], - newUserID: credentials['userID'], - newPrevBatch: credentials['prev_batch'], - newOlmAccount: credentials['olmAccount'], - ); - } - - /// Will be automatically called when the client is logged in successfully. - @override - Future storeClient() async { - final credentials = { - 'deviceID': client.deviceID, - 'deviceName': client.deviceName, - 'homeserver': client.homeserver, - 'matrixVersions': client.matrixVersions, - 'token': client.accessToken, - 'userID': client.userID, - 'olmAccount': client.pickledOlmAccount, - }; - await setItem(client.clientName, json.encode(credentials)); - return; - } - - /// Clears all tables from the database. - @override - Future clear() async { - storeMap = {}; - return; - } - - @override - Future getItem(String key) async { - return storeMap[key]; - } - - @override - Future setItem(String key, String value) async { - storeMap[key] = value; - return; - } - - String get _UserDeviceKeysKey => '${client.clientName}.user_device_keys'; - - @override - Future> getUserDeviceKeys() async { - final deviceKeysListString = await getItem(_UserDeviceKeysKey); - if (deviceKeysListString == null) return {}; - Map rawUserDeviceKeys = json.decode(deviceKeysListString); - var userDeviceKeys = {}; - for (final entry in rawUserDeviceKeys.entries) { - userDeviceKeys[entry.key] = DeviceKeysList.fromJson(entry.value); - } - return userDeviceKeys; - } - - @override - Future storeUserDeviceKeys( - Map userDeviceKeys) async { - await setItem(_UserDeviceKeysKey, json.encode(userDeviceKeys)); - } -} diff --git a/test/room_key_request_test.dart b/test/room_key_request_test.dart index cc400f7..194c4d7 100644 --- a/test/room_key_request_test.dart +++ b/test/room_key_request_test.dart @@ -25,7 +25,7 @@ import 'package:famedlysdk/famedlysdk.dart'; import 'package:test/test.dart'; import 'fake_matrix_api.dart'; -import 'fake_store.dart'; +import 'fake_database.dart'; void main() { /// All Tests related to device keys @@ -53,13 +53,13 @@ void main() { var matrix = Client('testclient', debug: true); matrix.httpClient = FakeMatrixApi(); - matrix.storeAPI = FakeStore(matrix, {}); + matrix.database = getDatabase(); await matrix.checkServer('https://fakeServer.notExisting'); await matrix.login('test', '1234'); var room = matrix.getRoomById('!726s6s6q:example.com'); if (matrix.encryptionEnabled) { await room.createOutboundGroupSession(); - rawJson['content']['body']['session_id'] = room.sessionKeys.keys.first; + rawJson['content']['body']['session_id'] = room.inboundGroupSessions.keys.first; var roomKeyRequest = RoomKeyRequest.fromToDeviceEvent( ToDeviceEvent.fromJson(rawJson), matrix); diff --git a/test/room_test.dart b/test/room_test.dart index 64936a5..eb75a5e 100644 --- a/test/room_test.dart +++ b/test/room_test.dart @@ -26,6 +26,7 @@ import 'package:famedlysdk/src/event.dart'; import 'package:famedlysdk/src/room.dart'; import 'package:famedlysdk/src/user.dart'; import 'package:famedlysdk/src/utils/matrix_file.dart'; +import 'package:famedlysdk/src/database/database.dart' show DbRoom, DbRoomState, DbRoomAccountData; import 'package:test/test.dart'; import 'fake_matrix_api.dart'; @@ -62,44 +63,50 @@ void main() { '@charley:example.org' ]; - var jsonObj = { - 'room_id': id, - 'membership': membership.toString().split('.').last, - 'avatar_url': '', - 'notification_count': notificationCount, - 'highlight_count': highlightCount, - 'prev_batch': '', - 'joined_member_count': notificationCount, - 'invited_member_count': notificationCount, - 'heroes': heroes.join(','), - }; + var dbRoom = DbRoom( + clientId: 1, + roomId: id, + membership: membership.toString().split('.').last, + highlightCount: highlightCount, + notificationCount: notificationCount, + prevBatch: '', + joinedMemberCount: notificationCount, + invitedMemberCount: notificationCount, + newestSortOrder: 0.0, + oldestSortOrder: 0.0, + heroes: heroes.join(','), + ); - Function states = () async => [ - { - 'content': {'join_rule': 'public'}, - 'event_id': '143273582443PhrSn:example.org', - 'origin_server_ts': 1432735824653, - 'room_id': id, - 'sender': '@example:example.org', - 'state_key': '', - 'type': 'm.room.join_rules', - 'unsigned': {'age': 1234} - } - ]; + var states = [ + DbRoomState( + clientId: 1, + eventId: '143273582443PhrSn:example.org', + roomId: id, + sortOrder: 0.0, + originServerTs: DateTime.fromMillisecondsSinceEpoch(1432735824653), + sender: '@example:example.org', + type: 'm.room.join_rules', + unsigned: '{"age": 1234}', + content: '{"join_rule": "public"}', + prevContent: '', + stateKey: '', + ), + ]; - Function roomAccountData = () async => [ - { - 'content': {'foo': 'bar'}, - 'room_id': id, - 'type': 'com.test.foo' - } - ]; + var roomAccountData = [ + DbRoomAccountData( + clientId: 1, + type: 'com.test.foo', + roomId: id, + content: '{"foo": "bar"}', + ), + ]; room = await Room.getRoomFromTableRow( - jsonObj, + dbRoom, matrix, - states: states(), - roomAccountData: roomAccountData(), + states: states, + roomAccountData: roomAccountData, ); expect(room.id, id); @@ -390,14 +397,14 @@ void main() { expect(room.outboundGroupSession != null, true); expect(room.outboundGroupSession.session_id().isNotEmpty, true); expect( - room.sessionKeys.containsKey(room.outboundGroupSession.session_id()), + room.inboundGroupSessions.containsKey(room.outboundGroupSession.session_id()), true); expect( - room.sessionKeys[room.outboundGroupSession.session_id()] + room.inboundGroupSessions[room.outboundGroupSession.session_id()] .content['session_key'], room.outboundGroupSession.session_key()); expect( - room.sessionKeys[room.outboundGroupSession.session_id()].indexes + room.inboundGroupSessions[room.outboundGroupSession.session_id()].indexes .length, 0); }); diff --git a/test/timeline_test.dart b/test/timeline_test.dart index bf7a49b..28c006f 100644 --- a/test/timeline_test.dart +++ b/test/timeline_test.dart @@ -65,7 +65,8 @@ void main() { 'status': 2, 'event_id': '1', 'origin_server_ts': testTimeStamp - })); + }, + sortOrder: room.newSortOrder)); client.onEvent.add(EventUpdate( type: 'timeline', @@ -78,7 +79,8 @@ void main() { 'status': 2, 'event_id': '2', 'origin_server_ts': testTimeStamp - 1000 - })); + }, + sortOrder: room.oldSortOrder)); expect(timeline.sub != null, true); @@ -125,7 +127,8 @@ void main() { 'redacts': '2', 'event_id': '3', 'origin_server_ts': testTimeStamp + 1000 - })); + }, + sortOrder: room.newSortOrder)); await Future.delayed(Duration(milliseconds: 50)); @@ -159,7 +162,8 @@ void main() { 'event_id': '42', 'unsigned': {'transaction_id': '1234'}, 'origin_server_ts': DateTime.now().millisecondsSinceEpoch - })); + }, + sortOrder: room.newSortOrder)); await Future.delayed(Duration(milliseconds: 50)); @@ -182,7 +186,8 @@ void main() { 'status': 0, 'event_id': 'abc', 'origin_server_ts': testTimeStamp - })); + }, + sortOrder: room.newSortOrder)); await Future.delayed(Duration(milliseconds: 50)); await room.sendTextEvent('test', txid: 'errortxid'); await Future.delayed(Duration(milliseconds: 50)); @@ -230,9 +235,9 @@ void main() { expect(updateCount, 20); expect(timeline.events.length, 9); - expect(timeline.events[6].eventId, '1143273582443PhrSn:example.org'); + expect(timeline.events[6].eventId, '3143273582443PhrSn:example.org'); expect(timeline.events[7].eventId, '2143273582443PhrSn:example.org'); - expect(timeline.events[8].eventId, '3143273582443PhrSn:example.org'); + expect(timeline.events[8].eventId, '1143273582443PhrSn:example.org'); expect(room.prev_batch, 't47409-4357353_219380_26003_2265'); }); }); diff --git a/test_driver/famedlysdk_test.dart b/test_driver/famedlysdk_test.dart index f07e21c..fc145cd 100644 --- a/test_driver/famedlysdk_test.dart +++ b/test_driver/famedlysdk_test.dart @@ -1,5 +1,5 @@ import 'package:famedlysdk/famedlysdk.dart'; -import '../test/fake_store.dart'; +import '../test/fake_database.dart'; void main() => test(); @@ -18,14 +18,14 @@ const String testMessage6 = 'Hello mars'; void test() async { print('++++ Login $testUserA ++++'); var testClientA = Client('TestClient', debug: false); - testClientA.storeAPI = FakeStore(testClientA, {}); + testClientA.database = getDatabase(); await testClientA.checkServer(homeserver); await testClientA.login(testUserA, testPasswordA); assert(testClientA.encryptionEnabled); print('++++ Login $testUserB ++++'); var testClientB = Client('TestClient', debug: false); - testClientB.storeAPI = FakeStore(testClientB, {}); + testClientB.database = getDatabase(); await testClientB.checkServer(homeserver); await testClientB.login(testUserB, testPasswordA); assert(testClientB.encryptionEnabled); @@ -128,12 +128,12 @@ void test() async { await Future.delayed(Duration(seconds: 5)); assert(room.outboundGroupSession != null); var currentSessionIdA = room.outboundGroupSession.session_id(); - assert(room.sessionKeys.containsKey(room.outboundGroupSession.session_id())); + assert(room.inboundGroupSessions.containsKey(room.outboundGroupSession.session_id())); assert(testClientA.olmSessions[testClientB.identityKey].length == 1); assert(testClientB.olmSessions[testClientA.identityKey].length == 1); assert(testClientA.olmSessions[testClientB.identityKey].first.session_id() == testClientB.olmSessions[testClientA.identityKey].first.session_id()); - assert(inviteRoom.sessionKeys + assert(inviteRoom.inboundGroupSessions .containsKey(room.outboundGroupSession.session_id())); assert(room.lastMessage == testMessage); assert(inviteRoom.lastMessage == testMessage); @@ -149,7 +149,7 @@ void test() async { testClientB.olmSessions[testClientA.identityKey].first.session_id()); assert(room.outboundGroupSession.session_id() == currentSessionIdA); - assert(inviteRoom.sessionKeys + assert(inviteRoom.inboundGroupSessions .containsKey(room.outboundGroupSession.session_id())); assert(room.lastMessage == testMessage2); assert(inviteRoom.lastMessage == testMessage2); @@ -163,9 +163,9 @@ void test() async { assert(testClientB.olmSessions[testClientA.identityKey].length == 1); assert(room.outboundGroupSession.session_id() == currentSessionIdA); assert(inviteRoom.outboundGroupSession != null); - assert(inviteRoom.sessionKeys + assert(inviteRoom.inboundGroupSessions .containsKey(inviteRoom.outboundGroupSession.session_id())); - assert(room.sessionKeys + assert(room.inboundGroupSessions .containsKey(inviteRoom.outboundGroupSession.session_id())); assert(inviteRoom.lastMessage == testMessage3); assert(room.lastMessage == testMessage3); @@ -174,7 +174,7 @@ void test() async { print('++++ Login $testUserB in another client ++++'); var testClientC = Client('TestClient', debug: false); - testClientC.storeAPI = FakeStore(testClientC, {}); + testClientC.database = getDatabase(); await testClientC.checkServer(homeserver); await testClientC.login(testUserB, testPasswordA); await Future.delayed(Duration(seconds: 3)); @@ -193,7 +193,7 @@ void test() async { testClientC.olmSessions[testClientA.identityKey].first.session_id()); assert(room.outboundGroupSession.session_id() != currentSessionIdA); currentSessionIdA = room.outboundGroupSession.session_id(); - assert(inviteRoom.sessionKeys + assert(inviteRoom.inboundGroupSessions .containsKey(room.outboundGroupSession.session_id())); assert(room.lastMessage == testMessage4); assert(inviteRoom.lastMessage == testMessage4); @@ -216,7 +216,7 @@ void test() async { testClientB.olmSessions[testClientA.identityKey].first.session_id()); assert(room.outboundGroupSession.session_id() != currentSessionIdA); currentSessionIdA = room.outboundGroupSession.session_id(); - assert(inviteRoom.sessionKeys + assert(inviteRoom.inboundGroupSessions .containsKey(room.outboundGroupSession.session_id())); assert(room.lastMessage == testMessage6); assert(inviteRoom.lastMessage == testMessage6); @@ -224,21 +224,22 @@ void test() async { "++++ ($testUserB) Received decrypted message: '${inviteRoom.lastMessage}' ++++"); print('++++ ($testUserA) Restore user ++++'); - FakeStore clientAStore = testClientA.storeAPI; + final clientADatabase = testClientA.database; testClientA = null; testClientA = Client('TestClient', debug: false); - testClientA.storeAPI = FakeStore(testClientA, clientAStore.storeMap); + testClientA.database = clientADatabase; + testClientA.connect(); await Future.delayed(Duration(seconds: 3)); var restoredRoom = testClientA.rooms.first; assert(room != null); assert(restoredRoom.id == room.id); assert(restoredRoom.outboundGroupSession.session_id() == room.outboundGroupSession.session_id()); - assert(restoredRoom.sessionKeys.length == 4); - assert(restoredRoom.sessionKeys.length == room.sessionKeys.length); - for (var i = 0; i < restoredRoom.sessionKeys.length; i++) { - assert(restoredRoom.sessionKeys.keys.toList()[i] == - room.sessionKeys.keys.toList()[i]); + assert(restoredRoom.inboundGroupSessions.length == 4); + assert(restoredRoom.inboundGroupSessions.length == room.inboundGroupSessions.length); + for (var i = 0; i < restoredRoom.inboundGroupSessions.length; i++) { + assert(restoredRoom.inboundGroupSessions.keys.toList()[i] == + room.inboundGroupSessions.keys.toList()[i]); } assert(testClientA.olmSessions[testClientB.identityKey].length == 1); assert(testClientB.olmSessions[testClientA.identityKey].length == 1); @@ -253,7 +254,7 @@ void test() async { assert(testClientA.olmSessions[testClientB.identityKey].first.session_id() == testClientB.olmSessions[testClientA.identityKey].first.session_id()); /*assert(restoredRoom.outboundGroupSession.session_id() == currentSessionIdA); - assert(inviteRoom.sessionKeys + assert(inviteRoom.inboundGroupSessions .containsKey(restoredRoom.outboundGroupSession.session_id()));*/ assert(restoredRoom.lastMessage == testMessage5); assert(inviteRoom.lastMessage == testMessage5);