[Connection] Add upload method
This commit is contained in:
parent
78b59212d1
commit
7d5da300c4
|
@ -23,6 +23,7 @@
|
|||
|
||||
import 'dart:async';
|
||||
import 'dart:core';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:famedlysdk/src/AccountData.dart';
|
||||
import 'package:famedlysdk/src/Presence.dart';
|
||||
|
@ -310,6 +311,18 @@ class Client {
|
|||
return resp["room_id"];
|
||||
}
|
||||
|
||||
/// Uploads a new user avatar for this user. Returns ErrorResponse if something went wrong.
|
||||
Future<dynamic> setAvatar(File file) async {
|
||||
final uploadResp = await connection.upload(file);
|
||||
if (uploadResp is ErrorResponse) return uploadResp;
|
||||
final setAvatarResp = 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;
|
||||
}
|
||||
|
||||
/// Fetches the pushrules for the logged in user.
|
||||
/// These are needed for notifications on Android
|
||||
Future<PushrulesResponse> getPushrules() async {
|
||||
|
|
|
@ -24,11 +24,13 @@
|
|||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:core';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:famedlysdk/src/Room.dart';
|
||||
import 'package:famedlysdk/src/RoomList.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:mime_type/mime_type.dart';
|
||||
|
||||
import 'Client.dart';
|
||||
import 'User.dart';
|
||||
|
@ -204,18 +206,23 @@ class Connection {
|
|||
/// ```
|
||||
///
|
||||
Future<dynamic> jsonRequest(
|
||||
{HTTPType type, String action, dynamic data = "", int timeout}) async {
|
||||
{HTTPType type,
|
||||
String action,
|
||||
dynamic data = "",
|
||||
int timeout,
|
||||
String contentType = "application/json"}) async {
|
||||
if (client.isLogged() == false && client.homeserver == null)
|
||||
throw ("No homeserver specified.");
|
||||
if (timeout == null) timeout = syncTimeoutSec + 5;
|
||||
dynamic json;
|
||||
if (data is Map) data.removeWhere((k, v) => v == null);
|
||||
(!(data is String)) ? json = jsonEncode(data) : json = data;
|
||||
if (data is List<int>) json = data;
|
||||
|
||||
final url = "${client.homeserver}/_matrix${action}";
|
||||
|
||||
Map<String, String> headers = {
|
||||
"Content-type": "application/json",
|
||||
"Content-Type": contentType,
|
||||
};
|
||||
if (client.isLogged())
|
||||
headers["Authorization"] = "Bearer ${client.accessToken}";
|
||||
|
@ -279,6 +286,24 @@ class Connection {
|
|||
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(File file) async {
|
||||
List<int> fileBytes;
|
||||
if (client.homeserver != "https://fakeServer.notExisting")
|
||||
fileBytes = await file.readAsBytes();
|
||||
String fileName = file.path.split("/").last;
|
||||
String mimeType = mime(file.path);
|
||||
print("[UPLOADING] $fileName, type: $mimeType, size: ${fileBytes?.length}");
|
||||
final 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"];
|
||||
}
|
||||
|
||||
Future<dynamic> _syncRequest;
|
||||
|
||||
Future<void> _sync() async {
|
||||
|
@ -487,10 +512,10 @@ class _LifecycleEventHandler extends WidgetsBindingObserver {
|
|||
case AppLifecycleState.inactive:
|
||||
case AppLifecycleState.paused:
|
||||
case AppLifecycleState.suspending:
|
||||
await suspendingCallBack();
|
||||
if (suspendingCallBack != null) await suspendingCallBack();
|
||||
break;
|
||||
case AppLifecycleState.resumed:
|
||||
await resumeCallBack();
|
||||
if (resumeCallBack != null) await resumeCallBack();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,6 +21,8 @@
|
|||
* along with famedlysdk. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:famedlysdk/src/Client.dart';
|
||||
import 'package:famedlysdk/src/Event.dart';
|
||||
import 'package:famedlysdk/src/RoomAccountData.dart';
|
||||
|
@ -29,6 +31,8 @@ import 'package:famedlysdk/src/responses/ErrorResponse.dart';
|
|||
import 'package:famedlysdk/src/sync/EventUpdate.dart';
|
||||
import 'package:famedlysdk/src/utils/ChatTime.dart';
|
||||
import 'package:famedlysdk/src/utils/MxContent.dart';
|
||||
import 'package:image/image.dart';
|
||||
import 'package:mime_type/mime_type.dart';
|
||||
|
||||
import './User.dart';
|
||||
import 'Connection.dart';
|
||||
|
@ -223,18 +227,100 @@ class Room {
|
|||
return res;
|
||||
}
|
||||
|
||||
/// Call the Matrix API to send a simple text message.
|
||||
Future<dynamic> sendText(String message, {String txid = null}) async {
|
||||
Future<dynamic> _sendRawEventNow(Map<String, dynamic> content,
|
||||
{String txid = null}) async {
|
||||
if (txid == null) txid = "txid${DateTime.now().millisecondsSinceEpoch}";
|
||||
final dynamic res = await client.connection.jsonRequest(
|
||||
type: HTTPType.PUT,
|
||||
action: "/client/r0/rooms/${id}/send/m.room.message/$txid",
|
||||
data: {"msgtype": "m.text", "body": message});
|
||||
data: content);
|
||||
if (res is ErrorResponse) client.connection.onError.add(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
Future<String> sendTextEvent(String message, {String txid = null}) async {
|
||||
Future<String> sendTextEvent(String message, {String txid = null}) =>
|
||||
sendEvent({"msgtype": "m.text", "body": message}, txid: txid);
|
||||
|
||||
Future<String> sendFileEvent(File file, String msgType,
|
||||
{String txid = null}) async {
|
||||
// Try to get the size of the file
|
||||
int size;
|
||||
try {
|
||||
size = (await file.readAsBytes()).length;
|
||||
} catch (e) {
|
||||
print("[UPLOAD] Could not get size. Reason: ${e.toString()}");
|
||||
}
|
||||
|
||||
// Upload file
|
||||
String fileName = file.path.split("/").last;
|
||||
String mimeType = mime(fileName);
|
||||
final dynamic uploadResp = await client.connection.upload(file);
|
||||
if (uploadResp is ErrorResponse) return null;
|
||||
|
||||
// Send event
|
||||
Map<String, dynamic> content = {
|
||||
"msgtype": msgType,
|
||||
"body": fileName,
|
||||
"filename": fileName,
|
||||
"url": uploadResp,
|
||||
"info": {
|
||||
"mimetype": mimeType,
|
||||
}
|
||||
};
|
||||
if (size != null)
|
||||
content["info"] = {
|
||||
"size": size,
|
||||
"mimetype": mimeType,
|
||||
};
|
||||
return await sendEvent(content, txid: txid);
|
||||
}
|
||||
|
||||
Future<String> sendImageEvent(File file, {String txid = null}) async {
|
||||
String fileName = file.path.split("/").last;
|
||||
Map<String, dynamic> info;
|
||||
|
||||
// Try to manipulate the file size and create a thumbnail
|
||||
try {
|
||||
Image image = copyResize(decodeImage(file.readAsBytesSync()), width: 800);
|
||||
Image thumbnail = copyResize(image, width: 236);
|
||||
|
||||
file = File(fileName)..writeAsBytesSync(encodePng(image));
|
||||
File thumbnailFile = File(fileName)
|
||||
..writeAsBytesSync(encodePng(thumbnail));
|
||||
final dynamic uploadThumbnailResp =
|
||||
await client.connection.upload(thumbnailFile);
|
||||
if (uploadThumbnailResp is ErrorResponse) throw (uploadThumbnailResp);
|
||||
info = {
|
||||
"size": image.getBytes().length,
|
||||
"mimetype": mime(file.path),
|
||||
"w": image.width,
|
||||
"h": image.height,
|
||||
"thumbnail_url": uploadThumbnailResp,
|
||||
"thumbnail_info": {
|
||||
"w": thumbnail.width,
|
||||
"h": thumbnail.height,
|
||||
"mimetype": mime(thumbnailFile.path),
|
||||
"size": thumbnail.getBytes().length
|
||||
},
|
||||
};
|
||||
} catch (e) {
|
||||
print(
|
||||
"[UPLOAD] Could not create thumbnail of image. Try to upload the unchanged file...");
|
||||
}
|
||||
|
||||
final dynamic uploadResp = await client.connection.upload(file);
|
||||
if (uploadResp is ErrorResponse) return null;
|
||||
Map<String, dynamic> content = {
|
||||
"msgtype": "m.image",
|
||||
"body": fileName,
|
||||
"url": uploadResp,
|
||||
};
|
||||
if (info != null) content["info"] = info;
|
||||
return await sendEvent(content, txid: txid);
|
||||
}
|
||||
|
||||
Future<String> sendEvent(Map<String, dynamic> content,
|
||||
{String txid = null}) async {
|
||||
final String type = "m.room.message";
|
||||
|
||||
// Create new transaction id
|
||||
|
@ -253,10 +339,7 @@ class Room {
|
|||
"sender": client.userID,
|
||||
"status": 0,
|
||||
"origin_server_ts": now,
|
||||
"content": {
|
||||
"msgtype": "m.text",
|
||||
"body": message,
|
||||
}
|
||||
"content": content
|
||||
});
|
||||
client.connection.onEvent.add(eventUpdate);
|
||||
await client.store?.transaction(() {
|
||||
|
@ -265,7 +348,7 @@ class Room {
|
|||
});
|
||||
|
||||
// Send the text and on success, store and display a *sent* event.
|
||||
final dynamic res = await sendText(message, txid: messageID);
|
||||
final dynamic res = await _sendRawEventNow(content, txid: messageID);
|
||||
|
||||
if (res is ErrorResponse || !(res["event_id"] is String)) {
|
||||
// On error, set status to -1
|
||||
|
@ -604,4 +687,17 @@ class Room {
|
|||
return powerLevelState.content["users"];
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Uploads a new user avatar for this room. Returns ErrorResponse if something went wrong
|
||||
/// and the event ID otherwise.
|
||||
Future<dynamic> setAvatar(File file) async {
|
||||
final uploadResp = await client.connection.upload(file);
|
||||
if (uploadResp is ErrorResponse) return uploadResp;
|
||||
final 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"];
|
||||
}
|
||||
}
|
||||
|
|
37
pubspec.lock
37
pubspec.lock
|
@ -8,6 +8,13 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.36.3"
|
||||
archive:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: archive
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.0.10"
|
||||
args:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -200,6 +207,13 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "3.1.3"
|
||||
image:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: image
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.1.4"
|
||||
intl:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -270,6 +284,13 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.9.6+3"
|
||||
mime_type:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: mime_type
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "0.2.4"
|
||||
package_config:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -298,6 +319,13 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.7.0"
|
||||
petitparser:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: petitparser
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "2.4.0"
|
||||
pool:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -450,6 +478,13 @@ packages:
|
|||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "1.0.13"
|
||||
xml:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: xml
|
||||
url: "https://pub.dartlang.org"
|
||||
source: hosted
|
||||
version: "3.5.0"
|
||||
yaml:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -458,5 +493,5 @@ packages:
|
|||
source: hosted
|
||||
version: "2.1.16"
|
||||
sdks:
|
||||
dart: ">=2.3.0-dev.0.1 <3.0.0"
|
||||
dart: ">=2.4.0 <3.0.0"
|
||||
flutter: ">=1.2.1 <2.0.0"
|
||||
|
|
|
@ -17,6 +17,8 @@ dependencies:
|
|||
|
||||
# Connection
|
||||
http: ^0.12.0+2
|
||||
mime_type: ^0.2.4
|
||||
image: ^2.1.4
|
||||
|
||||
# Time formatting
|
||||
intl: ^0.15.8
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
*/
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:famedlysdk/src/AccountData.dart';
|
||||
import 'package:famedlysdk/src/Client.dart';
|
||||
|
@ -270,6 +271,18 @@ void main() {
|
|||
expect(newID, "!1234:fakeServer.notExisting");
|
||||
});
|
||||
|
||||
test('upload', () async {
|
||||
final File testFile = File.fromUri(Uri.parse("fake/path/file.jpeg"));
|
||||
final dynamic resp = await matrix.connection.upload(testFile);
|
||||
expect(resp, "mxc://example.com/AQwafuaFswefuhsfAFAgsw");
|
||||
});
|
||||
|
||||
test('setAvatar', () async {
|
||||
final File testFile = File.fromUri(Uri.parse("fake/path/file.jpeg"));
|
||||
final dynamic resp = await matrix.setAvatar(testFile);
|
||||
expect(resp, null);
|
||||
});
|
||||
|
||||
test('getPushrules', () async {
|
||||
final PushrulesResponse pushrules = await matrix.getPushrules();
|
||||
final PushrulesResponse awaited_resp = PushrulesResponse.fromJson(
|
||||
|
|
|
@ -509,6 +509,8 @@ class FakeMatrixApi extends MockClient {
|
|||
"access_token": "abc123",
|
||||
"device_id": "GHTYAJCE"
|
||||
},
|
||||
"/media/r0/upload?filename=file.jpeg": (var req) =>
|
||||
{"content_uri": "mxc://example.com/AQwafuaFswefuhsfAFAgsw"},
|
||||
"/client/r0/logout": (var reqI) => {},
|
||||
"/client/r0/pushers/set": (var reqI) => {},
|
||||
"/client/r0/join/1234": (var reqI) => {"room_id": "1234"},
|
||||
|
@ -523,10 +525,18 @@ class FakeMatrixApi extends MockClient {
|
|||
"/client/r0/rooms/!localpart:server.abc/invite": (var reqI) => {},
|
||||
},
|
||||
"PUT": {
|
||||
"/client/r0/rooms/!localpart:server.abc/send/m.room.message/testtxid":
|
||||
(var reqI) => {
|
||||
"event_id": "42",
|
||||
},
|
||||
"/client/r0/rooms/!1234:example.com/send/m.room.message/1234":
|
||||
(var reqI) => {
|
||||
"event_id": "42",
|
||||
},
|
||||
"/client/r0/profile/@test:fakeServer.notExisting/avatar_url":
|
||||
(var reqI) => {},
|
||||
"/client/r0/rooms/!localpart:server.abc/state/m.room.avatar/":
|
||||
(var reqI) => {"event_id": "YUwRidLecu:example.com"},
|
||||
"/client/r0/rooms/!localpart:server.abc/state/m.room.name": (var reqI) =>
|
||||
{
|
||||
"event_id": "42",
|
||||
|
|
|
@ -21,6 +21,8 @@
|
|||
* along with famedlysdk. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:famedlysdk/src/Client.dart';
|
||||
import 'package:famedlysdk/src/Event.dart';
|
||||
import 'package:famedlysdk/src/Room.dart';
|
||||
|
@ -255,5 +257,39 @@ void main() {
|
|||
expect(user.stateKey, "@getme:example.com");
|
||||
expect(user.calcDisplayname(), "You got me");
|
||||
});
|
||||
|
||||
test('setAvatar', () async {
|
||||
final File testFile = File.fromUri(Uri.parse("fake/path/file.jpeg"));
|
||||
final dynamic resp = await room.setAvatar(testFile);
|
||||
expect(resp, "YUwRidLecu:example.com");
|
||||
});
|
||||
|
||||
test('sendEvent', () async {
|
||||
final dynamic resp = await room.sendEvent(
|
||||
{"msgtype": "m.text", "body": "hello world"},
|
||||
txid: "testtxid");
|
||||
expect(resp, "42");
|
||||
});
|
||||
|
||||
test('sendEvent', () async {
|
||||
final dynamic resp =
|
||||
await room.sendTextEvent("Hello world", txid: "testtxid");
|
||||
expect(resp, "42");
|
||||
});
|
||||
|
||||
// Not working because there is no real file to test it...
|
||||
test('sendImageEvent', () async {
|
||||
final File testFile = File.fromUri(Uri.parse("fake/path/file.jpeg"));
|
||||
final dynamic resp =
|
||||
await room.sendImageEvent(testFile, txid: "testtxid");
|
||||
expect(resp, "42");
|
||||
});
|
||||
|
||||
test('sendFileEvent', () async {
|
||||
final File testFile = File.fromUri(Uri.parse("fake/path/file.jpeg"));
|
||||
final dynamic resp =
|
||||
await room.sendFileEvent(testFile, "m.file", txid: "testtxid");
|
||||
expect(resp, "42");
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue