Merge branch 'soru/megolm-session-on-typing' into 'main'

feat: Start megolm sessions while typing

See merge request famedly/famedlysdk!589
This commit is contained in:
Sorunome 2020-12-28 15:21:07 +00:00
commit 71fb80aa1f
3 changed files with 65 additions and 4 deletions

View File

@ -271,7 +271,7 @@ class KeyManager {
/// devices have been changed. Returns false if the session has not been cleared because
/// it wasn't necessary. Otherwise returns true.
Future<bool> clearOrUseOutboundGroupSession(String roomId,
{bool wipe = false}) async {
{bool wipe = false, bool use = true}) async {
final room = client.getRoomById(roomId);
final sess = getOutboundGroupSession(roomId);
if (room == null || sess == null) {
@ -334,8 +334,13 @@ class KeyManager {
break;
}
// and now add all the new devices!
final oldDeviceIds = Set.from(sess.devices[userId].keys);
final newDeviceIds = Set.from(newDeviceKeyIds[userId].keys);
final oldDeviceIds = Set.from(sess.devices[userId].entries
.where((e) => !e.value)
.map((e) => e.key));
final newDeviceIds = Set.from(newDeviceKeyIds[userId]
.entries
.where((e) => !e.value)
.map((e) => e.key));
final newDevices = newDeviceIds.difference(oldDeviceIds);
if (newDeviceIds.isNotEmpty) {
devicesToReceive.addAll(newDeviceKeys.where(
@ -345,6 +350,9 @@ class KeyManager {
}
if (!wipe) {
if (!use) {
return false;
}
// okay, we use the outbound group session!
sess.sentMessages++;
sess.devices = newDeviceKeyIds;
@ -355,7 +363,7 @@ class KeyManager {
'session_key': sess.outboundGroupSession.session_key(),
};
try {
devicesToReceive.removeWhere((k) => k.blocked);
devicesToReceive.removeWhere((k) => !k.encryptToDevice);
if (devicesToReceive.isNotEmpty) {
// update allowedAtIndex
for (final device in devicesToReceive) {
@ -394,6 +402,7 @@ class KeyManager {
return true;
}
/// Store an outbound group session in the database
Future<void> storeOutboundGroupSession(
String roomId, OutboundGroupSession sess) async {
if (sess == null) {
@ -411,6 +420,7 @@ class KeyManager {
final Map<String, Future<OutboundGroupSession>>
_pendingNewOutboundGroupSessions = {};
/// Creates an outbound group session for a given room id
Future<OutboundGroupSession> createOutboundGroupSession(String roomId) async {
if (_pendingNewOutboundGroupSessions.containsKey(roomId)) {
return _pendingNewOutboundGroupSessions[roomId];
@ -421,6 +431,18 @@ class KeyManager {
return _pendingNewOutboundGroupSessions.remove(roomId);
}
/// Prepares an outbound group session for a given room ID. That is, load it from
/// the database, cycle it if needed and create it if absent.
Future<void> prepareOutboundGroupSession(String roomId) async {
if (getOutboundGroupSession(roomId) == null) {
await loadOutboundGroupSession(roomId);
}
await clearOrUseOutboundGroupSession(roomId, use: false);
if (getOutboundGroupSession(roomId) == null) {
await createOutboundGroupSession(roomId);
}
}
Future<OutboundGroupSession> _createOutboundGroupSession(
String roomId) async {
await clearOrUseOutboundGroupSession(roomId, wipe: true);
@ -477,10 +499,12 @@ class KeyManager {
return sess;
}
/// Get an outbound group session for a room id
OutboundGroupSession getOutboundGroupSession(String roomId) {
return _outboundGroupSessions[roomId];
}
/// Load an outbound group session from database
Future<void> loadOutboundGroupSession(String roomId) async {
if (_loadedOutboundGroupSessions.contains(roomId) ||
_outboundGroupSessions.containsKey(roomId) ||

View File

@ -22,6 +22,7 @@ import 'dart:core';
import 'dart:typed_data';
import 'package:http/http.dart' as http;
import 'package:pedantic/pedantic.dart';
import '../encryption.dart';
import '../famedlysdk.dart';
@ -542,6 +543,22 @@ class Client extends MatrixApi {
return mxc;
}
/// Sends a typing notification and initiates a megolm session, if needed
@override
Future<void> sendTypingNotification(
String userId,
String roomId,
bool typing, {
int timeout,
}) async {
await super
.sendTypingNotification(userId, roomId, typing, timeout: timeout);
final room = getRoomById(roomId);
if (typing && room != null && encryptionEnabled && room.encrypted) {
unawaited(encryption.keyManager.prepareOutboundGroupSession(roomId));
}
}
/// Uploads a new user avatar for this user.
Future<void> setAvatar(MatrixFile file) async {
final uploadResp = await upload(file.bytes, file.name);

View File

@ -133,6 +133,26 @@ void main() {
client.userDeviceKeys['@alice:example.com'].deviceKeys['JLAFKJWSCS']
.blocked = false;
// lazy-create if it would rotate
sess =
await client.encryption.keyManager.createOutboundGroupSession(roomId);
final oldSessKey = sess.outboundGroupSession.session_key();
client.userDeviceKeys['@alice:example.com'].deviceKeys['JLAFKJWSCS']
.blocked = true;
await client.encryption.keyManager.prepareOutboundGroupSession(roomId);
expect(
client.encryption.keyManager.getOutboundGroupSession(roomId) != null,
true);
expect(
client.encryption.keyManager
.getOutboundGroupSession(roomId)
.outboundGroupSession
.session_key() !=
oldSessKey,
true);
client.userDeviceKeys['@alice:example.com'].deviceKeys['JLAFKJWSCS']
.blocked = false;
// rotate if too far in the past
sess =
await client.encryption.keyManager.createOutboundGroupSession(roomId);