From 2117ca3fc178cb83bcb30893f133a6a718e5b5f5 Mon Sep 17 00:00:00 2001 From: otsmr Date: Fri, 31 Jan 2025 23:42:32 +0100 Subject: [PATCH] retransmition of images --- lib/src/model/messages_model.dart | 1 - lib/src/providers/api/api.dart | 101 +++++++++++------- lib/src/providers/api/server_messages.dart | 1 - .../signal/connect_signed_pre_key_store.dart | 1 - lib/src/views/chat_item_details_view.dart | 15 +-- lib/src/views/chat_list_view.dart | 17 +-- 6 files changed, 80 insertions(+), 56 deletions(-) diff --git a/lib/src/model/messages_model.dart b/lib/src/model/messages_model.dart index a5dd08b..a66f10a 100644 --- a/lib/src/model/messages_model.dart +++ b/lib/src/model/messages_model.dart @@ -154,7 +154,6 @@ class DbMessages extends CvModelBase { columnOtherUserId: userIdFrom, columnSendOrReceivedAt: DateTime.now().toIso8601String() }); - print("insertl"); globalCallBackOnMessageChange(userIdFrom); return messageId; } catch (e) { diff --git a/lib/src/providers/api/api.dart b/lib/src/providers/api/api.dart index 1b5f78e..1b49e2b 100644 --- a/lib/src/providers/api/api.dart +++ b/lib/src/providers/api/api.dart @@ -16,8 +16,6 @@ import 'package:twonly/src/utils/misc.dart'; // ignore: library_prefixes import 'package:twonly/src/utils/signal.dart' as SignalHelper; -// this functions ensures that the message is received by the server and in case of errors will try again later - Future tryTransmitMessages() async { List retransmit = await DbMessages.getAllMessagesForRetransmitting(); @@ -28,22 +26,28 @@ Future tryTransmitMessages() async { for (int i = 0; i < retransmit.length; i++) { int msgId = retransmit[i].messageId; debugPrint("msgId=$msgId"); - Uint8List? bytes = box.get("retransmit-$msgId"); - debugPrint("bytes == null =${bytes == null}"); + + Uint8List? bytes = box.get("retransmit-$msgId-textmessage"); if (bytes != null) { Result resp = await apiProvider.sendTextMessage( Int64(retransmit[i].otherUserId), bytes); if (resp.isSuccess) { DbMessages.acknowledgeMessageByServer(msgId); - box.delete("retransmit-$msgId"); + box.delete("retransmit-$msgId-textmessage"); } else { // in case of error do nothing. As the message is not removed the app will try again when relaunched } } + + Uint8List? encryptedMedia = await box.get("retransmit-$msgId-media"); + if (encryptedMedia != null) { + uploadMediaFile(msgId, Int64(retransmit[i].otherUserId), encryptedMedia); + } } } +// this functions ensures that the message is received by the server and in case of errors will try again later Future encryptAndSendMessage(Int64 userId, Message msg) async { Uint8List? bytes = await SignalHelper.encryptMessage(msg, userId); @@ -55,7 +59,7 @@ Future encryptAndSendMessage(Int64 userId, Message msg) async { Box box = await getMediaStorage(); if (msg.messageId != null) { debugPrint("putting=${msg.messageId}"); - box.put("retransmit-${msg.messageId}", bytes); + box.put("retransmit-${msg.messageId}-textmessage", bytes); } Result resp = await apiProvider.sendTextMessage(userId, bytes); @@ -63,7 +67,7 @@ Future encryptAndSendMessage(Int64 userId, Message msg) async { if (resp.isSuccess) { if (msg.messageId != null) { DbMessages.acknowledgeMessageByServer(msg.messageId!); - box.delete("retransmit-${msg.messageId}"); + box.delete("retransmit-${msg.messageId}-textmessage"); } } @@ -88,7 +92,53 @@ Future sendTextMessage(Int64 target, String message) async { encryptAndSendMessage(target, msg); } -Future sendImageToSingleTarget(Int64 target, Uint8List imageBytes) async { +// this will send the media file and ensures retransmission when errors occur +Future uploadMediaFile( + int messageId, Int64 target, Uint8List encryptedMedia) async { + Box box = await getMediaStorage(); + + if ((await box.get("retransmit-$messageId-media") == null)) { + await box.put("retransmit-$messageId-media", encryptedMedia); + } + + List? uploadToken = await box.get("retransmit-$messageId-uploadtoken"); + if (uploadToken == null) { + Result res = await apiProvider.getUploadToken(); + + if (res.isError || !res.value.hasUploadtoken()) { + Logger("api.dart").shout("Error getting upload token!"); + return; // will be retried on next app start + } + + uploadToken = res.value.uploadtoken; + + await box.put("retransmit-$messageId-uploadtoken", uploadToken); + } + + if (uploadToken == null) return; + + // TODO: fragmented upload... + if (!await apiProvider.uploadData(uploadToken, encryptedMedia, 0)) { + Logger("api.dart").shout("error while uploading media"); + return; + } + + box.delete("retransmit-$messageId-media"); + box.delete("retransmit-$messageId-uploadtoken"); + + // Ensures the retransmit of the message + await encryptAndSendMessage( + target, + Message( + kind: MessageKind.image, + messageId: messageId, + content: MessageContent(text: null, downloadToken: uploadToken), + timestamp: DateTime.now(), + ), + ); +} + +Future encryptAndUploadMediaFile(Int64 target, Uint8List imageBytes) async { int? messageId = await DbMessages.insertMyMessage(target.toInt(), MessageKind.image); if (messageId == null) return; @@ -96,38 +146,11 @@ Future sendImageToSingleTarget(Int64 target, Uint8List imageBytes) async { Uint8List? encryptBytes = await SignalHelper.encryptBytes(imageBytes, target); if (encryptBytes == null) { await DbMessages.deleteMessageById(messageId); - Logger("api.dart").shout("Error encrypting image! Deleting image."); + Logger("api.dart").shout("Error encrypting media! Deleting media."); return; } - Result res = await apiProvider.getUploadToken(); - - if (res.isError || !res.value.hasUploadtoken()) { - print("store encryptBytes in box to retransmit without an upload token"); - Logger("api.dart").shout("Error getting upload token!"); - return null; - } - - List uploadToken = res.value.uploadtoken; - - MessageContent content = - MessageContent(text: null, downloadToken: uploadToken); - - print("fragmentate the data"); - - if (!await apiProvider.uploadData(uploadToken, encryptBytes, 0)) { - Logger("api.dart").shout("error while uploading image"); - return; - } - - Message msg = Message( - kind: MessageKind.image, - messageId: messageId, - content: content, - timestamp: DateTime.now(), - ); - - await encryptAndSendMessage(target, msg); + await uploadMediaFile(messageId, target, encryptBytes); } Future sendImage(List userIds, String imagePath) async { @@ -142,7 +165,7 @@ Future sendImage(List userIds, String imagePath) async { } for (int i = 0; i < userIds.length; i++) { - sendImageToSingleTarget(userIds[i], imageBytes); + encryptAndUploadMediaFile(userIds[i], imageBytes); } } @@ -163,7 +186,7 @@ Future tryDownloadMedia(List mediaToken, {bool force = false}) async { if (media != null && media.isNotEmpty) { offset = media.length; } - //globalCallBackOnDownloadChange(mediaToken, true); + globalCallBackOnDownloadChange(mediaToken, true); apiProvider.triggerDownload(mediaToken, offset); } diff --git a/lib/src/providers/api/server_messages.dart b/lib/src/providers/api/server_messages.dart index 8f74c1d..3dfdec0 100644 --- a/lib/src/providers/api/server_messages.dart +++ b/lib/src/providers/api/server_messages.dart @@ -73,7 +73,6 @@ Future handleDownloadData(DownloadData data) async { SignalHelper.getSignalStore(); int? fromUserId = box.get("${data.uploadToken}_fromUserId"); if (fromUserId != null) { - print(fromUserId); Uint8List? rawBytes = await SignalHelper.decryptBytes(downloadedBytes, Int64(fromUserId)); diff --git a/lib/src/signal/connect_signed_pre_key_store.dart b/lib/src/signal/connect_signed_pre_key_store.dart index 1182784..945af2f 100644 --- a/lib/src/signal/connect_signed_pre_key_store.dart +++ b/lib/src/signal/connect_signed_pre_key_store.dart @@ -15,7 +15,6 @@ class ConnectSignedPreKeyStore extends SignedPreKeyStore { return store; } final storeHashMap = json.decode(storeSerialized); - print(storeHashMap); for (final item in storeHashMap) { store[item[0]] = base64Decode(item[1]); } diff --git a/lib/src/views/chat_item_details_view.dart b/lib/src/views/chat_item_details_view.dart index 94bcf79..0c4d630 100644 --- a/lib/src/views/chat_item_details_view.dart +++ b/lib/src/views/chat_item_details_view.dart @@ -7,6 +7,7 @@ import 'package:twonly/src/model/contacts_model.dart'; import 'package:twonly/src/model/json/message.dart'; import 'package:twonly/src/model/messages_model.dart'; import 'package:twonly/src/providers/api/api.dart'; +import 'package:twonly/src/providers/download_change_provider.dart'; import 'package:twonly/src/providers/messages_change_provider.dart'; import 'package:twonly/src/views/media_viewer_view.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; @@ -24,13 +25,13 @@ class ChatListEntry extends StatelessWidget { MessageSendState state = message.getSendState(); bool isDownloading = false; - // if (message.messageContent != null && - // message.messageContent!.downloadToken != null) { - // isDownloading = context - // .watch() - // .currentlyDownloading - // .contains(message.messageContent!.downloadToken!); - // } + if (message.messageContent != null && + message.messageContent!.downloadToken != null) { + isDownloading = context + .watch() + .currentlyDownloading + .contains(message.messageContent!.downloadToken!.toString()); + } Widget child = Container(); diff --git a/lib/src/views/chat_list_view.dart b/lib/src/views/chat_list_view.dart index 2df6caa..e072c1d 100644 --- a/lib/src/views/chat_list_view.dart +++ b/lib/src/views/chat_list_view.dart @@ -7,6 +7,7 @@ import 'package:twonly/src/model/contacts_model.dart'; import 'package:twonly/src/model/messages_model.dart'; import 'package:twonly/src/providers/api/api.dart'; import 'package:twonly/src/providers/contacts_change_provider.dart'; +import 'package:twonly/src/providers/download_change_provider.dart'; import 'package:twonly/src/providers/messages_change_provider.dart'; import 'package:twonly/src/utils/misc.dart'; import 'package:twonly/src/views/chat_item_details_view.dart'; @@ -150,13 +151,15 @@ class _UserListItem extends State { MessageSendState state = widget.lastMessage.getSendState(); bool isDownloading = false; - // if (widget.lastMessage.messageContent != null && - // widget.lastMessage.messageContent!.downloadToken != null) { - // isDownloading = context - // .watch() - // .currentlyDownloading - // .contains(widget.lastMessage.messageContent!.downloadToken!); - // } + + if (widget.lastMessage.messageContent != null && + widget.lastMessage.messageContent!.downloadToken != null) { + isDownloading = context + .watch() + .currentlyDownloading + .contains( + widget.lastMessage.messageContent!.downloadToken!.toString()); + } return UserContextMenu( user: widget.user,