From 99254f8d020c95216d10e4b30f9b4a545711c9d3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?S=C3=A9bastien=20Blin?=
 <sebastien.blin@savoirfairelinux.com>
Date: Wed, 22 May 2024 10:55:32 -0400
Subject: [PATCH] messagelistmodel: add support for file deletion

+ Add button to delete messages on file transfer
+ Show "Deleted media" on deleted files.
+ Update last interaction
+ Update icon for saving file, we're in 2024, no more floppy disk

Change-Id: I607b1a6beda443db85c60d8cf95a9aae29ce1f7c
GitLab: #1287
---
 resources/icons/save_file.svg                 |  2 +-
 .../DataTransferMessageDelegate.qml           | 63 ++++++++++++++++++-
 src/app/commoncomponents/SBSMessageBase.qml   |  6 +-
 src/app/commoncomponents/ShowMoreMenu.qml     |  2 +-
 src/app/conversationlistmodelbase.cpp         |  6 +-
 .../mainview/components/ChatViewHeader.qml    |  2 +-
 src/app/net/jami/Constants/JamiStrings.qml    |  3 +-
 src/libclient/api/messagelistmodel.h          |  1 +
 src/libclient/messagelistmodel.cpp            | 15 ++---
 9 files changed, 84 insertions(+), 16 deletions(-)

diff --git a/resources/icons/save_file.svg b/resources/icons/save_file.svg
index 9c84d19ae..4fcc1f87d 100644
--- a/resources/icons/save_file.svg
+++ b/resources/icons/save_file.svg
@@ -1 +1 @@
-<svg xmlns="http://www.w3.org/2000/svg" height="48" width="48"><path d="M42 13.85V39q0 1.2-.9 2.1-.9.9-2.1.9H9q-1.2 0-2.1-.9Q6 40.2 6 39V9q0-1.2.9-2.1Q7.8 6 9 6h25.15Zm-3 1.35L32.8 9H9v30h30ZM24 35.75q2.15 0 3.675-1.525T29.2 30.55q0-2.15-1.525-3.675T24 25.35q-2.15 0-3.675 1.525T18.8 30.55q0 2.15 1.525 3.675T24 35.75ZM11.65 18.8h17.9v-7.15h-17.9ZM9 15.2V39 9Z"/></svg>
\ No newline at end of file
+<svg xmlns="http://www.w3.org/2000/svg" height="48px" viewBox="0 -960 960 960" width="48px" fill="#e8eaed"><path d="M480-313 287-506l43-43 120 120v-371h60v371l120-120 43 43-193 193ZM220-160q-24 0-42-18t-18-42v-143h60v143h520v-143h60v143q0 24-18 42t-42 18H220Z"/></svg>
\ No newline at end of file
diff --git a/src/app/commoncomponents/DataTransferMessageDelegate.qml b/src/app/commoncomponents/DataTransferMessageDelegate.qml
index 9710e19a0..a87ee3447 100644
--- a/src/app/commoncomponents/DataTransferMessageDelegate.qml
+++ b/src/app/commoncomponents/DataTransferMessageDelegate.qml
@@ -40,9 +40,18 @@ Loader {
     property int seq: MsgSeq.single
     property string author: Author
     property string body: Body
+    property var tid: TID
     property int transferStatus: TransferStatus
+    onTidChanged: {
+        if (tid === "") {
+            sourceComponent = deletedMsgComp
+        }
+    }
     onTransferStatusChanged: {
-        if (transferStatus === Interaction.TransferStatus.TRANSFER_FINISHED) {
+        if (tid === "") {
+            sourceComponent = deletedMsgComp
+            return;
+        } else if (transferStatus === Interaction.TransferStatus.TRANSFER_FINISHED) {
             mediaInfo = MessagesAdapter.getMediaInfo(root.body);
             if (Object.keys(mediaInfo).length !== 0 && WITH_WEBENGINE) {
                 sourceComponent = localMediaMsgComp;
@@ -58,6 +67,54 @@ Loader {
     Behavior on opacity { NumberAnimation { duration: 100 } }
     onLoaded: opacity = 1
 
+    Component {
+        id: deletedMsgComp
+
+        SBSMessageBase {
+            id: deletedItem
+
+            isOutgoing: Author === CurrentAccount.uri
+            showTime: root.showTime
+            seq: root.seq
+            author: Author
+            readers: Readers
+            timestamp: root.timestamp
+            formattedTime: root.formattedTime
+            formattedDay: root.formattedTime
+            extraHeight: 0
+            textContentWidth: textEditId.width
+            textContentHeight: textEditId.height
+            innerContent.children: [
+                TextEdit {
+                    id: textEditId
+
+                    anchors.right: isOutgoing ? parent.right : undefined
+                    anchors.rightMargin: isOutgoing ? timeWidth : 0
+                    bottomPadding: 6
+                    topPadding: 6
+                    leftPadding: 10
+                    text: UtilsAdapter.getBestNameForUri(CurrentAccount.id, Author) + " " + JamiStrings.deletedMedia ;
+                    horizontalAlignment: Text.AlignLeft
+                    width:  Math.min((2 / 3) * parent.width, implicitWidth + 18, innerContent.width - senderMargin + 18)
+
+                    font.pointSize: JamiTheme.smallFontSize
+                    font.hintingPreference: Font.PreferNoHinting
+                    renderType: Text.NativeRendering
+                    textFormat: Text.RichText
+                    clip: true
+                    readOnly: true
+                    color: getBaseColor()
+                    opacity: 0.5
+
+                    function getBaseColor() {
+                        bubble.isDeleted = true
+                        return UtilsAdapter.luma(bubble.color) ? "white" : "dark"
+                    }
+                }
+            ]
+        }
+    }
+
     Component {
         id: dataTransferMsgComp
 
@@ -223,8 +280,8 @@ Loader {
                                    : JamiTheme.chatviewTextColorDark
                         }
                     }
-                }
-                ,ProgressBar {
+                },
+                ProgressBar {
                     id: progressBar
 
                     visible: root.transferStatus === Interaction.TransferStatus.TRANSFER_ONGOING
diff --git a/src/app/commoncomponents/SBSMessageBase.qml b/src/app/commoncomponents/SBSMessageBase.qml
index bbca1c891..a57b1ddb5 100644
--- a/src/app/commoncomponents/SBSMessageBase.qml
+++ b/src/app/commoncomponents/SBSMessageBase.qml
@@ -382,7 +382,11 @@ Control {
                     property bool bubbleHovered
                     property string imgSource
 
-                    width: (root.type === Interaction.Type.TEXT ? root.textContentWidth + (IsEmojiOnly || root.bigMsg ? 0 : root.timeWidth + root.editedWidth) : innerContent.childrenRect.width)
+                    width: (root.type === Interaction.Type.TEXT || isDeleted ?
+                            root.textContentWidth + (IsEmojiOnly || root.bigMsg ?
+                                                        0
+                                                        : root.timeWidth + root.editedWidth)
+                            : innerContent.childrenRect.width)
                     height: innerContent.childrenRect.height + (visible ? root.extraHeight : 0) + (root.bigMsg ? 15 : 0)
 
                     HoverHandler {
diff --git a/src/app/commoncomponents/ShowMoreMenu.qml b/src/app/commoncomponents/ShowMoreMenu.qml
index 22f95d0c1..26c4cdb8c 100644
--- a/src/app/commoncomponents/ShowMoreMenu.qml
+++ b/src/app/commoncomponents/ShowMoreMenu.qml
@@ -175,7 +175,7 @@ BaseContextMenu {
         GeneralMenuItem {
             id: deleteMessage
 
-            canTrigger: root.isOutgoing && type === Interaction.Type.TEXT
+            canTrigger: root.isOutgoing && (type === Interaction.Type.TEXT || type === Interaction.Type.DATA_TRANSFER)
             iconSource: JamiResources.delete_svg
             itemName: JamiStrings.deleteMessage
             onClicked: {
diff --git a/src/app/conversationlistmodelbase.cpp b/src/app/conversationlistmodelbase.cpp
index 180b5db44..2a169463e 100644
--- a/src/app/conversationlistmodelbase.cpp
+++ b/src/app/conversationlistmodelbase.cpp
@@ -125,7 +125,11 @@ ConversationListModelBase::dataForItem(item_t item, int role) const
             if (interaction.type == interaction::Type::UPDATE_PROFILE) {
                 lastInteractionBody = interaction::getProfileUpdatedString();
             } else if (interaction.type == interaction::Type::DATA_TRANSFER) {
-                lastInteractionBody = interaction.commit.value("displayName");
+                if (interaction.commit.value("tid").isEmpty()) {
+                    lastInteractionBody = tr("Deleted media");
+                } else {
+                    lastInteractionBody = interaction.commit.value("displayName");
+                }
             } else if (interaction.type == lrc::api::interaction::Type::CALL) {
                 const auto isOutgoing = interaction.authorUri == accInfo.profileInfo.uri;
                 lastInteractionBody = interaction::getCallInteractionString(isOutgoing, interaction);
diff --git a/src/app/mainview/components/ChatViewHeader.qml b/src/app/mainview/components/ChatViewHeader.qml
index 2ae782fe6..b11b0a7a4 100644
--- a/src/app/mainview/components/ChatViewHeader.qml
+++ b/src/app/mainview/components/ChatViewHeader.qml
@@ -88,7 +88,7 @@ Rectangle {
             mirror: UtilsAdapter.isRTL
 
             source: JamiResources.back_24dp_svg
-            toolTipText: CurrentConversation.inCall ? JamiStrings.backCall : JamiStrings.hideChat
+            toolTipText: CurrentConversation.inCall ? JamiStrings.returnToCall : JamiStrings.hideChat
 
             onClicked: root.backClicked()
         }
diff --git a/src/app/net/jami/Constants/JamiStrings.qml b/src/app/net/jami/Constants/JamiStrings.qml
index a74aa1962..1610904c3 100644
--- a/src/app/net/jami/Constants/JamiStrings.qml
+++ b/src/app/net/jami/Constants/JamiStrings.qml
@@ -346,7 +346,8 @@ Item {
     property string disabledAccount: qsTr("The account is disabled")
     property string noNetworkConnectivity: qsTr("No network connectivity")
     property string deletedMessage: qsTr("deleted a message")
-    property string backCall: qsTr("Back to Call")
+    property string deletedMedia: qsTr("deleted a media")
+    property string returnToCall: qsTr("Return to call")
 
     //MessagesResearch
     property string jumpTo: qsTr("Jump to")
diff --git a/src/libclient/api/messagelistmodel.h b/src/libclient/api/messagelistmodel.h
index 96f7adf54..c6f06703a 100644
--- a/src/libclient/api/messagelistmodel.h
+++ b/src/libclient/api/messagelistmodel.h
@@ -55,6 +55,7 @@ struct Info;
     X(ReplyToAuthor) \
     X(TotalSize) \
     X(TransferName) \
+    X(TID) \
     X(FileExtension) \
     X(Readers) \
     X(IsEmojiOnly) \
diff --git a/src/libclient/messagelistmodel.cpp b/src/libclient/messagelistmodel.cpp
index ce726dbb9..04a2ee5cb 100644
--- a/src/libclient/messagelistmodel.cpp
+++ b/src/libclient/messagelistmodel.cpp
@@ -201,16 +201,15 @@ MessageListModel::update(const QString& id, const interaction::Info& interaction
             return true;
         }
     }
-    // DataTransfer interactions should not be updated.
-    if (current.type == interaction::Type::DATA_TRANSFER) {
-        return true;
-    }
     // Just update bodies notify the view otherwise.
     current.body = interaction.body;
+    current.commit = interaction.commit;
     current.previousBodies = interaction.previousBodies;
     current.parsedBody = interaction.parsedBody;
     auto modelIndex = QAbstractListModel::index(indexOfMessage(id), 0);
-    Q_EMIT dataChanged(modelIndex, modelIndex, {Role::Body, Role::PreviousBodies, Role::ParsedBody});
+    Q_EMIT dataChanged(modelIndex,
+                       modelIndex,
+                       {Role::TID, Role::Body, Role::PreviousBodies, Role::ParsedBody});
     return true;
 }
 
@@ -251,8 +250,8 @@ MessageListModel::updateStatus(const QString& id,
 
 bool
 MessageListModel::updateTransferStatus(const QString& id,
-                               interaction::TransferStatus newStatus,
-                               const QString& newBody)
+                                       interaction::TransferStatus newStatus,
+                                       const QString& newBody)
 {
     const std::lock_guard<std::recursive_mutex> lk(mutex_);
     auto it = find(id);
@@ -594,6 +593,8 @@ MessageListModel::dataForItem(const item_t& item, int, int role) const
         return QVariant(item.second.commit["totalSize"].toInt());
     case Role::TransferName:
         return QVariant(item.second.commit["displayName"]);
+    case Role::TID:
+        return QVariant(item.second.commit["tid"]);
     case Role::FileExtension:
         return QVariant(QFileInfo(item.second.body).suffix());
     case Role::Readers:
-- 
GitLab