Add profile cache

This commit is contained in:
Christian Pauly 2020-05-18 11:45:49 +00:00
parent 11a83725d1
commit a6c7d88f00
3 changed files with 61 additions and 16 deletions

View file

@ -373,13 +373,39 @@ class Client {
return getProfileFromUserId(userID); return getProfileFromUserId(userID);
} }
/// Get the combined profile information for this user. This API may be used to final Map<String, Profile> _profileCache = {};
/// fetch the user's own profile information or other users; either locally
/// or on remote homeservers. /// Get the combined profile information for this user.
Future<Profile> getProfileFromUserId(String userId) async { /// If [getFromRooms] is true then the profile will first be searched from the
final dynamic resp = await jsonRequest( /// room memberships. This is unstable if the given user makes use of different displaynames
/// and avatars per room, which is common for some bots and bridges.
/// If [cache] is true then
/// the profile get cached for this session. Please note that then the profile may
/// become outdated if the user changes the displayname or avatar in this session.
Future<Profile> getProfileFromUserId(String userId,
{bool cache = true, bool getFromRooms = true}) async {
if (getFromRooms) {
final room = rooms.firstWhere(
(Room room) =>
room
.getParticipants()
.indexWhere((User user) => user.id == userId) !=
-1,
orElse: () => null);
if (room != null) {
final user =
room.getParticipants().firstWhere((User user) => user.id == userId);
return Profile(user.displayName, user.avatarUrl);
}
}
if (cache && _profileCache.containsKey(userId)) {
return _profileCache[userId];
}
final resp = await jsonRequest(
type: HTTPType.GET, action: '/client/r0/profile/${userId}'); type: HTTPType.GET, action: '/client/r0/profile/${userId}');
return Profile.fromJson(resp); final profile = Profile.fromJson(resp);
_profileCache[userId] = profile;
return profile;
} }
Future<List<Room>> get archive async { Future<List<Room>> get archive async {
@ -635,7 +661,8 @@ class Client {
StreamController.broadcast(); StreamController.broadcast();
/// Will be called when another device is requesting verification with this device. /// Will be called when another device is requesting verification with this device.
final StreamController<KeyVerification> onKeyVerificationRequest = StreamController.broadcast(); final StreamController<KeyVerification> onKeyVerificationRequest =
StreamController.broadcast();
final Map<String, KeyVerification> _keyVerificationRequests = {}; final Map<String, KeyVerification> _keyVerificationRequests = {};
@ -1065,7 +1092,9 @@ class Client {
void _cleanupKeyVerificationRequests() { void _cleanupKeyVerificationRequests() {
for (final entry in _keyVerificationRequests.entries) { for (final entry in _keyVerificationRequests.entries) {
(() async { (() async {
var dispose = entry.value.canceled || entry.value.state == KeyVerificationState.done || entry.value.state == KeyVerificationState.error; var dispose = entry.value.canceled ||
entry.value.state == KeyVerificationState.done ||
entry.value.state == KeyVerificationState.error;
if (!dispose) { if (!dispose) {
dispose = !(await entry.value.verifyActivity()); dispose = !(await entry.value.verifyActivity());
} }
@ -1119,13 +1148,18 @@ class Client {
return; return;
} }
// we have key verification going on! // we have key verification going on!
final transactionId = KeyVerification.getTransactionId(toDeviceEvent.content); final transactionId =
KeyVerification.getTransactionId(toDeviceEvent.content);
if (transactionId != null) { if (transactionId != null) {
if (_keyVerificationRequests.containsKey(transactionId)) { if (_keyVerificationRequests.containsKey(transactionId)) {
_keyVerificationRequests[transactionId].handlePayload(toDeviceEvent.type, toDeviceEvent.content); _keyVerificationRequests[transactionId]
.handlePayload(toDeviceEvent.type, toDeviceEvent.content);
} else { } else {
final newKeyRequest = KeyVerification(client: this, userId: toDeviceEvent.sender); final newKeyRequest =
newKeyRequest.handlePayload(toDeviceEvent.type, toDeviceEvent.content).then((res) { KeyVerification(client: this, userId: toDeviceEvent.sender);
newKeyRequest
.handlePayload(toDeviceEvent.type, toDeviceEvent.content)
.then((res) {
if (newKeyRequest.state != KeyVerificationState.askAccept) { if (newKeyRequest.state != KeyVerificationState.askAccept) {
// okay, something went wrong (unknown transaction id?), just dispose it // okay, something went wrong (unknown transaction id?), just dispose it
newKeyRequest.dispose(); newKeyRequest.dispose();

View file

@ -9,8 +9,11 @@ class Profile {
/// This API may return keys which are not limited to displayname or avatar_url. /// This API may return keys which are not limited to displayname or avatar_url.
final Map<String, dynamic> content; final Map<String, dynamic> content;
const Profile(this.displayname, this.avatarUrl, {this.content = const {}});
Profile.fromJson(Map<String, dynamic> json) Profile.fromJson(Map<String, dynamic> json)
: avatarUrl = json['avatar_url'] != null ? Uri.parse(json['avatar_url']) : null, : avatarUrl =
json['avatar_url'] != null ? Uri.parse(json['avatar_url']) : null,
displayname = json['displayname'], displayname = json['displayname'],
content = json; content = json;

View file

@ -160,14 +160,16 @@ void main() {
expect( expect(
matrix matrix
.rooms[1] .rooms[1]
.inboundGroupSessions['ciM/JWTPrmiWPPZNkRLDPQYf9AW/I46bxyLSr+Bx5oU'] .inboundGroupSessions[
'ciM/JWTPrmiWPPZNkRLDPQYf9AW/I46bxyLSr+Bx5oU']
.content['session_key'], .content['session_key'],
'AgAAAAAQcQ6XrFJk6Prm8FikZDqfry/NbDz8Xw7T6e+/9Yf/q3YHIPEQlzv7IZMNcYb51ifkRzFejVvtphS7wwG2FaXIp4XS2obla14iKISR0X74ugB2vyb1AydIHE/zbBQ1ic5s3kgjMFlWpu/S3FQCnCrv+DPFGEt3ERGWxIl3Bl5X53IjPyVkz65oljz2TZESwz0GH/QFvyOOm8ci0q/gceaF3S7Dmafg3dwTKYwcA5xkcc+BLyrLRzB6Hn+oMAqSNSscnm4mTeT5zYibIhrzqyUTMWr32spFtI9dNR/RFSzfCw'); 'AgAAAAAQcQ6XrFJk6Prm8FikZDqfry/NbDz8Xw7T6e+/9Yf/q3YHIPEQlzv7IZMNcYb51ifkRzFejVvtphS7wwG2FaXIp4XS2obla14iKISR0X74ugB2vyb1AydIHE/zbBQ1ic5s3kgjMFlWpu/S3FQCnCrv+DPFGEt3ERGWxIl3Bl5X53IjPyVkz65oljz2TZESwz0GH/QFvyOOm8ci0q/gceaF3S7Dmafg3dwTKYwcA5xkcc+BLyrLRzB6Hn+oMAqSNSscnm4mTeT5zYibIhrzqyUTMWr32spFtI9dNR/RFSzfCw');
if (olmEnabled) { if (olmEnabled) {
expect( expect(
matrix matrix
.rooms[1] .rooms[1]
.inboundGroupSessions['ciM/JWTPrmiWPPZNkRLDPQYf9AW/I46bxyLSr+Bx5oU'] .inboundGroupSessions[
'ciM/JWTPrmiWPPZNkRLDPQYf9AW/I46bxyLSr+Bx5oU']
.inboundGroupSession != .inboundGroupSession !=
null, null,
true); true);
@ -464,11 +466,17 @@ void main() {
}); });
test('getProfileFromUserId', () async { test('getProfileFromUserId', () async {
final profile = await matrix.getProfileFromUserId('@getme:example.com'); final profile = await matrix.getProfileFromUserId('@getme:example.com',
getFromRooms: false);
expect(profile.avatarUrl.toString(), 'mxc://test'); expect(profile.avatarUrl.toString(), 'mxc://test');
expect(profile.displayname, 'You got me'); expect(profile.displayname, 'You got me');
expect(profile.content['avatar_url'], profile.avatarUrl.toString()); expect(profile.content['avatar_url'], profile.avatarUrl.toString());
expect(profile.content['displayname'], profile.displayname); expect(profile.content['displayname'], profile.displayname);
final aliceProfile =
await matrix.getProfileFromUserId('@alice:example.com');
expect(aliceProfile.avatarUrl.toString(),
'mxc://example.org/SEsfnsuifSDFSSEF');
expect(aliceProfile.displayname, 'Alice Margatroid');
}); });
test('signJson', () { test('signJson', () {