Merge branch 'krille/create-rooms-methods' into 'main'

feat: More advanced create chat methods

See merge request famedly/company/frontend/famedlysdk!877
This commit is contained in:
Krille Fear 2021-11-09 15:37:44 +00:00
commit b350d5ab5b
7 changed files with 128 additions and 21 deletions

View File

@ -556,24 +556,102 @@ class Client extends MatrixApi {
}
/// Returns an existing direct room ID with this user or creates a new one.
/// Returns null on error.
Future<String> startDirectChat(String mxid) async {
/// By default encryption will be enabled if the client supports encryption
/// and the other user has uploaded any encryption keys.
Future<String> startDirectChat(
String mxid, {
bool? enableEncryption,
List<StateEvent>? initialState,
bool waitForSync = true,
}) async {
// Try to find an existing direct chat
var roomId = getDirectChatFromUserId(mxid);
if (roomId != null) return roomId;
final directChatRoomId = getDirectChatFromUserId(mxid);
if (directChatRoomId != null) return directChatRoomId;
enableEncryption ??= await userOwnsEncryptionKeys(mxid);
if (enableEncryption) {
initialState ??= [];
if (!initialState.any((s) => s.type == EventTypes.Encryption)) {
initialState.add(StateEvent(
content: {
'algorithm': supportedGroupEncryptionAlgorithms.first,
},
type: EventTypes.Encryption,
));
}
}
// Start a new direct chat
roomId = await createRoom(
final roomId = await createRoom(
invite: [mxid],
isDirect: true,
preset: CreateRoomPreset.trustedPrivateChat,
initialState: initialState,
);
if (waitForSync && getRoomById(roomId) == null) {
// Wait for room actually appears in sync
await onSync.stream
.firstWhere((sync) => sync.rooms?.join?.containsKey(roomId) ?? false);
}
await Room(id: roomId, client: this).addToDirectChat(mxid);
return roomId;
}
/// Simplified method to create a new group chat. By default it is a private
/// chat. The encryption is enabled if this client supports encryption and
/// the preset is not a public chat.
Future<String> createGroupChat({
String? groupName,
bool? enableEncryption,
List<String>? invite,
CreateRoomPreset preset = CreateRoomPreset.privateChat,
List<StateEvent>? initialState,
bool waitForSync = true,
}) async {
enableEncryption ??=
encryptionEnabled && preset != CreateRoomPreset.publicChat;
if (enableEncryption) {
initialState ??= [];
if (!initialState.any((s) => s.type == EventTypes.Encryption)) {
initialState.add(StateEvent(
content: {
'algorithm': supportedGroupEncryptionAlgorithms.first,
},
type: EventTypes.Encryption,
));
}
}
final roomId = await createRoom(
invite: invite,
preset: preset,
name: groupName,
initialState: initialState,
);
if (waitForSync) {
if (getRoomById(roomId) == null) {
// Wait for room actually appears in sync
await onSync.stream.firstWhere(
(sync) => sync.rooms?.join?.containsKey(roomId) ?? false);
}
}
return roomId;
}
/// Checks if the given user has encryption keys. May query keys from the
/// server to answer this.
Future<bool> userOwnsEncryptionKeys(String userId) async {
if (userId == userID) return encryptionEnabled;
if (_userDeviceKeys.containsKey(userId)) {
return true;
}
final keys = await queryKeys({userId: []});
return keys.deviceKeys?.isNotEmpty ?? false;
}
/// Creates a new space and returns the Room ID. The parameters are mostly
/// the same like in [createRoom()].
/// Be aware that spaces appear in the [rooms] list. You should check if a

View File

@ -148,7 +148,17 @@ class User extends Event {
/// Returns an existing direct chat ID with this user or creates a new one.
/// Returns null on error.
Future<String> startDirectChat() async => room.client.startDirectChat(id);
Future<String> startDirectChat({
bool? enableEncryption,
List<StateEvent>? initialState,
bool waitForSync = true,
}) async =>
room.client.startDirectChat(
id,
enableEncryption: enableEncryption,
initialState: initialState,
waitForSync: waitForSync,
);
/// The newest presence of this user if there is any and null if not.
Presence? get presence => room.client.presences[id];

View File

@ -67,14 +67,25 @@ class DeviceKeysList {
}
}
Future<KeyVerification> startVerification() async {
/// Starts a verification with this device. This might need to create a new
/// direct chat to send the verification request over this room. For this you
/// can set parameters here.
Future<KeyVerification> startVerification({
bool? newDirectChatEnableEncryption,
List<StateEvent>? newDirectChatInitialState,
}) async {
final encryption = client.encryption;
if (encryption == null) {
throw Exception('Encryption not enabled');
}
if (userId != client.userID) {
// in-room verification with someone else
final roomId = await client.startDirectChat(userId);
final roomId = await client.startDirectChat(
userId,
enableEncryption: newDirectChatEnableEncryption,
initialState: newDirectChatInitialState,
waitForSync: false,
);
final room =
client.getRoomById(roomId) ?? Room(id: roomId, client: client);

View File

@ -560,6 +560,12 @@ void main() {
bunnyContent);
await client.dispose(closeDatabase: true);
});
test('startDirectChat', () async {
await matrix.startDirectChat('@alice:example.com', waitForSync: false);
});
test('createGroupChat', () async {
await matrix.createGroupChat(groupName: 'Testgroup', waitForSync: false);
});
test('Test the fake store api', () async {
final database = await getDatabase(null);
final client1 = Client(

View File

@ -252,7 +252,7 @@ void main() {
expect(req?.room != null, false);
req = await client.userDeviceKeys['@alice:example.com']
?.startVerification();
?.startVerification(newDirectChatEnableEncryption: false);
expect(req != null, true);
expect(req?.room != null, true);
});

View File

@ -119,7 +119,9 @@ void main() {
client1.userDeviceKeys[client1.userID]!.masterKey!
.setDirectVerified(false);
final req1 =
await client1.userDeviceKeys[client2.userID]!.startVerification();
await client1.userDeviceKeys[client2.userID]!.startVerification(
newDirectChatEnableEncryption: false,
);
var evt = getLastSentEvent(req1);
expect(req1.state, KeyVerificationState.waitingAccept);
@ -221,8 +223,8 @@ void main() {
client1.userDeviceKeys[client1.userID]!.masterKey!
.setDirectVerified(true);
await client1.encryption!.ssss.clearCache();
final req1 =
await client1.userDeviceKeys[client2.userID]!.startVerification();
final req1 = await client1.userDeviceKeys[client2.userID]!
.startVerification(newDirectChatEnableEncryption: false);
expect(req1.state, KeyVerificationState.askSSSS);
await req1.openSSSS(recoveryKey: ssssKey);
await Future.delayed(Duration(seconds: 1));
@ -241,8 +243,8 @@ void main() {
// the other one has to have their master key verified to trigger asking for ssss
client2.userDeviceKeys[client2.userID]!.masterKey!
.setDirectVerified(true);
final req1 =
await client1.userDeviceKeys[client2.userID]!.startVerification();
final req1 = await client1.userDeviceKeys[client2.userID]!
.startVerification(newDirectChatEnableEncryption: false);
var evt = getLastSentEvent(req1);
expect(req1.state, KeyVerificationState.waitingAccept);
@ -345,8 +347,8 @@ void main() {
// make sure our master key is *not* verified to not triger SSSS for now
client1.userDeviceKeys[client1.userID]!.masterKey!
.setDirectVerified(false);
final req1 =
await client1.userDeviceKeys[client2.userID]!.startVerification();
final req1 = await client1.userDeviceKeys[client2.userID]!
.startVerification(newDirectChatEnableEncryption: false);
var evt = getLastSentEvent(req1);
expect(req1.state, KeyVerificationState.waitingAccept);
@ -375,8 +377,8 @@ void main() {
// make sure our master key is *not* verified to not triger SSSS for now
client1.userDeviceKeys[client1.userID]!.masterKey!
.setDirectVerified(false);
final req1 =
await client1.userDeviceKeys[client2.userID]!.startVerification();
final req1 = await client1.userDeviceKeys[client2.userID]!
.startVerification(newDirectChatEnableEncryption: false);
var evt = getLastSentEvent(req1);
expect(req1.state, KeyVerificationState.waitingAccept);
@ -436,8 +438,8 @@ void main() {
// make sure our master key is *not* verified to not triger SSSS for now
client1.userDeviceKeys[client1.userID]!.masterKey!
.setDirectVerified(false);
final req1 =
await client1.userDeviceKeys[client2.userID]!.startVerification();
final req1 = await client1.userDeviceKeys[client2.userID]!
.startVerification(newDirectChatEnableEncryption: false);
final evt = getLastSentEvent(req1);
expect(req1.state, KeyVerificationState.waitingAccept);

View File

@ -118,7 +118,7 @@ void main() {
await user1.setPower(50);
});
test('startDirectChat', () async {
await user1.startDirectChat();
await user1.startDirectChat(waitForSync: false);
});
test('getPresence', () async {
await client.handleSync(SyncUpdate.fromJson({