Merge branch 'matrixfile-refactor-all' into 'master'
MatrixFile refactoring for thumbnails See merge request famedly/famedlysdk!371
This commit is contained in:
commit
0ac7aec071
|
@ -449,7 +449,7 @@ class Client {
|
||||||
|
|
||||||
/// 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.path);
|
final uploadResp = await api.upload(file.bytes, file.name);
|
||||||
await api.setAvatarUrl(userID, Uri.parse(uploadResp));
|
await api.setAvatarUrl(userID, Uri.parse(uploadResp));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -424,7 +424,7 @@ class Event extends MatrixEvent {
|
||||||
encryptedFile.sha256 = fileMap['hashes']['sha256'];
|
encryptedFile.sha256 = fileMap['hashes']['sha256'];
|
||||||
uint8list = await decryptFile(encryptedFile);
|
uint8list = await decryptFile(encryptedFile);
|
||||||
}
|
}
|
||||||
return MatrixFile(bytes: uint8list, path: '/$body');
|
return MatrixFile(bytes: uint8list, name: body);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns a localized String representation of this event. For a
|
/// Returns a localized String representation of this event. For a
|
||||||
|
|
|
@ -25,9 +25,7 @@ import 'package:famedlysdk/src/event.dart';
|
||||||
import 'package:famedlysdk/src/utils/event_update.dart';
|
import 'package:famedlysdk/src/utils/event_update.dart';
|
||||||
import 'package:famedlysdk/src/utils/room_update.dart';
|
import 'package:famedlysdk/src/utils/room_update.dart';
|
||||||
import 'package:famedlysdk/src/utils/matrix_file.dart';
|
import 'package:famedlysdk/src/utils/matrix_file.dart';
|
||||||
import 'package:image/image.dart';
|
|
||||||
import 'package:matrix_file_e2ee/matrix_file_e2ee.dart';
|
import 'package:matrix_file_e2ee/matrix_file_e2ee.dart';
|
||||||
import 'package:mime/mime.dart';
|
|
||||||
import 'package:html_unescape/html_unescape.dart';
|
import 'package:html_unescape/html_unescape.dart';
|
||||||
|
|
||||||
import './user.dart';
|
import './user.dart';
|
||||||
|
@ -504,79 +502,53 @@ class Room {
|
||||||
return sendEvent(event, txid: txid, inReplyTo: inReplyTo);
|
return sendEvent(event, txid: txid, inReplyTo: inReplyTo);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sends a [file] to this room after uploading it. The [msgType] is optional
|
/// Sends a [file] to this room after uploading it. Returns the mxc uri of
|
||||||
/// and will be detected by the mimetype of the file. Returns the mxc uri of
|
|
||||||
/// the uploaded file. If [waitUntilSent] is true, the future will wait until
|
/// the uploaded file. If [waitUntilSent] is true, the future will wait until
|
||||||
/// the message event has received the server. Otherwise the future will only
|
/// the message event has received the server. Otherwise the future will only
|
||||||
/// wait until the file has been uploaded.
|
/// wait until the file has been uploaded.
|
||||||
Future<String> sendFileEvent(
|
Future<String> sendFileEvent(
|
||||||
MatrixFile file, {
|
MatrixFile file, {
|
||||||
String msgType,
|
|
||||||
String txid,
|
String txid,
|
||||||
Event inReplyTo,
|
Event inReplyTo,
|
||||||
Map<String, dynamic> info,
|
|
||||||
bool waitUntilSent = false,
|
bool waitUntilSent = false,
|
||||||
MatrixFile thumbnail,
|
MatrixImageFile thumbnail,
|
||||||
}) async {
|
}) async {
|
||||||
Image fileImage;
|
MatrixFile uploadFile = file; // ignore: omit_local_variable_types
|
||||||
Image thumbnailImage;
|
MatrixFile uploadThumbnail = thumbnail; // ignore: omit_local_variable_types
|
||||||
EncryptedFile encryptedThumbnail;
|
|
||||||
String thumbnailUploadResp;
|
|
||||||
|
|
||||||
var fileName = file.path.split('/').last;
|
|
||||||
final mimeType = lookupMimeType(file.path, headerBytes: file.bytes) ?? '';
|
|
||||||
if (msgType == null) {
|
|
||||||
final metaType = (mimeType).split('/')[0];
|
|
||||||
switch (metaType) {
|
|
||||||
case 'image':
|
|
||||||
case 'audio':
|
|
||||||
case 'video':
|
|
||||||
msgType = 'm.$metaType';
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
msgType = 'm.file';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (msgType == 'm.image') {
|
|
||||||
fileImage = decodeImage(file.bytes.toList());
|
|
||||||
if (thumbnail != null) {
|
|
||||||
thumbnailImage = decodeImage(thumbnail.bytes.toList());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
final sendEncrypted = encrypted && client.fileEncryptionEnabled;
|
|
||||||
EncryptedFile encryptedFile;
|
EncryptedFile encryptedFile;
|
||||||
if (sendEncrypted) {
|
EncryptedFile encryptedThumbnail;
|
||||||
|
if (encrypted && client.fileEncryptionEnabled) {
|
||||||
encryptedFile = await file.encrypt();
|
encryptedFile = await file.encrypt();
|
||||||
|
uploadFile = encryptedFile.toMatrixFile();
|
||||||
|
|
||||||
if (thumbnail != null) {
|
if (thumbnail != null) {
|
||||||
encryptedThumbnail = await thumbnail.encrypt();
|
encryptedThumbnail = await thumbnail.encrypt();
|
||||||
|
uploadThumbnail = encryptedThumbnail.toMatrixFile();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
final uploadResp = await client.api.upload(
|
final uploadResp = await client.api.upload(
|
||||||
file.bytes,
|
uploadFile.bytes,
|
||||||
file.path,
|
uploadFile.name,
|
||||||
contentType: sendEncrypted ? 'application/octet-stream' : null,
|
contentType: uploadFile.mimeType,
|
||||||
);
|
);
|
||||||
if (thumbnail != null) {
|
final thumbnailUploadResp = uploadThumbnail != null
|
||||||
thumbnailUploadResp = await client.api.upload(
|
? await client.api.upload(
|
||||||
thumbnail.bytes,
|
uploadThumbnail.bytes,
|
||||||
thumbnail.path,
|
uploadThumbnail.name,
|
||||||
contentType: sendEncrypted ? 'application/octet-stream' : null,
|
contentType: uploadThumbnail.mimeType,
|
||||||
);
|
)
|
||||||
}
|
: null;
|
||||||
|
|
||||||
// Send event
|
// Send event
|
||||||
var content = <String, dynamic>{
|
var content = <String, dynamic>{
|
||||||
'msgtype': msgType,
|
'msgtype': file.msgType,
|
||||||
'body': fileName,
|
'body': file.name,
|
||||||
'filename': fileName,
|
'filename': file.name,
|
||||||
if (!sendEncrypted) 'url': uploadResp,
|
if (encryptedFile == null) 'url': uploadResp,
|
||||||
if (sendEncrypted)
|
if (encryptedFile != null)
|
||||||
'file': {
|
'file': {
|
||||||
'url': uploadResp,
|
'url': uploadResp,
|
||||||
'mimetype': mimeType,
|
'mimetype': file.mimeType,
|
||||||
'v': 'v2',
|
'v': 'v2',
|
||||||
'key': {
|
'key': {
|
||||||
'alg': 'A256CTR',
|
'alg': 'A256CTR',
|
||||||
|
@ -588,37 +560,27 @@ class Room {
|
||||||
'iv': encryptedFile.iv,
|
'iv': encryptedFile.iv,
|
||||||
'hashes': {'sha256': encryptedFile.sha256}
|
'hashes': {'sha256': encryptedFile.sha256}
|
||||||
},
|
},
|
||||||
'info': info ??
|
'info': {
|
||||||
{
|
...file.info,
|
||||||
'mimetype': mimeType,
|
if (thumbnail != null && encryptedThumbnail == null)
|
||||||
'size': file.size,
|
'thumbnail_url': thumbnailUploadResp,
|
||||||
if (fileImage != null) 'h': fileImage.height,
|
if (thumbnail != null && encryptedThumbnail != null)
|
||||||
if (fileImage != null) 'w': fileImage.width,
|
'thumbnail_file': {
|
||||||
if (thumbnailUploadResp != null && !sendEncrypted)
|
'url': thumbnailUploadResp,
|
||||||
'thumbnail_url': thumbnailUploadResp,
|
'mimetype': thumbnail.mimeType,
|
||||||
if (thumbnailUploadResp != null && sendEncrypted)
|
'v': 'v2',
|
||||||
'thumbnail_file': {
|
'key': {
|
||||||
'url': thumbnailUploadResp,
|
'alg': 'A256CTR',
|
||||||
'mimetype': mimeType,
|
'ext': true,
|
||||||
'v': 'v2',
|
'k': encryptedThumbnail.k,
|
||||||
'key': {
|
'key_ops': ['encrypt', 'decrypt'],
|
||||||
'alg': 'A256CTR',
|
'kty': 'oct'
|
||||||
'ext': true,
|
},
|
||||||
'k': encryptedThumbnail.k,
|
'iv': encryptedThumbnail.iv,
|
||||||
'key_ops': ['encrypt', 'decrypt'],
|
'hashes': {'sha256': encryptedThumbnail.sha256}
|
||||||
'kty': 'oct'
|
},
|
||||||
},
|
if (thumbnail != null) 'thumbnail_info': thumbnail.info,
|
||||||
'iv': encryptedThumbnail.iv,
|
}
|
||||||
'hashes': {'sha256': encryptedThumbnail.sha256}
|
|
||||||
},
|
|
||||||
if (thumbnailImage != null)
|
|
||||||
'thumbnail_info': {
|
|
||||||
'h': thumbnailImage.height,
|
|
||||||
'mimetype': mimeType,
|
|
||||||
'size': thumbnail.size,
|
|
||||||
'w': thumbnailImage.width,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
final sendResponse = sendEvent(
|
final sendResponse = sendEvent(
|
||||||
content,
|
content,
|
||||||
|
@ -631,79 +593,6 @@ class Room {
|
||||||
return uploadResp;
|
return uploadResp;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Sends an audio file to this room and returns the mxc uri.
|
|
||||||
Future<String> sendAudioEvent(MatrixFile file,
|
|
||||||
{String txid, Event inReplyTo}) async {
|
|
||||||
return await sendFileEvent(file,
|
|
||||||
msgType: 'm.audio', txid: txid, inReplyTo: inReplyTo);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sends an image to this room and returns the mxc uri.
|
|
||||||
Future<String> sendImageEvent(MatrixFile file,
|
|
||||||
{String txid, int width, int height, Event inReplyTo}) async {
|
|
||||||
return await sendFileEvent(file,
|
|
||||||
msgType: 'm.image',
|
|
||||||
txid: txid,
|
|
||||||
inReplyTo: inReplyTo,
|
|
||||||
info: {
|
|
||||||
'size': file.size,
|
|
||||||
'mimetype': lookupMimeType(file.path, headerBytes: file.bytes),
|
|
||||||
'w': width,
|
|
||||||
'h': height,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sends an video to this room and returns the mxc uri.
|
|
||||||
Future<String> sendVideoEvent(MatrixFile file,
|
|
||||||
{String txid,
|
|
||||||
int videoWidth,
|
|
||||||
int videoHeight,
|
|
||||||
int duration,
|
|
||||||
MatrixFile thumbnail,
|
|
||||||
int thumbnailWidth,
|
|
||||||
int thumbnailHeight,
|
|
||||||
Event inReplyTo}) async {
|
|
||||||
var info = <String, dynamic>{
|
|
||||||
'size': file.size,
|
|
||||||
'mimetype': lookupMimeType(file.path, headerBytes: file.bytes),
|
|
||||||
};
|
|
||||||
if (videoWidth != null) {
|
|
||||||
info['w'] = videoWidth;
|
|
||||||
}
|
|
||||||
if (thumbnailHeight != null) {
|
|
||||||
info['h'] = thumbnailHeight;
|
|
||||||
}
|
|
||||||
if (duration != null) {
|
|
||||||
info['duration'] = duration;
|
|
||||||
}
|
|
||||||
if (thumbnail != null && !(encrypted && client.encryptionEnabled)) {
|
|
||||||
final thumbnailUploadResp = await client.api.upload(
|
|
||||||
thumbnail.bytes,
|
|
||||||
thumbnail.path,
|
|
||||||
);
|
|
||||||
info['thumbnail_url'] = thumbnailUploadResp;
|
|
||||||
info['thumbnail_info'] = {
|
|
||||||
'size': thumbnail.size,
|
|
||||||
'mimetype':
|
|
||||||
lookupMimeType(thumbnail.path, headerBytes: thumbnail.bytes),
|
|
||||||
};
|
|
||||||
if (thumbnailWidth != null) {
|
|
||||||
info['thumbnail_info']['w'] = thumbnailWidth;
|
|
||||||
}
|
|
||||||
if (thumbnailHeight != null) {
|
|
||||||
info['thumbnail_info']['h'] = thumbnailHeight;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return await sendFileEvent(
|
|
||||||
file,
|
|
||||||
msgType: 'm.video',
|
|
||||||
txid: txid,
|
|
||||||
inReplyTo: inReplyTo,
|
|
||||||
info: info,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sends an event to this room with this json as a content. Returns the
|
/// Sends an event to this room with this json as a content. Returns the
|
||||||
/// event ID generated from the server.
|
/// event ID generated from the server.
|
||||||
Future<String> sendEvent(Map<String, dynamic> content,
|
Future<String> sendEvent(Map<String, dynamic> content,
|
||||||
|
@ -1236,7 +1125,7 @@ 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.path);
|
final uploadResp = await client.api.upload(file.bytes, file.name);
|
||||||
return await client.api.sendState(
|
return await client.api.sendState(
|
||||||
id,
|
id,
|
||||||
EventTypes.RoomAvatar,
|
EventTypes.RoomAvatar,
|
||||||
|
|
|
@ -2,20 +2,100 @@
|
||||||
|
|
||||||
import 'dart:typed_data';
|
import 'dart:typed_data';
|
||||||
import 'package:matrix_file_e2ee/matrix_file_e2ee.dart';
|
import 'package:matrix_file_e2ee/matrix_file_e2ee.dart';
|
||||||
|
import 'package:mime/mime.dart';
|
||||||
|
|
||||||
class MatrixFile {
|
class MatrixFile {
|
||||||
Uint8List bytes;
|
Uint8List bytes;
|
||||||
String path;
|
String name;
|
||||||
|
String mimeType;
|
||||||
|
|
||||||
/// Encrypts this file, changes the [bytes] and returns the
|
/// Encrypts this file and returns the
|
||||||
/// encryption information as an [EncryptedFile].
|
/// encryption information as an [EncryptedFile].
|
||||||
Future<EncryptedFile> encrypt() async {
|
Future<EncryptedFile> encrypt() async {
|
||||||
var encryptFile2 = encryptFile(bytes);
|
return await encryptFile(bytes);
|
||||||
final encryptedFile = await encryptFile2;
|
}
|
||||||
bytes = encryptedFile.data;
|
|
||||||
return encryptedFile;
|
MatrixFile({this.bytes, this.name, this.mimeType}) {
|
||||||
|
mimeType ??= lookupMimeType(name, headerBytes: bytes);
|
||||||
|
name = name.split('/').last.toLowerCase();
|
||||||
}
|
}
|
||||||
|
|
||||||
MatrixFile({this.bytes, String path}) : path = path.toLowerCase();
|
|
||||||
int get size => bytes.length;
|
int get size => bytes.length;
|
||||||
|
|
||||||
|
String get msgType => 'm.file';
|
||||||
|
|
||||||
|
Map<String, dynamic> get info => ({
|
||||||
|
'mimetype': mimeType,
|
||||||
|
'size': size,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
class MatrixImageFile extends MatrixFile {
|
||||||
|
int width;
|
||||||
|
int height;
|
||||||
|
String blurhash;
|
||||||
|
|
||||||
|
MatrixImageFile(
|
||||||
|
{Uint8List bytes,
|
||||||
|
String name,
|
||||||
|
String mimeType,
|
||||||
|
this.width,
|
||||||
|
this.height,
|
||||||
|
this.blurhash})
|
||||||
|
: super(bytes: bytes, name: name, mimeType: mimeType);
|
||||||
|
@override
|
||||||
|
String get msgType => 'm.image';
|
||||||
|
@override
|
||||||
|
Map<String, dynamic> get info => ({
|
||||||
|
...super.info,
|
||||||
|
if (width != null) 'w': width,
|
||||||
|
if (height != null) 'h': height,
|
||||||
|
if (blurhash != null) 'xyz.amorgan.blurhash': blurhash,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
class MatrixVideoFile extends MatrixFile {
|
||||||
|
int width;
|
||||||
|
int height;
|
||||||
|
int duration;
|
||||||
|
|
||||||
|
MatrixVideoFile(
|
||||||
|
{Uint8List bytes,
|
||||||
|
String name,
|
||||||
|
String mimeType,
|
||||||
|
this.width,
|
||||||
|
this.height,
|
||||||
|
this.duration})
|
||||||
|
: super(bytes: bytes, name: name, mimeType: mimeType);
|
||||||
|
@override
|
||||||
|
String get msgType => 'm.video';
|
||||||
|
@override
|
||||||
|
Map<String, dynamic> get info => ({
|
||||||
|
...super.info,
|
||||||
|
if (width != null) 'w': width,
|
||||||
|
if (height != null) 'h': height,
|
||||||
|
if (duration != null) 'duration': duration,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
class MatrixAudioFile extends MatrixFile {
|
||||||
|
int duration;
|
||||||
|
|
||||||
|
MatrixAudioFile(
|
||||||
|
{Uint8List bytes, String name, String mimeType, this.duration})
|
||||||
|
: super(bytes: bytes, name: name, mimeType: mimeType);
|
||||||
|
@override
|
||||||
|
String get msgType => 'm.audio';
|
||||||
|
@override
|
||||||
|
Map<String, dynamic> get info => ({
|
||||||
|
...super.info,
|
||||||
|
if (duration != null) 'duration': duration,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
extension ToMatrixFile on EncryptedFile {
|
||||||
|
MatrixFile toMatrixFile() {
|
||||||
|
return MatrixFile(
|
||||||
|
bytes: data, name: 'crypt', mimeType: 'application/octet-stream');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -274,13 +274,6 @@ packages:
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.1.4"
|
version: "3.1.4"
|
||||||
image:
|
|
||||||
dependency: "direct main"
|
|
||||||
description:
|
|
||||||
name: image
|
|
||||||
url: "https://pub.dartlang.org"
|
|
||||||
source: hosted
|
|
||||||
version: "2.1.12"
|
|
||||||
io:
|
io:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
|
@ -11,7 +11,6 @@ dependencies:
|
||||||
http: ^0.12.1
|
http: ^0.12.1
|
||||||
mime: ^0.9.6
|
mime: ^0.9.6
|
||||||
canonical_json: ^1.0.0
|
canonical_json: ^1.0.0
|
||||||
image: ^2.1.4
|
|
||||||
markdown: ^2.1.3
|
markdown: ^2.1.3
|
||||||
html_unescape: ^1.0.1+3
|
html_unescape: ^1.0.1+3
|
||||||
moor: ^3.0.2
|
moor: ^3.0.2
|
||||||
|
|
|
@ -343,8 +343,7 @@ void main() {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('setAvatar', () async {
|
test('setAvatar', () async {
|
||||||
final testFile =
|
final testFile = MatrixFile(bytes: Uint8List(0), name: 'file.jpeg');
|
||||||
MatrixFile(bytes: Uint8List(0), path: 'fake/path/file.jpeg');
|
|
||||||
await matrix.setAvatar(testFile);
|
await matrix.setAvatar(testFile);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,7 @@ void main() {
|
||||||
test('Decrypt', () async {
|
test('Decrypt', () async {
|
||||||
final text = 'hello world';
|
final text = 'hello world';
|
||||||
final file = MatrixFile(
|
final file = MatrixFile(
|
||||||
path: '/path/to/file.txt',
|
name: 'file.txt',
|
||||||
bytes: Uint8List.fromList(text.codeUnits),
|
bytes: Uint8List.fromList(text.codeUnits),
|
||||||
);
|
);
|
||||||
var olmEnabled = true;
|
var olmEnabled = true;
|
||||||
|
|
|
@ -333,8 +333,7 @@ void main() {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('setAvatar', () async {
|
test('setAvatar', () async {
|
||||||
final testFile =
|
final testFile = MatrixFile(bytes: Uint8List(0), name: 'file.jpeg');
|
||||||
MatrixFile(bytes: Uint8List(0), path: 'fake/path/file.jpeg');
|
|
||||||
final dynamic resp = await room.setAvatar(testFile);
|
final dynamic resp = await room.setAvatar(testFile);
|
||||||
expect(resp, 'YUwRidLecu:example.com');
|
expect(resp, 'YUwRidLecu:example.com');
|
||||||
});
|
});
|
||||||
|
@ -361,10 +360,8 @@ void main() {
|
||||||
});*/
|
});*/
|
||||||
|
|
||||||
test('sendFileEvent', () async {
|
test('sendFileEvent', () async {
|
||||||
final testFile =
|
final testFile = MatrixFile(bytes: Uint8List(0), name: 'file.jpeg');
|
||||||
MatrixFile(bytes: Uint8List(0), path: 'fake/path/file.jpeg');
|
final dynamic resp = await room.sendFileEvent(testFile, txid: 'testtxid');
|
||||||
final dynamic resp = await room.sendFileEvent(testFile,
|
|
||||||
msgType: 'm.file', txid: 'testtxid');
|
|
||||||
expect(resp, 'mxc://example.com/AQwafuaFswefuhsfAFAgsw');
|
expect(resp, 'mxc://example.com/AQwafuaFswefuhsfAFAgsw');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue