Krille/make client extend matrixapi

This commit is contained in:
Christian Pauly 2020-08-11 16:11:51 +00:00
parent 574fe27101
commit fb9b505988
19 changed files with 230 additions and 269 deletions

View file

@ -167,7 +167,7 @@ class CrossSigning {
} }
if (signedKeys.isNotEmpty) { if (signedKeys.isNotEmpty) {
// post our new keys! // post our new keys!
await client.api.uploadKeySignatures(signedKeys); await client.uploadKeySignatures(signedKeys);
} }
} }

View file

@ -44,7 +44,7 @@ class KeyManager {
encryption.ssss.setValidator(MEGOLM_KEY, (String secret) async { encryption.ssss.setValidator(MEGOLM_KEY, (String secret) async {
final keyObj = olm.PkDecryption(); final keyObj = olm.PkDecryption();
try { try {
final info = await client.api.getRoomKeysBackup(); final info = await client.getRoomKeysBackup();
if (info.algorithm != RoomKeysAlgorithmType.v1Curve25519AesSha2) { if (info.algorithm != RoomKeysAlgorithmType.v1Curve25519AesSha2) {
return false; return false;
} }
@ -288,7 +288,7 @@ class KeyManager {
key: client.userID, key: client.userID,
); );
try { try {
await client.sendToDevice(deviceKeys, 'm.room_key', rawSession); await client.sendToDeviceEncrypted(deviceKeys, 'm.room_key', rawSession);
await storeOutboundGroupSession(roomId, sess); await storeOutboundGroupSession(roomId, sess);
_outboundGroupSessions[roomId] = sess; _outboundGroupSessions[roomId] = sess;
} catch (e, s) { } catch (e, s) {
@ -339,7 +339,7 @@ class KeyManager {
final privateKey = final privateKey =
base64.decode(await encryption.ssss.getCached(MEGOLM_KEY)); base64.decode(await encryption.ssss.getCached(MEGOLM_KEY));
final decryption = olm.PkDecryption(); final decryption = olm.PkDecryption();
final info = await client.api.getRoomKeysBackup(); final info = await client.getRoomKeysBackup();
String backupPubKey; String backupPubKey;
try { try {
backupPubKey = decryption.init_with_private_key(privateKey); backupPubKey = decryption.init_with_private_key(privateKey);
@ -387,9 +387,9 @@ class KeyManager {
} }
Future<void> loadSingleKey(String roomId, String sessionId) async { Future<void> loadSingleKey(String roomId, String sessionId) async {
final info = await client.api.getRoomKeysBackup(); final info = await client.getRoomKeysBackup();
final ret = final ret =
await client.api.getRoomKeysSingleKey(roomId, sessionId, info.version); await client.getRoomKeysSingleKey(roomId, sessionId, info.version);
final keys = RoomKeys.fromJson({ final keys = RoomKeys.fromJson({
'rooms': { 'rooms': {
roomId: { roomId: {
@ -434,8 +434,9 @@ class KeyManager {
sessionId: sessionId, sessionId: sessionId,
senderKey: senderKey, senderKey: senderKey,
); );
await client.sendToDevice( final userList = await room.requestParticipants();
[], await client.sendToDevicesOfUserIds(
userList.map<String>((u) => u.id).toSet(),
'm.room_key_request', 'm.room_key_request',
{ {
'action': 'request', 'action': 'request',
@ -448,8 +449,7 @@ class KeyManager {
'request_id': requestId, 'request_id': requestId,
'requesting_device_id': client.deviceID, 'requesting_device_id': client.deviceID,
}, },
encrypted: false, );
toUsers: await room.requestParticipants());
outgoingShareRequests[request.requestId] = request; outgoingShareRequests[request.requestId] = request;
} catch (e, s) { } catch (e, s) {
Logs.error( Logs.error(
@ -568,15 +568,24 @@ class KeyManager {
if (request.devices.isEmpty) { if (request.devices.isEmpty) {
return; // no need to send any cancellation return; // no need to send any cancellation
} }
await client.sendToDevice( // Send with send-to-device messaging
request.devices, final sendToDeviceMessage = {
'm.room_key_request',
{
'action': 'request_cancellation', 'action': 'request_cancellation',
'request_id': request.requestId, 'request_id': request.requestId,
'requesting_device_id': client.deviceID, 'requesting_device_id': client.deviceID,
}, };
encrypted: false); var data = <String, Map<String, Map<String, dynamic>>>{};
for (final device in request.devices) {
if (!data.containsKey(device.userId)) {
data[device.userId] = {};
}
data[device.userId][device.deviceId] = sendToDeviceMessage;
}
await client.sendToDevice(
'm.room_key_request',
client.generateUniqueTransactionId(),
data,
);
} else if (event.type == 'm.room_key') { } else if (event.type == 'm.room_key') {
if (event.encryptedContent == null) { if (event.encryptedContent == null) {
return; // the event wasn't encrypted, this is a security risk; return; // the event wasn't encrypted, this is a security risk;
@ -675,7 +684,7 @@ class RoomKeyRequest extends ToDeviceEvent {
message['session_key'] = session.inboundGroupSession message['session_key'] = session.inboundGroupSession
.export_session(session.inboundGroupSession.first_known_index()); .export_session(session.inboundGroupSession.first_known_index());
// send the actual reply of the key back to the requester // send the actual reply of the key back to the requester
await keyManager.client.sendToDevice( await keyManager.client.sendToDeviceEncrypted(
[requestingDevice], [requestingDevice],
'm.forwarded_room_key', 'm.forwarded_room_key',
message, message,

View file

@ -183,7 +183,7 @@ class OlmManager {
signJson(keysContent['device_keys'] as Map<String, dynamic>); signJson(keysContent['device_keys'] as Map<String, dynamic>);
} }
final response = await client.api.uploadDeviceKeys( final response = await client.uploadDeviceKeys(
deviceKeys: uploadDeviceKeys deviceKeys: uploadDeviceKeys
? MatrixDeviceKeys.fromJson(keysContent['device_keys']) ? MatrixDeviceKeys.fromJson(keysContent['device_keys'])
: null, : null,
@ -335,7 +335,7 @@ class OlmManager {
return; return;
} }
await startOutgoingOlmSessions([device]); await startOutgoingOlmSessions([device]);
await client.sendToDevice([device], 'm.dummy', {}); await client.sendToDeviceEncrypted([device], 'm.dummy', {});
} }
Future<ToDeviceEvent> decryptToDeviceEvent(ToDeviceEvent event) async { Future<ToDeviceEvent> decryptToDeviceEvent(ToDeviceEvent event) async {
@ -383,7 +383,7 @@ class OlmManager {
} }
final response = final response =
await client.api.requestOneTimeKeys(requestingKeysFrom, timeout: 10000); await client.requestOneTimeKeys(requestingKeysFrom, timeout: 10000);
for (var userKeysEntry in response.oneTimeKeys.entries) { for (var userKeysEntry in response.oneTimeKeys.entries) {
final userId = userKeysEntry.key; final userId = userKeysEntry.key;

View file

@ -222,7 +222,7 @@ class SSSS {
'mac': encrypted.mac, 'mac': encrypted.mac,
}; };
// store the thing in your account data // store the thing in your account data
await client.api.setAccountData(client.userID, type, content); await client.setAccountData(client.userID, type, content);
if (CACHE_TYPES.contains(type) && client.database != null) { if (CACHE_TYPES.contains(type) && client.database != null) {
// cache the thing // cache the thing
await client.database await client.database
@ -271,7 +271,7 @@ class SSSS {
devices: devices, devices: devices,
); );
pendingShareRequests[requestId] = request; pendingShareRequests[requestId] = request;
await client.sendToDevice(devices, 'm.secret.request', { await client.sendToDeviceEncrypted(devices, 'm.secret.request', {
'action': 'request', 'action': 'request',
'requesting_device_id': client.deviceID, 'requesting_device_id': client.deviceID,
'request_id': requestId, 'request_id': requestId,
@ -308,7 +308,7 @@ class SSSS {
} }
// okay, all checks out...time to share this secret! // okay, all checks out...time to share this secret!
Logs.info('[SSSS] Replying with secret for ${type}'); Logs.info('[SSSS] Replying with secret for ${type}');
await client.sendToDevice( await client.sendToDeviceEncrypted(
[device], [device],
'm.secret.send', 'm.secret.send',
{ {

View file

@ -571,7 +571,7 @@ class KeyVerification {
} else { } else {
Logs.info( Logs.info(
'[Key Verification] Sending to ${userId} device ${deviceId}...'); '[Key Verification] Sending to ${userId} device ${deviceId}...');
await client.sendToDevice( await client.sendToDeviceEncrypted(
[client.userDeviceKeys[userId].deviceKeys[deviceId]], type, payload); [client.userDeviceKeys[userId].deviceKeys[deviceId]], type, payload);
} }
} }

View file

@ -1329,8 +1329,11 @@ class MatrixApi {
/// This endpoint is used to send send-to-device events to a set of client devices. /// This endpoint is used to send send-to-device events to a set of client devices.
/// https://matrix.org/docs/spec/client_server/r0.6.1#put-matrix-client-r0-sendtodevice-eventtype-txnid /// https://matrix.org/docs/spec/client_server/r0.6.1#put-matrix-client-r0-sendtodevice-eventtype-txnid
Future<void> sendToDevice(String eventType, String txnId, Future<void> sendToDevice(
Map<String, Map<String, Map<String, dynamic>>> messages) async { String eventType,
String txnId,
Map<String, Map<String, Map<String, dynamic>>> messages,
) async {
await request( await request(
RequestType.PUT, RequestType.PUT,
'/client/r0/sendToDevice/${Uri.encodeComponent(eventType)}/${Uri.encodeComponent(txnId)}', '/client/r0/sendToDevice/${Uri.encodeComponent(eventType)}/${Uri.encodeComponent(txnId)}',

View file

@ -22,7 +22,6 @@ import 'dart:core';
import 'package:famedlysdk/encryption.dart'; import 'package:famedlysdk/encryption.dart';
import 'package:famedlysdk/famedlysdk.dart'; import 'package:famedlysdk/famedlysdk.dart';
import 'package:famedlysdk/matrix_api.dart';
import 'package:famedlysdk/src/room.dart'; import 'package:famedlysdk/src/room.dart';
import 'package:famedlysdk/src/utils/device_keys_list.dart'; import 'package:famedlysdk/src/utils/device_keys_list.dart';
import 'package:famedlysdk/src/utils/logs.dart'; import 'package:famedlysdk/src/utils/logs.dart';
@ -45,7 +44,7 @@ enum LoginState { logged, loggedOut }
/// Represents a Matrix client to communicate with a /// Represents a Matrix client to communicate with a
/// [Matrix](https://matrix.org) homeserver and is the entry point for this /// [Matrix](https://matrix.org) homeserver and is the entry point for this
/// SDK. /// SDK.
class Client { class Client extends MatrixApi {
int _id; int _id;
int get id => _id; int get id => _id;
@ -53,7 +52,8 @@ class Client {
bool enableE2eeRecovery; bool enableE2eeRecovery;
MatrixApi api; @deprecated
MatrixApi get api => this;
Encryption encryption; Encryption encryption;
@ -103,7 +103,7 @@ class Client {
EventTypes.RoomCanonicalAlias, EventTypes.RoomCanonicalAlias,
EventTypes.RoomTombstone, EventTypes.RoomTombstone,
]); ]);
api = MatrixApi(httpClient: httpClient); this.httpClient = httpClient;
} }
/// The required name for this client. /// The required name for this client.
@ -125,7 +125,7 @@ class Client {
String _deviceName; String _deviceName;
/// Returns the current login state. /// Returns the current login state.
bool isLogged() => api.accessToken != null; bool isLogged() => accessToken != null;
/// A list of all rooms the user is participating or invited. /// A list of all rooms the user is participating or invited.
List<Room> get rooms => _rooms; List<Room> get rooms => _rooms;
@ -160,21 +160,6 @@ class Client {
int _transactionCounter = 0; int _transactionCounter = 0;
@Deprecated('Use [api.request()] instead')
Future<Map<String, dynamic>> jsonRequest(
{RequestType type,
String action,
dynamic data = '',
int timeout,
String contentType = 'application/json'}) =>
api.request(
type,
action,
data: data,
timeout: timeout,
contentType: contentType,
);
String generateUniqueTransactionId() { String generateUniqueTransactionId() {
_transactionCounter++; _transactionCounter++;
return '${clientName}-${_transactionCounter}-${DateTime.now().millisecondsSinceEpoch}'; return '${clientName}-${_transactionCounter}-${DateTime.now().millisecondsSinceEpoch}';
@ -243,7 +228,7 @@ class Client {
Future<bool> checkServer(dynamic serverUrl) async { Future<bool> checkServer(dynamic serverUrl) async {
try { try {
if (serverUrl is Uri) { if (serverUrl is Uri) {
api.homeserver = serverUrl; homeserver = serverUrl;
} else { } else {
// URLs allow to have whitespace surrounding them, see https://www.w3.org/TR/2011/WD-html5-20110525/urls.html // URLs allow to have whitespace surrounding them, see https://www.w3.org/TR/2011/WD-html5-20110525/urls.html
// As we want to strip a trailing slash, though, we have to trim the url ourself // As we want to strip a trailing slash, though, we have to trim the url ourself
@ -253,9 +238,9 @@ class Client {
if (serverUrl.endsWith('/')) { if (serverUrl.endsWith('/')) {
serverUrl = serverUrl.substring(0, serverUrl.length - 1); serverUrl = serverUrl.substring(0, serverUrl.length - 1);
} }
api.homeserver = Uri.parse(serverUrl); homeserver = Uri.parse(serverUrl);
} }
final versions = await api.requestSupportedVersions(); final versions = await requestSupportedVersions();
for (var i = 0; i < versions.versions.length; i++) { for (var i = 0; i < versions.versions.length; i++) {
if (versions.versions[i] == 'r0.5.0' || if (versions.versions[i] == 'r0.5.0' ||
@ -266,7 +251,7 @@ class Client {
} }
} }
final loginTypes = await api.requestLoginTypes(); final loginTypes = await requestLoginTypes();
if (loginTypes.flows.indexWhere((f) => f.type == 'm.login.password') == if (loginTypes.flows.indexWhere((f) => f.type == 'm.login.password') ==
-1) { -1) {
return false; return false;
@ -274,7 +259,7 @@ class Client {
return true; return true;
} catch (_) { } catch (_) {
api.homeserver = null; homeserver = null;
rethrow; rethrow;
} }
} }
@ -282,16 +267,17 @@ class Client {
/// Checks to see if a username is available, and valid, for the server. /// Checks to see if a username is available, and valid, for the server.
/// Returns the fully-qualified Matrix user ID (MXID) that has been registered. /// Returns the fully-qualified Matrix user ID (MXID) that has been registered.
/// You have to call [checkServer] first to set a homeserver. /// You have to call [checkServer] first to set a homeserver.
Future<void> register({ @override
String kind, Future<LoginResponse> register({
String username, String username,
String password, String password,
Map<String, dynamic> auth,
String deviceId, String deviceId,
String initialDeviceDisplayName, String initialDeviceDisplayName,
bool inhibitLogin, bool inhibitLogin,
Map<String, dynamic> auth,
String kind,
}) async { }) async {
final response = await api.register( final response = await super.register(
username: username, username: username,
password: password, password: password,
auth: auth, auth: auth,
@ -309,66 +295,62 @@ class Client {
await connect( await connect(
newToken: response.accessToken, newToken: response.accessToken,
newUserID: response.userId, newUserID: response.userId,
newHomeserver: api.homeserver, newHomeserver: homeserver,
newDeviceName: initialDeviceDisplayName ?? '', newDeviceName: initialDeviceDisplayName ?? '',
newDeviceID: response.deviceId); newDeviceID: response.deviceId);
return; return response;
} }
/// Handles the login and allows the client to call all APIs which require /// Handles the login and allows the client to call all APIs which require
/// authentication. Returns false if the login was not successful. Throws /// authentication. Returns false if the login was not successful. Throws
/// MatrixException if login was not successful. /// MatrixException if login was not successful.
/// You have to call [checkServer] first to set a homeserver. /// You have to call [checkServer] first to set a homeserver.
Future<bool> login( @override
String username, Future<LoginResponse> login({
String password, { String type = 'm.login.password',
String initialDeviceDisplayName, String userIdentifierType = 'm.id.user',
String user,
String medium,
String address,
String password,
String token,
String deviceId, String deviceId,
String initialDeviceDisplayName,
}) async { }) async {
var data = <String, dynamic>{ final loginResp = await super.login(
'type': 'm.login.password', type: type,
'user': username, userIdentifierType: userIdentifierType,
'identifier': { user: user,
'type': 'm.id.user',
'user': username,
},
'password': password,
};
if (deviceId != null) data['device_id'] = deviceId;
if (initialDeviceDisplayName != null) {
data['initial_device_display_name'] = initialDeviceDisplayName;
}
final loginResp = await api.login(
type: 'm.login.password',
userIdentifierType: 'm.id.user',
user: username,
password: password, password: password,
deviceId: deviceId, deviceId: deviceId,
initialDeviceDisplayName: initialDeviceDisplayName, initialDeviceDisplayName: initialDeviceDisplayName,
medium: medium,
address: address,
token: token,
); );
// Connect if there is an access token in the response. // Connect if there is an access token in the response.
if (loginResp.accessToken == null || if (loginResp.accessToken == null ||
loginResp.deviceId == null || loginResp.deviceId == null ||
loginResp.userId == null) { loginResp.userId == null) {
throw 'Registered but token, device ID or user ID is null.'; throw Exception('Registered but token, device ID or user ID is null.');
} }
await connect( await connect(
newToken: loginResp.accessToken, newToken: loginResp.accessToken,
newUserID: loginResp.userId, newUserID: loginResp.userId,
newHomeserver: api.homeserver, newHomeserver: homeserver,
newDeviceName: initialDeviceDisplayName ?? '', newDeviceName: initialDeviceDisplayName ?? '',
newDeviceID: loginResp.deviceId, newDeviceID: loginResp.deviceId,
); );
return true; return loginResp;
} }
/// Sends a logout command to the homeserver and clears all local data, /// Sends a logout command to the homeserver and clears all local data,
/// including all persistent data from the store. /// including all persistent data from the store.
@override
Future<void> logout() async { Future<void> logout() async {
try { try {
await api.logout(); await super.logout();
} catch (e, s) { } catch (e, s) {
Logs.error(e, s); Logs.error(e, s);
rethrow; rethrow;
@ -421,19 +403,19 @@ class Client {
if (cache && _profileCache.containsKey(userId)) { if (cache && _profileCache.containsKey(userId)) {
return _profileCache[userId]; return _profileCache[userId];
} }
final profile = await api.requestProfile(userId); final profile = await requestProfile(userId);
_profileCache[userId] = profile; _profileCache[userId] = profile;
return profile; return profile;
} }
Future<List<Room>> get archive async { Future<List<Room>> get archive async {
var archiveList = <Room>[]; var archiveList = <Room>[];
final sync = await api.sync( final syncResp = await sync(
filter: '{"room":{"include_leave":true,"timeline":{"limit":10}}}', filter: '{"room":{"include_leave":true,"timeline":{"limit":10}}}',
timeout: 0, timeout: 0,
); );
if (sync.rooms.leave is Map<String, dynamic>) { if (syncResp.rooms.leave is Map<String, dynamic>) {
for (var entry in sync.rooms.leave.entries) { for (var entry in syncResp.rooms.leave.entries) {
final id = entry.key; final id = entry.key;
final room = entry.value; final room = entry.value;
var leftRoom = Room( var leftRoom = Room(
@ -460,14 +442,10 @@ class Client {
return archiveList; return archiveList;
} }
/// Changes the user's displayname.
Future<void> setDisplayname(String displayname) =>
api.setDisplayname(userID, displayname);
/// Uploads a new user avatar for this user. /// Uploads a new user avatar for this user.
Future<void> setAvatar(MatrixFile file) async { Future<void> setAvatar(MatrixFile file) async {
final uploadResp = await api.upload(file.bytes, file.name); final uploadResp = await upload(file.bytes, file.name);
await api.setAvatarUrl(userID, Uri.parse(uploadResp)); await setAvatarUrl(userID, Uri.parse(uploadResp));
return; return;
} }
@ -550,10 +528,6 @@ class Client {
final StreamController<KeyVerification> onKeyVerificationRequest = final StreamController<KeyVerification> onKeyVerificationRequest =
StreamController.broadcast(); StreamController.broadcast();
/// Matrix synchronisation is done with https long polling. This needs a
/// timeout which is usually 30 seconds.
int syncTimeoutSec = 30;
/// How long should the app wait until it retrys the synchronisation after /// How long should the app wait until it retrys the synchronisation after
/// an error? /// an error?
int syncErrorTimeoutSec = 3; int syncErrorTimeoutSec = 3;
@ -575,7 +549,7 @@ class Client {
/// "type": "m.login.password", /// "type": "m.login.password",
/// "user": "test", /// "user": "test",
/// "password": "1234", /// "password": "1234",
/// "initial_device_display_name": "Fluffy Matrix Client" /// "initial_device_display_name": "Matrix Client"
/// }); /// });
/// ``` /// ```
/// ///
@ -604,8 +578,8 @@ class Client {
final account = await database.getClient(clientName); final account = await database.getClient(clientName);
if (account != null) { if (account != null) {
_id = account.clientId; _id = account.clientId;
api.homeserver = Uri.parse(account.homeserverUrl); homeserver = Uri.parse(account.homeserverUrl);
api.accessToken = account.token; accessToken = account.token;
_userID = account.userId; _userID = account.userId;
_deviceID = account.deviceId; _deviceID = account.deviceId;
_deviceName = account.deviceName; _deviceName = account.deviceName;
@ -613,15 +587,15 @@ class Client {
olmAccount = account.olmAccount; olmAccount = account.olmAccount;
} }
} }
api.accessToken = newToken ?? api.accessToken; accessToken = newToken ?? accessToken;
api.homeserver = newHomeserver ?? api.homeserver; homeserver = newHomeserver ?? homeserver;
_userID = newUserID ?? _userID; _userID = newUserID ?? _userID;
_deviceID = newDeviceID ?? _deviceID; _deviceID = newDeviceID ?? _deviceID;
_deviceName = newDeviceName ?? _deviceName; _deviceName = newDeviceName ?? _deviceName;
prevBatch = newPrevBatch ?? prevBatch; prevBatch = newPrevBatch ?? prevBatch;
olmAccount = newOlmAccount ?? olmAccount; olmAccount = newOlmAccount ?? olmAccount;
if (api.accessToken == null || api.homeserver == null || _userID == null) { if (accessToken == null || homeserver == null || _userID == null) {
// we aren't logged in // we aren't logged in
encryption?.dispose(); encryption?.dispose();
encryption = null; encryption = null;
@ -636,8 +610,8 @@ class Client {
if (database != null) { if (database != null) {
if (id != null) { if (id != null) {
await database.updateClient( await database.updateClient(
api.homeserver.toString(), homeserver.toString(),
api.accessToken, accessToken,
_userID, _userID,
_deviceID, _deviceID,
_deviceName, _deviceName,
@ -648,8 +622,8 @@ class Client {
} else { } else {
_id = await database.insertClient( _id = await database.insertClient(
clientName, clientName,
api.homeserver.toString(), homeserver.toString(),
api.accessToken, accessToken,
_userID, _userID,
_deviceID, _deviceID,
_deviceName, _deviceName,
@ -666,7 +640,7 @@ class Client {
onLoginStateChanged.add(LoginState.logged); onLoginStateChanged.add(LoginState.logged);
Logs.success( Logs.success(
'Successfully connected as ${userID.localpart} with ${api.homeserver.toString()}', 'Successfully connected as ${userID.localpart} with ${homeserver.toString()}',
); );
return _sync(); return _sync();
@ -680,8 +654,8 @@ class Client {
/// Resets all settings and stops the synchronisation. /// Resets all settings and stops the synchronisation.
void clear() { void clear() {
database?.clear(id); database?.clear(id);
_id = api.accessToken = _id = accessToken =
api.homeserver = _userID = _deviceID = _deviceName = prevBatch = null; homeserver = _userID = _deviceID = _deviceName = prevBatch = null;
_rooms = []; _rooms = [];
encryption?.dispose(); encryption?.dispose();
encryption = null; encryption = null;
@ -694,13 +668,11 @@ class Client {
Future<void> _sync() async { Future<void> _sync() async {
if (isLogged() == false || _disposed) return; if (isLogged() == false || _disposed) return;
try { try {
_syncRequest = api _syncRequest = sync(
.sync(
filter: syncFilters, filter: syncFilters,
since: prevBatch, since: prevBatch,
timeout: prevBatch != null ? 30000 : null, timeout: prevBatch != null ? 30000 : null,
) ).catchError((e) {
.catchError((e) {
_lastSyncError = e; _lastSyncError = e;
return null; return null;
}); });
@ -1197,8 +1169,7 @@ class Client {
if (outdatedLists.isNotEmpty) { if (outdatedLists.isNotEmpty) {
// Request the missing device key lists from the server. // Request the missing device key lists from the server.
final response = final response = await requestDeviceKeys(outdatedLists, timeout: 10000);
await api.requestDeviceKeys(outdatedLists, timeout: 10000);
for (final rawDeviceKeyListEntry in response.deviceKeys.entries) { for (final rawDeviceKeyListEntry in response.deviceKeys.entries) {
final userId = rawDeviceKeyListEntry.key; final userId = rawDeviceKeyListEntry.key;
@ -1347,17 +1318,35 @@ class Client {
} }
} }
/// Send an (unencrypted) to device [message] of a specific [eventType] to all
/// devices of a set of [users].
Future<void> sendToDevicesOfUserIds(
Set<String> users,
String eventType,
Map<String, dynamic> message, {
String messageId,
}) async {
// Send with send-to-device messaging
var data = <String, Map<String, Map<String, dynamic>>>{};
for (var user in users) {
data[user] = {};
data[user]['*'] = message;
}
await sendToDevice(
eventType, messageId ?? generateUniqueTransactionId(), data);
return;
}
/// Sends an encrypted [message] of this [type] to these [deviceKeys]. To send /// Sends an encrypted [message] of this [type] to these [deviceKeys]. To send
/// the request to all devices of the current user, pass an empty list to [deviceKeys]. /// the request to all devices of the current user, pass an empty list to [deviceKeys].
Future<void> sendToDevice( Future<void> sendToDeviceEncrypted(
List<DeviceKeys> deviceKeys, List<DeviceKeys> deviceKeys,
String type, String eventType,
Map<String, dynamic> message, { Map<String, dynamic> message, {
bool encrypted = true, String messageId,
List<User> toUsers,
bool onlyVerified = false, bool onlyVerified = false,
}) async { }) async {
if (encrypted && !encryptionEnabled) return; if (!encryptionEnabled) return;
// Don't send this message to blocked devices, and if specified onlyVerified // Don't send this message to blocked devices, and if specified onlyVerified
// then only send it to verified devices // then only send it to verified devices
if (deviceKeys.isNotEmpty) { if (deviceKeys.isNotEmpty) {
@ -1368,36 +1357,13 @@ class Client {
if (deviceKeys.isEmpty) return; if (deviceKeys.isEmpty) return;
} }
var sendToDeviceMessage = message;
// Send with send-to-device messaging // Send with send-to-device messaging
var data = <String, Map<String, Map<String, dynamic>>>{}; var data = <String, Map<String, Map<String, dynamic>>>{};
if (deviceKeys.isEmpty) {
if (toUsers == null) {
data[userID] = {};
data[userID]['*'] = sendToDeviceMessage;
} else {
for (var user in toUsers) {
data[user.id] = {};
data[user.id]['*'] = sendToDeviceMessage;
}
}
} else {
if (encrypted) {
data = data =
await encryption.encryptToDeviceMessage(deviceKeys, type, message); await encryption.encryptToDeviceMessage(deviceKeys, eventType, message);
} else { eventType = EventTypes.Encrypted;
for (final device in deviceKeys) { await sendToDevice(
if (!data.containsKey(device.userId)) { eventType, messageId ?? generateUniqueTransactionId(), data);
data[device.userId] = {};
}
data[device.userId][device.deviceId] = sendToDeviceMessage;
}
}
}
if (encrypted) type = EventTypes.Encrypted;
final messageID = generateUniqueTransactionId();
await api.sendToDevice(type, messageID, data);
} }
/// Whether all push notifications are muted using the [.m.rule.master] /// Whether all push notifications are muted using the [.m.rule.master]
@ -1422,7 +1388,7 @@ class Client {
} }
Future<void> setMuteAllPushNotifications(bool muted) async { Future<void> setMuteAllPushNotifications(bool muted) async {
await api.enablePushRule( await enablePushRule(
'global', 'global',
PushRuleKind.override, PushRuleKind.override,
'.m.rule.master', '.m.rule.master',
@ -1432,6 +1398,7 @@ class Client {
} }
/// Changes the password. You should either set oldPasswort or another authentication flow. /// Changes the password. You should either set oldPasswort or another authentication flow.
@override
Future<void> changePassword(String newPassword, Future<void> changePassword(String newPassword,
{String oldPassword, Map<String, dynamic> auth}) async { {String oldPassword, Map<String, dynamic> auth}) async {
try { try {
@ -1442,7 +1409,7 @@ class Client {
'password': oldPassword, 'password': oldPassword,
}; };
} }
await api.changePassword(newPassword, auth: auth); await super.changePassword(newPassword, auth: auth);
} on MatrixException catch (matrixException) { } on MatrixException catch (matrixException) {
if (!matrixException.requireAdditionalAuthentication) { if (!matrixException.requireAdditionalAuthentication) {
rethrow; rethrow;

View file

@ -374,21 +374,21 @@ class Room {
/// Call the Matrix API to change the name of this room. Returns the event ID of the /// Call the Matrix API to change the name of this room. Returns the event ID of the
/// new m.room.name event. /// new m.room.name event.
Future<String> setName(String newName) => client.api.sendState( Future<String> setName(String newName) => client.sendState(
id, id,
EventTypes.RoomName, EventTypes.RoomName,
{'name': newName}, {'name': newName},
); );
/// Call the Matrix API to change the topic of this room. /// Call the Matrix API to change the topic of this room.
Future<String> setDescription(String newName) => client.api.sendState( Future<String> setDescription(String newName) => client.sendState(
id, id,
EventTypes.RoomTopic, EventTypes.RoomTopic,
{'topic': newName}, {'topic': newName},
); );
/// Add a tag to the room. /// Add a tag to the room.
Future<void> addTag(String tag, {double order}) => client.api.addRoomTag( Future<void> addTag(String tag, {double order}) => client.addRoomTag(
client.userID, client.userID,
id, id,
tag, tag,
@ -396,7 +396,7 @@ class Room {
); );
/// Removes a tag from the room. /// Removes a tag from the room.
Future<void> removeTag(String tag) => client.api.removeRoomTag( Future<void> removeTag(String tag) => client.removeRoomTag(
client.userID, client.userID,
id, id,
tag, tag,
@ -423,7 +423,7 @@ class Room {
/// Call the Matrix API to change the pinned events of this room. /// Call the Matrix API to change the pinned events of this room.
Future<String> setPinnedEvents(List<String> pinnedEventIds) => Future<String> setPinnedEvents(List<String> pinnedEventIds) =>
client.api.sendState( client.sendState(
id, id,
EventTypes.RoomPinnedEvents, EventTypes.RoomPinnedEvents,
{'pinned': pinnedEventIds}, {'pinned': pinnedEventIds},
@ -565,13 +565,13 @@ class Room {
uploadThumbnail = encryptedThumbnail.toMatrixFile(); uploadThumbnail = encryptedThumbnail.toMatrixFile();
} }
} }
final uploadResp = await client.api.upload( final uploadResp = await client.upload(
uploadFile.bytes, uploadFile.bytes,
uploadFile.name, uploadFile.name,
contentType: uploadFile.mimeType, contentType: uploadFile.mimeType,
); );
final thumbnailUploadResp = uploadThumbnail != null final thumbnailUploadResp = uploadThumbnail != null
? await client.api.upload( ? await client.upload(
uploadThumbnail.bytes, uploadThumbnail.bytes,
uploadThumbnail.name, uploadThumbnail.name,
contentType: uploadThumbnail.mimeType, contentType: uploadThumbnail.mimeType,
@ -705,7 +705,7 @@ class Room {
? await client.encryption ? await client.encryption
.encryptGroupMessagePayload(id, content, type: type) .encryptGroupMessagePayload(id, content, type: type)
: content; : content;
final res = await client.api.sendMessage( final res = await client.sendMessage(
id, id,
sendType, sendType,
messageID, messageID,
@ -731,7 +731,7 @@ class Room {
/// automatically be set. /// automatically be set.
Future<void> join() async { Future<void> join() async {
try { try {
await client.api.joinRoom(id); await client.joinRoom(id);
final invitation = getState(EventTypes.RoomMember, client.userID); final invitation = getState(EventTypes.RoomMember, client.userID);
if (invitation != null && if (invitation != null &&
invitation.content['is_direct'] is bool && invitation.content['is_direct'] is bool &&
@ -757,25 +757,25 @@ class Room {
/// chat, this will be removed too. /// chat, this will be removed too.
Future<void> leave() async { Future<void> leave() async {
if (directChatMatrixID != '') await removeFromDirectChat(); if (directChatMatrixID != '') await removeFromDirectChat();
await client.api.leaveRoom(id); await client.leaveRoom(id);
return; return;
} }
/// Call the Matrix API to forget this room if you already left it. /// Call the Matrix API to forget this room if you already left it.
Future<void> forget() async { Future<void> forget() async {
await client.database?.forgetRoom(client.id, id); await client.database?.forgetRoom(client.id, id);
await client.api.forgetRoom(id); await client.forgetRoom(id);
return; return;
} }
/// Call the Matrix API to kick a user from this room. /// Call the Matrix API to kick a user from this room.
Future<void> kick(String userID) => client.api.kickFromRoom(id, userID); Future<void> kick(String userID) => client.kickFromRoom(id, userID);
/// Call the Matrix API to ban a user from this room. /// Call the Matrix API to ban a user from this room.
Future<void> ban(String userID) => client.api.banFromRoom(id, userID); Future<void> ban(String userID) => client.banFromRoom(id, userID);
/// Call the Matrix API to unban a banned user from this room. /// Call the Matrix API to unban a banned user from this room.
Future<void> unban(String userID) => client.api.unbanInRoom(id, userID); Future<void> unban(String userID) => client.unbanInRoom(id, userID);
/// Set the power level of the user with the [userID] to the value [power]. /// Set the power level of the user with the [userID] to the value [power].
/// Returns the event ID of the new state event. If there is no known /// Returns the event ID of the new state event. If there is no known
@ -787,7 +787,7 @@ class Room {
if (powerMap['users'] == null) powerMap['users'] = {}; if (powerMap['users'] == null) powerMap['users'] = {};
powerMap['users'][userID] = power; powerMap['users'][userID] = power;
return await client.api.sendState( return await client.sendState(
id, id,
EventTypes.RoomPowerLevels, EventTypes.RoomPowerLevels,
powerMap, powerMap,
@ -795,14 +795,14 @@ class Room {
} }
/// Call the Matrix API to invite a user to this room. /// Call the Matrix API to invite a user to this room.
Future<void> invite(String userID) => client.api.inviteToRoom(id, userID); Future<void> invite(String userID) => client.inviteToRoom(id, userID);
/// Request more previous events from the server. [historyCount] defines how much events should /// Request more previous events from the server. [historyCount] defines how much events should
/// be received maximum. When the request is answered, [onHistoryReceived] will be triggered **before** /// be received maximum. When the request is answered, [onHistoryReceived] will be triggered **before**
/// the historical events will be published in the onEvent stream. /// the historical events will be published in the onEvent stream.
Future<void> requestHistory( Future<void> requestHistory(
{int historyCount = DefaultHistoryCount, onHistoryReceived}) async { {int historyCount = DefaultHistoryCount, onHistoryReceived}) async {
final resp = await client.api.requestMessages( final resp = await client.requestMessages(
id, id,
prev_batch, prev_batch,
Direction.b, Direction.b,
@ -853,7 +853,7 @@ class Room {
directChats[userID] = [id]; directChats[userID] = [id];
} }
await client.api.setAccountData( await client.setAccountData(
client.userID, client.userID,
'm.direct', 'm.direct',
directChats, directChats,
@ -871,7 +871,7 @@ class Room {
return; return;
} // Nothing to do here } // Nothing to do here
await client.api.setRoomAccountData( await client.setRoomAccountData(
client.userID, client.userID,
id, id,
'm.direct', 'm.direct',
@ -884,7 +884,7 @@ class Room {
Future<void> sendReadReceipt(String eventID) async { Future<void> sendReadReceipt(String eventID) async {
notificationCount = 0; notificationCount = 0;
await client.database?.resetNotificationCount(client.id, id); await client.database?.resetNotificationCount(client.id, id);
await client.api.sendReadMarker( await client.sendReadMarker(
id, id,
eventID, eventID,
readReceiptLocationEventId: eventID, readReceiptLocationEventId: eventID,
@ -1017,7 +1017,7 @@ class Room {
} }
} }
if (participantListComplete) return getParticipants(); if (participantListComplete) return getParticipants();
final matrixEvents = await client.api.requestMembers(id); final matrixEvents = await client.requestMembers(id);
final users = final users =
matrixEvents.map((e) => Event.fromMatrixEvent(e, this).asUser).toList(); matrixEvents.map((e) => Event.fromMatrixEvent(e, this).asUser).toList();
for (final user in users) { for (final user in users) {
@ -1080,7 +1080,7 @@ class Room {
if (mxID == null || !_requestingMatrixIds.add(mxID)) return null; if (mxID == null || !_requestingMatrixIds.add(mxID)) return null;
Map<String, dynamic> resp; Map<String, dynamic> resp;
try { try {
resp = await client.api.requestStateContent( resp = await client.requestStateContent(
id, id,
EventTypes.RoomMember, EventTypes.RoomMember,
mxID, mxID,
@ -1093,7 +1093,7 @@ class Room {
} }
if (resp == null && requestProfile) { if (resp == null && requestProfile) {
try { try {
final profile = await client.api.requestProfile(mxID); final profile = await client.requestProfile(mxID);
resp = { resp = {
'displayname': profile.displayname, 'displayname': profile.displayname,
'avatar_url': profile.avatarUrl, 'avatar_url': profile.avatarUrl,
@ -1135,7 +1135,7 @@ class Room {
/// Searches for the event on the server. Returns null if not found. /// Searches for the event on the server. Returns null if not found.
Future<Event> getEventById(String eventID) async { Future<Event> getEventById(String eventID) async {
final matrixEvent = await client.api.requestEvent(id, eventID); final matrixEvent = await client.requestEvent(id, eventID);
return Event.fromMatrixEvent(matrixEvent, this); return Event.fromMatrixEvent(matrixEvent, this);
} }
@ -1169,8 +1169,8 @@ class Room {
/// Uploads a new user avatar for this room. Returns the event ID of the new /// Uploads a new user avatar for this room. Returns the event ID of the new
/// m.room.avatar event. /// m.room.avatar event.
Future<String> setAvatar(MatrixFile file) async { Future<String> setAvatar(MatrixFile file) async {
final uploadResp = await client.api.upload(file.bytes, file.name); final uploadResp = await client.upload(file.bytes, file.name);
return await client.api.sendState( return await client.sendState(
id, id,
EventTypes.RoomAvatar, EventTypes.RoomAvatar,
{'url': uploadResp}, {'url': uploadResp},
@ -1267,23 +1267,23 @@ class Room {
// All push notifications should be sent to the user // All push notifications should be sent to the user
case PushRuleState.notify: case PushRuleState.notify:
if (pushRuleState == PushRuleState.dont_notify) { if (pushRuleState == PushRuleState.dont_notify) {
await client.api.deletePushRule('global', PushRuleKind.override, id); await client.deletePushRule('global', PushRuleKind.override, id);
} else if (pushRuleState == PushRuleState.mentions_only) { } else if (pushRuleState == PushRuleState.mentions_only) {
await client.api.deletePushRule('global', PushRuleKind.room, id); await client.deletePushRule('global', PushRuleKind.room, id);
} }
break; break;
// Only when someone mentions the user, a push notification should be sent // Only when someone mentions the user, a push notification should be sent
case PushRuleState.mentions_only: case PushRuleState.mentions_only:
if (pushRuleState == PushRuleState.dont_notify) { if (pushRuleState == PushRuleState.dont_notify) {
await client.api.deletePushRule('global', PushRuleKind.override, id); await client.deletePushRule('global', PushRuleKind.override, id);
await client.api.setPushRule( await client.setPushRule(
'global', 'global',
PushRuleKind.room, PushRuleKind.room,
id, id,
[PushRuleAction.dont_notify], [PushRuleAction.dont_notify],
); );
} else if (pushRuleState == PushRuleState.notify) { } else if (pushRuleState == PushRuleState.notify) {
await client.api.setPushRule( await client.setPushRule(
'global', 'global',
PushRuleKind.room, PushRuleKind.room,
id, id,
@ -1294,9 +1294,9 @@ class Room {
// No push notification should be ever sent for this room. // No push notification should be ever sent for this room.
case PushRuleState.dont_notify: case PushRuleState.dont_notify:
if (pushRuleState == PushRuleState.mentions_only) { if (pushRuleState == PushRuleState.mentions_only) {
await client.api.deletePushRule('global', PushRuleKind.room, id); await client.deletePushRule('global', PushRuleKind.room, id);
} }
await client.api.setPushRule( await client.setPushRule(
'global', 'global',
PushRuleKind.override, PushRuleKind.override,
id, id,
@ -1322,7 +1322,7 @@ class Room {
} }
var data = <String, dynamic>{}; var data = <String, dynamic>{};
if (reason != null) data['reason'] = reason; if (reason != null) data['reason'] = reason;
return await client.api.redact( return await client.redact(
id, id,
eventId, eventId,
messageID, messageID,
@ -1335,7 +1335,7 @@ class Room {
'typing': isTyping, 'typing': isTyping,
}; };
if (timeout != null) data['timeout'] = timeout; if (timeout != null) data['timeout'] = timeout;
return client.api.sendTypingNotification(client.userID, id, isTyping); return client.sendTypingNotification(client.userID, id, isTyping);
} }
/// This is sent by the caller when they wish to establish a call. /// This is sent by the caller when they wish to establish a call.
@ -1349,7 +1349,7 @@ class Room {
{String type = 'offer', int version = 0, String txid}) async { {String type = 'offer', int version = 0, String txid}) async {
txid ??= 'txid${DateTime.now().millisecondsSinceEpoch}'; txid ??= 'txid${DateTime.now().millisecondsSinceEpoch}';
return await client.api.sendMessage( return await client.sendMessage(
id, id,
EventTypes.CallInvite, EventTypes.CallInvite,
txid, txid,
@ -1387,7 +1387,7 @@ class Room {
String txid, String txid,
}) async { }) async {
txid ??= 'txid${DateTime.now().millisecondsSinceEpoch}'; txid ??= 'txid${DateTime.now().millisecondsSinceEpoch}';
return await client.api.sendMessage( return await client.sendMessage(
id, id,
EventTypes.CallCandidates, EventTypes.CallCandidates,
txid, txid,
@ -1407,7 +1407,7 @@ class Room {
Future<String> answerCall(String callId, String sdp, Future<String> answerCall(String callId, String sdp,
{String type = 'answer', int version = 0, String txid}) async { {String type = 'answer', int version = 0, String txid}) async {
txid ??= 'txid${DateTime.now().millisecondsSinceEpoch}'; txid ??= 'txid${DateTime.now().millisecondsSinceEpoch}';
return await client.api.sendMessage( return await client.sendMessage(
id, id,
EventTypes.CallAnswer, EventTypes.CallAnswer,
txid, txid,
@ -1425,7 +1425,7 @@ class Room {
Future<String> hangupCall(String callId, Future<String> hangupCall(String callId,
{int version = 0, String txid}) async { {int version = 0, String txid}) async {
txid ??= 'txid${DateTime.now().millisecondsSinceEpoch}'; txid ??= 'txid${DateTime.now().millisecondsSinceEpoch}';
return await client.api.sendMessage( return await client.sendMessage(
id, id,
EventTypes.CallHangup, EventTypes.CallHangup,
txid, txid,
@ -1461,7 +1461,7 @@ class Room {
/// Changes the join rules. You should check first if the user is able to change it. /// Changes the join rules. You should check first if the user is able to change it.
Future<void> setJoinRules(JoinRules joinRules) async { Future<void> setJoinRules(JoinRules joinRules) async {
await client.api.sendState( await client.sendState(
id, id,
EventTypes.RoomJoinRules, EventTypes.RoomJoinRules,
{ {
@ -1486,7 +1486,7 @@ class Room {
/// Changes the guest access. You should check first if the user is able to change it. /// Changes the guest access. You should check first if the user is able to change it.
Future<void> setGuestAccess(GuestAccess guestAccess) async { Future<void> setGuestAccess(GuestAccess guestAccess) async {
await client.api.sendState( await client.sendState(
id, id,
EventTypes.GuestAccess, EventTypes.GuestAccess,
{ {
@ -1512,7 +1512,7 @@ class Room {
/// Changes the history visibility. You should check first if the user is able to change it. /// Changes the history visibility. You should check first if the user is able to change it.
Future<void> setHistoryVisibility(HistoryVisibility historyVisibility) async { Future<void> setHistoryVisibility(HistoryVisibility historyVisibility) async {
await client.api.sendState( await client.sendState(
id, id,
EventTypes.HistoryVisibility, EventTypes.HistoryVisibility,
{ {
@ -1539,7 +1539,7 @@ class Room {
Future<void> enableEncryption({int algorithmIndex = 0}) async { Future<void> enableEncryption({int algorithmIndex = 0}) async {
if (encrypted) throw ('Encryption is already enabled!'); if (encrypted) throw ('Encryption is already enabled!');
final algorithm = Client.supportedGroupEncryptionAlgorithms[algorithmIndex]; final algorithm = Client.supportedGroupEncryptionAlgorithms[algorithmIndex];
await client.api.sendState( await client.sendState(
id, id,
EventTypes.Encryption, EventTypes.Encryption,
{ {

View file

@ -146,7 +146,7 @@ class User extends Event {
if (roomID != null) return roomID; if (roomID != null) return roomID;
// Start a new direct chat // Start a new direct chat
final newRoomID = await room.client.api.createRoom( final newRoomID = await room.client.createRoom(
invite: [id], invite: [id],
isDirect: true, isDirect: true,
preset: CreateRoomPreset.trusted_private_chat, preset: CreateRoomPreset.trusted_private_chat,

View file

@ -22,8 +22,8 @@ import 'dart:core';
extension MxcUriExtension on Uri { extension MxcUriExtension on Uri {
/// Returns a download Link to this content. /// Returns a download Link to this content.
String getDownloadLink(Client matrix) => isScheme('mxc') String getDownloadLink(Client matrix) => isScheme('mxc')
? matrix.api.homeserver != null ? matrix.homeserver != null
? '${matrix.api.homeserver.toString()}/_matrix/media/r0/download/$host$path' ? '${matrix.homeserver.toString()}/_matrix/media/r0/download/$host$path'
: '' : ''
: toString(); : toString();
@ -36,8 +36,8 @@ extension MxcUriExtension on Uri {
final methodStr = method.toString().split('.').last; final methodStr = method.toString().split('.').last;
width = width.round(); width = width.round();
height = height.round(); height = height.round();
return matrix.api.homeserver != null return matrix.homeserver != null
? '${matrix.api.homeserver.toString()}/_matrix/media/r0/thumbnail/$host$path?width=$width&height=$height&method=$methodStr' ? '${matrix.homeserver.toString()}/_matrix/media/r0/thumbnail/$host$path?width=$width&height=$height&method=$methodStr'
: ''; : '';
} }
} }

View file

@ -46,7 +46,7 @@ void main() {
const fingerprintKey = 'gjL//fyaFHADt9KBADGag8g7F8Up78B/K1zXeiEPLJo'; const fingerprintKey = 'gjL//fyaFHADt9KBADGag8g7F8Up78B/K1zXeiEPLJo';
/// All Tests related to the Login /// All Tests related to the Login
group('FluffyMatrix', () { group('Client', () {
/// Check if all Elements get created /// Check if all Elements get created
matrix = Client('testclient', httpClient: FakeMatrixApi()); matrix = Client('testclient', httpClient: FakeMatrixApi());
@ -74,7 +74,7 @@ void main() {
accountDataCounter++; accountDataCounter++;
}); });
expect(matrix.api.homeserver, null); expect(matrix.homeserver, null);
try { try {
await matrix.checkServer('https://fakeserver.wrongaddress'); await matrix.checkServer('https://fakeserver.wrongaddress');
@ -82,17 +82,9 @@ void main() {
expect(exception != null, true); expect(exception != null, true);
} }
await matrix.checkServer('https://fakeserver.notexisting'); await matrix.checkServer('https://fakeserver.notexisting');
expect( expect(matrix.homeserver.toString(), 'https://fakeserver.notexisting');
matrix.api.homeserver.toString(), 'https://fakeserver.notexisting');
final resp = await matrix.api.login( final available = await matrix.usernameAvailable('testuser');
type: 'm.login.password',
user: 'test',
password: '1234',
initialDeviceDisplayName: 'Fluffy Matrix Client',
);
final available = await matrix.api.usernameAvailable('testuser');
expect(available, true); expect(available, true);
var loginStateFuture = matrix.onLoginStateChanged.stream.first; var loginStateFuture = matrix.onLoginStateChanged.stream.first;
@ -100,21 +92,16 @@ void main() {
var syncFuture = matrix.onSync.stream.first; var syncFuture = matrix.onSync.stream.first;
matrix.connect( matrix.connect(
newToken: resp.accessToken, newToken: 'abcd',
newUserID: resp.userId, newUserID: '@test:fakeServer.notExisting',
newHomeserver: matrix.api.homeserver, newHomeserver: matrix.homeserver,
newDeviceName: 'Text Matrix Client', newDeviceName: 'Text Matrix Client',
newDeviceID: resp.deviceId, newDeviceID: 'GHTYAJCE',
newOlmAccount: pickledOlmAccount, newOlmAccount: pickledOlmAccount,
); );
await Future.delayed(Duration(milliseconds: 50)); await Future.delayed(Duration(milliseconds: 50));
expect(matrix.api.accessToken == resp.accessToken, true);
expect(matrix.deviceName == 'Text Matrix Client', true);
expect(matrix.deviceID == resp.deviceId, true);
expect(matrix.userID == resp.userId, true);
var loginState = await loginStateFuture; var loginState = await loginStateFuture;
var firstSync = await firstSyncFuture; var firstSync = await firstSyncFuture;
var sync = await syncFuture; var sync = await syncFuture;
@ -208,14 +195,11 @@ void main() {
}); });
test('Logout', () async { test('Logout', () async {
await matrix.api.logout();
var loginStateFuture = matrix.onLoginStateChanged.stream.first; var loginStateFuture = matrix.onLoginStateChanged.stream.first;
await matrix.logout();
matrix.clear(); expect(matrix.accessToken == null, true);
expect(matrix.homeserver == null, true);
expect(matrix.api.accessToken == null, true);
expect(matrix.api.homeserver == null, true);
expect(matrix.userID == null, true); expect(matrix.userID == null, true);
expect(matrix.deviceID == null, true); expect(matrix.deviceID == null, true);
expect(matrix.deviceName == null, true); expect(matrix.deviceName == null, true);
@ -330,10 +314,10 @@ void main() {
final checkResp = final checkResp =
await matrix.checkServer('https://fakeServer.notExisting'); await matrix.checkServer('https://fakeServer.notExisting');
final loginResp = await matrix.login('test', '1234'); final loginResp = await matrix.login(user: 'test', password: '1234');
expect(checkResp, true); expect(checkResp, true);
expect(loginResp, true); expect(loginResp != null, true);
}); });
test('setAvatar', () async { test('setAvatar', () async {
@ -386,8 +370,8 @@ void main() {
} }
} }
}, matrix); }, matrix);
test('sendToDevice', () async { test('sendToDeviceEncrypted', () async {
await matrix.sendToDevice( await matrix.sendToDeviceEncrypted(
[deviceKeys], [deviceKeys],
'm.message', 'm.message',
{ {
@ -420,9 +404,9 @@ void main() {
await Future.delayed(Duration(milliseconds: 100)); await Future.delayed(Duration(milliseconds: 100));
expect(client2.isLogged(), true); expect(client2.isLogged(), true);
expect(client2.api.accessToken, client1.api.accessToken); expect(client2.accessToken, client1.accessToken);
expect(client2.userID, client1.userID); expect(client2.userID, client1.userID);
expect(client2.api.homeserver, client1.api.homeserver); expect(client2.homeserver, client1.homeserver);
expect(client2.deviceID, client1.deviceID); expect(client2.deviceID, client1.deviceID);
expect(client2.deviceName, client1.deviceName); expect(client2.deviceName, client1.deviceName);
if (client2.encryptionEnabled) { if (client2.encryptionEnabled) {

View file

@ -54,7 +54,7 @@ void main() {
otherClient.connect( otherClient.connect(
newToken: 'abc', newToken: 'abc',
newUserID: '@othertest:fakeServer.notExisting', newUserID: '@othertest:fakeServer.notExisting',
newHomeserver: otherClient.api.homeserver, newHomeserver: otherClient.homeserver,
newDeviceName: 'Text Matrix Client', newDeviceName: 'Text Matrix Client',
newDeviceID: 'FOXDEVICE', newDeviceID: 'FOXDEVICE',
newOlmAccount: otherPickledOlmAccount, newOlmAccount: otherPickledOlmAccount,

View file

@ -89,7 +89,7 @@ void main() {
client2.connect( client2.connect(
newToken: 'abc', newToken: 'abc',
newUserID: '@othertest:fakeServer.notExisting', newUserID: '@othertest:fakeServer.notExisting',
newHomeserver: client2.api.homeserver, newHomeserver: client2.homeserver,
newDeviceName: 'Text Matrix Client', newDeviceName: 'Text Matrix Client',
newDeviceID: 'FOXDEVICE', newDeviceID: 'FOXDEVICE',
newOlmAccount: otherPickledOlmAccount, newOlmAccount: otherPickledOlmAccount,

View file

@ -246,7 +246,7 @@ void main() {
test('sendAgain', () async { test('sendAgain', () async {
var matrix = Client('testclient', httpClient: FakeMatrixApi()); var matrix = Client('testclient', httpClient: FakeMatrixApi());
await matrix.checkServer('https://fakeServer.notExisting'); await matrix.checkServer('https://fakeServer.notExisting');
await matrix.login('test', '1234'); await matrix.login(user: 'test', password: '1234');
var event = Event.fromJson( var event = Event.fromJson(
jsonObj, Room(id: '!1234:example.com', client: matrix)); jsonObj, Room(id: '!1234:example.com', client: matrix));
@ -262,7 +262,7 @@ void main() {
test('requestKey', () async { test('requestKey', () async {
var matrix = Client('testclient', httpClient: FakeMatrixApi()); var matrix = Client('testclient', httpClient: FakeMatrixApi());
await matrix.checkServer('https://fakeServer.notExisting'); await matrix.checkServer('https://fakeServer.notExisting');
await matrix.login('test', '1234'); await matrix.login(user: 'test', password: '1234');
var event = Event.fromJson( var event = Event.fromJson(
jsonObj, Room(id: '!1234:example.com', client: matrix)); jsonObj, Room(id: '!1234:example.com', client: matrix));

View file

@ -32,18 +32,12 @@ Future<Client> getClient() async {
final client = Client('testclient', httpClient: FakeMatrixApi()); final client = Client('testclient', httpClient: FakeMatrixApi());
client.database = getDatabase(); client.database = getDatabase();
await client.checkServer('https://fakeServer.notExisting'); await client.checkServer('https://fakeServer.notExisting');
final resp = await client.api.login(
type: 'm.login.password',
user: 'test',
password: '1234',
initialDeviceDisplayName: 'Fluffy Matrix Client',
);
client.connect( client.connect(
newToken: resp.accessToken, newToken: 'abcd',
newUserID: resp.userId, newUserID: '@test:fakeServer.notExisting',
newHomeserver: client.api.homeserver, newHomeserver: client.homeserver,
newDeviceName: 'Text Matrix Client', newDeviceName: 'Text Matrix Client',
newDeviceID: resp.deviceId, newDeviceID: 'GHTYAJCE',
newOlmAccount: pickledOlmAccount, newOlmAccount: pickledOlmAccount,
); );
await Future.delayed(Duration(milliseconds: 10)); await Future.delayed(Duration(milliseconds: 10));

View file

@ -82,6 +82,10 @@ class FakeMatrixApi extends MockClient {
action.contains( action.contains(
'/client/r0/rooms/!1234%3AfakeServer.notExisting/send/')) { '/client/r0/rooms/!1234%3AfakeServer.notExisting/send/')) {
res = {'event_id': '\$event${FakeMatrixApi.eventCounter++}'}; res = {'event_id': '\$event${FakeMatrixApi.eventCounter++}'};
} else if (action.contains('/client/r0/sync')) {
res = {
'next_batch': DateTime.now().millisecondsSinceEpoch.toString
};
} else { } else {
res = { res = {
'errcode': 'M_UNRECOGNIZED', 'errcode': 'M_UNRECOGNIZED',

View file

@ -33,13 +33,13 @@ void main() {
expect(content.isScheme('mxc'), true); expect(content.isScheme('mxc'), true);
expect(content.getDownloadLink(client), expect(content.getDownloadLink(client),
'${client.api.homeserver.toString()}/_matrix/media/r0/download/exampleserver.abc/abcdefghijklmn'); '${client.homeserver.toString()}/_matrix/media/r0/download/exampleserver.abc/abcdefghijklmn');
expect(content.getThumbnail(client, width: 50, height: 50), expect(content.getThumbnail(client, width: 50, height: 50),
'${client.api.homeserver.toString()}/_matrix/media/r0/thumbnail/exampleserver.abc/abcdefghijklmn?width=50&height=50&method=crop'); '${client.homeserver.toString()}/_matrix/media/r0/thumbnail/exampleserver.abc/abcdefghijklmn?width=50&height=50&method=crop');
expect( expect(
content.getThumbnail(client, content.getThumbnail(client,
width: 50, height: 50, method: ThumbnailMethod.scale), width: 50, height: 50, method: ThumbnailMethod.scale),
'${client.api.homeserver.toString()}/_matrix/media/r0/thumbnail/exampleserver.abc/abcdefghijklmn?width=50&height=50&method=scale'); '${client.homeserver.toString()}/_matrix/media/r0/thumbnail/exampleserver.abc/abcdefghijklmn?width=50&height=50&method=scale');
}); });
}); });
} }

View file

@ -102,7 +102,7 @@ void main() {
}); });
test('startDirectChat', () async { test('startDirectChat', () async {
await client.checkServer('https://fakeserver.notexisting'); await client.checkServer('https://fakeserver.notexisting');
await client.login('test', '1234'); await client.login(user: 'test', password: '1234');
await user1.startDirectChat(); await user1.startDirectChat();
}); });
test('getPresence', () async { test('getPresence', () async {

View file

@ -22,14 +22,14 @@ void test() async {
var testClientA = Client('TestClientA'); var testClientA = Client('TestClientA');
testClientA.database = getDatabase(); testClientA.database = getDatabase();
await testClientA.checkServer(homeserver); await testClientA.checkServer(homeserver);
await testClientA.login(testUserA, testPasswordA); await testClientA.login(user: testUserA, password: testPasswordA);
assert(testClientA.encryptionEnabled); assert(testClientA.encryptionEnabled);
Logs.success('++++ Login $testUserB ++++'); Logs.success('++++ Login $testUserB ++++');
var testClientB = Client('TestClientB'); var testClientB = Client('TestClientB');
testClientB.database = getDatabase(); testClientB.database = getDatabase();
await testClientB.checkServer(homeserver); await testClientB.checkServer(homeserver);
await testClientB.login(testUserB, testPasswordA); await testClientB.login(user: testUserB, password: testPasswordA);
assert(testClientB.encryptionEnabled); assert(testClientB.encryptionEnabled);
Logs.success('++++ ($testUserA) Leave all rooms ++++'); Logs.success('++++ ($testUserA) Leave all rooms ++++');
@ -72,7 +72,7 @@ void test() async {
.userDeviceKeys[testUserB].deviceKeys[testClientB.deviceID].blocked); .userDeviceKeys[testUserB].deviceKeys[testClientB.deviceID].blocked);
Logs.success('++++ ($testUserA) Create room and invite $testUserB ++++'); Logs.success('++++ ($testUserA) Create room and invite $testUserB ++++');
await testClientA.api.createRoom(invite: [testUserB]); await testClientA.createRoom(invite: [testUserB]);
await Future.delayed(Duration(seconds: 1)); await Future.delayed(Duration(seconds: 1));
var room = testClientA.rooms.first; var room = testClientA.rooms.first;
assert(room != null); assert(room != null);
@ -217,7 +217,7 @@ void test() async {
Logs.success('++++ Login $testUserB in another client ++++'); Logs.success('++++ Login $testUserB in another client ++++');
var testClientC = Client('TestClientC', database: getDatabase()); var testClientC = Client('TestClientC', database: getDatabase());
await testClientC.checkServer(homeserver); await testClientC.checkServer(homeserver);
await testClientC.login(testUserB, testPasswordA); await testClientC.login(user: testUserB, password: testPasswordA);
await Future.delayed(Duration(seconds: 3)); await Future.delayed(Duration(seconds: 3));
Logs.success( Logs.success(
@ -346,8 +346,8 @@ void test() async {
await Future.delayed(Duration(seconds: 1)); await Future.delayed(Duration(seconds: 1));
await testClientA.dispose(); await testClientA.dispose();
await testClientB.dispose(); await testClientB.dispose();
await testClientA.api.logoutAll(); await testClientA.logoutAll();
await testClientB.api.logoutAll(); await testClientB.logoutAll();
testClientA = null; testClientA = null;
testClientB = null; testClientB = null;
return; return;