feat: Store presences in database

This commit is contained in:
Krille 2023-11-15 06:48:46 +01:00
parent 7bcd5840e1
commit 8cc863b1a3
No known key found for this signature in database
7 changed files with 103 additions and 16 deletions

View File

@ -1791,12 +1791,13 @@ class Client extends MatrixApi {
await _handleRooms(leave, direction: direction); await _handleRooms(leave, direction: direction);
} }
} }
for (final newPresence in sync.presence ?? []) { for (final newPresence in sync.presence ?? <Presence>[]) {
final cachedPresence = CachedPresence.fromMatrixEvent(newPresence); final cachedPresence = CachedPresence.fromMatrixEvent(newPresence);
presences[newPresence.senderId] = cachedPresence; presences[newPresence.senderId] = cachedPresence;
// ignore: deprecated_member_use_from_same_package // ignore: deprecated_member_use_from_same_package
onPresence.add(newPresence); onPresence.add(newPresence);
onPresenceChanged.add(cachedPresence); onPresenceChanged.add(cachedPresence);
await database?.storePresence(newPresence.senderId, cachedPresence);
} }
for (final newAccountData in sync.accountData ?? []) { for (final newAccountData in sync.accountData ?? []) {
await database?.storeAccountData( await database?.storeAccountData(
@ -2924,6 +2925,25 @@ class Client extends MatrixApi {
return; 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<CachedPresence> 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 _disposed = false;
bool _aborted = false; bool _aborted = false;
Future _currentTransaction = Future.sync(() => {}); Future _currentTransaction = Future.sync(() => {});

View File

@ -312,4 +312,8 @@ abstract class DatabaseApi {
Future<String> exportDump(); Future<String> exportDump();
Future<bool> importDump(String export); Future<bool> importDump(String export);
Future<void> storePresence(String userId, CachedPresence presence);
Future<CachedPresence?> getPresence(String userId);
} }

View File

@ -1478,6 +1478,18 @@ class HiveCollectionsDatabase extends DatabaseApi {
return raw; return raw;
} }
@override
Future<void> storePresence(String userId, CachedPresence presence) =>
_presencesBox.put(userId, presence.toJson());
@override
Future<CachedPresence?> getPresence(String userId) async {
final rawPresence = await _presencesBox.get(userId);
if (rawPresence == null) return null;
return CachedPresence.fromJson(copyMap(rawPresence));
}
@override @override
Future<String> exportDump() async { Future<String> exportDump() async {
final dataMap = { final dataMap = {

View File

@ -1450,6 +1450,18 @@ class FamedlySdkHiveDatabase extends DatabaseApi {
return raw as String; return raw as String;
} }
@override
Future<void> storePresence(String userId, CachedPresence presence) =>
_presencesBox.put(userId, presence.toJson());
@override
Future<CachedPresence?> getPresence(String userId) async {
final rawPresence = await _presencesBox.get(userId);
if (rawPresence == null) return null;
return CachedPresence.fromJson(copyMap(rawPresence));
}
@override @override
Future<String> exportDump() { Future<String> exportDump() {
// see no need to implement this in a deprecated part // see no need to implement this in a deprecated part

View File

@ -25,6 +25,36 @@ class CachedPresence {
bool? currentlyActive; bool? currentlyActive;
String userid; String userid;
factory CachedPresence.fromJson(Map<String, Object?> 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<String, Object?> 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, CachedPresence(this.presence, int? lastActiveAgo, this.statusMsg,
this.currentlyActive, this.userid) { this.currentlyActive, this.userid) {
if (lastActiveAgo != null) { if (lastActiveAgo != null) {
@ -49,7 +79,7 @@ class CachedPresence {
Presence toPresence() { Presence toPresence() {
final content = <String, dynamic>{ final content = <String, dynamic>{
'presence': presence.toString(), 'presence': presence.name.toString(),
}; };
if (currentlyActive != null) content['currently_active'] = currentlyActive!; if (currentlyActive != null) content['currently_active'] = currentlyActive!;
if (lastActiveTimestamp != null) { if (lastActiveTimestamp != null) {

View File

@ -155,20 +155,10 @@ class User extends Event {
@Deprecated('Use fetchCurrentPresence() instead') @Deprecated('Use fetchCurrentPresence() instead')
Future<CachedPresence> get currentPresence => fetchCurrentPresence(); Future<CachedPresence> get currentPresence => fetchCurrentPresence();
/// The newest presence of this user if there is any. Fetches it from the server if necessary or returns offline. /// The newest presence of this user if there is any. Fetches it from the
Future<CachedPresence> fetchCurrentPresence() async { /// database first and then from the server if necessary or returns offline.
final cachedPresence = room.client.presences[id]; Future<CachedPresence> fetchCurrentPresence() =>
if (cachedPresence != null) { room.client.fetchCurrentPresence(id);
return cachedPresence;
}
try {
final newPresence = await room.client.getPresence(id);
return CachedPresence.fromPresenceResponse(newPresence, id);
} catch (e) {
return CachedPresence.neverSeen(id);
}
}
/// Whether the client is able to ban/unban this user. /// Whether the client is able to ban/unban this user.
bool get canBan => room.canBan && powerLevel < room.ownPowerLevel; bool get canBan => room.canBan && powerLevel < room.ownPowerLevel;

View File

@ -457,6 +457,25 @@ void testDatabase(
'deviceId', '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 // Clearing up from here
test('clearSSSSCache', () async { test('clearSSSSCache', () async {