From 8cc863b1a313132c48836bae7b73349a7b8d93ed Mon Sep 17 00:00:00 2001 From: Krille Date: Wed, 15 Nov 2023 06:48:46 +0100 Subject: [PATCH] feat: Store presences in database --- lib/src/client.dart | 22 ++++++++++++- lib/src/database/database_api.dart | 4 +++ .../database/hive_collections_database.dart | 12 +++++++ lib/src/database/hive_database.dart | 12 +++++++ lib/src/presence.dart | 32 ++++++++++++++++++- lib/src/user.dart | 18 +++-------- test/database_api_test.dart | 19 +++++++++++ 7 files changed, 103 insertions(+), 16 deletions(-) diff --git a/lib/src/client.dart b/lib/src/client.dart index 23d2195c5..79fc2efbe 100644 --- a/lib/src/client.dart +++ b/lib/src/client.dart @@ -1791,12 +1791,13 @@ class Client extends MatrixApi { await _handleRooms(leave, direction: direction); } } - for (final newPresence in sync.presence ?? []) { + for (final newPresence in sync.presence ?? []) { final cachedPresence = CachedPresence.fromMatrixEvent(newPresence); presences[newPresence.senderId] = cachedPresence; // ignore: deprecated_member_use_from_same_package onPresence.add(newPresence); onPresenceChanged.add(cachedPresence); + await database?.storePresence(newPresence.senderId, cachedPresence); } for (final newAccountData in sync.accountData ?? []) { await database?.storeAccountData( @@ -2924,6 +2925,25 @@ class Client extends MatrixApi { return; } + /// The newest presence of this user if there is any. Fetches it from the + /// database first and then from the server if necessary or returns offline. + Future fetchCurrentPresence(String userId) async { + final cachedPresence = presences[userId]; + if (cachedPresence != null) { + return cachedPresence; + } + + final dbPresence = await database?.getPresence(userId); + if (dbPresence != null) return presences[userId] = dbPresence; + + try { + final newPresence = await getPresence(userId); + return CachedPresence.fromPresenceResponse(newPresence, userId); + } catch (e) { + return CachedPresence.neverSeen(userId); + } + } + bool _disposed = false; bool _aborted = false; Future _currentTransaction = Future.sync(() => {}); diff --git a/lib/src/database/database_api.dart b/lib/src/database/database_api.dart index 589e71022..b33f30723 100644 --- a/lib/src/database/database_api.dart +++ b/lib/src/database/database_api.dart @@ -312,4 +312,8 @@ abstract class DatabaseApi { Future exportDump(); Future importDump(String export); + + Future storePresence(String userId, CachedPresence presence); + + Future getPresence(String userId); } diff --git a/lib/src/database/hive_collections_database.dart b/lib/src/database/hive_collections_database.dart index 117ce23f7..e40262bac 100644 --- a/lib/src/database/hive_collections_database.dart +++ b/lib/src/database/hive_collections_database.dart @@ -1478,6 +1478,18 @@ class HiveCollectionsDatabase extends DatabaseApi { return raw; } + @override + Future storePresence(String userId, CachedPresence presence) => + _presencesBox.put(userId, presence.toJson()); + + @override + Future getPresence(String userId) async { + final rawPresence = await _presencesBox.get(userId); + if (rawPresence == null) return null; + + return CachedPresence.fromJson(copyMap(rawPresence)); + } + @override Future exportDump() async { final dataMap = { diff --git a/lib/src/database/hive_database.dart b/lib/src/database/hive_database.dart index f3bfa9c75..21ed897e6 100644 --- a/lib/src/database/hive_database.dart +++ b/lib/src/database/hive_database.dart @@ -1450,6 +1450,18 @@ class FamedlySdkHiveDatabase extends DatabaseApi { return raw as String; } + @override + Future storePresence(String userId, CachedPresence presence) => + _presencesBox.put(userId, presence.toJson()); + + @override + Future getPresence(String userId) async { + final rawPresence = await _presencesBox.get(userId); + if (rawPresence == null) return null; + + return CachedPresence.fromJson(copyMap(rawPresence)); + } + @override Future exportDump() { // see no need to implement this in a deprecated part diff --git a/lib/src/presence.dart b/lib/src/presence.dart index b0a7086c8..e249322ed 100644 --- a/lib/src/presence.dart +++ b/lib/src/presence.dart @@ -25,6 +25,36 @@ class CachedPresence { bool? currentlyActive; String userid; + factory CachedPresence.fromJson(Map json) => + CachedPresence._( + presence: PresenceType.values + .singleWhere((type) => type.name == json['presence']), + lastActiveTimestamp: json['last_active_timestamp'] != null + ? DateTime.fromMillisecondsSinceEpoch( + json['last_active_timestamp'] as int) + : null, + statusMsg: json['status_msg'] as String?, + currentlyActive: json['currently_active'] as bool?, + userid: json['user_id'] as String, + ); + + Map toJson() => { + 'user_id': userid, + 'presence': presence.name, + if (lastActiveTimestamp != null) + 'last_active_timestamp': lastActiveTimestamp?.millisecondsSinceEpoch, + if (statusMsg != null) 'status_msg': statusMsg, + if (currentlyActive != null) 'currently_active': currentlyActive, + }; + + CachedPresence._({ + required this.userid, + required this.presence, + this.lastActiveTimestamp, + this.statusMsg, + this.currentlyActive, + }); + CachedPresence(this.presence, int? lastActiveAgo, this.statusMsg, this.currentlyActive, this.userid) { if (lastActiveAgo != null) { @@ -49,7 +79,7 @@ class CachedPresence { Presence toPresence() { final content = { - 'presence': presence.toString(), + 'presence': presence.name.toString(), }; if (currentlyActive != null) content['currently_active'] = currentlyActive!; if (lastActiveTimestamp != null) { diff --git a/lib/src/user.dart b/lib/src/user.dart index 5842cfe78..5948e69e1 100644 --- a/lib/src/user.dart +++ b/lib/src/user.dart @@ -155,20 +155,10 @@ class User extends Event { @Deprecated('Use fetchCurrentPresence() instead') Future get currentPresence => fetchCurrentPresence(); - /// The newest presence of this user if there is any. Fetches it from the server if necessary or returns offline. - Future fetchCurrentPresence() async { - final cachedPresence = room.client.presences[id]; - if (cachedPresence != null) { - return cachedPresence; - } - - try { - final newPresence = await room.client.getPresence(id); - return CachedPresence.fromPresenceResponse(newPresence, id); - } catch (e) { - return CachedPresence.neverSeen(id); - } - } + /// The newest presence of this user if there is any. Fetches it from the + /// database first and then from the server if necessary or returns offline. + Future fetchCurrentPresence() => + room.client.fetchCurrentPresence(id); /// Whether the client is able to ban/unban this user. bool get canBan => room.canBan && powerLevel < room.ownPowerLevel; diff --git a/test/database_api_test.dart b/test/database_api_test.dart index ecdec026d..9d096fe8c 100644 --- a/test/database_api_test.dart +++ b/test/database_api_test.dart @@ -457,6 +457,25 @@ void testDatabase( 'deviceId', ); }); + test('getStorePresences', () async { + const userId = '@alice:example.com'; + final presence = CachedPresence( + PresenceType.online, + 100, + 'test message', + true, + '@alice:example.com', + ); + await database.storePresence( + userId, + presence, + ); + final storedPresence = await database.getPresence(userId); + expect( + presence.toJson(), + storedPresence?.toJson(), + ); + }); // Clearing up from here test('clearSSSSCache', () async {