[Connection] Throw MatrixErrors
This commit is contained in:
parent
ccdb33ba69
commit
4c6c3715d9
|
@ -24,12 +24,12 @@
|
|||
library famedlysdk;
|
||||
|
||||
export 'package:famedlysdk/src/requests/SetPushersRequest.dart';
|
||||
export 'package:famedlysdk/src/responses/ErrorResponse.dart';
|
||||
export 'package:famedlysdk/src/responses/PushrulesResponse.dart';
|
||||
export 'package:famedlysdk/src/sync/RoomUpdate.dart';
|
||||
export 'package:famedlysdk/src/sync/EventUpdate.dart';
|
||||
export 'package:famedlysdk/src/sync/UserUpdate.dart';
|
||||
export 'package:famedlysdk/src/utils/ChatTime.dart';
|
||||
export 'package:famedlysdk/src/utils/MatrixException.dart';
|
||||
export 'package:famedlysdk/src/utils/MatrixFile.dart';
|
||||
export 'package:famedlysdk/src/utils/MxContent.dart';
|
||||
export 'package:famedlysdk/src/utils/StatesMap.dart';
|
||||
|
|
|
@ -38,7 +38,6 @@ import 'RoomList.dart';
|
|||
import 'RoomState.dart';
|
||||
import 'User.dart';
|
||||
import 'requests/SetPushersRequest.dart';
|
||||
import 'responses/ErrorResponse.dart';
|
||||
import 'responses/PushrulesResponse.dart';
|
||||
import 'utils/Profile.dart';
|
||||
|
||||
|
@ -156,28 +155,19 @@ class Client {
|
|||
/// Checks the supported versions of the Matrix protocol and the supported
|
||||
/// login types. Returns false if the server is not compatible with the
|
||||
/// client. Automatically sets [matrixVersions] and [lazyLoadMembers].
|
||||
/// Throws FormatException, TimeoutException and MatrixException on error.
|
||||
Future<bool> checkServer(serverUrl) async {
|
||||
try {
|
||||
homeserver = serverUrl;
|
||||
|
||||
final versionResp = await connection.jsonRequest(
|
||||
type: HTTPType.GET, action: "/client/versions");
|
||||
if (versionResp is ErrorResponse) {
|
||||
connection.onError.add(ErrorResponse(errcode: "NO_RESPONSE", error: ""));
|
||||
return false;
|
||||
}
|
||||
|
||||
final List<String> versions = List<String>.from(versionResp["versions"]);
|
||||
|
||||
if (versions == null) {
|
||||
connection.onError.add(ErrorResponse(errcode: "NO_RESPONSE", error: ""));
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < versions.length; i++) {
|
||||
if (versions[i] == "r0.5.0")
|
||||
break;
|
||||
else if (i == versions.length - 1) {
|
||||
connection.onError.add(ErrorResponse(errcode: "NO_SUPPORT", error: ""));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -186,17 +176,14 @@ class Client {
|
|||
|
||||
if (versionResp.containsKey("unstable_features") &&
|
||||
versionResp["unstable_features"].containsKey("m.lazy_load_members")) {
|
||||
lazyLoadMembers = versionResp["unstable_features"]["m.lazy_load_members"]
|
||||
lazyLoadMembers = versionResp["unstable_features"]
|
||||
["m.lazy_load_members"]
|
||||
? true
|
||||
: false;
|
||||
}
|
||||
|
||||
final loginResp = await connection.jsonRequest(
|
||||
type: HTTPType.GET, action: "/client/r0/login");
|
||||
if (loginResp is ErrorResponse) {
|
||||
connection.onError.add(loginResp);
|
||||
return false;
|
||||
}
|
||||
|
||||
final List<dynamic> flows = loginResp["flows"];
|
||||
|
||||
|
@ -205,16 +192,19 @@ class Client {
|
|||
flows[i]["type"] == "m.login.password")
|
||||
break;
|
||||
else if (i == flows.length - 1) {
|
||||
connection.onError.add(ErrorResponse(errcode: "NO_SUPPORT", error: ""));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
} catch (_) {
|
||||
this.homeserver = this.matrixVersions = null;
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
/// Handles the login and allows the client to call all APIs which require
|
||||
/// authentication. Returns false if the login was not successful.
|
||||
/// authentication. Returns false if the login was not successful. Throws
|
||||
/// MatrixException if login was not successful.
|
||||
Future<bool> login(String username, String password) async {
|
||||
final loginResp = await connection
|
||||
.jsonRequest(type: HTTPType.POST, action: "/client/r0/login", data: {
|
||||
|
@ -228,15 +218,10 @@ class Client {
|
|||
"initial_device_display_name": "Famedly Talk"
|
||||
});
|
||||
|
||||
if (loginResp is ErrorResponse) {
|
||||
connection.onError.add(loginResp);
|
||||
return false;
|
||||
}
|
||||
|
||||
final userID = loginResp["user_id"];
|
||||
final accessToken = loginResp["access_token"];
|
||||
if (userID == null || accessToken == null) {
|
||||
connection.onError.add(ErrorResponse(errcode: "NO_SUPPORT", error: ""));
|
||||
return false;
|
||||
}
|
||||
|
||||
await connection.connect(
|
||||
|
@ -253,12 +238,15 @@ class Client {
|
|||
/// Sends a logout command to the homeserver and clears all local data,
|
||||
/// including all persistent data from the store.
|
||||
Future<void> logout() async {
|
||||
final dynamic resp = await connection.jsonRequest(
|
||||
try {
|
||||
await connection.jsonRequest(
|
||||
type: HTTPType.POST, action: "/client/r0/logout");
|
||||
if (resp is ErrorResponse) connection.onError.add(resp);
|
||||
|
||||
} catch (exception) {
|
||||
rethrow;
|
||||
} finally {
|
||||
await connection.clear();
|
||||
}
|
||||
}
|
||||
|
||||
/// 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
|
||||
|
@ -266,10 +254,6 @@ class Client {
|
|||
Future<Profile> getProfileFromUserId(String userId) async {
|
||||
final dynamic resp = await connection.jsonRequest(
|
||||
type: HTTPType.GET, action: "/client/r0/profile/${userId}");
|
||||
if (resp is ErrorResponse) {
|
||||
connection.onError.add(resp);
|
||||
return null;
|
||||
}
|
||||
return Profile.fromJson(resp);
|
||||
}
|
||||
|
||||
|
@ -295,8 +279,7 @@ class Client {
|
|||
String action = "/client/r0/sync?filter=$syncFilters&timeout=0";
|
||||
final sync =
|
||||
await connection.jsonRequest(type: HTTPType.GET, action: action);
|
||||
if (!(sync is ErrorResponse) &&
|
||||
sync["rooms"]["leave"] is Map<String, dynamic>) {
|
||||
if (sync["rooms"]["leave"] is Map<String, dynamic>) {
|
||||
for (var entry in sync["rooms"]["leave"].entries) {
|
||||
final String id = entry.key;
|
||||
final dynamic room = entry.value;
|
||||
|
@ -377,6 +360,7 @@ class Client {
|
|||
if (params == null && invite != null)
|
||||
for (int i = 0; i < invite.length; i++) inviteIDs.add(invite[i].id);
|
||||
|
||||
try {
|
||||
final dynamic resp = await connection.jsonRequest(
|
||||
type: HTTPType.POST,
|
||||
action: "/client/r0/createRoom",
|
||||
|
@ -385,25 +369,20 @@ class Client {
|
|||
"invite": inviteIDs,
|
||||
}
|
||||
: params);
|
||||
|
||||
if (resp is ErrorResponse) {
|
||||
connection.onError.add(resp);
|
||||
return null;
|
||||
}
|
||||
|
||||
return resp["room_id"];
|
||||
} catch (e) {
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
|
||||
/// Uploads a new user avatar for this user. Returns ErrorResponse if something went wrong.
|
||||
Future<dynamic> setAvatar(MatrixFile file) async {
|
||||
/// Uploads a new user avatar for this user.
|
||||
Future<void> setAvatar(MatrixFile file) async {
|
||||
final uploadResp = await connection.upload(file);
|
||||
if (uploadResp is ErrorResponse) return uploadResp;
|
||||
final setAvatarResp = await connection.jsonRequest(
|
||||
await connection.jsonRequest(
|
||||
type: HTTPType.PUT,
|
||||
action: "/client/r0/profile/$userID/avatar_url",
|
||||
data: {"avatar_url": uploadResp});
|
||||
if (setAvatarResp is ErrorResponse) return setAvatarResp;
|
||||
return null;
|
||||
return;
|
||||
}
|
||||
|
||||
/// Fetches the pushrules for the logged in user.
|
||||
|
@ -414,26 +393,16 @@ class Client {
|
|||
action: "/client/r0/pushrules/",
|
||||
);
|
||||
|
||||
if (resp is ErrorResponse) {
|
||||
connection.onError.add(resp);
|
||||
return null;
|
||||
}
|
||||
|
||||
return PushrulesResponse.fromJson(resp);
|
||||
}
|
||||
|
||||
/// This endpoint allows the creation, modification and deletion of pushers for this user ID.
|
||||
Future<dynamic> setPushers(SetPushersRequest data) async {
|
||||
final dynamic resp = await connection.jsonRequest(
|
||||
Future<void> setPushers(SetPushersRequest data) async {
|
||||
await connection.jsonRequest(
|
||||
type: HTTPType.POST,
|
||||
action: "/client/r0/pushers/set",
|
||||
data: data.toJson(),
|
||||
);
|
||||
|
||||
if (resp is ErrorResponse) {
|
||||
connection.onError.add(resp);
|
||||
}
|
||||
|
||||
return resp;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,10 +33,10 @@ import 'package:mime_type/mime_type.dart';
|
|||
|
||||
import 'Client.dart';
|
||||
import 'User.dart';
|
||||
import 'responses/ErrorResponse.dart';
|
||||
import 'sync/EventUpdate.dart';
|
||||
import 'sync/RoomUpdate.dart';
|
||||
import 'sync/UserUpdate.dart';
|
||||
import 'utils/MatrixException.dart';
|
||||
|
||||
enum HTTPType { GET, POST, PUT, DELETE }
|
||||
|
||||
|
@ -74,7 +74,7 @@ class Connection {
|
|||
new StreamController.broadcast();
|
||||
|
||||
/// Synchronization erros are coming here.
|
||||
final StreamController<ErrorResponse> onError =
|
||||
final StreamController<MatrixException> onError =
|
||||
new StreamController.broadcast();
|
||||
|
||||
/// This is called once, when the first sync has received.
|
||||
|
@ -177,6 +177,8 @@ class Connection {
|
|||
|
||||
/// Used for all Matrix json requests using the [c2s API](https://matrix.org/docs/spec/client_server/r0.4.0.html).
|
||||
///
|
||||
/// Throws: TimeoutException, FormatException, MatrixException
|
||||
///
|
||||
/// You must first call [this.connect()] or set [this.homeserver] before you can use
|
||||
/// this! For example to send a message to a Matrix room with the id
|
||||
/// '!fjd823j:example.com' you call:
|
||||
|
@ -192,7 +194,7 @@ class Connection {
|
|||
/// );
|
||||
/// ```
|
||||
///
|
||||
Future<dynamic> jsonRequest(
|
||||
Future<Map<String, dynamic>> jsonRequest(
|
||||
{HTTPType type,
|
||||
String action,
|
||||
dynamic data = "",
|
||||
|
@ -219,6 +221,7 @@ class Connection {
|
|||
"[REQUEST ${type.toString().split('.').last}] Action: $action, Data: $data");
|
||||
|
||||
http.Response resp;
|
||||
Map<String, dynamic> jsonResp = {};
|
||||
try {
|
||||
switch (type.toString().split('.').last) {
|
||||
case "GET":
|
||||
|
@ -242,52 +245,47 @@ class Connection {
|
|||
.timeout(Duration(seconds: timeout));
|
||||
break;
|
||||
}
|
||||
} on TimeoutException catch (_) {
|
||||
return ErrorResponse(
|
||||
error: "No connection possible...",
|
||||
errcode: "TIMEOUT",
|
||||
request: resp?.request);
|
||||
} catch (e) {
|
||||
return ErrorResponse(
|
||||
error: "No connection possible...",
|
||||
errcode: "NO_CONNECTION",
|
||||
request: resp?.request);
|
||||
jsonResp = jsonDecode(resp.body)
|
||||
as Map<String, dynamic>; // May throw FormatException
|
||||
|
||||
if (jsonResp.containsKey("errcode") && jsonResp["errcode"] is String) {
|
||||
// The server has responsed with an matrix related error.
|
||||
MatrixException exception = MatrixException(resp);
|
||||
if (exception.error == MatrixError.M_UNKNOWN_TOKEN) {
|
||||
// The token is no longer valid. Need to sign off....
|
||||
onError.add(exception);
|
||||
clear();
|
||||
}
|
||||
|
||||
Map<String, dynamic> jsonResp;
|
||||
try {
|
||||
jsonResp = jsonDecode(resp.body) as Map<String, dynamic>;
|
||||
} catch (e) {
|
||||
return ErrorResponse(
|
||||
error: "No connection possible...",
|
||||
errcode: "MALFORMED",
|
||||
request: resp?.request);
|
||||
}
|
||||
if (jsonResp.containsKey("errcode") && jsonResp["errcode"] is String) {
|
||||
if (jsonResp["errcode"] == "M_UNKNOWN_TOKEN") clear();
|
||||
return ErrorResponse.fromJson(jsonResp, resp?.request);
|
||||
throw exception;
|
||||
}
|
||||
|
||||
if (client.debug) print("[RESPONSE] ${jsonResp.toString()}");
|
||||
} on ArgumentError catch (exception) {
|
||||
print(exception);
|
||||
// Ignore this error
|
||||
} catch (_) {
|
||||
print(_);
|
||||
rethrow;
|
||||
}
|
||||
|
||||
return jsonResp;
|
||||
}
|
||||
|
||||
/// Uploads a file with the name [fileName] as base64 encoded to the server
|
||||
/// and returns the mxc url as a string or an [ErrorResponse].
|
||||
Future<dynamic> upload(MatrixFile file) async {
|
||||
/// and returns the mxc url as a string.
|
||||
Future<String> upload(MatrixFile file) async {
|
||||
dynamic fileBytes;
|
||||
if (client.homeserver != "https://fakeServer.notExisting")
|
||||
fileBytes = file.bytes;
|
||||
String fileName = file.path.split("/").last.toLowerCase();
|
||||
String mimeType = mime(file.path);
|
||||
print("[UPLOADING] $fileName, type: $mimeType, size: ${fileBytes?.length}");
|
||||
final dynamic resp = await jsonRequest(
|
||||
final Map<String, dynamic> resp = await jsonRequest(
|
||||
type: HTTPType.POST,
|
||||
action: "/media/r0/upload?filename=$fileName",
|
||||
data: fileBytes,
|
||||
contentType: mimeType);
|
||||
if (resp is ErrorResponse) return resp;
|
||||
return resp["content_uri"];
|
||||
}
|
||||
|
||||
|
@ -302,15 +300,11 @@ class Connection {
|
|||
action += "&timeout=30000";
|
||||
action += "&since=${client.prevBatch}";
|
||||
}
|
||||
try {
|
||||
_syncRequest = jsonRequest(type: HTTPType.GET, action: action);
|
||||
final int hash = _syncRequest.hashCode;
|
||||
final syncResp = await _syncRequest;
|
||||
if (hash != _syncRequest.hashCode) return;
|
||||
if (syncResp is ErrorResponse) {
|
||||
//onError.add(syncResp);
|
||||
await Future.delayed(Duration(seconds: syncErrorTimeoutSec), () {});
|
||||
} else {
|
||||
try {
|
||||
if (client.store != null)
|
||||
await client.store.transaction(() {
|
||||
handleSync(syncResp);
|
||||
|
@ -321,13 +315,13 @@ class Connection {
|
|||
await handleSync(syncResp);
|
||||
if (client.prevBatch == null) client.connection.onFirstSync.add(true);
|
||||
client.prevBatch = syncResp["next_batch"];
|
||||
} catch (e) {
|
||||
onError
|
||||
.add(ErrorResponse(errcode: "CRITICAL_ERROR", error: e.toString()));
|
||||
await Future.delayed(Duration(seconds: syncErrorTimeoutSec), () {});
|
||||
}
|
||||
}
|
||||
if (hash == _syncRequest.hashCode) _sync();
|
||||
} on MatrixException catch (exception) {
|
||||
onError.add(exception);
|
||||
await Future.delayed(Duration(seconds: syncErrorTimeoutSec), _sync);
|
||||
} catch (exception) {
|
||||
await Future.delayed(Duration(seconds: syncErrorTimeoutSec), _sync);
|
||||
}
|
||||
}
|
||||
|
||||
void handleSync(dynamic sync) {
|
||||
|
|
|
@ -25,10 +25,10 @@ import 'package:famedlysdk/src/Client.dart';
|
|||
import 'package:famedlysdk/src/Event.dart';
|
||||
import 'package:famedlysdk/src/RoomAccountData.dart';
|
||||
import 'package:famedlysdk/src/RoomState.dart';
|
||||
import 'package:famedlysdk/src/responses/ErrorResponse.dart';
|
||||
import 'package:famedlysdk/src/sync/EventUpdate.dart';
|
||||
import 'package:famedlysdk/src/sync/RoomUpdate.dart';
|
||||
import 'package:famedlysdk/src/utils/ChatTime.dart';
|
||||
import 'package:famedlysdk/src/utils/MatrixException.dart';
|
||||
import 'package:famedlysdk/src/utils/MatrixFile.dart';
|
||||
import 'package:famedlysdk/src/utils/MxContent.dart';
|
||||
//import 'package:image/image.dart';
|
||||
|
@ -248,35 +248,33 @@ class Room {
|
|||
return ChatTime.now();
|
||||
}
|
||||
|
||||
/// Call the Matrix API to change the name of this room.
|
||||
Future<dynamic> setName(String newName) async {
|
||||
dynamic res = await client.connection.jsonRequest(
|
||||
/// Call the Matrix API to change the name of this room. Returns the event ID of the
|
||||
/// new m.room.name event.
|
||||
Future<String> setName(String newName) async {
|
||||
final Map<String, dynamic> resp = await client.connection.jsonRequest(
|
||||
type: HTTPType.PUT,
|
||||
action: "/client/r0/rooms/${id}/state/m.room.name",
|
||||
data: {"name": newName});
|
||||
if (res is ErrorResponse) client.connection.onError.add(res);
|
||||
return res;
|
||||
return resp["event_id"];
|
||||
}
|
||||
|
||||
/// Call the Matrix API to change the topic of this room.
|
||||
Future<dynamic> setDescription(String newName) async {
|
||||
dynamic res = await client.connection.jsonRequest(
|
||||
Future<String> setDescription(String newName) async {
|
||||
final Map<String, dynamic> resp = await client.connection.jsonRequest(
|
||||
type: HTTPType.PUT,
|
||||
action: "/client/r0/rooms/${id}/state/m.room.topic",
|
||||
data: {"topic": newName});
|
||||
if (res is ErrorResponse) client.connection.onError.add(res);
|
||||
return res;
|
||||
return resp["event_id"];
|
||||
}
|
||||
|
||||
Future<dynamic> _sendRawEventNow(Map<String, dynamic> content,
|
||||
Future<String> _sendRawEventNow(Map<String, dynamic> content,
|
||||
{String txid = null}) async {
|
||||
if (txid == null) txid = "txid${DateTime.now().millisecondsSinceEpoch}";
|
||||
final dynamic res = await client.connection.jsonRequest(
|
||||
final Map<String, dynamic> res = await client.connection.jsonRequest(
|
||||
type: HTTPType.PUT,
|
||||
action: "/client/r0/rooms/${id}/send/m.room.message/$txid",
|
||||
data: content);
|
||||
if (res is ErrorResponse) client.connection.onError.add(res);
|
||||
return res;
|
||||
return res["event_id"];
|
||||
}
|
||||
|
||||
Future<String> sendTextEvent(String message, {String txid = null}) =>
|
||||
|
@ -291,8 +289,7 @@ class Room {
|
|||
if (msgType == "m.video") return sendAudioEvent(file);
|
||||
String fileName = file.path.split("/").last;
|
||||
|
||||
final dynamic uploadResp = await client.connection.upload(file);
|
||||
if (uploadResp is ErrorResponse) return null;
|
||||
final String uploadResp = await client.connection.upload(file);
|
||||
|
||||
// Send event
|
||||
Map<String, dynamic> content = {
|
||||
|
@ -311,8 +308,7 @@ class Room {
|
|||
Future<String> sendAudioEvent(MatrixFile file,
|
||||
{String txid = null, int width, int height}) async {
|
||||
String fileName = file.path.split("/").last;
|
||||
final dynamic uploadResp = await client.connection.upload(file);
|
||||
if (uploadResp is ErrorResponse) return null;
|
||||
final String uploadResp = await client.connection.upload(file);
|
||||
Map<String, dynamic> content = {
|
||||
"msgtype": "m.audio",
|
||||
"body": fileName,
|
||||
|
@ -329,8 +325,7 @@ class Room {
|
|||
Future<String> sendImageEvent(MatrixFile file,
|
||||
{String txid = null, int width, int height}) async {
|
||||
String fileName = file.path.split("/").last;
|
||||
final dynamic uploadResp = await client.connection.upload(file);
|
||||
if (uploadResp is ErrorResponse) return null;
|
||||
final String uploadResp = await client.connection.upload(file);
|
||||
Map<String, dynamic> content = {
|
||||
"msgtype": "m.image",
|
||||
"body": fileName,
|
||||
|
@ -354,8 +349,7 @@ class Room {
|
|||
int thumbnailWidth,
|
||||
int thumbnailHeight}) async {
|
||||
String fileName = file.path.split("/").last;
|
||||
final dynamic uploadResp = await client.connection.upload(file);
|
||||
if (uploadResp is ErrorResponse) return null;
|
||||
final String uploadResp = await client.connection.upload(file);
|
||||
Map<String, dynamic> content = {
|
||||
"msgtype": "m.video",
|
||||
"body": fileName,
|
||||
|
@ -376,8 +370,7 @@ class Room {
|
|||
}
|
||||
if (thumbnail != null) {
|
||||
String thumbnailName = file.path.split("/").last;
|
||||
final dynamic thumbnailUploadResp = await client.connection.upload(file);
|
||||
if (thumbnailUploadResp is ErrorResponse) return null;
|
||||
final String thumbnailUploadResp = await client.connection.upload(file);
|
||||
content["info"]["thumbnail_url"] = thumbnailUploadResp;
|
||||
content["info"]["thumbnail_info"] = {
|
||||
"size": thumbnail.size,
|
||||
|
@ -422,9 +415,18 @@ class Room {
|
|||
});
|
||||
|
||||
// Send the text and on success, store and display a *sent* event.
|
||||
final dynamic res = await _sendRawEventNow(content, txid: messageID);
|
||||
|
||||
if (res is ErrorResponse || !(res["event_id"] is String)) {
|
||||
try {
|
||||
final String res = await _sendRawEventNow(content, txid: messageID);
|
||||
eventUpdate.content["status"] = 1;
|
||||
eventUpdate.content["unsigned"] = {"transaction_id": messageID};
|
||||
eventUpdate.content["event_id"] = res;
|
||||
client.connection.onEvent.add(eventUpdate);
|
||||
await client.store?.transaction(() {
|
||||
client.store.storeEventUpdate(eventUpdate);
|
||||
return;
|
||||
});
|
||||
return res;
|
||||
} catch (exception) {
|
||||
// On error, set status to -1
|
||||
eventUpdate.content["status"] = -1;
|
||||
eventUpdate.content["unsigned"] = {"transaction_id": messageID};
|
||||
|
@ -433,16 +435,6 @@ class Room {
|
|||
client.store.storeEventUpdate(eventUpdate);
|
||||
return;
|
||||
});
|
||||
} else {
|
||||
eventUpdate.content["status"] = 1;
|
||||
eventUpdate.content["unsigned"] = {"transaction_id": messageID};
|
||||
eventUpdate.content["event_id"] = res["event_id"];
|
||||
client.connection.onEvent.add(eventUpdate);
|
||||
await client.store?.transaction(() {
|
||||
client.store.storeEventUpdate(eventUpdate);
|
||||
return;
|
||||
});
|
||||
return res["event_id"];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
@ -450,12 +442,16 @@ class Room {
|
|||
/// Call the Matrix API to join this room if the user is not already a member.
|
||||
/// If this room is intended to be a direct chat, the direct chat flag will
|
||||
/// automatically be set.
|
||||
Future<dynamic> join() async {
|
||||
dynamic res = await client.connection.jsonRequest(
|
||||
Future<void> join() async {
|
||||
try {
|
||||
await client.connection.jsonRequest(
|
||||
type: HTTPType.POST, action: "/client/r0/rooms/${id}/join");
|
||||
if (res is ErrorResponse) {
|
||||
client.connection.onError.add(res);
|
||||
if (res.error == "No known servers") {
|
||||
if (states.containsKey(client.userID) &&
|
||||
states[client.userID].content["is_direct"] is bool &&
|
||||
states[client.userID].content["is_direct"])
|
||||
addToDirectChat(states[client.userID].sender.id);
|
||||
} on MatrixException catch (exception) {
|
||||
if (exception.errorMessage == "No known servers") {
|
||||
client.store?.forgetRoom(id);
|
||||
client.connection.onRoomUpdate.add(
|
||||
RoomUpdate(
|
||||
|
@ -465,88 +461,78 @@ class Room {
|
|||
highlight_count: 0),
|
||||
);
|
||||
}
|
||||
return res;
|
||||
rethrow;
|
||||
}
|
||||
if (states.containsKey(client.userID) &&
|
||||
states[client.userID].content["is_direct"] is bool &&
|
||||
states[client.userID].content["is_direct"])
|
||||
addToDirectChat(states[client.userID].sender.id);
|
||||
return res;
|
||||
}
|
||||
|
||||
/// Call the Matrix API to leave this room. If this room is set as a direct
|
||||
/// chat, this will be removed too.
|
||||
Future<dynamic> leave() async {
|
||||
Future<void> leave() async {
|
||||
if (directChatMatrixID != "") await removeFromDirectChat();
|
||||
dynamic res = await client.connection.jsonRequest(
|
||||
await client.connection.jsonRequest(
|
||||
type: HTTPType.POST, action: "/client/r0/rooms/${id}/leave");
|
||||
if (res is ErrorResponse) client.connection.onError.add(res);
|
||||
return res;
|
||||
return;
|
||||
}
|
||||
|
||||
/// Call the Matrix API to forget this room if you already left it.
|
||||
Future<dynamic> forget() async {
|
||||
Future<void> forget() async {
|
||||
client.store.forgetRoom(id);
|
||||
dynamic res = await client.connection.jsonRequest(
|
||||
await client.connection.jsonRequest(
|
||||
type: HTTPType.POST, action: "/client/r0/rooms/${id}/forget");
|
||||
if (res is ErrorResponse) client.connection.onError.add(res);
|
||||
return res;
|
||||
return;
|
||||
}
|
||||
|
||||
/// Call the Matrix API to kick a user from this room.
|
||||
Future<dynamic> kick(String userID) async {
|
||||
dynamic res = await client.connection.jsonRequest(
|
||||
Future<void> kick(String userID) async {
|
||||
await client.connection.jsonRequest(
|
||||
type: HTTPType.POST,
|
||||
action: "/client/r0/rooms/${id}/kick",
|
||||
data: {"user_id": userID});
|
||||
if (res is ErrorResponse) client.connection.onError.add(res);
|
||||
return res;
|
||||
return;
|
||||
}
|
||||
|
||||
/// Call the Matrix API to ban a user from this room.
|
||||
Future<dynamic> ban(String userID) async {
|
||||
dynamic res = await client.connection.jsonRequest(
|
||||
Future<void> ban(String userID) async {
|
||||
await client.connection.jsonRequest(
|
||||
type: HTTPType.POST,
|
||||
action: "/client/r0/rooms/${id}/ban",
|
||||
data: {"user_id": userID});
|
||||
if (res is ErrorResponse) client.connection.onError.add(res);
|
||||
return res;
|
||||
return;
|
||||
}
|
||||
|
||||
/// Call the Matrix API to unban a banned user from this room.
|
||||
Future<dynamic> unban(String userID) async {
|
||||
dynamic res = await client.connection.jsonRequest(
|
||||
Future<void> unban(String userID) async {
|
||||
await client.connection.jsonRequest(
|
||||
type: HTTPType.POST,
|
||||
action: "/client/r0/rooms/${id}/unban",
|
||||
data: {"user_id": userID});
|
||||
if (res is ErrorResponse) client.connection.onError.add(res);
|
||||
return res;
|
||||
return;
|
||||
}
|
||||
|
||||
/// Set the power level of the user with the [userID] to the value [power].
|
||||
Future<dynamic> setPower(String userID, int power) async {
|
||||
/// Returns the event ID of the new state event. If there is no known
|
||||
/// power level event, there might something broken and this returns null.
|
||||
Future<String> setPower(String userID, int power) async {
|
||||
if (states["m.room.power_levels"] == null) return null;
|
||||
Map<String, dynamic> powerMap = {}
|
||||
..addAll(states["m.room.power_levels"].content);
|
||||
if (powerMap["users"] == null) powerMap["users"] = {};
|
||||
powerMap["users"][userID] = power;
|
||||
|
||||
dynamic res = await client.connection.jsonRequest(
|
||||
final Map<String, dynamic> resp = await client.connection.jsonRequest(
|
||||
type: HTTPType.PUT,
|
||||
action: "/client/r0/rooms/$id/state/m.room.power_levels",
|
||||
data: powerMap);
|
||||
if (res is ErrorResponse) client.connection.onError.add(res);
|
||||
return res;
|
||||
return resp["event_id"];
|
||||
}
|
||||
|
||||
/// Call the Matrix API to invite a user to this room.
|
||||
Future<dynamic> invite(String userID) async {
|
||||
dynamic res = await client.connection.jsonRequest(
|
||||
Future<void> invite(String userID) async {
|
||||
await client.connection.jsonRequest(
|
||||
type: HTTPType.POST,
|
||||
action: "/client/r0/rooms/${id}/invite",
|
||||
data: {"user_id": userID});
|
||||
if (res is ErrorResponse) client.connection.onError.add(res);
|
||||
return res;
|
||||
return;
|
||||
}
|
||||
|
||||
/// Request more previous events from the server. [historyCount] defines how much events should
|
||||
|
@ -559,8 +545,6 @@ class Room {
|
|||
action:
|
||||
"/client/r0/rooms/$id/messages?from=${prev_batch}&dir=b&limit=$historyCount&filter=${Connection.syncFilters}");
|
||||
|
||||
if (resp is ErrorResponse) return;
|
||||
|
||||
if (onHistoryReceived != null) onHistoryReceived();
|
||||
prev_batch = resp["end"];
|
||||
client.store?.storeRoomPrevBatch(this);
|
||||
|
@ -634,51 +618,51 @@ class Room {
|
|||
);
|
||||
}
|
||||
|
||||
/// Sets this room as a direct chat for this user.
|
||||
Future<dynamic> addToDirectChat(String userID) async {
|
||||
/// Sets this room as a direct chat for this user if not already.
|
||||
Future<void> addToDirectChat(String userID) async {
|
||||
Map<String, dynamic> directChats = client.directChats;
|
||||
if (directChats.containsKey(userID)) if (!directChats[userID].contains(id))
|
||||
directChats[userID].add(id);
|
||||
else
|
||||
return null; // Is already in direct chats
|
||||
return; // Is already in direct chats
|
||||
else
|
||||
directChats[userID] = [id];
|
||||
|
||||
final resp = await client.connection.jsonRequest(
|
||||
await client.connection.jsonRequest(
|
||||
type: HTTPType.PUT,
|
||||
action: "/client/r0/user/${client.userID}/account_data/m.direct",
|
||||
data: directChats);
|
||||
return resp;
|
||||
return;
|
||||
}
|
||||
|
||||
/// Sets this room as a direct chat for this user.
|
||||
Future<dynamic> removeFromDirectChat() async {
|
||||
/// Removes this room from all direct chat tags.
|
||||
Future<void> removeFromDirectChat() async {
|
||||
Map<String, dynamic> directChats = client.directChats;
|
||||
if (directChats.containsKey(directChatMatrixID) &&
|
||||
directChats[directChatMatrixID].contains(id))
|
||||
directChats[directChatMatrixID].remove(id);
|
||||
else
|
||||
return null; // Nothing to do here
|
||||
return; // Nothing to do here
|
||||
|
||||
final resp = await client.connection.jsonRequest(
|
||||
await client.connection.jsonRequest(
|
||||
type: HTTPType.PUT,
|
||||
action: "/client/r0/user/${client.userID}/account_data/m.direct",
|
||||
data: directChats);
|
||||
return resp;
|
||||
return;
|
||||
}
|
||||
|
||||
/// Sends *m.fully_read* and *m.read* for the given event ID.
|
||||
Future<dynamic> sendReadReceipt(String eventID) async {
|
||||
Future<void> sendReadReceipt(String eventID) async {
|
||||
this.notificationCount = 0;
|
||||
client?.store?.resetNotificationCount(this.id);
|
||||
final dynamic resp = client.connection.jsonRequest(
|
||||
client.connection.jsonRequest(
|
||||
type: HTTPType.POST,
|
||||
action: "/client/r0/rooms/$id/read_markers",
|
||||
data: {
|
||||
"m.fully_read": eventID,
|
||||
"m.read": eventID,
|
||||
});
|
||||
return resp;
|
||||
return;
|
||||
}
|
||||
|
||||
/// Returns a Room from a json String which comes normally from the store. If the
|
||||
|
@ -770,8 +754,6 @@ class Room {
|
|||
|
||||
dynamic res = await client.connection.jsonRequest(
|
||||
type: HTTPType.GET, action: "/client/r0/rooms/${id}/members");
|
||||
if (res is ErrorResponse || !(res["chunk"] is List<dynamic>))
|
||||
return participants;
|
||||
|
||||
for (num i = 0; i < res["chunk"].length; i++) {
|
||||
User newUser = RoomState.fromJson(res["chunk"][i], this).asUser;
|
||||
|
@ -794,7 +776,9 @@ class Room {
|
|||
if (states[mxID] != null)
|
||||
return states[mxID].asUser;
|
||||
else {
|
||||
try {
|
||||
requestUser(mxID);
|
||||
} catch (_) {}
|
||||
return User(mxID, room: this);
|
||||
}
|
||||
}
|
||||
|
@ -805,12 +789,14 @@ class Room {
|
|||
/// lazy loading.
|
||||
Future<User> requestUser(String mxID) async {
|
||||
if (mxID == null || !_requestingMatrixIds.add(mxID)) return null;
|
||||
final dynamic resp = await client.connection.jsonRequest(
|
||||
Map<String, dynamic> resp;
|
||||
try {
|
||||
resp = await client.connection.jsonRequest(
|
||||
type: HTTPType.GET,
|
||||
action: "/client/r0/rooms/$id/state/m.room.member/$mxID");
|
||||
if (resp is ErrorResponse) {
|
||||
} catch (exception) {
|
||||
_requestingMatrixIds.remove(mxID);
|
||||
return null;
|
||||
rethrow;
|
||||
}
|
||||
final User user = User(mxID,
|
||||
displayName: resp["displayname"],
|
||||
|
@ -837,7 +823,6 @@ class Room {
|
|||
Future<Event> getEventById(String eventID) async {
|
||||
final dynamic resp = await client.connection.jsonRequest(
|
||||
type: HTTPType.GET, action: "/client/r0/rooms/$id/event/$eventID");
|
||||
if (resp is ErrorResponse) return null;
|
||||
return Event.fromJson(resp, this);
|
||||
}
|
||||
|
||||
|
@ -865,16 +850,15 @@ class Room {
|
|||
return null;
|
||||
}
|
||||
|
||||
/// Uploads a new user avatar for this room. Returns ErrorResponse if something went wrong
|
||||
/// and the event ID otherwise.
|
||||
Future<dynamic> setAvatar(MatrixFile file) async {
|
||||
final uploadResp = await client.connection.upload(file);
|
||||
if (uploadResp is ErrorResponse) return uploadResp;
|
||||
final setAvatarResp = await client.connection.jsonRequest(
|
||||
/// Uploads a new user avatar for this room. Returns the event ID of the new
|
||||
/// m.room.avatar event.
|
||||
Future<String> setAvatar(MatrixFile file) async {
|
||||
final String uploadResp = await client.connection.upload(file);
|
||||
final Map<String, dynamic> setAvatarResp = await client.connection
|
||||
.jsonRequest(
|
||||
type: HTTPType.PUT,
|
||||
action: "/client/r0/rooms/$id/state/m.room.avatar/",
|
||||
data: {"url": uploadResp});
|
||||
if (setAvatarResp is ErrorResponse) return setAvatarResp;
|
||||
return setAvatarResp["event_id"];
|
||||
}
|
||||
|
||||
|
@ -979,7 +963,6 @@ class Room {
|
|||
type: HTTPType.DELETE,
|
||||
action: "/client/r0/pushrules/global/override/$id",
|
||||
data: {});
|
||||
if (resp == ErrorResponse) return resp;
|
||||
resp = await client.connection.jsonRequest(
|
||||
type: HTTPType.PUT,
|
||||
action: "/client/r0/pushrules/global/room/$id",
|
||||
|
@ -1001,7 +984,6 @@ class Room {
|
|||
type: HTTPType.DELETE,
|
||||
action: "/client/r0/pushrules/global/room/$id",
|
||||
data: {});
|
||||
if (resp == ErrorResponse) return resp;
|
||||
}
|
||||
resp = await client.connection.jsonRequest(
|
||||
type: HTTPType.PUT,
|
||||
|
|
|
@ -24,7 +24,6 @@
|
|||
import 'package:famedlysdk/famedlysdk.dart';
|
||||
import 'package:famedlysdk/src/Room.dart';
|
||||
import 'package:famedlysdk/src/RoomState.dart';
|
||||
import 'package:famedlysdk/src/responses/ErrorResponse.dart';
|
||||
import 'package:famedlysdk/src/utils/ChatTime.dart';
|
||||
import 'package:famedlysdk/src/utils/MxContent.dart';
|
||||
|
||||
|
@ -113,28 +112,16 @@ class User extends RoomState {
|
|||
: displayName;
|
||||
|
||||
/// Call the Matrix API to kick this user from this room.
|
||||
Future<dynamic> kick() async {
|
||||
dynamic res = await room.kick(id);
|
||||
return res;
|
||||
}
|
||||
Future<void> kick() => room.kick(id);
|
||||
|
||||
/// Call the Matrix API to ban this user from this room.
|
||||
Future<dynamic> ban() async {
|
||||
dynamic res = await room.ban(id);
|
||||
return res;
|
||||
}
|
||||
Future<void> ban() => room.ban(id);
|
||||
|
||||
/// Call the Matrix API to unban this banned user from this room.
|
||||
Future<dynamic> unban() async {
|
||||
dynamic res = await room.unban(id);
|
||||
return res;
|
||||
}
|
||||
Future<void> unban() => room.unban(id);
|
||||
|
||||
/// Call the Matrix API to change the power level of this user.
|
||||
Future<dynamic> setPower(int power) async {
|
||||
dynamic res = await room.setPower(id, power);
|
||||
return res;
|
||||
}
|
||||
Future<void> setPower(int power) => room.setPower(id, power);
|
||||
|
||||
/// Returns an existing direct chat ID with this user or creates a new one.
|
||||
/// Returns null on error.
|
||||
|
@ -153,11 +140,6 @@ class User extends RoomState {
|
|||
"preset": "trusted_private_chat"
|
||||
});
|
||||
|
||||
if (resp is ErrorResponse) {
|
||||
room.client.connection.onError.add(resp);
|
||||
return null;
|
||||
}
|
||||
|
||||
final String newRoomID = resp["room_id"];
|
||||
|
||||
if (newRoomID == null) return newRoomID;
|
||||
|
|
|
@ -1,51 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2019 Zender & Kurtz GbR.
|
||||
*
|
||||
* Authors:
|
||||
* Christian Pauly <krille@famedly.com>
|
||||
* Marcel Radzio <mtrnord@famedly.com>
|
||||
*
|
||||
* This file is part of famedlysdk.
|
||||
*
|
||||
* famedlysdk is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* famedlysdk is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with famedlysdk. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import 'package:http/http.dart' as http;
|
||||
|
||||
/// Represents a special response from the Homeserver for errors.
|
||||
class ErrorResponse {
|
||||
/// The unique identifier for this error.
|
||||
String errcode;
|
||||
|
||||
/// A human readable error description.
|
||||
String error;
|
||||
|
||||
/// The frozen request which triggered this Error
|
||||
http.Request request;
|
||||
|
||||
ErrorResponse({this.errcode, this.error, this.request});
|
||||
|
||||
ErrorResponse.fromJson(Map<String, dynamic> json, http.Request newRequest) {
|
||||
errcode = json['errcode'];
|
||||
error = json['error'] ?? "";
|
||||
request = newRequest;
|
||||
}
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
final Map<String, dynamic> data = new Map<String, dynamic>();
|
||||
data['errcode'] = this.errcode;
|
||||
data['error'] = this.error;
|
||||
return data;
|
||||
}
|
||||
}
|
105
lib/src/utils/MatrixException.dart
Normal file
105
lib/src/utils/MatrixException.dart
Normal file
|
@ -0,0 +1,105 @@
|
|||
/*
|
||||
* Copyright (c) 2019 Zender & Kurtz GbR.
|
||||
*
|
||||
* Authors:
|
||||
* Christian Pauly <krille@famedly.com>
|
||||
* Marcel Radzio <mtrnord@famedly.com>
|
||||
*
|
||||
* This file is part of famedlysdk.
|
||||
*
|
||||
* famedlysdk is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* famedlysdk is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with famedlysdk. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:http/http.dart' as http;
|
||||
|
||||
enum MatrixError {
|
||||
M_UNKNOWN,
|
||||
M_UNKNOWN_TOKEN,
|
||||
M_NOT_FOUND,
|
||||
M_FORBIDDEN,
|
||||
M_LIMIT_EXCEEDED,
|
||||
M_USER_IN_USE,
|
||||
M_THREEPID_IN_USE,
|
||||
M_THREEPID_DENIED,
|
||||
M_THREEPID_NOT_FOUND,
|
||||
M_THREEPID_AUTH_FAILED,
|
||||
M_TOO_LARGE,
|
||||
M_MISSING_PARAM,
|
||||
M_UNSUPPORTED_ROOM_VERSION,
|
||||
M_UNRECOGNIZED,
|
||||
}
|
||||
|
||||
/// Represents a special response from the Homeserver for errors.
|
||||
class MatrixException implements Exception {
|
||||
final Map<String, dynamic> raw;
|
||||
|
||||
/// The unique identifier for this error.
|
||||
String get errcode => raw["errcode"];
|
||||
|
||||
/// A human readable error description.
|
||||
String get errorMessage => raw["error"];
|
||||
|
||||
/// The frozen request which triggered this Error
|
||||
http.Response response;
|
||||
|
||||
MatrixException(this.response) : this.raw = json.decode(response.body);
|
||||
|
||||
@override
|
||||
String toString() => "$errcode: $errorMessage";
|
||||
|
||||
/// Returns the [ResponseError]. Is ResponseError.NONE if there wasn't an error.
|
||||
MatrixError get error => MatrixError.values.firstWhere(
|
||||
(e) => e.toString() == 'MatrixError.${(raw["errcode"] ?? "")}',
|
||||
orElse: () => MatrixError.M_UNKNOWN);
|
||||
|
||||
int get retryAfterMs => raw["retry_after_ms"];
|
||||
|
||||
/// This is a session identifier that the client must pass back to the homeserver, if one is provided,
|
||||
/// in subsequent attempts to authenticate in the same API call.
|
||||
String get session => raw["session"];
|
||||
|
||||
/// Returns true if the server requires additional authentication.
|
||||
bool get requireAdditionalAuthentication => response.statusCode == 401;
|
||||
|
||||
/// For each endpoint, a server offers one or more 'flows' that the client can use
|
||||
/// to authenticate itself. Each flow comprises a series of stages. If this request
|
||||
/// doesn't need additional authentication, then this is null.
|
||||
List<AuthenticationFlow> get authenticationFlows {
|
||||
if (!raw.containsKey("flows") || !(raw["flows"] is List)) return null;
|
||||
List<AuthenticationFlow> flows = [];
|
||||
for (Map<String, dynamic> flow in raw["flows"]) {
|
||||
if (flow["stages"] is List<String>) {
|
||||
flows.add(AuthenticationFlow(flow["stages"]));
|
||||
}
|
||||
}
|
||||
return flows;
|
||||
}
|
||||
|
||||
/// This section contains any information that the client will need to know in order to use a given type
|
||||
/// of authentication. For each authentication type presented, that type may be present as a key in this
|
||||
/// dictionary. For example, the public part of an OAuth client ID could be given here.
|
||||
Map<String, dynamic> get authenticationParams => raw["params"];
|
||||
|
||||
/// Returns the list of already completed authentication flows from previous requests.
|
||||
List<String> get completedAuthenticationFlows => raw["completed"];
|
||||
}
|
||||
|
||||
/// For each endpoint, a server offers one or more 'flows' that the client can use
|
||||
/// to authenticate itself. Each flow comprises a series of stages
|
||||
class AuthenticationFlow {
|
||||
final List<String> stages;
|
||||
const AuthenticationFlow(this.stages);
|
||||
}
|
|
@ -30,11 +30,11 @@ import 'package:famedlysdk/src/Presence.dart';
|
|||
import 'package:famedlysdk/src/Room.dart';
|
||||
import 'package:famedlysdk/src/User.dart';
|
||||
import 'package:famedlysdk/src/requests/SetPushersRequest.dart';
|
||||
import 'package:famedlysdk/src/responses/ErrorResponse.dart';
|
||||
import 'package:famedlysdk/src/responses/PushrulesResponse.dart';
|
||||
import 'package:famedlysdk/src/sync/EventUpdate.dart';
|
||||
import 'package:famedlysdk/src/sync/RoomUpdate.dart';
|
||||
import 'package:famedlysdk/src/sync/UserUpdate.dart';
|
||||
import 'package:famedlysdk/src/utils/MatrixException.dart';
|
||||
import 'package:famedlysdk/src/utils/MatrixFile.dart';
|
||||
import 'package:famedlysdk/src/utils/Profile.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
@ -60,9 +60,6 @@ void main() {
|
|||
userUpdateListFuture = matrix.connection.onUserEvent.stream.toList();
|
||||
|
||||
test('Login', () async {
|
||||
Future<ErrorResponse> errorFuture =
|
||||
matrix.connection.onError.stream.first;
|
||||
|
||||
int presenceCounter = 0;
|
||||
int accountDataCounter = 0;
|
||||
matrix.onPresence = (Presence data) {
|
||||
|
@ -72,25 +69,26 @@ void main() {
|
|||
accountDataCounter++;
|
||||
};
|
||||
|
||||
final bool checkResp1 =
|
||||
expect(matrix.homeserver, null);
|
||||
expect(matrix.matrixVersions, null);
|
||||
|
||||
try {
|
||||
await matrix.checkServer("https://fakeserver.wrongaddress");
|
||||
final bool checkResp2 =
|
||||
} on FormatException catch (exception) {
|
||||
expect(exception != null, true);
|
||||
}
|
||||
await matrix.checkServer("https://fakeserver.notexisting");
|
||||
expect(matrix.homeserver, "https://fakeserver.notexisting");
|
||||
expect(matrix.matrixVersions,
|
||||
["r0.0.1", "r0.1.0", "r0.2.0", "r0.3.0", "r0.4.0", "r0.5.0"]);
|
||||
|
||||
ErrorResponse checkError = await errorFuture;
|
||||
|
||||
expect(checkResp1, false);
|
||||
expect(checkResp2, true);
|
||||
expect(checkError.errcode, "NO_RESPONSE");
|
||||
|
||||
final resp = await matrix.connection
|
||||
final Map<String, dynamic> resp = await matrix.connection
|
||||
.jsonRequest(type: HTTPType.POST, action: "/client/r0/login", data: {
|
||||
"type": "m.login.password",
|
||||
"user": "test",
|
||||
"password": "1234",
|
||||
"initial_device_display_name": "Fluffy Matrix Client"
|
||||
});
|
||||
expect(resp is ErrorResponse, false);
|
||||
|
||||
Future<LoginState> loginStateFuture =
|
||||
matrix.connection.onLoginStateChanged.stream.first;
|
||||
|
@ -176,15 +174,19 @@ void main() {
|
|||
});
|
||||
|
||||
test('Try to get ErrorResponse', () async {
|
||||
final resp = await matrix.connection
|
||||
MatrixException expectedException;
|
||||
try {
|
||||
await matrix.connection
|
||||
.jsonRequest(type: HTTPType.PUT, action: "/non/existing/path");
|
||||
expect(resp is ErrorResponse, true);
|
||||
} on MatrixException catch (exception) {
|
||||
expectedException = exception;
|
||||
}
|
||||
expect(expectedException.error, MatrixError.M_UNRECOGNIZED);
|
||||
});
|
||||
|
||||
test('Logout', () async {
|
||||
final dynamic resp = await matrix.connection
|
||||
await matrix.connection
|
||||
.jsonRequest(type: HTTPType.POST, action: "/client/r0/logout");
|
||||
expect(resp is ErrorResponse, false);
|
||||
|
||||
Future<LoginState> loginStateFuture =
|
||||
matrix.connection.onLoginStateChanged.stream.first;
|
||||
|
@ -331,8 +333,7 @@ void main() {
|
|||
test('setAvatar', () async {
|
||||
final MatrixFile testFile =
|
||||
MatrixFile(bytes: [], path: "fake/path/file.jpeg");
|
||||
final dynamic resp = await matrix.setAvatar(testFile);
|
||||
expect(resp, null);
|
||||
await matrix.setAvatar(testFile);
|
||||
});
|
||||
|
||||
test('getPushrules', () async {
|
||||
|
@ -352,8 +353,7 @@ void main() {
|
|||
lang: "en",
|
||||
data: PusherData(
|
||||
format: "event_id_only", url: "https://examplepushserver.com"));
|
||||
final dynamic resp = await matrix.setPushers(data);
|
||||
expect(resp is ErrorResponse, false);
|
||||
await matrix.setPushers(data);
|
||||
});
|
||||
|
||||
test('joinRoomById', () async {
|
||||
|
@ -389,8 +389,13 @@ void main() {
|
|||
test('Logout when token is unknown', () async {
|
||||
Future<LoginState> loginStateFuture =
|
||||
matrix.connection.onLoginStateChanged.stream.first;
|
||||
|
||||
try {
|
||||
await matrix.connection
|
||||
.jsonRequest(type: HTTPType.DELETE, action: "/unknown/token");
|
||||
} on MatrixException catch (exception) {
|
||||
expect(exception.error, MatrixError.M_UNKNOWN_TOKEN);
|
||||
}
|
||||
|
||||
LoginState state = await loginStateFuture;
|
||||
expect(state, LoginState.loggedOut);
|
||||
|
|
|
@ -48,12 +48,17 @@ class FakeMatrixApi extends MockClient {
|
|||
|
||||
if (request.url.origin != "https://fakeserver.notexisting")
|
||||
return Response(
|
||||
"<html><head></head><body>Not found...</body></html>", 50);
|
||||
"<html><head></head><body>Not found...</body></html>", 404);
|
||||
|
||||
// Call API
|
||||
if (api.containsKey(method) && api[method].containsKey(action))
|
||||
res = api[method][action](data);
|
||||
else
|
||||
else if (method == "GET" &&
|
||||
action.contains("/client/r0/rooms/") &&
|
||||
action.contains("/state/m.room.member/")) {
|
||||
res = {"displayname": ""};
|
||||
return Response(json.encode(res), 200);
|
||||
} else
|
||||
res = {
|
||||
"errcode": "M_UNRECOGNIZED",
|
||||
"error": "Unrecognized request"
|
||||
|
@ -62,6 +67,64 @@ class FakeMatrixApi extends MockClient {
|
|||
return Response(json.encode(res), 100);
|
||||
});
|
||||
|
||||
static Map<String, dynamic> messagesResponse = {
|
||||
"start": "t47429-4392820_219380_26003_2265",
|
||||
"end": "t47409-4357353_219380_26003_2265",
|
||||
"chunk": [
|
||||
{
|
||||
"content": {
|
||||
"body": "This is an example text message",
|
||||
"msgtype": "m.text",
|
||||
"format": "org.matrix.custom.html",
|
||||
"formatted_body": "<b>This is an example text message</b>"
|
||||
},
|
||||
"type": "m.room.message",
|
||||
"event_id": "3143273582443PhrSn:example.org",
|
||||
"room_id": "!1234:example.com",
|
||||
"sender": "@example:example.org",
|
||||
"origin_server_ts": 1432735824653,
|
||||
"unsigned": {"age": 1234}
|
||||
},
|
||||
{
|
||||
"content": {"name": "The room name"},
|
||||
"type": "m.room.name",
|
||||
"event_id": "2143273582443PhrSn:example.org",
|
||||
"room_id": "!1234:example.com",
|
||||
"sender": "@example:example.org",
|
||||
"origin_server_ts": 1432735824653,
|
||||
"unsigned": {"age": 1234},
|
||||
"state_key": ""
|
||||
},
|
||||
{
|
||||
"content": {
|
||||
"body": "Gangnam Style",
|
||||
"url": "mxc://example.org/a526eYUSFFxlgbQYZmo442",
|
||||
"info": {
|
||||
"thumbnail_url": "mxc://example.org/FHyPlCeYUSFFxlgbQYZmoEoe",
|
||||
"thumbnail_info": {
|
||||
"mimetype": "image/jpeg",
|
||||
"size": 46144,
|
||||
"w": 300,
|
||||
"h": 300
|
||||
},
|
||||
"w": 480,
|
||||
"h": 320,
|
||||
"duration": 2140786,
|
||||
"size": 1563685,
|
||||
"mimetype": "video/mp4"
|
||||
},
|
||||
"msgtype": "m.video"
|
||||
},
|
||||
"type": "m.room.message",
|
||||
"event_id": "1143273582443PhrSn:example.org",
|
||||
"room_id": "!1234:example.com",
|
||||
"sender": "@example:example.org",
|
||||
"origin_server_ts": 1432735824653,
|
||||
"unsigned": {"age": 1234}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
static Map<String, dynamic> syncResponse = {
|
||||
"next_batch": Random().nextDouble().toString(),
|
||||
"presence": {
|
||||
|
@ -457,6 +520,8 @@ class FakeMatrixApi extends MockClient {
|
|||
|
||||
static final Map<String, Map<String, dynamic>> api = {
|
||||
"GET": {
|
||||
"/client/r0/rooms/1/state/m.room.member/@alice:example.com": (var req) =>
|
||||
{"displayname": "Alice"},
|
||||
"/client/r0/profile/@getme:example.com": (var req) => {
|
||||
"avatar_url": "mxc://test",
|
||||
"displayname": "You got me",
|
||||
|
@ -480,65 +545,10 @@ class FakeMatrixApi extends MockClient {
|
|||
"origin_server_ts": 1432735824653,
|
||||
"unsigned": {"age": 1234}
|
||||
},
|
||||
"/client/r0/rooms/!localpart:server.abc/messages?from=&dir=b&limit=100&filter=%7B%22room%22:%7B%22state%22:%7B%22lazy_load_members%22:true%7D%7D%7D":
|
||||
(var req) => messagesResponse,
|
||||
"/client/r0/rooms/!1234:example.com/messages?from=1234&dir=b&limit=100&filter=%7B%22room%22:%7B%22state%22:%7B%22lazy_load_members%22:true%7D%7D%7D":
|
||||
(var req) => {
|
||||
"start": "t47429-4392820_219380_26003_2265",
|
||||
"end": "t47409-4357353_219380_26003_2265",
|
||||
"chunk": [
|
||||
{
|
||||
"content": {
|
||||
"body": "This is an example text message",
|
||||
"msgtype": "m.text",
|
||||
"format": "org.matrix.custom.html",
|
||||
"formatted_body": "<b>This is an example text message</b>"
|
||||
},
|
||||
"type": "m.room.message",
|
||||
"event_id": "3143273582443PhrSn:example.org",
|
||||
"room_id": "!1234:example.com",
|
||||
"sender": "@example:example.org",
|
||||
"origin_server_ts": 1432735824653,
|
||||
"unsigned": {"age": 1234}
|
||||
},
|
||||
{
|
||||
"content": {"name": "The room name"},
|
||||
"type": "m.room.name",
|
||||
"event_id": "2143273582443PhrSn:example.org",
|
||||
"room_id": "!1234:example.com",
|
||||
"sender": "@example:example.org",
|
||||
"origin_server_ts": 1432735824653,
|
||||
"unsigned": {"age": 1234},
|
||||
"state_key": ""
|
||||
},
|
||||
{
|
||||
"content": {
|
||||
"body": "Gangnam Style",
|
||||
"url": "mxc://example.org/a526eYUSFFxlgbQYZmo442",
|
||||
"info": {
|
||||
"thumbnail_url":
|
||||
"mxc://example.org/FHyPlCeYUSFFxlgbQYZmoEoe",
|
||||
"thumbnail_info": {
|
||||
"mimetype": "image/jpeg",
|
||||
"size": 46144,
|
||||
"w": 300,
|
||||
"h": 300
|
||||
},
|
||||
"w": 480,
|
||||
"h": 320,
|
||||
"duration": 2140786,
|
||||
"size": 1563685,
|
||||
"mimetype": "video/mp4"
|
||||
},
|
||||
"msgtype": "m.video"
|
||||
},
|
||||
"type": "m.room.message",
|
||||
"event_id": "1143273582443PhrSn:example.org",
|
||||
"room_id": "!1234:example.com",
|
||||
"sender": "@example:example.org",
|
||||
"origin_server_ts": 1432735824653,
|
||||
"unsigned": {"age": 1234}
|
||||
}
|
||||
]
|
||||
},
|
||||
(var req) => messagesResponse,
|
||||
"/client/versions": (var req) => {
|
||||
"versions": [
|
||||
"r0.0.1",
|
||||
|
|
|
@ -29,14 +29,17 @@ import 'package:famedlysdk/src/sync/RoomUpdate.dart';
|
|||
import 'package:famedlysdk/src/utils/ChatTime.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import 'FakeMatrixApi.dart';
|
||||
|
||||
void main() {
|
||||
/// All Tests related to the MxContent
|
||||
group("RoomList", () {
|
||||
final roomID = "!1:example.com";
|
||||
|
||||
test("Create and insert one room", () async {
|
||||
final Client client = Client("testclient");
|
||||
client.homeserver = "https://testserver.abc";
|
||||
final Client client = Client("testclient", debug: true);
|
||||
client.connection.httpClient = FakeMatrixApi();
|
||||
await client.checkServer("https://fakeserver.notexisting");
|
||||
client.prevBatch = "1234";
|
||||
|
||||
int updateCount = 0;
|
||||
|
@ -84,8 +87,9 @@ void main() {
|
|||
});
|
||||
|
||||
test("Restort", () async {
|
||||
final Client client = Client("testclient");
|
||||
client.homeserver = "https://testserver.abc";
|
||||
final Client client = Client("testclient", debug: true);
|
||||
client.connection.httpClient = FakeMatrixApi();
|
||||
await client.checkServer("https://fakeserver.notexisting");
|
||||
client.prevBatch = "1234";
|
||||
|
||||
int updateCount = 0;
|
||||
|
@ -186,7 +190,7 @@ void main() {
|
|||
await new Future.delayed(new Duration(milliseconds: 50));
|
||||
|
||||
expect(updateCount, 5);
|
||||
expect(roomUpdates, 2);
|
||||
expect(roomUpdates, 3);
|
||||
expect(insertList, [0, 1]);
|
||||
expect(removeList, []);
|
||||
|
||||
|
@ -224,8 +228,9 @@ void main() {
|
|||
});
|
||||
|
||||
test("onlyLeft", () async {
|
||||
final Client client = Client("testclient");
|
||||
client.homeserver = "https://testserver.abc";
|
||||
final Client client = Client("testclient", debug: true);
|
||||
client.connection.httpClient = FakeMatrixApi();
|
||||
await client.checkServer("https://fakeserver.notexisting");
|
||||
client.prevBatch = "1234";
|
||||
|
||||
int updateCount = 0;
|
||||
|
@ -268,9 +273,9 @@ void main() {
|
|||
expect(roomList.eventSub != null, true);
|
||||
expect(roomList.roomSub != null, true);
|
||||
expect(roomList.rooms[0].id, "2");
|
||||
expect(updateCount, 2);
|
||||
expect(insertList, [0]);
|
||||
expect(removeList, []);
|
||||
expect(updateCount, 2);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -145,9 +145,7 @@ void main() {
|
|||
});
|
||||
|
||||
test("sendReadReceipt", () async {
|
||||
final dynamic resp =
|
||||
await room.sendReadReceipt("§1234:fakeServer.notExisting");
|
||||
expect(resp, {});
|
||||
});
|
||||
|
||||
test("requestParticipants", () async {
|
||||
|
@ -167,28 +165,25 @@ void main() {
|
|||
});
|
||||
|
||||
test("setName", () async {
|
||||
final dynamic resp = await room.setName("Testname");
|
||||
expect(resp["event_id"], "42");
|
||||
final String eventId = await room.setName("Testname");
|
||||
expect(eventId, "42");
|
||||
});
|
||||
|
||||
test("setDescription", () async {
|
||||
final dynamic resp = await room.setDescription("Testname");
|
||||
expect(resp["event_id"], "42");
|
||||
final String eventId = await room.setDescription("Testname");
|
||||
expect(eventId, "42");
|
||||
});
|
||||
|
||||
test("kick", () async {
|
||||
final dynamic resp = await room.kick("Testname");
|
||||
expect(resp, {});
|
||||
await room.kick("Testname");
|
||||
});
|
||||
|
||||
test("ban", () async {
|
||||
final dynamic resp = await room.ban("Testname");
|
||||
expect(resp, {});
|
||||
await room.ban("Testname");
|
||||
});
|
||||
|
||||
test("unban", () async {
|
||||
final dynamic resp = await room.unban("Testname");
|
||||
expect(resp, {});
|
||||
await room.unban("Testname");
|
||||
});
|
||||
|
||||
test("PowerLevels", () async {
|
||||
|
@ -259,18 +254,17 @@ void main() {
|
|||
expect(room.canSendEvent("m.room.power_levels"), false);
|
||||
expect(room.canSendEvent("m.room.member"), false);
|
||||
expect(room.canSendEvent("m.room.message"), true);
|
||||
final dynamic resp =
|
||||
final String resp =
|
||||
await room.setPower("@test:fakeServer.notExisting", 90);
|
||||
expect(resp["event_id"], "42");
|
||||
expect(resp, "42");
|
||||
});
|
||||
|
||||
test("invite", () async {
|
||||
final dynamic resp = await room.invite("Testname");
|
||||
expect(resp, {});
|
||||
await room.invite("Testname");
|
||||
});
|
||||
|
||||
test("getParticipants", () async {
|
||||
room.states["@alice:test.abc"] = RoomState(
|
||||
room.setState(RoomState(
|
||||
senderId: "@alice:test.abc",
|
||||
typeKey: "m.room.member",
|
||||
roomId: room.id,
|
||||
|
@ -278,15 +272,14 @@ void main() {
|
|||
eventId: "12345",
|
||||
time: ChatTime.now(),
|
||||
content: {"displayname": "alice"},
|
||||
stateKey: "@alice:test.abc");
|
||||
stateKey: "@alice:test.abc"));
|
||||
final List<User> userList = room.getParticipants();
|
||||
expect(userList.length, 1);
|
||||
expect(userList[0].displayName, "alice");
|
||||
expect(userList.length, 4);
|
||||
expect(userList[3].displayName, "alice");
|
||||
});
|
||||
|
||||
test("addToDirectChat", () async {
|
||||
final dynamic resp = await room.addToDirectChat("Testname");
|
||||
expect(resp, {});
|
||||
await room.addToDirectChat("Testname");
|
||||
});
|
||||
|
||||
test("getTimeline", () async {
|
||||
|
@ -295,7 +288,10 @@ void main() {
|
|||
});
|
||||
|
||||
test("getUserByMXID", () async {
|
||||
final User user = await room.getUserByMXID("@getme:example.com");
|
||||
User user;
|
||||
try {
|
||||
user = await room.getUserByMXID("@getme:example.com");
|
||||
} catch (_) {}
|
||||
expect(user.stateKey, "@getme:example.com");
|
||||
expect(user.calcDisplayname(), "You got me");
|
||||
});
|
||||
|
|
Loading…
Reference in a new issue