Merge branch 'krille/retry-failed-sent-file' into 'main'

fix: Retry sending a file event

Closes #276

See merge request famedly/company/frontend/famedlysdk!1004
This commit is contained in:
Krille Fear 2022-04-08 08:56:49 +00:00
commit c027cd276b
3 changed files with 100 additions and 8 deletions

View File

@ -22,6 +22,7 @@ import 'dart:typed_data';
import 'package:collection/collection.dart';
import 'package:html/parser.dart';
import 'package:http/http.dart' as http;
import 'package:matrix/src/utils/file_send_request_credentials.dart';
import '../matrix.dart';
import 'utils/event_localizations.dart';
@ -136,6 +137,18 @@ class Event extends MatrixEvent {
);
}
}
// If this is failed to send and the file is no longer cached, it should be removed!
if (!status.isSent &&
{
MessageTypes.Image,
MessageTypes.Video,
MessageTypes.Audio,
MessageTypes.File,
}.contains(messageType) &&
!room.sendingFilePlaceholders.containsKey(eventId)) {
remove();
}
}
static Map<String, dynamic> getMapFromPayload(dynamic payload) {
@ -327,12 +340,34 @@ class Event extends MatrixEvent {
/// Try to send this event again. Only works with events of status -1.
Future<String?> sendAgain({String? txid}) async {
if (!status.isError) return null;
// If this is a failed file sending event, try to fetch the file from the
// database first.
final url = getAttachmentUrl();
if (url?.scheme == 'local') {
final file = await downloadAndDecryptAttachment();
return await room.sendFileEvent(file, extraContent: content);
// Retry sending a file:
if ({
MessageTypes.Image,
MessageTypes.Video,
MessageTypes.Audio,
MessageTypes.File,
}.contains(messageType)) {
final file = room.sendingFilePlaceholders[eventId];
if (file == null) {
await remove();
throw Exception('Can not try to send again. File is no longer cached.');
}
final thumbnail = room.sendingFileThumbnails[eventId];
final credentials = FileSendRequestCredentials.fromJson(unsigned ?? {});
final inReplyTo = credentials.inReplyTo == null
? null
: await room.getEventById(credentials.inReplyTo!);
txid ??= unsigned?.tryGet<String>('transaction_id');
return await room.sendFileEvent(
file,
txid: txid,
thumbnail: thumbnail,
inReplyTo: inReplyTo,
editEventId: credentials.editEventId,
shrinkImageMaxDimension: credentials.shrinkImageMaxDimension,
extraContent: credentials.extraContent,
);
}
// we do not remove the event here. It will automatically be updated

View File

@ -23,6 +23,7 @@ import 'dart:typed_data';
import 'package:collection/collection.dart';
import 'package:html_unescape/html_unescape.dart';
import 'package:matrix/src/utils/crypto/crypto.dart';
import 'package:matrix/src/utils/file_send_request_credentials.dart';
import 'package:matrix/src/utils/space_child.dart';
import 'package:matrix/widget.dart';
@ -683,6 +684,7 @@ class Room {
}
final Map<String, MatrixFile> sendingFilePlaceholders = {};
final Map<String, MatrixImageFile> sendingFileThumbnails = {};
/// 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
@ -706,6 +708,9 @@ class Room {
}) 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(
@ -728,6 +733,12 @@ class Room {
unsigned: {
messageSendingStatusKey: EventStatus.sending.intValue,
'transaction_id': txid,
...FileSendRequestCredentials(
inReplyTo: inReplyTo?.eventId,
editEventId: editEventId,
shrinkImageMaxDimension: shrinkImageMaxDimension,
extraContent: extraContent,
).toJson(),
},
),
],
@ -805,14 +816,12 @@ class Room {
syncUpdate.rooms!.join!.values.first.timeline!.events!.first
.unsigned![messageSendingStatusKey] = EventStatus.error.intValue;
await _handleFakeSync(syncUpdate);
sendingFilePlaceholders.remove(txid);
rethrow;
} catch (_) {
if (DateTime.now().isAfter(timeoutDate)) {
syncUpdate.rooms!.join!.values.first.timeline!.events!.first
.unsigned![messageSendingStatusKey] = EventStatus.error.intValue;
await _handleFakeSync(syncUpdate);
sendingFilePlaceholders.remove(txid);
rethrow;
}
Logs().v('Send File into room failed. Try again...');
@ -875,6 +884,7 @@ class Room {
editEventId: editEventId,
);
sendingFilePlaceholders.remove(txid);
sendingFileThumbnails.remove(txid);
return eventId;
}

View File

@ -0,0 +1,47 @@
/*
* Famedly Matrix SDK
* Copyright (C) 2019, 2020, 2021 Famedly GmbH
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
class FileSendRequestCredentials {
final String? inReplyTo;
final String? editEventId;
final int? shrinkImageMaxDimension;
final Map<String, dynamic>? extraContent;
const FileSendRequestCredentials({
this.inReplyTo,
this.editEventId,
this.shrinkImageMaxDimension,
this.extraContent,
});
factory FileSendRequestCredentials.fromJson(Map<String, dynamic> json) =>
FileSendRequestCredentials(
inReplyTo: json['in_reply_to'],
editEventId: json['edit_event_id'],
shrinkImageMaxDimension: json['shrink_image_max_dimension'],
extraContent: json['extra_content'],
);
Map<String, dynamic> toJson() => {
if (inReplyTo != null) 'in_reply_to': inReplyTo,
if (editEventId != null) 'edit_event_id': editEventId,
if (shrinkImageMaxDimension != null)
'shrink_image_max_dimension': shrinkImageMaxDimension,
if (extraContent != null) 'extra_content': extraContent,
};
}