From 0ba8441c1d1f799af6b473be714f73d4431caa29 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?S=C3=A9bastien=20Blin?=
 <sebastien.blin@savoirfairelinux.com>
Date: Tue, 13 Apr 2021 17:00:47 -0400
Subject: [PATCH] swarm: fix build

Change-Id: I7e93ecac1639a07b9dd71607b9e7e57493165816
---
 src/api/contactmodel.h                    |   2 +-
 src/api/conversation.h                    |   3 +-
 src/api/datatransfermodel.h               |  34 +-
 src/api/interaction.h                     |  55 +--
 src/authority/storagehelper.cpp           |   3 +-
 src/callbackshandler.cpp                  |  80 ++++-
 src/callbackshandler.h                    |  34 +-
 src/contactmodel.cpp                      |   6 +-
 src/conversationmodel.cpp                 | 396 +++++++++++++---------
 src/datatransfermodel.cpp                 | 120 ++++---
 src/messageslist.cpp                      |  32 +-
 src/newaccountmodel.cpp                   |   2 +-
 src/qtwrapper/configurationmanager_wrap.h |  93 +++--
 src/web-chatview/chatview.js              |  53 +--
 14 files changed, 548 insertions(+), 365 deletions(-)

diff --git a/src/api/contactmodel.h b/src/api/contactmodel.h
index e7cec154..83f43cf9 100644
--- a/src/api/contactmodel.h
+++ b/src/api/contactmodel.h
@@ -157,7 +157,7 @@ Q_SIGNALS:
      * @param dringId Daemon's ID for incoming transfer
      * @param transferInfo DataTransferInfo structure from daemon
      */
-    void newAccountTransfer(DataTransferId dringId, datatransfer::Info info) const;
+    void newAccountTransfer(const QString& fileId, datatransfer::Info info) const;
     /**
      * Connect this signal to know when a contact is banned or unbanned
      * @param contactUri
diff --git a/src/api/conversation.h b/src/api/conversation.h
index fe828bf5..dace0784 100644
--- a/src/api/conversation.h
+++ b/src/api/conversation.h
@@ -72,7 +72,8 @@ struct Info
     std::map<QString, QString> lastDisplayedMessageUid;
     unsigned int unreadMessages = 0;
 
-    QString getCallId() { return confId.isEmpty() ? callId : confId; }
+    QString getCallId() const { return confId.isEmpty() ? callId : confId; }
+    bool isNotASwarm() const { return mode == Mode::NON_SWARM; }
 
     Mode mode = Mode::NON_SWARM;
     bool needsSyncing = false;
diff --git a/src/api/datatransfermodel.h b/src/api/datatransfermodel.h
index 1e6fadf0..19083559 100644
--- a/src/api/datatransfermodel.h
+++ b/src/api/datatransfermodel.h
@@ -57,32 +57,32 @@ public:
                   const QString& file_path,
                   const QString& display_name);
 
-    void transferInfo(const QString& accountId,
-                      const QString& conversationId,
-                      DataTransferId ringId,
-                      datatransfer::Info& lrc_info);
+    void transferInfo(const QString& accountId, const QString& fileId, datatransfer::Info& lrc_info);
 
-    void bytesProgress(const QString& accountId,
-                       const QString& conversationId,
-                       const QString& interactionId,
-                       int64_t& total,
-                       int64_t& progress);
+    void fileTransferInfo(const QString& accountId,
+                          const QString& conversationId,
+                          const QString& fileId,
+                          QString& path,
+                          qlonglong& total,
+                          qlonglong& progress);
 
     QString accept(const QString& accountId,
-                   const QString& conversationId,
-                   const QString& interactionId,
+                   const QString& fileId,
                    const QString& file_path,
                    std::size_t offset);
 
-    void cancel(const QString& accountId,
-                const QString& conversationId,
-                const QString& interactionId);
+    void download(const QString& accountId,
+                  const QString& convId,
+                  const QString& fileId,
+                  const QString& path);
 
-    void registerTransferId(DataTransferId dringId, const QString& interactionId);
+    void cancel(const QString& accountId, const QString& conversationId, const QString& fileId);
 
-    QString getInteractionIdFromDringId(DataTransferId dringId);
+    void registerTransferId(const QString& fileId, const QString& interactionId);
 
-    DataTransferId getDringIdFromInteractionId(const QString& interactionId);
+    QString getInteractionIdFromFileId(const QString& fileId);
+
+    QString getFileIdFromInteractionId(const QString& fileId);
 
     /**
      *  Creates APPDATA/received and return the path
diff --git a/src/api/interaction.h b/src/api/interaction.h
index d78fa9a6..34c17b0e 100644
--- a/src/api/interaction.h
+++ b/src/api/interaction.h
@@ -172,27 +172,25 @@ to_status(const QString& status)
         return Status::INVALID;
 }
 
-enum class ContactAction {
-    ADD,
-    JOIN,
-    LEAVE,
-    BANNED
-};
+enum class ContactAction { ADD, JOIN, LEAVE, BANNED, INVALID };
 Q_ENUM_NS(ContactAction)
 
 static inline const QString
 to_string(const ContactAction& action)
 {
     switch (action) {
-        case ContactAction::ADD:
-            return "ADD";
-        case ContactAction::JOIN:
-            return "JOIN";
-        case ContactAction::LEAVE:
-            return "LEAVE";
-        case ContactAction::BANNED:
-            return "BANNED";
+    case ContactAction::ADD:
+        return "ADD";
+    case ContactAction::JOIN:
+        return "JOIN";
+    case ContactAction::LEAVE:
+        return "LEAVE";
+    case ContactAction::BANNED:
+        return "BANNED";
+    case ContactAction::INVALID:
+        return {};
     }
+    return {};
 }
 
 static inline ContactAction
@@ -204,26 +202,29 @@ to_action(const QString& action)
         return ContactAction::JOIN;
     else if (action == "remove")
         return ContactAction::LEAVE;
-    else if (action == "baned")
+    else if (action == "banned")
         return ContactAction::BANNED;
+    return ContactAction::INVALID;
 }
 
 static inline QString
 getContactInteractionString(const QString& authorUri, const ContactAction& action)
 {
     switch (action) {
-        case ContactAction::ADD:
-            if (authorUri.isEmpty()) {
-                return QObject::tr("Contact added");
-            }
-            return QObject::tr("Invitation received");
-        case ContactAction::JOIN:
-            return QObject::tr("Invitation accepted");
-        case ContactAction::LEAVE:
-            return QObject::tr("Contact left conversation");
-        case ContactAction::BANNED:
-            return {};
+    case ContactAction::ADD:
+        if (authorUri.isEmpty()) {
+            return QObject::tr("Contact added");
+        }
+        return QObject::tr("Invitation received");
+    case ContactAction::JOIN:
+        return QObject::tr("Invitation accepted");
+    case ContactAction::LEAVE:
+        return QObject::tr("Contact left conversation");
+    case ContactAction::BANNED:
+    case ContactAction::INVALID:
+        return {};
     }
+    return {};
 }
 
 /**
@@ -245,6 +246,7 @@ struct Info
     Type type = Type::INVALID;
     Status status = Status::INVALID;
     bool isRead = false;
+    MapStringString commit;
 
     Info() {}
 
@@ -288,6 +290,7 @@ struct Info
         if (type == Type::CALL) {
             duration = message["duration"].toInt() / 1000;
         }
+        commit = message;
     }
 };
 
diff --git a/src/authority/storagehelper.cpp b/src/authority/storagehelper.cpp
index 8c453397..fcdcc0ad 100644
--- a/src/authority/storagehelper.cpp
+++ b/src/authority/storagehelper.cpp
@@ -509,6 +509,7 @@ addOrUpdateMessage(Database& db,
         return id;
     }
 }
+
 QString
 addDataTransferToConversation(Database& db,
                               const QString& conversationId,
@@ -528,7 +529,7 @@ addDataTransferToConversation(Database& db,
                           {":conversation", convId},
                           {":timestamp", toQString(std::time(nullptr))},
                           {":body", infoFromDaemon.path},
-                          {":type", infoFromDaemon.isOutgoing ? "DATA_TRANSFER" : "DATA_TRANSFER"},
+                          {":type", "DATA_TRANSFER"},
                           {":status", "TRANSFER_CREATED"},
                           {":is_read", "0"},
                           {":daemon_id", infoFromDaemon.uid}});
diff --git a/src/callbackshandler.cpp b/src/callbackshandler.cpp
index 0101b60d..0f912104 100644
--- a/src/callbackshandler.cpp
+++ b/src/callbackshandler.cpp
@@ -22,6 +22,7 @@
 #include "api/account.h"
 #include "api/lrc.h"
 #include "api/newaccountmodel.h"
+#include "api/datatransfer.h"
 #include "api/datatransfermodel.h"
 #include "api/behaviorcontroller.h"
 
@@ -34,6 +35,8 @@
 // DRing
 #include <datatransfer_interface.h>
 
+#include <QFileInfo>
+
 #ifdef ENABLE_LIBWRAP
 // For the debugMessageReceived connection that queues const std::string refs
 // when not using dbus
@@ -44,6 +47,38 @@ namespace lrc {
 
 using namespace api;
 
+static inline datatransfer::Status
+convertDataTransferEvent(DRing::DataTransferEventCode event)
+{
+    switch (event) {
+    case DRing::DataTransferEventCode::invalid:
+        return datatransfer::Status::INVALID;
+    case DRing::DataTransferEventCode::created:
+        return datatransfer::Status::on_connection;
+    case DRing::DataTransferEventCode::unsupported:
+        return datatransfer::Status::unsupported;
+    case DRing::DataTransferEventCode::wait_peer_acceptance:
+        return datatransfer::Status::on_connection;
+    case DRing::DataTransferEventCode::wait_host_acceptance:
+        return datatransfer::Status::on_connection;
+    case DRing::DataTransferEventCode::ongoing:
+        return datatransfer::Status::on_progress;
+    case DRing::DataTransferEventCode::finished:
+        return datatransfer::Status::success;
+    case DRing::DataTransferEventCode::closed_by_host:
+        return datatransfer::Status::stop_by_host;
+    case DRing::DataTransferEventCode::closed_by_peer:
+        return datatransfer::Status::stop_by_peer;
+    case DRing::DataTransferEventCode::invalid_pathname:
+        return datatransfer::Status::invalid_pathname;
+    case DRing::DataTransferEventCode::unjoinable_peer:
+        return datatransfer::Status::unjoinable_peer;
+    case DRing::DataTransferEventCode::timeout_expired:
+        return datatransfer::Status::timeout_expired;
+    }
+    throw std::runtime_error("BUG: broken convertDataTransferEvent() switch");
+}
+
 CallbacksHandler::CallbacksHandler(const Lrc& parent)
     : QObject()
     , parent(parent)
@@ -521,15 +556,34 @@ CallbacksHandler::slotAccountMessageStatusChanged(const QString& accountId,
 void
 CallbacksHandler::slotDataTransferEvent(const QString& accountId,
                                         const QString& conversationId,
-                                        DataTransferId dringId,
+                                        const QString&,
+                                        const QString& fileId,
                                         uint codeStatus)
 {
     auto event = DRing::DataTransferEventCode(codeStatus);
 
     api::datatransfer::Info info;
-    parent.getAccountModel()
-        .getAccountInfo(accountId)
-        .dataTransferModel->transferInfo(accountId, conversationId, dringId, info);
+    if (conversationId.isEmpty()) {
+        parent.getAccountModel().getAccountInfo(accountId).dataTransferModel->transferInfo(accountId,
+                                                                                           fileId,
+                                                                                           info);
+    } else {
+        info.uid = fileId;
+        info.status = convertDataTransferEvent(event);
+        info.conversationId = conversationId;
+        info.accountId = accountId;
+        qlonglong totalSize, progress;
+        QString path;
+        parent.getAccountModel().getAccountInfo(accountId).dataTransferModel->fileTransferInfo(
+            accountId, conversationId, fileId, path, totalSize, progress);
+        auto fi = QFileInfo(path);
+        if (fi.isSymLink()) {
+            path = fi.symLinkTarget();
+        }
+        info.path = path;
+        info.totalSize = totalSize;
+        info.progress = progress;
+    }
 
     // WARNING: info.status could be INVALID in case of async signaling
     // So listeners must only take account of dringId in such case.
@@ -537,33 +591,33 @@ CallbacksHandler::slotDataTransferEvent(const QString& accountId,
 
     switch (event) {
     case DRing::DataTransferEventCode::created:
-        emit transferStatusCreated(static_cast<DataTransferId>(dringId), info);
+        emit transferStatusCreated(fileId, info);
         break;
     case DRing::DataTransferEventCode::closed_by_host:
     case DRing::DataTransferEventCode::closed_by_peer:
-        emit transferStatusCanceled(static_cast<DataTransferId>(dringId), info);
+        emit transferStatusCanceled(fileId, info);
         break;
     case DRing::DataTransferEventCode::wait_peer_acceptance:
-        emit transferStatusAwaitingPeer(static_cast<DataTransferId>(dringId), info);
+        emit transferStatusAwaitingPeer(fileId, info);
         break;
     case DRing::DataTransferEventCode::wait_host_acceptance:
-        emit transferStatusAwaitingHost(static_cast<DataTransferId>(dringId), info);
+        emit transferStatusAwaitingHost(fileId, info);
         break;
     case DRing::DataTransferEventCode::ongoing:
-        emit transferStatusOngoing(static_cast<DataTransferId>(dringId), info);
+        emit transferStatusOngoing(fileId, info);
         break;
     case DRing::DataTransferEventCode::finished:
-        emit transferStatusFinished(static_cast<DataTransferId>(dringId), info);
+        emit transferStatusFinished(fileId, info);
         break;
     case DRing::DataTransferEventCode::invalid_pathname:
     case DRing::DataTransferEventCode::unsupported:
-        emit transferStatusError(static_cast<DataTransferId>(dringId), info);
+        emit transferStatusError(fileId, info);
         break;
     case DRing::DataTransferEventCode::timeout_expired:
-        emit transferStatusTimeoutExpired(static_cast<DataTransferId>(dringId), info);
+        emit transferStatusTimeoutExpired(fileId, info);
         break;
     case DRing::DataTransferEventCode::unjoinable_peer:
-        emit transferStatusUnjoinable(static_cast<DataTransferId>(dringId), info);
+        emit transferStatusUnjoinable(fileId, info);
         break;
     case DRing::DataTransferEventCode::invalid:
         break;
diff --git a/src/callbackshandler.h b/src/callbackshandler.h
index 0cc689ee..ddfd9f2b 100644
--- a/src/callbackshandler.h
+++ b/src/callbackshandler.h
@@ -204,15 +204,15 @@ Q_SIGNALS:
                                      const QString& messageId,
                                      int status);
 
-    void transferStatusCreated(DataTransferId dringId, api::datatransfer::Info info);
-    void transferStatusCanceled(DataTransferId dringId, api::datatransfer::Info info);
-    void transferStatusAwaitingPeer(DataTransferId dringId, api::datatransfer::Info info);
-    void transferStatusAwaitingHost(DataTransferId dringId, api::datatransfer::Info info);
-    void transferStatusOngoing(DataTransferId dringId, api::datatransfer::Info info);
-    void transferStatusFinished(DataTransferId dringId, api::datatransfer::Info info);
-    void transferStatusError(DataTransferId dringId, api::datatransfer::Info info);
-    void transferStatusTimeoutExpired(DataTransferId dringId, api::datatransfer::Info info);
-    void transferStatusUnjoinable(DataTransferId dringId, api::datatransfer::Info info);
+    void transferStatusCreated(const QString& fileId, api::datatransfer::Info info);
+    void transferStatusCanceled(const QString& fileId, api::datatransfer::Info info);
+    void transferStatusAwaitingPeer(const QString& fileId, api::datatransfer::Info info);
+    void transferStatusAwaitingHost(const QString& fileId, api::datatransfer::Info info);
+    void transferStatusOngoing(const QString& fileId, api::datatransfer::Info info);
+    void transferStatusFinished(const QString& fileId, api::datatransfer::Info info);
+    void transferStatusError(const QString& fileId, api::datatransfer::Info info);
+    void transferStatusTimeoutExpired(const QString& fileId, api::datatransfer::Info info);
+    void transferStatusUnjoinable(const QString& fileId, api::datatransfer::Info info);
 
     /**
      * Connect this signal to get when a device name changed or a device is added
@@ -508,12 +508,16 @@ private Q_SLOTS:
      * @param status, new status for this message
      */
     void slotAccountMessageStatusChanged(const QString& accountId,
-                                     const QString& conversationId,
-                                     const QString& peer,
-                                     const QString& messageId,
-                                     int status);
-
-    void slotDataTransferEvent(const QString& accountId, const QString& conversationId, DataTransferId id, uint code);
+                                         const QString& conversationId,
+                                         const QString& peer,
+                                         const QString& messageId,
+                                         int status);
+
+    void slotDataTransferEvent(const QString& accountId,
+                               const QString& conversationId,
+                               const QString& interactionId,
+                               const QString& fileId,
+                               uint code);
 
     /**
      * Emit knownDevicesChanged
diff --git a/src/contactmodel.cpp b/src/contactmodel.cpp
index 341dfb6f..743b50a5 100644
--- a/src/contactmodel.cpp
+++ b/src/contactmodel.cpp
@@ -194,7 +194,7 @@ public Q_SLOTS:
      * @param dringId Daemon's ID for incoming transfer
      * @param transferInfo DataTransferInfo structure from daemon
      */
-    void slotNewAccountTransfer(DataTransferId dringId, datatransfer::Info info);
+    void slotNewAccountTransfer(const QString& fileId, datatransfer::Info info);
 
     /**
      * Listen from daemon to know when a VCard is received
@@ -1125,7 +1125,7 @@ ContactModelPimpl::sipUriReceivedFilter(const QString& uri)
 }
 
 void
-ContactModelPimpl::slotNewAccountTransfer(DataTransferId dringId, datatransfer::Info info)
+ContactModelPimpl::slotNewAccountTransfer(const QString& fileId, datatransfer::Info info)
 {
     if (info.accountId != linked.owner.id)
         return;
@@ -1149,7 +1149,7 @@ ContactModelPimpl::slotNewAccountTransfer(DataTransferId dringId, datatransfer::
         emit behaviorController.newTrustRequest(linked.owner.id, info.peerUri);
     }
 
-    emit linked.newAccountTransfer(dringId, info);
+    emit linked.newAccountTransfer(fileId, info);
 }
 
 void
diff --git a/src/conversationmodel.cpp b/src/conversationmodel.cpp
index 40c80c85..c00a039e 100644
--- a/src/conversationmodel.cpp
+++ b/src/conversationmodel.cpp
@@ -199,11 +199,11 @@ public:
                         int conversationIdx,
                         const QString& interactionId);
 
-    bool usefulDataFromDataTransfer(DataTransferId dringId,
+    bool usefulDataFromDataTransfer(const QString& fileId,
                                     const datatransfer::Info& info,
                                     QString& interactionId,
                                     QString& conversationId);
-    void awaitingHost(DataTransferId dringId, datatransfer::Info info);
+    void awaitingHost(const QString& fileId, datatransfer::Info info);
 
     bool hasOneOneSwarmWith(const QString& participant);
 
@@ -214,6 +214,10 @@ public:
      * @param final name of the file
      */
     void acceptTransfer(const QString& convUid, const QString& interactionId, const QString& path);
+    void handleIncomingFile(const QString& convId,
+                            const QString& interactionId,
+                            const QString& displayName,
+                            int totalSize);
     void addConversationRequest(const MapStringString& convRequest);
     void addContactRequest(const QString& contactUri);
     const VectorString peersForConversation(const conversation::Info& conversation)
@@ -332,16 +336,16 @@ public Q_SLOTS:
                                     const QString& contactUri,
                                     bool isComposing);
 
-    void slotTransferStatusCreated(DataTransferId dringId, api::datatransfer::Info info);
-    void slotTransferStatusCanceled(DataTransferId dringId, api::datatransfer::Info info);
-    void slotTransferStatusAwaitingPeer(DataTransferId dringId, api::datatransfer::Info info);
-    void slotTransferStatusAwaitingHost(DataTransferId dringId, api::datatransfer::Info info);
-    void slotTransferStatusOngoing(DataTransferId dringId, api::datatransfer::Info info);
-    void slotTransferStatusFinished(DataTransferId dringId, api::datatransfer::Info info);
-    void slotTransferStatusError(DataTransferId dringId, api::datatransfer::Info info);
-    void slotTransferStatusTimeoutExpired(DataTransferId dringId, api::datatransfer::Info info);
-    void slotTransferStatusUnjoinable(DataTransferId dringId, api::datatransfer::Info info);
-    bool updateTransferStatus(DataTransferId dringId,
+    void slotTransferStatusCreated(const QString& fileId, api::datatransfer::Info info);
+    void slotTransferStatusCanceled(const QString& fileId, api::datatransfer::Info info);
+    void slotTransferStatusAwaitingPeer(const QString& fileId, api::datatransfer::Info info);
+    void slotTransferStatusAwaitingHost(const QString& fileId, api::datatransfer::Info info);
+    void slotTransferStatusOngoing(const QString& fileId, api::datatransfer::Info info);
+    void slotTransferStatusFinished(const QString& fileId, api::datatransfer::Info info);
+    void slotTransferStatusError(const QString& fileId, api::datatransfer::Info info);
+    void slotTransferStatusTimeoutExpired(const QString& fileId, api::datatransfer::Info info);
+    void slotTransferStatusUnjoinable(const QString& fileId, api::datatransfer::Info info);
+    bool updateTransferStatus(const QString& fileId,
                               datatransfer::Info info,
                               interaction::Status newStatus,
                               bool& updated);
@@ -587,12 +591,13 @@ ConversationModel::getFilteredConversations(const FilterType& filter,
                     return (owner.profileInfo.type == profile::Type::SIP && !entry.isRequest);
                 case FilterType::REQUEST:
                     return entry.isRequest;
+                case FilterType::INVALID:
                 default:
                     break;
                 }
             } catch (...) {
-                return false;
             }
+            return false;
         })
         .validate();
 }
@@ -616,6 +621,9 @@ ConversationModel::getConversationForPeerUri(const QString& uri)
                 if (!pimpl_->isCoreDialog(conv)) {
                     return false;
                 }
+                if (conv.mode == conversation::Mode::ONE_TO_ONE) {
+                    return pimpl_->peersForConversation(conv).indexOf(uri) != -1;
+                }
                 return uri == pimpl_->peersForConversation(conv).front();
             },
             true));
@@ -1482,6 +1490,9 @@ ConversationModel::acceptConversationRequest(const QString& conversationId)
         }
         break;
     }
+    case conversation::Mode::ADMIN_INVITES_ONLY:
+    case conversation::Mode::INVITES_ONLY:
+    case conversation::Mode::PUBLIC:
     default:
         break;
     }
@@ -1904,10 +1915,9 @@ ConversationModelPimpl::peersForConversation(const conversation::Info& conversat
     default:
         break;
     }
-    for (const auto& participant : conversation.participants) {
+    for (const auto& participant : conversation.participants)
         if (participant != linked.owner.profileInfo.uri)
             result.push_back(participant);
-    }
     return result;
 }
 
@@ -1930,8 +1940,9 @@ ConversationModelPimpl::isCoreDialog(const conversation::Info& conversation) con
             return true;
         }
     default:
-        return false;
+        break;
     }
+    return false;
 }
 
 bool
@@ -2083,7 +2094,7 @@ ConversationModelPimpl::sendContactRequest(const QString& contactUri)
         linked.owner.contactModel->addContact(contact);
 }
 void
-ConversationModelPimpl::slotConversationLoaded(uint32_t requestId,
+ConversationModelPimpl::slotConversationLoaded(uint32_t,
                                                const QString& accountId,
                                                const QString& conversationId,
                                                const VectorMapStringString& messages)
@@ -2108,8 +2119,25 @@ ConversationModelPimpl::slotConversationLoaded(uint32_t requestId,
             auto msgId = message["id"];
             auto msg = interaction::Info(message, linked.owner.profileInfo.uri);
             if (msg.type == interaction::Type::DATA_TRANSFER) {
-                auto daemponID = message["tid"];
-                storage::updateDataTransferInteractionForDaemonId(db, daemponID, msg);
+                auto fileId = message["fileId"];
+                QString path;
+                qlonglong bytesProgress, totalSize;
+                linked.owner.dataTransferModel->fileTransferInfo(accountId,
+                                                                 conversationId,
+                                                                 fileId,
+                                                                 path,
+                                                                 totalSize,
+                                                                 bytesProgress);
+                QFileInfo fi(path);
+                if (fi.isSymLink()) {
+                    msg.body = fi.symLinkTarget();
+                } else {
+                    msg.body = path;
+                }
+                msg.status = bytesProgress == 0 ? interaction::Status::TRANSFER_AWAITING_HOST
+                             : bytesProgress == totalSize ? interaction::Status::TRANSFER_FINISHED
+                                                          : interaction::Status::TRANSFER_ONGOING;
+                linked.owner.dataTransferModel->registerTransferId(fileId, msgId);
             } else if (msg.type == interaction::Type::CALL) {
                 msg.body = storage::getCallInteractionString(msg.authorUri, msg.duration);
             }
@@ -2154,27 +2182,30 @@ ConversationModelPimpl::slotMessageReceived(const QString& accountId,
         auto msgId = message["id"];
         auto msg = interaction::Info(message, linked.owner.profileInfo.uri);
         api::datatransfer::Info info;
-        DataTransferId transferIdInt;
+        QString fileId;
+
         if (msg.type == interaction::Type::DATA_TRANSFER) {
             // save data transfer interaction to db and assosiate daemon id with interaction id,
             // conversation id and db id
-            QString transferId = message["tid"];
-            transferIdInt = std::stoull(message["tid"].toStdString());
-            linked.owner.dataTransferModel->transferInfo(accountId,
-                                                         conversationId,
-                                                         transferIdInt,
-                                                         info);
-            // create db entry for valid data transfer
-            if (info.status != datatransfer::Status::INVALID) {
-                msg.body = info.path;
-                msg.status = (info.status == datatransfer::Status::on_connection
-                              && !interaction::isOutgoing(msg))
-                                 ? interaction::Status::TRANSFER_AWAITING_HOST
-                                 : interaction::Status::TRANSFER_CREATED;
+            QString fileId = message["fileId"];
+            QString path;
+            qlonglong bytesProgress, totalSize;
+            linked.owner.dataTransferModel->fileTransferInfo(accountId,
+                                                             conversationId,
+                                                             fileId,
+                                                             path,
+                                                             totalSize,
+                                                             bytesProgress);
+            QFileInfo fi(path);
+            if (fi.isSymLink()) {
+                msg.body = fi.symLinkTarget();
+            } else {
+                msg.body = path;
             }
-            auto interactionId = storage::addDataTransferToConversation(db, conversationId, info);
-            transfIdToDbIntId[transferId] = interactionId;
-            linked.owner.dataTransferModel->registerTransferId(transferIdInt, msgId);
+            msg.status = bytesProgress == 0           ? interaction::Status::TRANSFER_AWAITING_HOST
+                         : bytesProgress == totalSize ? interaction::Status::TRANSFER_FINISHED
+                                                      : interaction::Status::TRANSFER_ONGOING;
+            linked.owner.dataTransferModel->registerTransferId(fileId, msgId);
         } else if (msg.type == interaction::Type::CALL) {
             msg.body = storage::getCallInteractionString(msg.authorUri, msg.duration);
         } else if (msg.type == interaction::Type::TEXT
@@ -2197,7 +2228,10 @@ ConversationModelPimpl::slotMessageReceived(const QString& accountId,
         emit linked.newInteraction(conversationId, msgId, msg);
         emit linked.modelChanged();
         if (msg.status == interaction::Status::TRANSFER_AWAITING_HOST) {
-            awaitingHost(transferIdInt, info);
+            handleIncomingFile(conversationId,
+                               msgId,
+                               message["displayName"],
+                               message["totalSize"].toInt());
         }
         Q_EMIT linked.dataChanged(indexOf(conversationId));
     } catch (const std::exception& e) {
@@ -2231,7 +2265,7 @@ ConversationModelPimpl::insertSwarmInteraction(const QString& interactionId,
 
 void
 ConversationModelPimpl::slotConversationRequestReceived(const QString& accountId,
-                                                        const QString& conversationId,
+                                                        const QString&,
                                                         const MapStringString& metadatas)
 {
     if (accountId != linked.owner.id) {
@@ -2267,20 +2301,23 @@ ConversationModelPimpl::slotConversationReady(const QString& accountId,
                 auto& conversation = getConversationForPeerUri(member["uri"]).get();
                 // remove non swarm conversation
                 if (conversation.mode == conversation::Mode::NON_SWARM) {
+                    Q_EMIT linked.beginRemoveRows(indexOf(conversation.uid));
                     conversations.erase(conversations.begin() + indexOf(conversation.uid));
                     storage::removeContact(db, member["uri"]);
+                    Q_EMIT linked.endRemoveRows();
                     invalidateModel();
                     emit linked.conversationRemoved(conversation.uid);
+                    emit linked.modelChanged();
                 }
             } catch (...) {
             }
         }
     }
 
-    Q_EMIT linked.beginInsertRows(conversations.size());
     bool conversationExists = indexOf(conversationId) >= 0;
 
     if (!conversationExists) {
+        Q_EMIT linked.beginInsertRows(conversations.size());
         addSwarmConversation(conversationId);
     }
     auto& conversation = getConversationForUid(conversationId).get();
@@ -2293,10 +2330,16 @@ ConversationModelPimpl::slotConversationReady(const QString& accountId,
         conversation.mode = conversation::to_mode(details["mode"].toInt());
         conversation.isRequest = false;
         conversation.needsSyncing = false;
+        emit linked.conversationUpdated(conversationId);
+        Q_EMIT linked.dataChanged(indexOf(conversationId));
         auto id = ConfigurationManager::instance().loadConversationMessages(linked.owner.id,
                                                                             conversationId,
                                                                             "",
                                                                             0);
+        auto& peers = peersForConversation(conversation);
+        if (peers.size() == 1)
+            emit linked.conversationReady(conversationId, peers.front());
+        return;
     }
     invalidateModel();
     // we use conversationReady callback only for conversation with one participant. We could use
@@ -2324,36 +2367,33 @@ ConversationModelPimpl::slotConversationRemoved(const QString& accountId,
         // we should create non swarm conversation only for removed one-to-one conversation.
         if (conversation.mode != conversation::Mode::ONE_TO_ONE) {
             // remove swarm conversation
+            Q_EMIT linked.beginRemoveRows(conversationIndex);
             conversations.erase(conversations.begin() + conversationIndex);
             emit linked.conversationRemoved(conversationId);
+            Q_EMIT linked.endRemoveRows();
             return;
         }
         auto& peers = peersForConversation(conversation);
         if (peers.isEmpty()) {
             // remove swarm conversation
+            Q_EMIT linked.beginRemoveRows(conversationIndex);
             conversations.erase(conversations.begin() + conversationIndex);
             emit linked.conversationRemoved(conversationId);
+            Q_EMIT linked.endRemoveRows();
             return;
         }
         auto contactId = peers.first();
         // remove swarm conversation
+        Q_EMIT linked.beginRemoveRows(conversationIndex);
         conversations.erase(conversations.begin() + conversationIndex);
-        auto contact = linked.owner.contactModel->getContact(contactId);
-        if (contact.profileInfo.type != api::profile::Type::JAMI) {
-            emit linked.conversationRemoved(conversationId);
-            return;
-        }
-        Q_EMIT linked.beginInsertRows(conversations.size());
+        emit linked.conversationRemoved(conversationId);
+        Q_EMIT linked.endRemoveRows();
         auto conv = storage::getConversationsWithPeer(db, contactId);
         if (conv.empty()) {
             conv.push_back(storage::beginConversationWithPeer(db, contactId));
         }
         addConversationWith(conv[0], contactId);
-        invalidateModel();
-        emit linked.conversationRemoved(conversationId);
         emit linked.newConversation(conv[0]);
-        emit linked.modelChanged();
-        Q_EMIT linked.endInsertRows();
     } catch (...) {
     }
 }
@@ -2420,6 +2460,7 @@ ConversationModelPimpl::slotContactAdded(const QString& contactUri)
             QStringList swarms = ConfigurationManager::instance().getConversations(linked.owner.id);
             bool needsSyncing = swarms.indexOf(conversation.uid) == -1;
             if (conversation.needsSyncing != needsSyncing) {
+                conversation.isRequest = false;
                 conversation.needsSyncing = needsSyncing;
                 invalidateModel();
                 emit linked.modelChanged();
@@ -2430,8 +2471,14 @@ ConversationModelPimpl::slotContactAdded(const QString& contactUri)
             conv.push_back(storage::beginConversationWithPeer(db, contactUri));
         }
         // remove temporary conversation that was added when receiving an incoming request
-        if (indexOf(contactUri) >= 0) {
-            conversations.erase(conversations.begin() + indexOf(contactUri));
+        auto contactIdx = indexOf(contactUri);
+        if (contactIdx >= 0) {
+            Q_EMIT linked.beginRemoveRows(contactIdx);
+            conversations.erase(conversations.begin() + contactIdx);
+            Q_EMIT linked.endRemoveRows();
+
+            invalidateModel();
+            emit linked.modelChanged();
         }
         // add a conversation if not exists
         addConversation = indexOf(conv[0]) == -1;
@@ -2447,13 +2494,9 @@ ConversationModelPimpl::slotContactAdded(const QString& contactUri)
         }
     }
     if (addConversation) {
-        Q_EMIT linked.beginInsertRows(conversations.size());
         addConversationWith(conv[0], contactUri);
-        invalidateModel();
         emit linked.conversationReady(conv[0], contactUri);
         emit linked.newConversation(conv[0]);
-        emit linked.modelChanged();
-        Q_EMIT linked.endInsertRows();
     }
 }
 
@@ -2461,7 +2504,7 @@ void
 ConversationModelPimpl::addContactRequest(const QString& contactUri)
 {
     try {
-        auto& conv = getConversationForPeerUri(contactUri).get();
+        getConversationForPeerUri(contactUri).get();
         // request from contact already exists, return
         return;
     } catch (std::out_of_range&) {
@@ -2617,9 +2660,8 @@ ConversationModelPimpl::addSwarmConversation(const QString& convId)
     for (auto& member : members) {
         // this check should be removed once all usage of participants replaced by
         // peersForConversation. We should have ourself in participants list
-        if (member["uri"] != accountURI) {
+        if (member["uri"] != accountURI)
             participants.append(member["uri"]);
-        }
     }
     if (participants.isEmpty()) {
         return;
@@ -2799,7 +2841,7 @@ ConversationModelPimpl::slotIncomingCall(const QString& fromId, const QString& c
     qDebug() << "Add call to conversation with " << fromId;
     conversation.callId = callId;
 
-    addOrUpdateCallMessage(callId, fromId);
+    addOrUpdateCallMessage(callId, fromId, true);
     emit behaviorController.showIncomingCallView(linked.owner.id, conversation.uid);
 }
 
@@ -3318,7 +3360,7 @@ ConversationModel::acceptTransfer(const QString& convUid,
 }
 
 void
-ConversationModel::cancelTransfer(const QString& convUid, const QString& interactionId)
+ConversationModel::cancelTransfer(const QString& convUid, const QString& fileId)
 {
     // For this action, we change interaction status before effective canceling as daemon will
     // emit Finished event code immediately (before leaving this method) in non-DBus mode.
@@ -3328,13 +3370,13 @@ ConversationModel::cancelTransfer(const QString& convUid, const QString& interac
     if (conversationIdx != -1) {
         std::lock_guard<std::mutex> lk(pimpl_->interactionsLocks[convUid]);
         auto& interactions = pimpl_->conversations[conversationIdx].interactions;
-        auto it = interactions.find(interactionId);
+        auto it = interactions.find(fileId);
         if (it != interactions.end()) {
             it->second.status = interaction::Status::TRANSFER_CANCELED;
 
             // update information in the db
             storage::updateInteractionStatus(pimpl_->db,
-                                             interactionId,
+                                             fileId,
                                              interaction::Status::TRANSFER_CANCELED);
             emitUpdated = true;
             itCopy = it->second;
@@ -3344,13 +3386,12 @@ ConversationModel::cancelTransfer(const QString& convUid, const QString& interac
         // for swarm conversations we need to provide conversation id to accept file, for not swarm
         // conversations we need peer uri
         lrc::api::datatransfer::Info info = {};
-        getTransferInfo(convUid, interactionId, info);
-        auto identifier = info.conversationId.isEmpty() ? info.peerUri : convUid;
+        getTransferInfo(convUid, fileId, info);
         // Forward cancel action to daemon (will invoke slotTransferStatusCanceled)
-        owner.dataTransferModel->cancel(owner.id, identifier, interactionId);
+        owner.dataTransferModel->cancel(owner.id, convUid, fileId);
         pimpl_->invalidateModel();
-        emit interactionStatusUpdated(convUid, interactionId, itCopy);
-        emit pimpl_->behaviorController.newReadInteraction(owner.id, convUid, interactionId);
+        emit interactionStatusUpdated(convUid, fileId, itCopy);
+        emit pimpl_->behaviorController.newReadInteraction(owner.id, convUid, fileId);
     }
 }
 
@@ -3359,15 +3400,24 @@ ConversationModel::getTransferInfo(const QString& conversationId,
                                    const QString& interactionId,
                                    datatransfer::Info& info)
 {
-    try {
-        auto dringId = pimpl_->linked.owner.dataTransferModel->getDringIdFromInteractionId(
-            interactionId);
-        pimpl_->linked.owner.dataTransferModel->transferInfo(owner.id,
-                                                             conversationId,
-                                                             dringId,
-                                                             info);
-    } catch (...) {
-        info.status = datatransfer::Status::INVALID;
+    auto convOpt = getConversationForUid(conversationId);
+    if (!convOpt)
+        return;
+    auto fileId = owner.dataTransferModel->getFileIdFromInteractionId(interactionId);
+    if (convOpt->get().mode == conversation::Mode::NON_SWARM) {
+        try {
+            owner.dataTransferModel->transferInfo(owner.id, fileId, info);
+        } catch (...) {
+            info.status = datatransfer::Status::INVALID;
+        }
+    } else {
+        QString path;
+        qlonglong bytesProgress, totalSize;
+        owner.dataTransferModel
+            ->fileTransferInfo(owner.id, conversationId, fileId, path, totalSize, bytesProgress);
+        info.path = path;
+        info.totalSize = totalSize;
+        info.progress = bytesProgress;
     }
 }
 
@@ -3378,7 +3428,7 @@ ConversationModel::getNumberOfUnreadMessagesFor(const QString& convUid)
 }
 
 bool
-ConversationModelPimpl::usefulDataFromDataTransfer(DataTransferId dringId,
+ConversationModelPimpl::usefulDataFromDataTransfer(const QString& fileId,
                                                    const datatransfer::Info& info,
                                                    QString& interactionId,
                                                    QString& conversationId)
@@ -3386,19 +3436,19 @@ ConversationModelPimpl::usefulDataFromDataTransfer(DataTransferId dringId,
     if (info.accountId != linked.owner.id)
         return false;
     try {
-        interactionId = linked.owner.dataTransferModel->getInteractionIdFromDringId(dringId);
+        interactionId = linked.owner.dataTransferModel->getInteractionIdFromFileId(fileId);
         conversationId = info.conversationId.isEmpty()
                              ? storage::conversationIdFromInteractionId(db, interactionId)
                              : info.conversationId;
     } catch (const std::out_of_range& e) {
-        qWarning() << "Couldn't get interaction from daemon Id: " << dringId;
+        qWarning() << "Couldn't get interaction from daemon Id: " << fileId;
         return false;
     }
     return true;
 }
 
 void
-ConversationModelPimpl::slotTransferStatusCreated(DataTransferId dringId, datatransfer::Info info)
+ConversationModelPimpl::slotTransferStatusCreated(const QString& fileId, datatransfer::Info info)
 {
     // check if transfer is for the current account
     if (info.accountId != linked.owner.id)
@@ -3428,8 +3478,8 @@ ConversationModelPimpl::slotTransferStatusCreated(DataTransferId dringId, datatr
     const auto& convId = convIds[0];
     auto interactionId = storage::addDataTransferToConversation(db, convId, info);
 
-    // map dringId and interactionId for latter retrivial from client (that only known the interactionId)
-    linked.owner.dataTransferModel->registerTransferId(dringId, interactionId);
+    // map fileId and interactionId for latter retrivial from client (that only known the interactionId)
+    linked.owner.dataTransferModel->registerTransferId(fileId, interactionId);
 
     auto interaction = interaction::Info {info.isOutgoing ? "" : info.peerUri,
                                           info.isOutgoing ? info.path : info.displayName,
@@ -3464,22 +3514,22 @@ ConversationModelPimpl::slotTransferStatusCreated(DataTransferId dringId, datatr
 }
 
 void
-ConversationModelPimpl::slotTransferStatusAwaitingPeer(DataTransferId dringId,
+ConversationModelPimpl::slotTransferStatusAwaitingPeer(const QString& fileId,
                                                        datatransfer::Info info)
 {
     if (info.accountId != linked.owner.id)
         return;
     bool intUpdated;
-    updateTransferStatus(dringId, info, interaction::Status::TRANSFER_AWAITING_PEER, intUpdated);
+    updateTransferStatus(fileId, info, interaction::Status::TRANSFER_AWAITING_PEER, intUpdated);
 }
 
 void
-ConversationModelPimpl::slotTransferStatusAwaitingHost(DataTransferId dringId,
+ConversationModelPimpl::slotTransferStatusAwaitingHost(const QString& fileId,
                                                        datatransfer::Info info)
 {
     if (info.accountId != linked.owner.id)
         return;
-    awaitingHost(dringId, info);
+    awaitingHost(fileId, info);
 }
 
 bool
@@ -3494,18 +3544,18 @@ ConversationModelPimpl::hasOneOneSwarmWith(const QString& participant)
 }
 
 void
-ConversationModelPimpl::awaitingHost(DataTransferId dringId, datatransfer::Info info)
+ConversationModelPimpl::awaitingHost(const QString& fileId, datatransfer::Info info)
 {
     if (info.accountId != linked.owner.id)
         return;
     QString interactionId;
     QString conversationId;
-    if (not usefulDataFromDataTransfer(dringId, info, interactionId, conversationId))
+    if (not usefulDataFromDataTransfer(fileId, info, interactionId, conversationId))
         return;
 
     bool intUpdated;
 
-    if (!updateTransferStatus(dringId,
+    if (!updateTransferStatus(fileId,
                               info,
                               interaction::Status::TRANSFER_AWAITING_HOST,
                               intUpdated)) {
@@ -3527,11 +3577,22 @@ ConversationModelPimpl::awaitingHost(DataTransferId dringId, datatransfer::Info
             return;
         }
     }
+    handleIncomingFile(conversationId, interactionId, info.displayName, info.totalSize);
+}
+
+void
+ConversationModelPimpl::handleIncomingFile(const QString& convId,
+                                           const QString& interactionId,
+                                           const QString& displayName,
+                                           int totalSize)
+{
     // If it's an accepted file type and less than 20 MB, accept transfer.
     if (linked.owner.dataTransferModel->automaticAcceptTransfer) {
         if (linked.owner.dataTransferModel->acceptBehindMb == 0
-            || info.totalSize < linked.owner.dataTransferModel->acceptBehindMb * 1024 * 1024) {
-            acceptTransfer(conversationId, interactionId, info.displayName);
+            || (totalSize > 0
+                && static_cast<unsigned>(totalSize)
+                       < linked.owner.dataTransferModel->acceptBehindMb * 1024 * 1024)) {
+            acceptTransfer(convId, interactionId, displayName);
         }
     }
 }
@@ -3554,48 +3615,79 @@ ConversationModelPimpl::acceptTransfer(const QString& convUid,
     if (!dir.exists())
         dir.mkpath(".");
     auto& conversation = getConversationForUid(convUid).get();
-    // for swarm conversation we need converstion id to accept file and for not swarm participant uri
-    auto identifier = conversation.mode != conversation::Mode::NON_SWARM
-                          ? convUid
-                          : peersForConversation(conversation).front();
-    auto acceptedFilePath = linked.owner.dataTransferModel->accept(linked.owner.id,
-                                                                   identifier,
-                                                                   interactionId,
-                                                                   destinationDir + path,
-                                                                   0);
-    auto dringId = linked.owner.dataTransferModel->getDringIdFromInteractionId(interactionId);
-    if (transfIdToDbIntId.find(QString::number(dringId)) != transfIdToDbIntId.end()) {
-        auto dbInteractionId = transfIdToDbIntId[QString::number(dringId)];
-        storage::updateInteractionBody(db, dbInteractionId, acceptedFilePath);
-        storage::updateInteractionStatus(db,
-                                         dbInteractionId,
-                                         interaction::Status::TRANSFER_ACCEPTED);
-    } else {
-        storage::updateInteractionBody(db, interactionId, acceptedFilePath);
-        storage::updateInteractionStatus(db, interactionId, interaction::Status::TRANSFER_ACCEPTED);
-    }
-    // prepare interaction Info and emit signal for the client
-    auto conversationIdx = indexOf(convUid);
-    interaction::Info itCopy;
-    bool emitUpdated = false;
-    if (conversationIdx != -1) {
-        std::lock_guard<std::mutex> lk(interactionsLocks[convUid]);
-        auto& interactions = conversations[conversationIdx].interactions;
-        auto it = interactions.find(interactionId);
-        if (it != interactions.end()) {
-            it->second.body = acceptedFilePath;
-            it->second.status = interaction::Status::TRANSFER_ACCEPTED;
-            emitUpdated = true;
-            itCopy = it->second;
+    if (conversation.mode == conversation::Mode::NON_SWARM) {
+        auto acceptedFilePath = linked.owner.dataTransferModel->accept(linked.owner.id,
+                                                                       interactionId,
+                                                                       destinationDir + path,
+                                                                       0);
+        auto fileId = linked.owner.dataTransferModel->getFileIdFromInteractionId(interactionId);
+        if (transfIdToDbIntId.find(fileId) != transfIdToDbIntId.end()) {
+            auto dbInteractionId = transfIdToDbIntId[fileId];
+            storage::updateInteractionBody(db, dbInteractionId, acceptedFilePath);
+            storage::updateInteractionStatus(db,
+                                             dbInteractionId,
+                                             interaction::Status::TRANSFER_ACCEPTED);
+        } else {
+            storage::updateInteractionBody(db, interactionId, acceptedFilePath);
+            storage::updateInteractionStatus(db,
+                                             interactionId,
+                                             interaction::Status::TRANSFER_ACCEPTED);
         }
+        // prepare interaction Info and emit signal for the client
+        auto conversationIdx = indexOf(convUid);
+        interaction::Info itCopy;
+        bool emitUpdated = false;
+        if (conversationIdx != -1) {
+            std::lock_guard<std::mutex> lk(interactionsLocks[convUid]);
+            auto& interactions = conversations[conversationIdx].interactions;
+            auto it = interactions.find(interactionId);
+            if (it != interactions.end()) {
+                it->second.body = acceptedFilePath;
+                it->second.status = interaction::Status::TRANSFER_ACCEPTED;
+                emitUpdated = true;
+                itCopy = it->second;
+            }
+        }
+        if (emitUpdated) {
+            if (isCoreDialog(conversations[conversationIdx])) {
+                sendContactRequest(peersForConversation(conversations[conversationIdx]).front());
+            }
+            invalidateModel();
+            emit linked.interactionStatusUpdated(convUid, interactionId, itCopy);
+            emit behaviorController.newReadInteraction(linked.owner.id, convUid, interactionId);
+        }
+        return;
     }
-    if (emitUpdated) {
-        if (isCoreDialog(conversations[conversationIdx])) {
-            sendContactRequest(peersForConversation(conversations[conversationIdx]).front());
+
+    auto interaction = conversation.interactions.find(interactionId);
+    if (interaction != conversation.interactions.end()) {
+        auto displayName = interaction->second.commit["displayName"];
+        auto fileId = interaction->second.commit["fileId"];
+        if (fileId.isEmpty()) {
+            qWarning() << "Cannot download file without fileId";
         }
-        invalidateModel();
-        emit linked.interactionStatusUpdated(convUid, interactionId, itCopy);
-        emit behaviorController.newReadInteraction(linked.owner.id, convUid, interactionId);
+        if (displayName.isEmpty()) {
+            displayName = interactionId;
+        }
+        int i = 0;
+        auto path = destinationDir + displayName;
+        do {
+            if (i > 0) {
+                path = destinationDir + QString::number(i) + displayName;
+            }
+            QFileInfo fi(path);
+            if (!fi.isFile())
+                break;
+            ++i;
+
+        } while (i < 1000);
+        if (i == 1000) {
+            qWarning() << "Too much duplicates for " << destinationDir << path;
+            return;
+        }
+        linked.owner.dataTransferModel->download(linked.owner.id, convUid, fileId, path);
+    } else {
+        qWarning() << "Cannot download file without valid interaction";
     }
 }
 
@@ -3607,17 +3699,17 @@ ConversationModelPimpl::invalidateModel()
 }
 
 void
-ConversationModelPimpl::slotTransferStatusOngoing(DataTransferId dringId, datatransfer::Info info)
+ConversationModelPimpl::slotTransferStatusOngoing(const QString& fileId, datatransfer::Info info)
 {
     if (info.accountId != linked.owner.id)
         return;
     QString interactionId;
     QString conversationId;
-    if (not usefulDataFromDataTransfer(dringId, info, interactionId, conversationId))
+    if (not usefulDataFromDataTransfer(fileId, info, interactionId, conversationId))
         return;
     bool intUpdated;
 
-    if (!updateTransferStatus(dringId, info, interaction::Status::TRANSFER_ONGOING, intUpdated)) {
+    if (!updateTransferStatus(fileId, info, interaction::Status::TRANSFER_ONGOING, intUpdated)) {
         return;
     }
     if (!intUpdated) {
@@ -3632,13 +3724,13 @@ ConversationModelPimpl::slotTransferStatusOngoing(DataTransferId dringId, datatr
 }
 
 void
-ConversationModelPimpl::slotTransferStatusFinished(DataTransferId dringId, datatransfer::Info info)
+ConversationModelPimpl::slotTransferStatusFinished(const QString& fileId, datatransfer::Info info)
 {
     if (info.accountId != linked.owner.id)
         return;
     QString interactionId;
     QString conversationId;
-    if (not usefulDataFromDataTransfer(dringId, info, interactionId, conversationId))
+    if (not usefulDataFromDataTransfer(fileId, info, interactionId, conversationId))
         return;
     // prepare interaction Info and emit signal for the client
     auto conversationIdx = indexOf(conversationId);
@@ -3663,65 +3755,65 @@ ConversationModelPimpl::slotTransferStatusFinished(DataTransferId dringId, datat
         if (emitUpdated) {
             invalidateModel();
             if (conversations[conversationIdx].mode != conversation::Mode::NON_SWARM) {
-                if (transfIdToDbIntId.find(QString::number(dringId)) != transfIdToDbIntId.end()) {
-                    auto dbIntId = transfIdToDbIntId[QString::number(dringId)];
+                if (transfIdToDbIntId.find(fileId) != transfIdToDbIntId.end()) {
+                    auto dbIntId = transfIdToDbIntId[fileId];
                     storage::updateInteractionStatus(db, dbIntId, newStatus);
                 }
             } else {
                 storage::updateInteractionStatus(db, interactionId, newStatus);
             }
             emit linked.interactionStatusUpdated(conversationId, interactionId, itCopy);
-            transfIdToDbIntId.remove(QString::number(dringId));
+            transfIdToDbIntId.remove(fileId);
         }
     }
 }
 
 void
-ConversationModelPimpl::slotTransferStatusCanceled(DataTransferId dringId, datatransfer::Info info)
+ConversationModelPimpl::slotTransferStatusCanceled(const QString& fileId, datatransfer::Info info)
 {
     if (info.accountId != linked.owner.id)
         return;
     bool intUpdated;
-    updateTransferStatus(dringId, info, interaction::Status::TRANSFER_CANCELED, intUpdated);
+    updateTransferStatus(fileId, info, interaction::Status::TRANSFER_CANCELED, intUpdated);
 }
 
 void
-ConversationModelPimpl::slotTransferStatusError(DataTransferId dringId, datatransfer::Info info)
+ConversationModelPimpl::slotTransferStatusError(const QString& fileId, datatransfer::Info info)
 {
     if (info.accountId != linked.owner.id)
         return;
     bool intUpdated;
-    updateTransferStatus(dringId, info, interaction::Status::TRANSFER_ERROR, intUpdated);
+    updateTransferStatus(fileId, info, interaction::Status::TRANSFER_ERROR, intUpdated);
 }
 
 void
-ConversationModelPimpl::slotTransferStatusUnjoinable(DataTransferId dringId, datatransfer::Info info)
+ConversationModelPimpl::slotTransferStatusUnjoinable(const QString& fileId, datatransfer::Info info)
 {
     if (info.accountId != linked.owner.id)
         return;
     bool intUpdated;
-    updateTransferStatus(dringId, info, interaction::Status::TRANSFER_UNJOINABLE_PEER, intUpdated);
+    updateTransferStatus(fileId, info, interaction::Status::TRANSFER_UNJOINABLE_PEER, intUpdated);
 }
 
 void
-ConversationModelPimpl::slotTransferStatusTimeoutExpired(DataTransferId dringId,
+ConversationModelPimpl::slotTransferStatusTimeoutExpired(const QString& fileId,
                                                          datatransfer::Info info)
 {
     if (info.accountId != linked.owner.id)
         return;
     bool intUpdated;
-    updateTransferStatus(dringId, info, interaction::Status::TRANSFER_TIMEOUT_EXPIRED, intUpdated);
+    updateTransferStatus(fileId, info, interaction::Status::TRANSFER_TIMEOUT_EXPIRED, intUpdated);
 }
 
 bool
-ConversationModelPimpl::updateTransferStatus(DataTransferId dringId,
+ConversationModelPimpl::updateTransferStatus(const QString& fileId,
                                              datatransfer::Info info,
                                              interaction::Status newStatus,
                                              bool& updated)
 {
     QString interactionId;
     QString conversationId;
-    if (not usefulDataFromDataTransfer(dringId, info, interactionId, conversationId)) {
+    if (not usefulDataFromDataTransfer(fileId, info, interactionId, conversationId)) {
         return false;
     }
 
@@ -3729,13 +3821,7 @@ ConversationModelPimpl::updateTransferStatus(DataTransferId dringId,
     if (conversationIdx < 0) {
         return false;
     }
-    if (conversations[conversationIdx].mode != conversation::Mode::NON_SWARM) {
-        if (transfIdToDbIntId.find(QString::number(dringId)) == transfIdToDbIntId.end()) {
-            return false;
-        }
-        auto dbIntId = transfIdToDbIntId[QString::number(dringId)];
-        storage::updateInteractionStatus(db, dbIntId, newStatus);
-    } else {
+    if (conversations[conversationIdx].mode == conversation::Mode::NON_SWARM) {
         storage::updateInteractionStatus(db, interactionId, newStatus);
     }
     bool emitUpdated = false;
diff --git a/src/datatransfermodel.cpp b/src/datatransfermodel.cpp
index c691cb56..11a55c93 100644
--- a/src/datatransfermodel.cpp
+++ b/src/datatransfermodel.cpp
@@ -82,8 +82,8 @@ public:
     QString getUniqueFilePath(const QString& filename);
 
     DataTransferModel& upLink;
-    std::map<DataTransferId, QString> dring2lrcIdMap;
-    std::map<QString, DataTransferId> lrc2dringIdMap; // stricly the reverse map of dring2lrcIdMap
+    MapStringString file2InteractionId;
+    MapStringString interactionToFileId; // stricly the reverse map of file2InteractionId
 };
 
 DataTransferModel::Impl::Impl(DataTransferModel& up_link)
@@ -113,11 +113,11 @@ DataTransferModel::Impl::getUniqueFilePath(const QString& filename)
 }
 
 void
-DataTransferModel::registerTransferId(DataTransferId dringId, const QString& interactionId)
+DataTransferModel::registerTransferId(const QString& fileId, const QString& interactionId)
 {
-    pimpl_->dring2lrcIdMap.emplace(dringId, interactionId);
-    pimpl_->lrc2dringIdMap.erase(interactionId); // Because a file transfer can be retried
-    pimpl_->lrc2dringIdMap.emplace(interactionId, dringId);
+    pimpl_->file2InteractionId[fileId] = interactionId;
+    pimpl_->interactionToFileId.remove(interactionId); // Because a file transfer can be retried
+    pimpl_->interactionToFileId[interactionId] = fileId;
 }
 
 DataTransferModel::DataTransferModel()
@@ -129,17 +129,12 @@ DataTransferModel::~DataTransferModel() = default;
 
 void
 DataTransferModel::transferInfo(const QString& accountId,
-                                const QString& conversationId,
-                                DataTransferId ringId,
+                                const QString& fileId,
                                 datatransfer::Info& lrc_info)
 {
     DataTransferInfo infoFromDaemon;
-    if (ConfigurationManager::instance().dataTransferInfo(accountId,
-                                                          conversationId,
-                                                          ringId,
-                                                          infoFromDaemon)
-        == 0) {
-        lrc_info.uid = QString::number(ringId);
+    if (ConfigurationManager::instance().dataTransferInfo(accountId, fileId, infoFromDaemon) == 0) {
+        lrc_info.uid = fileId;
         lrc_info.status = convertDataTransferEvent(
             DRing::DataTransferEventCode(infoFromDaemon.lastEvent));
         lrc_info.isOutgoing = !(infoFromDaemon.flags
@@ -159,89 +154,92 @@ DataTransferModel::transferInfo(const QString& accountId,
 }
 
 void
-DataTransferModel::sendFile(const QString& account_id,
+DataTransferModel::sendFile(const QString& accountId,
                             const QString& peer_uri,
                             const QString& conversationId,
-                            const QString& file_path,
-                            const QString& display_name)
+                            const QString& filePath,
+                            const QString& displayName)
 {
-    DataTransferInfo info;
+    if (conversationId.isEmpty()) {
+        // Fallback
+        DataTransferInfo info;
 #ifdef ENABLE_LIBWRAP
-    DRing::DataTransferId id;
+        DRing::DataTransferId id;
 #else
-    qulonglong id;
+        qulonglong id;
 #endif
-    info.accountId = account_id;
-    info.peer = peer_uri;
-    info.path = file_path;
-    info.conversationId = conversationId;
-    info.displayName = display_name;
-    info.bytesProgress = 0;
-    if (ConfigurationManager::instance().sendFile(info, id) != 0) {
-        qDebug() << "DataTransferModel::sendFile(), error";
+        info.accountId = accountId;
+        info.peer = peer_uri;
+        info.path = filePath;
+        info.conversationId = conversationId;
+        info.displayName = displayName;
+        info.bytesProgress = 0;
+        if (ConfigurationManager::instance().sendFileLegacy(info, id) != 0)
+            qWarning() << "DataTransferModel::sendFile(), error";
         return;
     }
+
+    ConfigurationManager::instance().sendFile(accountId,
+                                              conversationId,
+                                              filePath,
+                                              displayName,
+                                              {} /* TODO parent */);
 }
 
 void
-DataTransferModel::bytesProgress(const QString& accountId,
-                                 const QString& conversationId,
-                                 const QString& interactionId,
-                                 int64_t& total,
-                                 int64_t& progress)
+DataTransferModel::fileTransferInfo(const QString& accountId,
+                                    const QString& conversationId,
+                                    const QString& fileId,
+                                    QString& path,
+                                    qlonglong& total,
+                                    qlonglong& progress)
 {
     ConfigurationManager::instance()
-#ifdef ENABLE_LIBWRAP
-        .dataTransferBytesProgress(accountId,
-                                   conversationId,
-                                   pimpl_->lrc2dringIdMap.at(interactionId),
-                                   total,
-                                   progress);
-#else
-        .dataTransferBytesProgress(accountId,
-                                   conversationId,
-                                   pimpl_->lrc2dringIdMap.at(interactionId),
-                                   reinterpret_cast<qlonglong&>(total),
-                                   reinterpret_cast<qlonglong&>(progress));
-#endif
+        .fileTransferInfo(accountId, conversationId, fileId, path, total, progress);
 }
 
 QString
 DataTransferModel::accept(const QString& accountId,
-                          const QString& conversationId,
-                          const QString& interactionId,
+                          const QString& fileId,
                           const QString& file_path,
                           std::size_t offset)
 {
     auto unique_file_path = pimpl_->getUniqueFilePath(file_path);
-    auto dring_id = pimpl_->lrc2dringIdMap.at(interactionId);
-    ConfigurationManager::instance().acceptFileTransfer(accountId,
-                                                        conversationId,
-                                                        dring_id,
-                                                        unique_file_path,
-                                                        offset);
+    auto dring_id = pimpl_->interactionToFileId[fileId];
+    ConfigurationManager::instance().acceptFileTransfer(accountId, dring_id, unique_file_path);
     return unique_file_path;
 }
 
+void
+DataTransferModel::download(const QString& accountId,
+                            const QString& convId,
+                            const QString& fileId,
+                            const QString& path)
+{
+    ConfigurationManager::instance().downloadFile(accountId, convId, fileId, path);
+}
+
 void
 DataTransferModel::cancel(const QString& accountId,
                           const QString& conversationId,
                           const QString& interactionId)
 {
-    auto dring_id = pimpl_->lrc2dringIdMap.at(interactionId);
-    ConfigurationManager::instance().cancelDataTransfer(accountId, conversationId, dring_id);
+    qWarning() << "@@@ " << accountId << " - " << conversationId << " - " << interactionId;
+    ConfigurationManager::instance().cancelDataTransfer(accountId,
+                                                        conversationId,
+                                                        getFileIdFromInteractionId(interactionId));
 }
 
 QString
-DataTransferModel::getInteractionIdFromDringId(DataTransferId dringId)
+DataTransferModel::getInteractionIdFromFileId(const QString& fileId)
 {
-    return pimpl_->dring2lrcIdMap.at(dringId);
+    return pimpl_->file2InteractionId[fileId];
 }
 
-DataTransferId
-DataTransferModel::getDringIdFromInteractionId(const QString& interactionId)
+QString
+DataTransferModel::getFileIdFromInteractionId(const QString& interactionId)
 {
-    return pimpl_->lrc2dringIdMap.at(interactionId);
+    return pimpl_->interactionToFileId[interactionId];
 }
 
 QString
diff --git a/src/messageslist.cpp b/src/messageslist.cpp
index 6fdc88ba..795b6bbc 100644
--- a/src/messageslist.cpp
+++ b/src/messageslist.cpp
@@ -79,21 +79,24 @@ MessagesList::erase(QString msgId)
             return 1;
         }
     }
+    return 0;
 }
+
 interaction::Info&
 MessagesList::operator[](QString messageId)
 {
     for (auto it = interactions_.cbegin(); it != interactions_.cend(); ++it) {
-         if (it->first == messageId) {
-             return const_cast<interaction::Info&>(it->second);
-         }
-     }
-     // element not find, add it to the end
-     interaction::Info newMessage = {};
-     interactions_.insert(interactions_.end(), qMakePair(messageId, newMessage));
-     if (interactions_.last().first == messageId) {
-         return const_cast<interaction::Info&>(interactions_.last().second);
-     }
+        if (it->first == messageId) {
+            return const_cast<interaction::Info&>(it->second);
+        }
+    }
+    // element not find, add it to the end
+    interaction::Info newMessage = {};
+    interactions_.insert(interactions_.end(), qMakePair(messageId, newMessage));
+    if (interactions_.last().first == messageId) {
+        return const_cast<interaction::Info&>(interactions_.last().second);
+    }
+    throw std::out_of_range("Cannot find message");
 }
 
 iterator
@@ -200,15 +203,14 @@ int
 MessagesList::indexOfMessage(QString msgId, bool reverse) const
 {
     auto getIndex = [reverse, &msgId](const auto& start, const auto& end) -> int {
-        auto it = std::find_if(start, end, [&msgId] (const auto& it) { return it.first == msgId; });
+        auto it = std::find_if(start, end, [&msgId](const auto& it) { return it.first == msgId; });
         if (it == end) {
             return -1;
         }
-        return reverse ? std::distance(it, end) - 1 :  std::distance(start, it);
+        return reverse ? std::distance(it, end) - 1 : std::distance(start, it);
     };
-    return reverse ?
-       getIndex(interactions_.rbegin(), interactions_.rend()) :
-       getIndex(interactions_.begin(), interactions_.end());
+    return reverse ? getIndex(interactions_.rbegin(), interactions_.rend())
+                   : getIndex(interactions_.begin(), interactions_.end());
 }
 
 void
diff --git a/src/newaccountmodel.cpp b/src/newaccountmodel.cpp
index fe644aa6..344299d3 100644
--- a/src/newaccountmodel.cpp
+++ b/src/newaccountmodel.cpp
@@ -482,7 +482,7 @@ NewAccountModelPimpl::updateAccountDetails(account::Info& accountInfo)
     // Fill account::Info::confProperties credentials
     VectorMapStringString credGet = ConfigurationManager::instance().getCredentials(accountInfo.id);
     VectorMapStringString credToStore;
-    for (auto const& i : credGet.toStdVector()) {
+    for (auto const& i : std::vector<MapStringString>(credGet.begin(), credGet.end())) {
         MapStringString credMap;
         for (auto const& j : i.toStdMap()) {
             credMap[j.first] = j.second;
diff --git a/src/qtwrapper/configurationmanager_wrap.h b/src/qtwrapper/configurationmanager_wrap.h
index 7a833ab9..25412ec0 100644
--- a/src/qtwrapper/configurationmanager_wrap.h
+++ b/src/qtwrapper/configurationmanager_wrap.h
@@ -259,11 +259,13 @@ public:
             exportable_callback<DataTransferSignal::DataTransferEvent>(
                 [this](const std::string& accountId,
                        const std::string& conversationId,
-                       const uint64_t& transfer_id,
+                       const std::string& interactionId,
+                       const std::string& fileId,
                        const uint32_t& code) {
                     Q_EMIT this->dataTransferEvent(QString(accountId.c_str()),
                                                    QString(conversationId.c_str()),
-                                                   transfer_id,
+                                                   QString(interactionId.c_str()),
+                                                   QString(fileId.c_str()),
                                                    code);
                 }),
         };
@@ -325,6 +327,17 @@ public Q_SLOTS: // METHODS
         return temp;
     }
 
+    void downloadFile(const QString& accountId,
+                      const QString& convId,
+                      const QString& fileId,
+                      const QString& path)
+    {
+        DRing::downloadFile(accountId.toStdString(),
+                            convId.toStdString(),
+                            fileId.toStdString(),
+                            path.toStdString());
+    }
+
     bool exportOnRing(const QString& accountID, const QString& password)
     {
         return DRing::exportOnRing(accountID.toStdString(), password.toStdString());
@@ -794,7 +807,7 @@ public Q_SLOTS: // METHODS
         return convertMap(DRing::getContactDetails(accountID.toStdString(), uri.toStdString()));
     }
 
-    uint32_t sendFile(const DataTransferInfo& lrc_info, DRing::DataTransferId& id)
+    uint32_t sendFileLegacy(const DataTransferInfo& lrc_info, DRing::DataTransferId& id)
     {
         DRing::DataTransferInfo dring_info;
         dring_info.accountId = lrc_info.accountId.toStdString();
@@ -807,19 +820,27 @@ public Q_SLOTS: // METHODS
         dring_info.displayName = lrc_info.displayName.toStdString();
         dring_info.path = lrc_info.path.toStdString();
         dring_info.mimetype = lrc_info.mimetype.toStdString();
-        return uint32_t(DRing::sendFile(dring_info, id));
+        return uint32_t(DRing::sendFileLegacy(dring_info, id));
     }
 
-    uint32_t dataTransferInfo(QString accountId,
-                              QString conversationId,
-                              const DRing::DataTransferId& transfer_id,
-                              DataTransferInfo& lrc_info)
+    void sendFile(const QString& accountId,
+                  const QString& conversationId,
+                  const QString& filePath,
+                  const QString& fileDisplayName,
+                  const QString& parent)
+    {
+        DRing::sendFile(accountId.toStdString(),
+                        conversationId.toStdString(),
+                        filePath.toStdString(),
+                        fileDisplayName.toStdString(),
+                        parent.toStdString());
+    }
+
+    uint32_t dataTransferInfo(QString accountId, QString transfer_id, DataTransferInfo& lrc_info)
     {
         DRing::DataTransferInfo dring_info;
-        auto error = uint32_t(DRing::dataTransferInfo(accountId.toStdString(),
-                                                      conversationId.toStdString(),
-                                                      transfer_id,
-                                                      dring_info));
+        auto error = uint32_t(
+            DRing::dataTransferInfo(accountId.toStdString(), transfer_id.toStdString(), dring_info));
         lrc_info.accountId = QString::fromStdString(dring_info.accountId);
         lrc_info.lastEvent = quint32(dring_info.lastEvent);
         lrc_info.flags = dring_info.flags;
@@ -833,39 +854,36 @@ public Q_SLOTS: // METHODS
         return error;
     }
 
-    uint64_t dataTransferBytesProgress(QString accountId,
-                                       QString conversationId,
-                                       const DRing::DataTransferId& transfer_id,
-                                       int64_t& total,
-                                       int64_t& progress)
-    {
-        return uint32_t(DRing::dataTransferBytesProgress(accountId.toStdString(),
-                                                         conversationId.toStdString(),
-                                                         transfer_id,
-                                                         total,
-                                                         progress));
+    uint64_t fileTransferInfo(QString accountId,
+                              QString conversationId,
+                              QString fileId,
+                              QString& path,
+                              int64_t& total,
+                              int64_t& progress)
+    {
+        std::string pathstr;
+        auto result = uint32_t(DRing::fileTransferInfo(accountId.toStdString(),
+                                                       conversationId.toStdString(),
+                                                       fileId.toStdString(),
+                                                       pathstr,
+                                                       total,
+                                                       progress));
+        path = pathstr.c_str();
+        return result;
     }
 
-    uint32_t acceptFileTransfer(QString accountId,
-                                QString conversationId,
-                                const DRing::DataTransferId& transfer_id,
-                                const QString& file_path,
-                                int64_t offset)
+    uint32_t acceptFileTransfer(QString accountId, const QString& fileId, const QString& file_path)
     {
         return uint32_t(DRing::acceptFileTransfer(accountId.toStdString(),
-                                                  conversationId.toStdString(),
-                                                  transfer_id,
-                                                  file_path.toStdString(),
-                                                  offset));
+                                                  fileId.toStdString(),
+                                                  file_path.toStdString()));
     }
 
-    uint32_t cancelDataTransfer(QString accountId,
-                                QString conversationId,
-                                const DRing::DataTransferId& transfer_id)
+    uint32_t cancelDataTransfer(QString accountId, QString conversationId, QString transfer_id)
     {
         return uint32_t(DRing::cancelDataTransfer(accountId.toStdString(),
                                                   conversationId.toStdString(),
-                                                  transfer_id));
+                                                  transfer_id.toStdString()));
     }
 
     void enableProxyClient(const QString& accountID, bool enable)
@@ -1068,7 +1086,8 @@ Q_SIGNALS: // SIGNALS
     void profileReceived(const QString& accountID, const QString& peer, const QString& vCard);
     void dataTransferEvent(const QString& accountId,
                            const QString& conversationId,
-                           DataTransferId transfer_id,
+                           const QString& interactionId,
+                           const QString& fileId,
                            uint code);
     void deviceRevocationEnded(const QString& accountId, const QString& deviceId, int status);
     void accountProfileReceived(const QString& accountId,
diff --git a/src/web-chatview/chatview.js b/src/web-chatview/chatview.js
index 311d50a7..4fe5b26d 100644
--- a/src/web-chatview/chatview.js
+++ b/src/web-chatview/chatview.js
@@ -24,6 +24,8 @@ var historyBuffer = []
 // If we use qt
 var use_qt = false
 
+var is_swarm = false
+
 if (navigator.userAgent == "jami-windows") {
     use_qt = true
 }
@@ -185,6 +187,11 @@ function init_picker(dark) {
     });
 }
 
+/* exported set_is_swarm */
+function set_is_swarm(value) {
+    is_swarm = value
+}
+
 function set_titles() {
     if (use_qt){
         backButton.title = i18nStringData["Hide chat view"]
@@ -951,14 +958,14 @@ function humanFileSize(bytes) {
  */
 function updateProgressBar(progress_bar, message_object) {
     var delivery_status = message_object["delivery_status"]
-    if ("progress" in message_object && !isErrorStatus(delivery_status) && message_object["progress"] !== 100) {
+    if (delivery_status === "ongoing" && "progress" in message_object && message_object["progress"] !== 100) {
         var progress_percent = (100 * message_object["progress"] / message_object["totalSize"])
         if (progress_percent !== 100)
-            progress_bar.childNodes[0].setAttribute("style", "width: " + progress_percent + "%")
+            progress_bar.childNodes[0].setAttribute("style", "display: block; width: " + progress_percent + "%")
         else
-            progress_bar.setAttribute("style", "display: none")
+            progress_bar.setAttribute("style", "display: none;")
     } else
-        progress_bar.setAttribute("style", "display: none")
+        progress_bar.setAttribute("style", "display: none;")
 }
 
 /**
@@ -976,7 +983,7 @@ function isErrorStatus(status) {
  * Build a new file interaction
  * @param message_id
  */
-function fileInteraction(message_id, message_direction) {
+function fileInteraction(message_id) {
     var message_wrapper = document.createElement("div")
     message_wrapper.setAttribute("class", "message_wrapper")
 
@@ -1030,6 +1037,7 @@ function fileInteraction(message_id, message_direction) {
     var message_transfer_progress_completion = document.createElement("span")
     message_transfer_progress_bar.appendChild(message_transfer_progress_completion)
     message_wrapper.appendChild(message_transfer_progress_bar)
+    message_transfer_progress_completion.setAttribute("style", "display: none;")
 
     const internal_mes_wrapper = document.createElement("div")
     internal_mes_wrapper.setAttribute("class", "internal_mes_wrapper")
@@ -1105,6 +1113,9 @@ function updateFileInteraction(message_div, message_object, forceTypeToFile = fa
         refuseSvg = "<svg height=\"24\" viewBox=\"0 0 24 24\" width=\"24\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z\"/><path d=\"M0 0h24v24H0z\" fill=\"none\"/></svg>",
         fileSvg = "<svg class=\"filesvg\" height=\"24\" viewBox=\"0 0 24 24\" width=\"24\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M16.5 6v11.5c0 2.21-1.79 4-4 4s-4-1.79-4-4V5c0-1.38 1.12-2.5 2.5-2.5s2.5 1.12 2.5 2.5v10.5c0 .55-.45 1-1 1s-1-.45-1-1V6H10v9.5c0 1.38 1.12 2.5 2.5 2.5s2.5-1.12 2.5-2.5V5c0-2.21-1.79-4-4-4S7 2.79 7 5v12.5c0 3.04 2.46 5.5 5.5 5.5s5.5-2.46 5.5-5.5V6h-1.5z\"/><path d=\"M0 0h24v24H0z\" fill=\"none\"/></svg>",
         warningSvg = "<svg class=\"filesvg\" height=\"24\" viewBox=\"0 0 24 24\" width=\"24\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M0 0h24v24H0z\" fill=\"none\"/><path d=\"M1 21h22L12 2 1 21zm12-3h-2v-2h2v2zm0-4h-2v-4h2v4z\"/></svg>"
+    if (is_swarm) {
+        acceptSvg = "<svg xmlns=\"http://www.w3.org/2000/svg\" height=\"24px\" viewBox=\"0 0 24 24\" width=\"24px\"><path d=\"M0 0h24v24H0z\" fill=\"none\"/><path d=\"M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z\"/></svg>"
+    }
     var message_delivery_status = message_object["delivery_status"]
     var message_direction = message_object["direction"]
     var message_id = message_object["id"]
@@ -1129,7 +1140,7 @@ function updateFileInteraction(message_div, message_object, forceTypeToFile = fa
                 media_wrapper.parentNode.removeChild(media_wrapper)
             }
 
-            var new_interaction = fileInteraction(message_id, message_direction)
+            var new_interaction = fileInteraction(message_id)
             var new_message_wrapper = new_interaction.querySelector(".message_wrapper")
             wrapper.prepend(new_message_wrapper)
             updateFileInteraction(message_div, message_object, true)
@@ -1281,19 +1292,22 @@ function updateFileInteraction(message_div, message_object, forceTypeToFile = fa
             left_buttons.appendChild(accept_button)
         }
 
-        var refuse_button = document.createElement("div")
-        refuse_button.innerHTML = refuseSvg
-        refuse_button.setAttribute("title", use_qt ? i18nStringData["Refuse"] :
-            i18n.gettext("Refuse"))
-        refuse_button.setAttribute("class", "flat-button refuse")
-        refuse_button.onclick = function () {
-            if (use_qt) {
-                window.jsbridge.refuseFile(message_id)
-            } else {
-                window.prompt(`REFUSE_FILE:${message_id}`)
+        if (!is_swarm || message_delivery_status.indexOf("ongoing") === 0) {
+            var refuse_button = document.createElement("div")
+            refuse_button.innerHTML = refuseSvg
+            refuse_button.setAttribute("title", use_qt ? i18nStringData["Refuse"] :
+                i18n.gettext("Refuse"))
+            refuse_button.setAttribute("class", "flat-button refuse")
+            refuse_button.onclick = function () {
+                if (use_qt) {
+                    window.jsbridge.refuseFile(message_id)
+                } else {
+                    window.prompt(`REFUSE_FILE:${message_id}`)
+                }
             }
+            left_buttons.appendChild(refuse_button)
         }
-        left_buttons.appendChild(refuse_button)
+
     } else {
         var status_button = document.createElement("div")
         var statusFile = fileSvg
@@ -1665,7 +1679,7 @@ function buildNewMessage(message_object) {
                     media_wrapper.parentNode.removeChild(media_wrapper)
                 }
 
-                var new_interaction = fileInteraction(message_id, message_direction)
+                var new_interaction = fileInteraction(message_id)
                 var new_message_wrapper = new_interaction.querySelector(".message_wrapper")
                 wrapper.prepend(new_message_wrapper)
                 updateFileInteraction(message_div, message_object, true)
@@ -1674,7 +1688,8 @@ function buildNewMessage(message_object) {
             message_div.querySelector("img").id = message_id
             message_div.querySelector("img").msg_obj = message_object
         } else {
-            message_div.append(fileInteraction(message_id, message_direction))
+            message_div.append(fileInteraction(message_id))
+            updateProgressBar(message_div.querySelector(".message_progress_bar"), message_object)
         }
     } else if (message_type === "text") {
         // TODO add the possibility to update messages (remove and rebuild)
-- 
GitLab