feat: Store timestamp in the presence events

This fixes the presence never being accurate in the app.
This commit is contained in:
Nicolas Werner 2022-05-05 17:55:21 +02:00
parent 2f232fefba
commit 907a0d2317
6 changed files with 103 additions and 6 deletions

View File

@ -26,6 +26,7 @@ export 'src/database/database_api.dart';
export 'src/database/hive_database.dart';
export 'src/database/fluffybox_database.dart';
export 'src/event.dart';
export 'src/presence.dart';
export 'src/event_status.dart';
export 'src/voip.dart';
export 'src/voip_content.dart';

View File

@ -262,7 +262,7 @@ class Client extends MatrixApi {
Map<String, BasicEvent> get accountData => _accountData;
/// Presences of users by a given matrix ID
Map<String, Presence> presences = {};
Map<String, CachedPresence> presences = {};
int _transactionCounter = 0;
@ -889,8 +889,14 @@ class Client extends MatrixApi {
StreamController.broadcast();
/// Callback will be called on presences.
@Deprecated(
'Deprecated, use onPresenceChanged instead which has a timestamp.')
final StreamController<Presence> onPresence = StreamController.broadcast();
/// Callback will be called on presence updates.
final StreamController<CachedPresence> onPresenceChanged =
StreamController.broadcast();
/// Callback will be called on account data updates.
final StreamController<BasicEvent> onAccountData =
StreamController.broadcast();
@ -1467,8 +1473,11 @@ class Client extends MatrixApi {
}
}
for (final newPresence in sync.presence ?? []) {
presences[newPresence.senderId] = newPresence;
final cachedPresence = CachedPresence.fromMatrixEvent(newPresence);
presences[newPresence.senderId] = cachedPresence;
// ignore: deprecated_member_use_from_same_package
onPresence.add(newPresence);
onPresenceChanged.add(cachedPresence);
}
for (final newAccountData in sync.accountData ?? []) {
await database?.storeAccountData(

71
lib/src/presence.dart Normal file
View File

@ -0,0 +1,71 @@
/*
* Famedly Matrix SDK
* Copyright (C) 2019, 2020, 2021 Famedly GmbH
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
import '../matrix.dart';
class CachedPresence {
PresenceType presence;
DateTime? lastActiveTimestamp;
String? statusMsg;
bool? currentlyActive;
String userid;
CachedPresence(this.presence, int? lastActiveAgo, this.statusMsg,
this.currentlyActive, this.userid) {
if (lastActiveAgo != null) {
lastActiveTimestamp =
DateTime.now().subtract(Duration(milliseconds: lastActiveAgo));
}
}
CachedPresence.fromMatrixEvent(Presence event)
: this(
event.presence.presence,
event.presence.lastActiveAgo,
event.presence.statusMsg,
event.presence.currentlyActive,
event.senderId);
CachedPresence.fromPresenceResponse(GetPresenceResponse event, String userid)
: this(event.presence, event.lastActiveAgo, event.statusMsg,
event.currentlyActive, userid);
CachedPresence.neverSeen(String userid)
: presence = PresenceType.offline,
userid = userid;
Presence toPresence() {
final content = <String, dynamic>{
'presence': presence.toString(),
};
if (currentlyActive != null) content['currently_active'] = currentlyActive!;
if (lastActiveTimestamp != null) {
content['last_active_ago'] =
DateTime.now().difference(lastActiveTimestamp!).inMilliseconds;
}
if (statusMsg != null) content['status_msg'] = statusMsg!;
final json = {
'content': content,
'sender': '@example:localhost',
'type': 'm.presence'
};
return Presence.fromJson(json);
}
}

View File

@ -158,7 +158,23 @@ class User extends Event {
);
/// The newest presence of this user if there is any and null if not.
Presence? get presence => room.client.presences[id];
@Deprecated('Deprecated in favour of currentPresence.')
Presence? get presence => room.client.presences[id]?.toPresence();
/// The newest presence of this user if there is any. Fetches it from the server if necessary or returns offline.
Future<CachedPresence> get currentPresence 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);
}
}
/// Whether the client is able to ban/unban this user.
bool get canBan => room.canBan && powerLevel < room.ownPowerLevel;

View File

@ -71,7 +71,7 @@ void main() {
var presenceCounter = 0;
var accountDataCounter = 0;
matrix.onPresence.stream.listen((Presence data) {
matrix.onPresenceChanged.stream.listen((CachedPresence data) {
presenceCounter++;
});
matrix.onAccountData.stream.listen((BasicEvent data) {
@ -150,7 +150,7 @@ void main() {
expect(matrix.rooms.length, 2);
expect(matrix.rooms[1].canonicalAlias,
"#famedlyContactDiscovery:${matrix.userID!.split(":")[1]}");
expect(matrix.presences['@alice:example.com']?.presence.presence,
expect(matrix.presences['@alice:example.com']?.presence,
PresenceType.online);
expect(presenceCounter, 1);
expect(accountDataCounter, 9);

View File

@ -131,7 +131,7 @@ void main() {
]
}
}));
expect(user1.presence?.presence.presence, PresenceType.online);
expect((await user1.currentPresence).presence, PresenceType.online);
});
test('canBan', () async {
expect(user1.canBan, false);