refactor: Make database null safe

This commit is contained in:
Christian Pauly 2021-08-19 09:22:22 +02:00
parent 5eb2d22207
commit 1fd40dd186
2 changed files with 55 additions and 53 deletions

View File

@ -29,7 +29,7 @@ import '../../matrix.dart';
abstract class DatabaseApi { abstract class DatabaseApi {
int get maxFileSize => 1 * 1024 * 1024; int get maxFileSize => 1 * 1024 * 1024;
bool get supportsFileStoring => false; bool get supportsFileStoring => false;
Future<Map<String, dynamic>> getClient(String name); Future<Map<String, dynamic>?> getClient(String name);
Future updateClient( Future updateClient(
String homeserverUrl, String homeserverUrl,
@ -66,7 +66,7 @@ abstract class DatabaseApi {
/// [transaction]. /// [transaction].
Future<void> storeEventUpdate(int clientId, EventUpdate eventUpdate); Future<void> storeEventUpdate(int clientId, EventUpdate eventUpdate);
Future<Event> getEventById(int clientId, String eventId, Room room); Future<Event?> getEventById(int clientId, String eventId, Room room);
Future<void> forgetRoom(int clientId, String roomId); Future<void> forgetRoom(int clientId, String roomId);
@ -74,13 +74,13 @@ abstract class DatabaseApi {
Future<void> clear(int clientId); Future<void> clear(int clientId);
Future<User> getUser(int clientId, String userId, Room room); Future<User?> getUser(int clientId, String userId, Room room);
Future<List<User>> getUsers(int clientId, Room room); Future<List<User>> getUsers(int clientId, Room room);
Future<List<Event>> getEventList(int clientId, Room room); Future<List<Event>> getEventList(int clientId, Room room);
Future<Uint8List> getFile(Uri mxcUri); Future<Uint8List?> getFile(String mxcUri);
Future storeFile(Uri mxcUri, Uint8List bytes, int time); Future storeFile(Uri mxcUri, Uint8List bytes, int time);
@ -90,9 +90,9 @@ abstract class DatabaseApi {
Future<Map<String, DeviceKeysList>> getUserDeviceKeys(Client client); Future<Map<String, DeviceKeysList>> getUserDeviceKeys(Client client);
Future<SSSSCache> getSSSSCache(int clientId, String type); Future<SSSSCache?> getSSSSCache(int clientId, String type);
Future<OutboundGroupSession> getOutboundGroupSession( Future<OutboundGroupSession?> getOutboundGroupSession(
int clientId, int clientId,
String roomId, String roomId,
String userId, String userId,
@ -102,7 +102,7 @@ abstract class DatabaseApi {
int clientId, int clientId,
); );
Future<StoredInboundGroupSession> getInboundGroupSession( Future<StoredInboundGroupSession?> getInboundGroupSession(
int clientId, int clientId,
String roomId, String roomId,
String sessionId, String sessionId,

View File

@ -1,4 +1,3 @@
// @dart=2.9
/* /*
* Famedly Matrix SDK * Famedly Matrix SDK
* Copyright (C) 2019, 2020, 2021 Famedly GmbH * Copyright (C) 2019, 2020, 2021 Famedly GmbH
@ -39,41 +38,41 @@ import 'package:hive/hive.dart';
class FamedlySdkHiveDatabase extends DatabaseApi { class FamedlySdkHiveDatabase extends DatabaseApi {
static const int version = 3; static const int version = 3;
final String name; final String name;
Box _clientBox; late Box _clientBox;
Box _accountDataBox; late Box _accountDataBox;
Box _roomsBox; late Box _roomsBox;
Box _toDeviceQueueBox; late Box _toDeviceQueueBox;
/// Key is a tuple as MultiKey(roomId, type) where stateKey can be /// Key is a tuple as MultiKey(roomId, type) where stateKey can be
/// an empty string. /// an empty string.
LazyBox _roomStateBox; late LazyBox _roomStateBox;
/// Key is a tuple as MultiKey(roomId, userId) /// Key is a tuple as MultiKey(roomId, userId)
LazyBox _roomMembersBox; late LazyBox _roomMembersBox;
/// Key is a tuple as MultiKey(roomId, type) /// Key is a tuple as MultiKey(roomId, type)
LazyBox _roomAccountDataBox; late LazyBox _roomAccountDataBox;
LazyBox _inboundGroupSessionsBox; late LazyBox _inboundGroupSessionsBox;
LazyBox _outboundGroupSessionsBox; late LazyBox _outboundGroupSessionsBox;
LazyBox _olmSessionsBox; late LazyBox _olmSessionsBox;
/// Key is a tuple as MultiKey(userId, deviceId) /// Key is a tuple as MultiKey(userId, deviceId)
LazyBox _userDeviceKeysBox; late LazyBox _userDeviceKeysBox;
/// Key is the user ID as a String /// Key is the user ID as a String
LazyBox _userDeviceKeysOutdatedBox; late LazyBox _userDeviceKeysOutdatedBox;
/// Key is a tuple as MultiKey(userId, publicKey) /// Key is a tuple as MultiKey(userId, publicKey)
LazyBox _userCrossSigningKeysBox; late LazyBox _userCrossSigningKeysBox;
LazyBox _ssssCacheBox; late LazyBox _ssssCacheBox;
LazyBox _presencesBox; late LazyBox _presencesBox;
/// Key is a tuple as Multikey(roomId, fragmentId) while the default /// Key is a tuple as Multikey(roomId, fragmentId) while the default
/// fragmentId is an empty String /// fragmentId is an empty String
LazyBox _timelineFragmentsBox; late LazyBox _timelineFragmentsBox;
/// Key is a tuple as MultiKey(roomId, eventId) /// Key is a tuple as MultiKey(roomId, eventId)
LazyBox _eventsBox; late LazyBox _eventsBox;
String get _clientBoxName => '$name.box.client'; String get _clientBoxName => '$name.box.client';
String get _accountDataBoxName => '$name.box.account_data'; String get _accountDataBoxName => '$name.box.account_data';
@ -95,7 +94,7 @@ class FamedlySdkHiveDatabase extends DatabaseApi {
String get _timelineFragmentsBoxName => '$name.box.timeline_fragments'; String get _timelineFragmentsBoxName => '$name.box.timeline_fragments';
String get _eventsBoxName => '$name.box.events'; String get _eventsBoxName => '$name.box.events';
final HiveCipher encryptionCipher; final HiveCipher? encryptionCipher;
FamedlySdkHiveDatabase(this.name, {this.encryptionCipher}); FamedlySdkHiveDatabase(this.name, {this.encryptionCipher});
@ -194,7 +193,7 @@ class FamedlySdkHiveDatabase extends DatabaseApi {
); );
// Check version and check if we need a migration // Check version and check if we need a migration
final currentVersion = (await _clientBox.get('version') as int); final currentVersion = (await _clientBox.get('version') as int?);
if (currentVersion == null) { if (currentVersion == null) {
await _clientBox.put('version', version); await _clientBox.put('version', version);
} else if (currentVersion != version) { } else if (currentVersion != version) {
@ -292,7 +291,7 @@ class FamedlySdkHiveDatabase extends DatabaseApi {
} }
@override @override
Future<Map<String, dynamic>> getClient(String name) async { Future<Map<String, dynamic>?> getClient(String name) async {
final map = <String, dynamic>{}; final map = <String, dynamic>{};
for (final key in _clientBox.keys) { for (final key in _clientBox.keys) {
if (key == 'version') continue; if (key == 'version') continue;
@ -303,7 +302,7 @@ class FamedlySdkHiveDatabase extends DatabaseApi {
} }
@override @override
Future<Event> getEventById(int clientId, String eventId, Room room) async { Future<Event?> getEventById(int clientId, String eventId, Room room) async {
final raw = await _eventsBox.get(MultiKey(room.id, eventId).toString()); final raw = await _eventsBox.get(MultiKey(room.id, eventId).toString());
if (raw == null) return null; if (raw == null) return null;
return Event.fromJson(convertToJson(raw), room); return Event.fromJson(convertToJson(raw), room);
@ -329,12 +328,12 @@ class FamedlySdkHiveDatabase extends DatabaseApi {
} }
@override @override
Future<Uint8List> getFile(Uri mxcUri) async { Future<Uint8List?> getFile(String mxcUri) async {
return null; return null;
} }
@override @override
Future<StoredInboundGroupSession> getInboundGroupSession( Future<StoredInboundGroupSession?> getInboundGroupSession(
int clientId, int clientId,
String roomId, String roomId,
String sessionId, String sessionId,
@ -374,7 +373,7 @@ class FamedlySdkHiveDatabase extends DatabaseApi {
Future<void> storeOlmSession(int clientId, String identityKey, Future<void> storeOlmSession(int clientId, String identityKey,
String sessionId, String pickle, int lastReceived) async { String sessionId, String pickle, int lastReceived) async {
final rawSessions = final rawSessions =
(await _olmSessionsBox.get(identityKey.toHiveKey) as Map) ?? {}; (await _olmSessionsBox.get(identityKey.toHiveKey) as Map?) ?? {};
rawSessions[sessionId] = <String, dynamic>{ rawSessions[sessionId] = <String, dynamic>{
'identity_key': identityKey, 'identity_key': identityKey,
'pickle': pickle, 'pickle': pickle,
@ -388,8 +387,9 @@ class FamedlySdkHiveDatabase extends DatabaseApi {
@override @override
Future<List<OlmSession>> getOlmSessions( Future<List<OlmSession>> getOlmSessions(
int clientId, String identityKey, String userId) async { int clientId, String identityKey, String userId) async {
final rawSessions = await _olmSessionsBox.get(identityKey.toHiveKey) as Map; final rawSessions =
if (rawSessions?.isEmpty ?? true) return <OlmSession>[]; await _olmSessionsBox.get(identityKey.toHiveKey) as Map?;
if (rawSessions == null || rawSessions.isEmpty) return <OlmSession>[];
return rawSessions.values return rawSessions.values
.map((json) => OlmSession.fromJson(convertToJson(json), userId)) .map((json) => OlmSession.fromJson(convertToJson(json), userId))
.toList(); .toList();
@ -404,7 +404,7 @@ class FamedlySdkHiveDatabase extends DatabaseApi {
} }
@override @override
Future<OutboundGroupSession> getOutboundGroupSession( Future<OutboundGroupSession?> getOutboundGroupSession(
int clientId, String roomId, String userId) async { int clientId, String roomId, String userId) async {
final raw = await _outboundGroupSessionsBox.get(roomId.toHiveKey); final raw = await _outboundGroupSessionsBox.get(roomId.toHiveKey);
if (raw == null) return null; if (raw == null) return null;
@ -451,8 +451,8 @@ class FamedlySdkHiveDatabase extends DatabaseApi {
// Get the "important" room states. All other states will be loaded once // Get the "important" room states. All other states will be loaded once
// `getUnimportantRoomStates()` is called. // `getUnimportantRoomStates()` is called.
for (final type in importantRoomStates) { for (final type in importantRoomStates) {
final Map states = final states =
await _roomStateBox.get(MultiKey(room.id, type).toString()); await _roomStateBox.get(MultiKey(room.id, type).toString()) as Map?;
if (states == null) continue; if (states == null) continue;
final stateEvents = states.values final stateEvents = states.values
.map((raw) => Event.fromJson(convertToJson(raw), room)) .map((raw) => Event.fromJson(convertToJson(raw), room))
@ -474,7 +474,7 @@ class FamedlySdkHiveDatabase extends DatabaseApi {
final basicRoomEvent = BasicRoomEvent.fromJson( final basicRoomEvent = BasicRoomEvent.fromJson(
convertToJson(raw), convertToJson(raw),
); );
rooms[roomId].roomAccountData[basicRoomEvent.type] = basicRoomEvent; rooms[roomId]!.roomAccountData[basicRoomEvent.type] = basicRoomEvent;
} else { } else {
Logs().w('Found account data for unknown room $roomId. Delete now...'); Logs().w('Found account data for unknown room $roomId. Delete now...');
await _roomAccountDataBox.delete(key); await _roomAccountDataBox.delete(key);
@ -485,7 +485,7 @@ class FamedlySdkHiveDatabase extends DatabaseApi {
} }
@override @override
Future<SSSSCache> getSSSSCache(int clientId, String type) async { Future<SSSSCache?> getSSSSCache(int clientId, String type) async {
final raw = await _ssssCacheBox.get(type); final raw = await _ssssCacheBox.get(type);
if (raw == null) return null; if (raw == null) return null;
return SSSSCache.fromJson(convertToJson(raw)); return SSSSCache.fromJson(convertToJson(raw));
@ -517,7 +517,7 @@ class FamedlySdkHiveDatabase extends DatabaseApi {
} }
@override @override
Future<User> getUser(int clientId, String userId, Room room) async { Future<User?> getUser(int clientId, String userId, Room room) async {
final state = final state =
await _roomMembersBox.get(MultiKey(room.id, userId).toString()); await _roomMembersBox.get(MultiKey(room.id, userId).toString());
if (state == null) return null; if (state == null) return null;
@ -734,7 +734,7 @@ class FamedlySdkHiveDatabase extends DatabaseApi {
Future<void> setVerifiedUserCrossSigningKey( Future<void> setVerifiedUserCrossSigningKey(
bool verified, int clientId, String userId, String publicKey) async { bool verified, int clientId, String userId, String publicKey) async {
final raw = (await _userCrossSigningKeysBox final raw = (await _userCrossSigningKeysBox
.get(MultiKey(userId, publicKey).toString()) as Map) ?? .get(MultiKey(userId, publicKey).toString()) as Map?) ??
{}; {};
raw['verified'] = verified; raw['verified'] = verified;
await _userCrossSigningKeysBox.put( await _userCrossSigningKeysBox.put(
@ -803,9 +803,10 @@ class FamedlySdkHiveDatabase extends DatabaseApi {
?.tryGet<int>(messageSendingStatusKey) ?? ?.tryGet<int>(messageSendingStatusKey) ??
2; 2;
final status = newStatus == -1 || prevEvent?.status == null final status =
? newStatus newStatus == -1 || prevEvent == null || prevEvent.status == null
: max(prevEvent.status, newStatus); ? newStatus
: max(prevEvent.status, newStatus);
// Add the status and the sort order to the content so it get stored // Add the status and the sort order to the content so it get stored
eventUpdate.content['unsigned'] ??= <String, dynamic>{}; eventUpdate.content['unsigned'] ??= <String, dynamic>{};
@ -931,7 +932,7 @@ class FamedlySdkHiveDatabase extends DatabaseApi {
'pickle': pickle, 'pickle': pickle,
'device_ids': deviceIds, 'device_ids': deviceIds,
'creation_time': creationTime, 'creation_time': creationTime,
'sent_messages': sentMessages ?? 0, 'sent_messages': sentMessages,
}); });
return; return;
} }
@ -944,7 +945,8 @@ class FamedlySdkHiveDatabase extends DatabaseApi {
} }
@override @override
Future<void> storeRoomUpdate(int clientId, RoomUpdate roomUpdate, [_]) async { Future<void> storeRoomUpdate(int clientId, RoomUpdate roomUpdate,
[dynamic _]) async {
// Leave room if membership is leave // Leave room if membership is leave
if ({Membership.leave, Membership.ban}.contains(roomUpdate.membership)) { if ({Membership.leave, Membership.ban}.contains(roomUpdate.membership)) {
await forgetRoom(clientId, roomUpdate.id); await forgetRoom(clientId, roomUpdate.id);
@ -957,8 +959,8 @@ class FamedlySdkHiveDatabase extends DatabaseApi {
Room( Room(
id: roomUpdate.id, id: roomUpdate.id,
membership: roomUpdate.membership, membership: roomUpdate.membership,
highlightCount: roomUpdate.highlight_count, highlightCount: roomUpdate.highlight_count?.toInt(),
notificationCount: roomUpdate.notification_count, notificationCount: roomUpdate.notification_count?.toInt(),
prev_batch: roomUpdate.prev_batch, prev_batch: roomUpdate.prev_batch,
summary: roomUpdate.summary, summary: roomUpdate.summary,
).toJson()); ).toJson());
@ -970,10 +972,10 @@ class FamedlySdkHiveDatabase extends DatabaseApi {
Room( Room(
id: roomUpdate.id, id: roomUpdate.id,
membership: roomUpdate.membership ?? currentRoom.membership, membership: roomUpdate.membership ?? currentRoom.membership,
highlightCount: highlightCount: roomUpdate.highlight_count?.toInt() ??
roomUpdate.highlight_count ?? currentRoom.highlightCount, currentRoom.highlightCount,
notificationCount: notificationCount: roomUpdate.notification_count?.toInt() ??
roomUpdate.notification_count ?? currentRoom.notificationCount, currentRoom.notificationCount,
prev_batch: roomUpdate.prev_batch ?? currentRoom.prev_batch, prev_batch: roomUpdate.prev_batch ?? currentRoom.prev_batch,
summary: RoomSummary.fromJson(currentRoom.summary.toJson() summary: RoomSummary.fromJson(currentRoom.summary.toJson()
..addAll(roomUpdate.summary?.toJson() ?? {})), ..addAll(roomUpdate.summary?.toJson() ?? {})),
@ -1148,7 +1150,7 @@ Map<String, dynamic> convertToJson(Map map) {
class MultiKey { class MultiKey {
final List<String> parts; final List<String> parts;
MultiKey(String key1, [String key2, String key3]) MultiKey(String key1, [String? key2, String? key3])
: parts = [ : parts = [
key1, key1,
if (key2 != null) key2, if (key2 != null) key2,