From 2f4d455eeeaba23aee7ba8addcba95e1b7f8eb0a Mon Sep 17 00:00:00 2001 From: Krille Fear Date: Fri, 8 Apr 2022 08:21:40 +0200 Subject: [PATCH] fix: Retry sending a file event --- lib/src/event.dart | 47 ++++++++++++++++--- lib/src/room.dart | 14 +++++- .../utils/file_send_request_credentials.dart | 47 +++++++++++++++++++ 3 files changed, 100 insertions(+), 8 deletions(-) create mode 100644 lib/src/utils/file_send_request_credentials.dart diff --git a/lib/src/event.dart b/lib/src/event.dart index 4cc0866b..79e21919 100644 --- a/lib/src/event.dart +++ b/lib/src/event.dart @@ -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 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 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('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 diff --git a/lib/src/room.dart b/lib/src/room.dart index e0bc88bf..9a37956f 100644 --- a/lib/src/room.dart +++ b/lib/src/room.dart @@ -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 sendingFilePlaceholders = {}; + final Map 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; } diff --git a/lib/src/utils/file_send_request_credentials.dart b/lib/src/utils/file_send_request_credentials.dart new file mode 100644 index 00000000..9ac0ed74 --- /dev/null +++ b/lib/src/utils/file_send_request_credentials.dart @@ -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 . + */ + +class FileSendRequestCredentials { + final String? inReplyTo; + final String? editEventId; + final int? shrinkImageMaxDimension; + final Map? extraContent; + + const FileSendRequestCredentials({ + this.inReplyTo, + this.editEventId, + this.shrinkImageMaxDimension, + this.extraContent, + }); + + factory FileSendRequestCredentials.fromJson(Map json) => + FileSendRequestCredentials( + inReplyTo: json['in_reply_to'], + editEventId: json['edit_event_id'], + shrinkImageMaxDimension: json['shrink_image_max_dimension'], + extraContent: json['extra_content'], + ); + + Map 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, + }; +}