sendFileEvent method
Sends a file
to this room after uploading it. Returns the mxc uri of
the uploaded file. If waitUntilSent
is true, the future will wait until
the message event has received the server. Otherwise the future will only
wait until the file has been uploaded.
Optionally specify extraContent
to tack on to the event.
In case file
is a SDNImageFile, thumbnail
is automatically
computed unless it is explicitly provided.
Set shrinkImageMaxDimension
to for example 1600
if you want to shrink
your image before sending. This is ignored if the File is not a
SDNImageFile.
Implementation
Future<String?> sendFileEvent(
SDNFile file, {
String? txid,
Event? inReplyTo,
String? editEventId,
int? shrinkImageMaxDimension,
SDNImageFile? thumbnail,
Map<String, dynamic>? extraContent,
String? threadRootEventId,
String? threadLastEventId,
}) async {
txid ??= client.generateUniqueTransactionId();
sendingFilePlaceholders[txid] = file;
if (thumbnail != null) {
sendingFileThumbnails[txid] = thumbnail;
}
// Create a fake Event object as a placeholder for the uploading file:
final syncUpdate = SyncUpdate(
nextBatch: '',
rooms: RoomsUpdate(
join: {
id: JoinedRoomUpdate(
timeline: TimelineUpdate(
events: [
SDNEvent(
content: {
'msgtype': file.msgType,
'body': file.name,
'filename': file.name,
},
type: EventTypes.Message,
eventId: txid,
senderId: client.userID!,
originServerTs: DateTime.now(),
unsigned: {
messageSendingStatusKey: EventStatus.sending.intValue,
'transaction_id': txid,
...FileSendRequestCredentials(
inReplyTo: inReplyTo?.eventId,
editEventId: editEventId,
shrinkImageMaxDimension: shrinkImageMaxDimension,
extraContent: extraContent,
).toJson(),
},
),
],
),
),
},
),
);
SDNFile uploadFile = file; // ignore: omit_local_variable_types
// computing the thumbnail in case we can
if (file is SDNImageFile &&
(thumbnail == null || shrinkImageMaxDimension != null)) {
syncUpdate.rooms!.join!.values.first.timeline!.events!.first
.unsigned![fileSendingStatusKey] =
FileSendingStatus.generatingThumbnail.name;
await _handleFakeSync(syncUpdate);
thumbnail ??= await file.generateThumbnail(
nativeImplementations: client.nativeImplementations,
customImageResizer: client.customImageResizer,
);
if (shrinkImageMaxDimension != null) {
file = await SDNImageFile.shrink(
bytes: file.bytes,
name: file.name,
maxDimension: shrinkImageMaxDimension,
customImageResizer: client.customImageResizer,
nativeImplementations: client.nativeImplementations,
);
}
if (thumbnail != null && file.size < thumbnail.size) {
thumbnail = null; // in this case, the thumbnail is not usefull
}
}
// Check media config of the server before sending the file. Stop if the
// Media config is unreachable or the file is bigger than the given maxsize.
try {
final mediaConfig = await client.getConfig();
final maxMediaSize = mediaConfig.mUploadSize;
if (maxMediaSize != null && maxMediaSize < file.bytes.lengthInBytes) {
throw FileTooBigSDNException(file.bytes.lengthInBytes, maxMediaSize);
}
} catch (e) {
Logs().d('Config error while sending file', e);
syncUpdate.rooms!.join!.values.first.timeline!.events!.first
.unsigned![messageSendingStatusKey] = EventStatus.error.intValue;
await _handleFakeSync(syncUpdate);
rethrow;
}
SDNFile? uploadThumbnail = thumbnail; // ignore: omit_local_variable_types
EncryptedFile? encryptedFile;
EncryptedFile? encryptedThumbnail;
if (encrypted && client.fileEncryptionEnabled) {
syncUpdate.rooms!.join!.values.first.timeline!.events!.first
.unsigned![fileSendingStatusKey] = FileSendingStatus.encrypting.name;
await _handleFakeSync(syncUpdate);
encryptedFile = await file.encrypt();
uploadFile = encryptedFile.toSDNFile();
if (thumbnail != null) {
encryptedThumbnail = await thumbnail.encrypt();
uploadThumbnail = encryptedThumbnail.toSDNFile();
}
}
Uri? uploadResp, thumbnailUploadResp;
final timeoutDate = DateTime.now().add(client.sendTimelineEventTimeout);
syncUpdate.rooms!.join!.values.first.timeline!.events!.first
.unsigned![fileSendingStatusKey] = FileSendingStatus.uploading.name;
while (uploadResp == null ||
(uploadThumbnail != null && thumbnailUploadResp == null)) {
try {
uploadResp = await client.uploadContent(
uploadFile.bytes,
filename: uploadFile.name,
contentType: uploadFile.mimeType,
);
thumbnailUploadResp = uploadThumbnail != null
? await client.uploadContent(
uploadThumbnail.bytes,
filename: uploadThumbnail.name,
contentType: uploadThumbnail.mimeType,
)
: null;
} on SDNException catch (_) {
syncUpdate.rooms!.join!.values.first.timeline!.events!.first
.unsigned![messageSendingStatusKey] = EventStatus.error.intValue;
await _handleFakeSync(syncUpdate);
rethrow;
} catch (_) {
if (DateTime.now().isAfter(timeoutDate)) {
syncUpdate.rooms!.join!.values.first.timeline!.events!.first
.unsigned![messageSendingStatusKey] = EventStatus.error.intValue;
await _handleFakeSync(syncUpdate);
rethrow;
}
Logs().v('Send File into room failed. Try again...');
await Future.delayed(Duration(seconds: 1));
}
}
// Send event
final content = <String, dynamic>{
'msgtype': file.msgType,
'body': file.name,
'filename': file.name,
if (encryptedFile == null) 'url': uploadResp.toString(),
if (encryptedFile != null)
'file': {
'url': uploadResp.toString(),
'mimetype': file.mimeType,
'v': 'v2',
'key': {
'alg': 'A256CTR',
'ext': true,
'k': encryptedFile.k,
'key_ops': ['encrypt', 'decrypt'],
'kty': 'oct'
},
'iv': encryptedFile.iv,
'hashes': {'sha256': encryptedFile.sha256}
},
'info': {
...file.info,
if (thumbnail != null && encryptedThumbnail == null)
'thumbnail_url': thumbnailUploadResp.toString(),
if (thumbnail != null && encryptedThumbnail != null)
'thumbnail_file': {
'url': thumbnailUploadResp.toString(),
'mimetype': thumbnail.mimeType,
'v': 'v2',
'key': {
'alg': 'A256CTR',
'ext': true,
'k': encryptedThumbnail.k,
'key_ops': ['encrypt', 'decrypt'],
'kty': 'oct'
},
'iv': encryptedThumbnail.iv,
'hashes': {'sha256': encryptedThumbnail.sha256}
},
if (thumbnail != null) 'thumbnail_info': thumbnail.info,
if (thumbnail?.blurhash != null &&
file is SDNImageFile &&
file.blurhash == null)
'xyz.amorgan.blurhash': thumbnail!.blurhash
},
if (extraContent != null) ...extraContent,
};
final eventId = await sendEvent(
content,
txid: txid,
inReplyTo: inReplyTo,
editEventId: editEventId,
threadRootEventId: threadRootEventId,
threadLastEventId: threadLastEventId,
);
sendingFilePlaceholders.remove(txid);
sendingFileThumbnails.remove(txid);
return eventId;
}