diff --git a/lib/src/client.dart b/lib/src/client.dart index 56c244b..8ef4001 100644 --- a/lib/src/client.dart +++ b/lib/src/client.dart @@ -373,13 +373,39 @@ class Client { return getProfileFromUserId(userID); } - /// Get the combined profile information for this user. This API may be used to - /// fetch the user's own profile information or other users; either locally - /// or on remote homeservers. - Future getProfileFromUserId(String userId) async { - final dynamic resp = await jsonRequest( + final Map _profileCache = {}; + + /// Get the combined profile information for this user. + /// If [getFromRooms] is true then the profile will first be searched from the + /// 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 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}'); - return Profile.fromJson(resp); + final profile = Profile.fromJson(resp); + _profileCache[userId] = profile; + return profile; } Future> get archive async { @@ -635,7 +661,8 @@ class Client { StreamController.broadcast(); /// Will be called when another device is requesting verification with this device. - final StreamController onKeyVerificationRequest = StreamController.broadcast(); + final StreamController onKeyVerificationRequest = + StreamController.broadcast(); final Map _keyVerificationRequests = {}; @@ -1065,7 +1092,9 @@ class Client { void _cleanupKeyVerificationRequests() { for (final entry in _keyVerificationRequests.entries) { (() 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) { dispose = !(await entry.value.verifyActivity()); } @@ -1119,13 +1148,18 @@ class Client { return; } // we have key verification going on! - final transactionId = KeyVerification.getTransactionId(toDeviceEvent.content); + final transactionId = + KeyVerification.getTransactionId(toDeviceEvent.content); if (transactionId != null) { if (_keyVerificationRequests.containsKey(transactionId)) { - _keyVerificationRequests[transactionId].handlePayload(toDeviceEvent.type, toDeviceEvent.content); + _keyVerificationRequests[transactionId] + .handlePayload(toDeviceEvent.type, toDeviceEvent.content); } else { - final newKeyRequest = KeyVerification(client: this, userId: toDeviceEvent.sender); - newKeyRequest.handlePayload(toDeviceEvent.type, toDeviceEvent.content).then((res) { + final newKeyRequest = + KeyVerification(client: this, userId: toDeviceEvent.sender); + newKeyRequest + .handlePayload(toDeviceEvent.type, toDeviceEvent.content) + .then((res) { if (newKeyRequest.state != KeyVerificationState.askAccept) { // okay, something went wrong (unknown transaction id?), just dispose it newKeyRequest.dispose(); diff --git a/lib/src/utils/profile.dart b/lib/src/utils/profile.dart index f01c347..83ac5aa 100644 --- a/lib/src/utils/profile.dart +++ b/lib/src/utils/profile.dart @@ -9,8 +9,11 @@ class Profile { /// This API may return keys which are not limited to displayname or avatar_url. final Map content; + const Profile(this.displayname, this.avatarUrl, {this.content = const {}}); + Profile.fromJson(Map 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'], content = json; diff --git a/test/client_test.dart b/test/client_test.dart index 9b6303e..d30b4cf 100644 --- a/test/client_test.dart +++ b/test/client_test.dart @@ -160,14 +160,16 @@ void main() { expect( matrix .rooms[1] - .inboundGroupSessions['ciM/JWTPrmiWPPZNkRLDPQYf9AW/I46bxyLSr+Bx5oU'] + .inboundGroupSessions[ + 'ciM/JWTPrmiWPPZNkRLDPQYf9AW/I46bxyLSr+Bx5oU'] .content['session_key'], 'AgAAAAAQcQ6XrFJk6Prm8FikZDqfry/NbDz8Xw7T6e+/9Yf/q3YHIPEQlzv7IZMNcYb51ifkRzFejVvtphS7wwG2FaXIp4XS2obla14iKISR0X74ugB2vyb1AydIHE/zbBQ1ic5s3kgjMFlWpu/S3FQCnCrv+DPFGEt3ERGWxIl3Bl5X53IjPyVkz65oljz2TZESwz0GH/QFvyOOm8ci0q/gceaF3S7Dmafg3dwTKYwcA5xkcc+BLyrLRzB6Hn+oMAqSNSscnm4mTeT5zYibIhrzqyUTMWr32spFtI9dNR/RFSzfCw'); if (olmEnabled) { expect( matrix .rooms[1] - .inboundGroupSessions['ciM/JWTPrmiWPPZNkRLDPQYf9AW/I46bxyLSr+Bx5oU'] + .inboundGroupSessions[ + 'ciM/JWTPrmiWPPZNkRLDPQYf9AW/I46bxyLSr+Bx5oU'] .inboundGroupSession != null, true); @@ -464,11 +466,17 @@ void main() { }); 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.displayname, 'You got me'); expect(profile.content['avatar_url'], profile.avatarUrl.toString()); 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', () {