diff --git a/src/jamidht/conversation.cpp b/src/jamidht/conversation.cpp
index c5984fa47f842af7257827cd50a2fab24bfda7a1..9e5d01245d331a7679271e563095a3ceecb983a7 100644
--- a/src/jamidht/conversation.cpp
+++ b/src/jamidht/conversation.cpp
@@ -40,43 +40,45 @@ namespace jami {
 
 ConvInfo::ConvInfo(const Json::Value& json)
 {
-    id = json["id"].asString();
-    created = json["created"].asLargestUInt();
-    removed = json["removed"].asLargestUInt();
-    erased = json["erased"].asLargestUInt();
-    for (const auto& v : json["members"]) {
+    id = json[ConversationMapKeys::ID].asString();
+    created = json[ConversationMapKeys::CREATED].asLargestUInt();
+    removed = json[ConversationMapKeys::REMOVED].asLargestUInt();
+    erased = json[ConversationMapKeys::ERASED].asLargestUInt();
+    for (const auto& v : json[ConversationMapKeys::MEMBERS]) {
         members.emplace_back(v["uri"].asString());
     }
+    lastDisplayed = json[ConversationMapKeys::LAST_DISPLAYED].asString();
 }
 
 Json::Value
 ConvInfo::toJson() const
 {
     Json::Value json;
-    json["id"] = id;
-    json["created"] = Json::Int64(created);
+    json[ConversationMapKeys::ID] = id;
+    json[ConversationMapKeys::CREATED] = Json::Int64(created);
     if (removed) {
-        json["removed"] = Json::Int64(removed);
+        json[ConversationMapKeys::REMOVED] = Json::Int64(removed);
     }
     if (erased) {
-        json["erased"] = Json::Int64(erased);
+        json[ConversationMapKeys::ERASED] = Json::Int64(erased);
     }
     for (const auto& m : members) {
         Json::Value member;
         member["uri"] = m;
-        json["members"].append(member);
+        json[ConversationMapKeys::MEMBERS].append(member);
     }
+    json[ConversationMapKeys::LAST_DISPLAYED] = lastDisplayed;
     return json;
 }
 
 // ConversationRequest
 ConversationRequest::ConversationRequest(const Json::Value& json)
 {
-    received = json["received"].asLargestUInt();
-    declined = json["declined"].asLargestUInt();
-    from = json["from"].asString();
-    conversationId = json["conversationId"].asString();
-    auto& md = json["metadatas"];
+    received = json[ConversationMapKeys::RECEIVED].asLargestUInt();
+    declined = json[ConversationMapKeys::DECLINED].asLargestUInt();
+    from = json[ConversationMapKeys::FROM].asString();
+    conversationId = json[ConversationMapKeys::CONVERSATIONID].asString();
+    auto& md = json[ConversationMapKeys::METADATAS];
     for (const auto& member : md.getMemberNames()) {
         metadatas.emplace(member, md[member].asString());
     }
@@ -86,13 +88,13 @@ Json::Value
 ConversationRequest::toJson() const
 {
     Json::Value json;
-    json["conversationId"] = conversationId;
-    json["from"] = from;
-    json["received"] = static_cast<uint32_t>(received);
+    json[ConversationMapKeys::CONVERSATIONID] = conversationId;
+    json[ConversationMapKeys::FROM] = from;
+    json[ConversationMapKeys::RECEIVED] = static_cast<uint32_t>(received);
     if (declined)
-        json["declined"] = static_cast<uint32_t>(declined);
+        json[ConversationMapKeys::DECLINED] = static_cast<uint32_t>(declined);
     for (const auto& [key, value] : metadatas) {
-        json["metadatas"][key] = value;
+        json[ConversationMapKeys::METADATAS][key] = value;
     }
     return json;
 }
@@ -101,11 +103,11 @@ std::map<std::string, std::string>
 ConversationRequest::toMap() const
 {
     auto result = metadatas;
-    result["id"] = conversationId;
-    result["from"] = from;
+    result[ConversationMapKeys::ID] = conversationId;
+    result[ConversationMapKeys::FROM] = from;
     if (declined)
-        result["declined"] = std::to_string(declined);
-    result["received"] = std::to_string(received);
+        result[ConversationMapKeys::DECLINED] = std::to_string(declined);
+    result[ConversationMapKeys::RECEIVED] = std::to_string(received);
     return result;
 }
 
@@ -161,7 +163,7 @@ public:
                                     + shared->getAccountID() + DIR_SEPARATOR_STR
                                     + "conversation_data" + DIR_SEPARATOR_STR + repository_->id();
             fetchedPath_ = conversationDataPath_ + DIR_SEPARATOR_STR + "fetched";
-            lastDisplayedPath_ = conversationDataPath_ + DIR_SEPARATOR_STR + "lastDisplayed";
+            lastDisplayedPath_ = conversationDataPath_ + DIR_SEPARATOR_STR + ConversationMapKeys::LAST_DISPLAYED;
             loadFetched();
             loadLastDisplayed();
         }
@@ -202,7 +204,7 @@ public:
         }
         auto convId = repository_->id();
         auto ok = !commits.empty();
-        auto lastId = ok ? commits.rbegin()->at("id") : "";
+        auto lastId = ok ? commits.rbegin()->at(ConversationMapKeys::ID) : "";
         if (ok) {
             bool announceMember = false;
             for (const auto& c : commits) {
@@ -244,6 +246,27 @@ public:
                 emitSignal<DRing::ConversationSignal::MessageReceived>(shared->getAccountID(),
                                                                        convId,
                                                                        c);
+                // check if we should update lastDisplayed
+                std::unique_lock<std::mutex> lk(lastDisplayedMtx_);
+                auto cached = lastDisplayed_.find(ConversationMapKeys::CACHED);
+                auto updateCached = false;
+                if (cached != lastDisplayed_.end()) {
+                    // If we found the commit we wait
+                    if (cached->second == c.at(ConversationMapKeys::ID)) {
+                        updateCached = true;
+                        lastDisplayed_.erase(cached);
+                    }
+                } else if (c.at("author") == shared->getUsername()) {
+                    updateCached = true;
+                }
+
+                if (updateCached) {
+                    lastDisplayed_[shared->getUsername()] = c.at(ConversationMapKeys::ID);
+                    saveLastDisplayed();
+                    lk.unlock();
+                    if (lastDisplayedUpdatedCb_)
+                        lastDisplayedUpdatedCb_(convId, c.at(ConversationMapKeys::ID));
+                }
             }
 
             if (announceMember) {
@@ -274,7 +297,7 @@ public:
         msgpack::pack(file, fetchedDevices_);
     }
 
-    void loadLastDisplayed()
+    void loadLastDisplayed() const
     {
         try {
             // read file
@@ -288,7 +311,7 @@ public:
         }
     }
 
-    void saveLastDisplayed()
+    void saveLastDisplayed() const
     {
         std::ofstream file(lastDisplayedPath_, std::ios::trunc | std::ios::binary);
         msgpack::pack(file, lastDisplayed_);
@@ -313,9 +336,11 @@ public:
     std::string fetchedPath_ {};
     std::mutex fetchedDevicesMtx_ {};
     std::set<std::string> fetchedDevices_ {};
+    // Manage last message displayed
     std::string lastDisplayedPath_ {};
-    std::mutex lastDisplayedMtx_ {};
-    std::map<std::string, std::string> lastDisplayed_ {};
+    mutable std::mutex lastDisplayedMtx_ {}; // for lastDisplayed_
+    mutable std::map<std::string, std::string> lastDisplayed_ {};
+    std::function<void(const std::string&, const std::string&)> lastDisplayedUpdatedCb_ {};
 };
 
 bool
@@ -404,7 +429,7 @@ Conversation::Impl::convCommitToMap(const ConversationCommit& commit) const
         if (!extension.empty())
             message["fileId"] += "." + extension;
     }
-    message["id"] = commit.id;
+    message[ConversationMapKeys::ID] = commit.id;
     message["parents"] = parents;
     message["linearizedParent"] = commit.linearized_parent;
     message["author"] = authorId;
@@ -574,7 +599,7 @@ Conversation::getMembers(bool includeInvited, bool includeLeft) const
         if (itDisplayed != pimpl_->lastDisplayed_.end()) {
             lastDisplayed = itDisplayed->second;
         }
-        mm["lastDisplayed"] = std::move(lastDisplayed);
+        mm[ConversationMapKeys::LAST_DISPLAYED] = std::move(lastDisplayed);
         result.emplace_back(std::move(mm));
     }
     return result;
@@ -751,7 +776,7 @@ Conversation::lastCommitId() const
     auto messages = pimpl_->loadMessages("", "", 1);
     if (messages.empty())
         return {};
-    return messages.front().at("id");
+    return messages.front().at(ConversationMapKeys::ID);
 }
 
 bool
@@ -914,9 +939,9 @@ Conversation::generateInvitation() const
     std::map<std::string, std::string> invite;
     Json::Value root;
     for (const auto& [k, v] : infos()) {
-        root["metadatas"][k] = v;
+        root[ConversationMapKeys::METADATAS][k] = v;
     }
-    root["conversationId"] = id();
+    root[ConversationMapKeys::CONVERSATIONID] = id();
     Json::StreamWriterBuilder wbuilder;
     wbuilder["commentStyle"] = "None";
     wbuilder["indentation"] = "";
@@ -1125,9 +1150,67 @@ Conversation::hasFetched(const std::string& deviceId)
 void
 Conversation::setMessageDisplayed(const std::string& uri, const std::string& interactionId)
 {
-    std::lock_guard<std::mutex> lk(pimpl_->lastDisplayedMtx_);
-    pimpl_->lastDisplayed_[uri] = interactionId;
-    pimpl_->saveLastDisplayed();
+    if (auto acc = pimpl_->account_.lock()) {
+        if (uri == acc->getUsername() && pimpl_->lastDisplayedUpdatedCb_)
+            pimpl_->lastDisplayedUpdatedCb_(pimpl_->repository_->id(), interactionId);
+        std::lock_guard<std::mutex> lk(pimpl_->lastDisplayedMtx_);
+        pimpl_->lastDisplayed_[uri] = interactionId;
+        pimpl_->saveLastDisplayed();
+    }
+}
+
+void
+Conversation::updateLastDisplayed(const std::string& lastDisplayed)
+{
+    auto acc = pimpl_->account_.lock();
+    if (!acc or !pimpl_->repository_)
+        return;
+
+    // Here, there can be several different scenarios
+    // 1. lastDisplayed is the current last displayed interaction. Nothing to do.
+    std::unique_lock<std::mutex> lk(pimpl_->lastDisplayedMtx_);
+    auto& currentLastDisplayed = pimpl_->lastDisplayed_[acc->getUsername()];
+    if (lastDisplayed == currentLastDisplayed)
+        return;
+
+    auto updateLastDisplayed = [&]() {
+        currentLastDisplayed = lastDisplayed;
+        pimpl_->saveLastDisplayed();
+        lk.unlock();
+        if (pimpl_->lastDisplayedUpdatedCb_)
+            pimpl_->lastDisplayedUpdatedCb_(pimpl_->repository_->id(), lastDisplayed);
+    };
+
+    auto hasCommit = pimpl_->repository_->getCommit(lastDisplayed, false) != std::nullopt;
+
+    // 2. lastDisplayed can be a future commit, not fetched yet
+    // In this case, we can cache it here, and check future announces to update it
+    if (!hasCommit) {
+        pimpl_->lastDisplayed_[ConversationMapKeys::CACHED] = lastDisplayed;
+        pimpl_->saveLastDisplayed();
+        return;
+    }
+
+    // 3. There is no lastDisplayed yet
+    if (currentLastDisplayed.empty()) {
+        updateLastDisplayed();
+        return;
+    }
+
+    // 4. lastDisplayed is present in the repository. In this can, if it's a more recent
+    // commit than the current one, update it, else drop it.
+    auto commitsSinceLast = pimpl_->repository_->log("", lastDisplayed, false, true).size();
+    auto commitsSincePreviousLast = pimpl_->repository_->log("", currentLastDisplayed, false, true)
+                                        .size();
+    if (commitsSincePreviousLast > commitsSinceLast)
+        updateLastDisplayed();
+}
+
+void
+Conversation::onLastDisplayedUpdated(
+    std::function<void(const std::string&, const std::string&)>&& lastDisplayedUpdatedCb)
+{
+    pimpl_->lastDisplayedUpdatedCb_ = std::move(lastDisplayedUpdatedCb);
 }
 
 uint32_t
diff --git a/src/jamidht/conversation.h b/src/jamidht/conversation.h
index 41adc79dd3f3adff4a42e80d6781bfeb7b05da27..88a3b24f3b70b2820ac147e5a03b2d7a8ae8a209 100644
--- a/src/jamidht/conversation.h
+++ b/src/jamidht/conversation.h
@@ -31,6 +31,21 @@
 
 namespace jami {
 
+namespace ConversationMapKeys {
+static constexpr const char* ID = "id";
+static constexpr const char* CREATED = "created";
+static constexpr const char* REMOVED = "removed";
+static constexpr const char* ERASED = "erased";
+static constexpr const char* MEMBERS = "members";
+static constexpr const char* LAST_DISPLAYED = "lastDisplayed";
+static constexpr const char* CACHED = "cached";
+static constexpr const char* RECEIVED = "received";
+static constexpr const char* DECLINED = "declined";
+static constexpr const char* FROM = "from";
+static constexpr const char* CONVERSATIONID = "conversationId";
+static constexpr const char* METADATAS = "metadatas";
+}
+
 /**
  * A ConversationRequest is a request which corresponds to a trust request, but for conversations
  * It's signed by the sender and contains the members list, the conversationId, and the metadatas
@@ -69,13 +84,14 @@ struct ConvInfo
     time_t removed {0};
     time_t erased {0};
     std::vector<std::string> members;
+    std::string lastDisplayed {};
 
     ConvInfo() = default;
     ConvInfo(const Json::Value& json);
 
     Json::Value toJson() const;
 
-    MSGPACK_DEFINE_MAP(id, created, removed, erased, members)
+    MSGPACK_DEFINE_MAP(id, created, removed, erased, members, lastDisplayed)
 };
 
 class JamiAccount;
@@ -102,6 +118,14 @@ public:
                  const std::string& conversationId);
     ~Conversation();
 
+    /**
+     * Add a callback to update upper layers
+     * @note to call after the construction (and before ConversationReady)
+     * @param lastDisplayedUpdatedCb    Triggered when last displayed for account is updated
+     */
+    void onLastDisplayedUpdated(
+        std::function<void(const std::string&, const std::string&)>&& lastDisplayedUpdatedCb);
+
     std::string id() const;
 
     // Member management
@@ -119,7 +143,7 @@ public:
      * {
      *  "uri":"xxx",
      *  "role":"member/admin/invited",
-     *  "lastRead":"id"
+     *  "lastDisplayed":"id"
      *  ...
      * }
      */
@@ -332,6 +356,12 @@ public:
      */
     void setMessageDisplayed(const std::string& uri, const std::string& interactionId);
 
+    /**
+     * Compute, with multi device support the last message displayed of a conversation
+     * @param lastDisplayed      Latest displayed interaction
+     */
+    void updateLastDisplayed(const std::string& lastDisplayed);
+
     /**
      * Retrieve how many interactions there is from HEAD to interactionId
      * @param toId      "" for getting the whole history
diff --git a/src/jamidht/conversation_module.cpp b/src/jamidht/conversation_module.cpp
index fadcb26021a0ee2395f74edbc8d6d703b294de2b..244dbc1e317702e4299ff4f0f7337ceb31067cf8 100644
--- a/src/jamidht/conversation_module.cpp
+++ b/src/jamidht/conversation_module.cpp
@@ -58,10 +58,12 @@ public:
      * Clone a conversation (initial) from device
      * @param deviceId
      * @param convId
+     * @param lastDisplayed      Last message displayed by account
      */
     void cloneConversation(const std::string& deviceId,
                            const std::string& peer,
-                           const std::string& convId);
+                           const std::string& convId,
+                           const std::string& lastDisplayed = "");
 
     /**
      * Pull remote device
@@ -122,6 +124,27 @@ public:
         saveConvInfos();
     }
 
+    /**
+     * Updates last displayed for sync infos and client
+     */
+    void onLastDisplayedUpdated(const std::string& convId, const std::string& lastId)
+    {
+        // must not lock as used in callback from a conversation,
+        // so convInfos_ cannot change for convId
+        auto itConv = convInfos_.find(convId);
+        if (itConv != convInfos_.end())
+            itConv->second.lastDisplayed = lastId;
+        saveConvInfos();
+
+        // Updates info for client
+        emitSignal<DRing::ConfigurationSignal::AccountMessageStatusChanged>(
+            accountId_,
+            convId,
+            username_,
+            lastId,
+            static_cast<int>(DRing::Account::MessageStates::DISPLAYED));
+    }
+
     std::weak_ptr<JamiAccount> account_;
     NeedsSyncingCb needsSyncingCb_;
     SengMsgCb sendMsgCb_;
@@ -256,7 +279,8 @@ ConversationModule::Impl::Impl(std::weak_ptr<JamiAccount>&& account,
 void
 ConversationModule::Impl::cloneConversation(const std::string& deviceId,
                                             const std::string& peerUri,
-                                            const std::string& convId)
+                                            const std::string& convId,
+                                            const std::string& lastDisplayed)
 {
     JAMI_DBG("[Account %s] Clone conversation on device %s", accountId_.c_str(), deviceId.c_str());
 
@@ -268,6 +292,13 @@ ConversationModule::Impl::cloneConversation(const std::string& deviceId,
         // at the same time.
         if (!startFetch(convId, deviceId)) {
             JAMI_WARN("[Account %s] Already fetching %s", accountId_.c_str(), convId.c_str());
+            std::lock_guard<std::mutex> lk(convInfosMtx_);
+            auto ci = convInfos_.find(convId);
+            if (ci != convInfos_.end() && ci->second.lastDisplayed.empty()) {
+                // If fetchNewCommits called before sync
+                ci->second.lastDisplayed = lastDisplayed;
+                saveConvInfos();
+            }
             return;
         }
         onNeedSocket_(convId, deviceId, [=](const auto& channel) {
@@ -297,6 +328,7 @@ ConversationModule::Impl::cloneConversation(const std::string& deviceId,
         info.id = convId;
         info.created = std::time(nullptr);
         info.members.emplace_back(username_);
+        info.lastDisplayed = lastDisplayed;
         if (peerUri != username_)
             info.members.emplace_back(peerUri);
 
@@ -304,6 +336,10 @@ ConversationModule::Impl::cloneConversation(const std::string& deviceId,
         convInfos_[info.id] = std::move(info);
         saveConvInfos();
     } else {
+        std::unique_lock<std::mutex> lk(conversationsMtx_);
+        auto conversation = conversations_.find(convId);
+        if (conversation != conversations_.end() && conversation->second)
+            conversation->second->updateLastDisplayed(lastDisplayed);
         JAMI_INFO("[Account %s] Already have conversation %s", accountId_.c_str(), convId.c_str());
     }
 }
@@ -450,6 +486,8 @@ ConversationModule::Impl::handlePendingConversation(const std::string& conversat
     };
     try {
         auto conversation = std::make_shared<Conversation>(account_, deviceId, conversationId);
+        conversation->onLastDisplayedUpdated(
+            std::move([&](auto convId, auto lastId) { onLastDisplayedUpdated(convId, lastId); }));
         if (!conversation->isMember(username_, true)) {
             JAMI_ERR("Conversation cloned but doesn't seems to be a valid member");
             conversation->erase();
@@ -464,6 +502,9 @@ ConversationModule::Impl::handlePendingConversation(const std::string& conversat
             auto itConv = convInfos_.find(conversationId);
             if (itConv != convInfos_.end() && itConv->second.removed)
                 removeRepo = true;
+            if (itConv != convInfos_.end() && !itConv->second.lastDisplayed.empty()) {
+                conversation->updateLastDisplayed(itConv->second.lastDisplayed);
+            }
             conversations_.emplace(conversationId, conversation);
         }
         if (removeRepo) {
@@ -726,6 +767,8 @@ ConversationModule::loadConversations()
     for (const auto& repository : conversationsRepositories) {
         try {
             auto conv = std::make_shared<Conversation>(pimpl_->account_, repository);
+            conv->onLastDisplayedUpdated(std::move(
+                [&](auto convId, auto lastId) { pimpl_->onLastDisplayedUpdated(convId, lastId); }));
             auto convInfo = pimpl_->convInfos_.find(repository);
             if (convInfo == pimpl_->convInfos_.end()) {
                 JAMI_ERR() << "Missing conv info for " << repository << ". This is a bug!";
@@ -733,6 +776,7 @@ ConversationModule::loadConversations()
                 info.id = repository;
                 info.created = std::time(nullptr);
                 info.members = conv->memberUris();
+                info.lastDisplayed = conv->infos()[ConversationMapKeys::LAST_DISPLAYED];
                 addConvInfo(info);
             }
             pimpl_->conversations_.emplace(repository, std::move(conv));
@@ -942,6 +986,8 @@ ConversationModule::startConversation(ConversationMode mode, const std::string&
     std::shared_ptr<Conversation> conversation;
     try {
         conversation = std::make_shared<Conversation>(pimpl_->account_, mode, otherMember);
+        conversation->onLastDisplayedUpdated(std::move(
+            [&](auto convId, auto lastId) { pimpl_->onLastDisplayedUpdated(convId, lastId); }));
     } catch (const std::exception& e) {
         JAMI_ERR("[Account %s] Error while generating a conversation %s",
                  pimpl_->accountId_.c_str(),
@@ -1175,7 +1221,7 @@ ConversationModule::onSyncData(const SyncMsg& msg,
             auto itConv = pimpl_->convInfos_.find(convId);
             if (itConv != pimpl_->convInfos_.end() && itConv->second.removed)
                 continue;
-            pimpl_->cloneConversation(deviceId, peerId, convId);
+            pimpl_->cloneConversation(deviceId, peerId, convId, convInfo.lastDisplayed);
         } else {
             {
                 std::lock_guard<std::mutex> lk(pimpl_->conversationsMtx_);
diff --git a/test/unitTest/syncHistory/syncHistory.cpp b/test/unitTest/syncHistory/syncHistory.cpp
index 4ea7e78c1ae7148aee5715163e3afc24b79ca2ba..4d40559faf8f0924648f7205b21bb7a6be7173c4 100644
--- a/test/unitTest/syncHistory/syncHistory.cpp
+++ b/test/unitTest/syncHistory/syncHistory.cpp
@@ -20,6 +20,7 @@
 #include <cppunit/TestFixture.h>
 #include <cppunit/extensions/HelperMacros.h>
 
+#include <chrono>
 #include <condition_variable>
 #include <filesystem>
 
@@ -34,6 +35,7 @@
 #include "common.h"
 
 using namespace DRing::Account;
+using namespace std::literals::chrono_literals;
 
 namespace jami {
 namespace test {
@@ -70,6 +72,8 @@ private:
     void testSyncOneToOne();
     void testConversationRequestRemoved();
     void testProfileReceivedMultiDevice();
+    void testLastInteractionAfterClone();
+    void testLastInteractionAfterSomeMessages();
 
     CPPUNIT_TEST_SUITE(SyncHistoryTest);
     CPPUNIT_TEST(testCreateConversationThenSync);
@@ -84,6 +88,8 @@ private:
     CPPUNIT_TEST(testSyncOneToOne);
     CPPUNIT_TEST(testConversationRequestRemoved);
     CPPUNIT_TEST(testProfileReceivedMultiDevice);
+    CPPUNIT_TEST(testLastInteractionAfterClone);
+    CPPUNIT_TEST(testLastInteractionAfterSomeMessages);
     CPPUNIT_TEST_SUITE_END();
 };
 
@@ -153,9 +159,7 @@ SyncHistoryTest::testCreateConversationThenSync()
             }));
     DRing::registerSignalHandlers(confHandlers);
     alice2Id = Manager::instance().addAccount(details);
-    CPPUNIT_ASSERT(cv.wait_for(lk, std::chrono::seconds(30), [&] {
-        return alice2Ready && conversationReady;
-    }));
+    CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return alice2Ready && conversationReady; }));
     DRing::unregisterSignalHandlers();
 }
 
@@ -203,9 +207,7 @@ SyncHistoryTest::testCreateConversationWithOnlineDevice()
             }));
     DRing::registerSignalHandlers(confHandlers);
     alice2Id = Manager::instance().addAccount(details);
-    CPPUNIT_ASSERT(cv.wait_for(lk, std::chrono::seconds(60), [&] {
-        return alice2Ready && conversationReady;
-    }));
+    CPPUNIT_ASSERT(cv.wait_for(lk, 60s, [&] { return alice2Ready && conversationReady; }));
     DRing::unregisterSignalHandlers();
 }
 
@@ -241,13 +243,13 @@ SyncHistoryTest::testCreateConversationWithMessagesThenAddDevice()
     // Start conversation
     messageReceived = false;
     DRing::sendMessage(aliceId, convId, std::string("Message 1"), "");
-    CPPUNIT_ASSERT(cv.wait_for(lk, std::chrono::seconds(10), [&] { return messageReceived; }));
+    CPPUNIT_ASSERT(cv.wait_for(lk, 10s, [&] { return messageReceived; }));
     messageReceived = false;
     DRing::sendMessage(aliceId, convId, std::string("Message 2"), "");
-    CPPUNIT_ASSERT(cv.wait_for(lk, std::chrono::seconds(10), [&] { return messageReceived; }));
+    CPPUNIT_ASSERT(cv.wait_for(lk, 10s, [&] { return messageReceived; }));
     messageReceived = false;
     DRing::sendMessage(aliceId, convId, std::string("Message 3"), "");
-    CPPUNIT_ASSERT(cv.wait_for(lk, std::chrono::seconds(10), [&] { return messageReceived; }));
+    CPPUNIT_ASSERT(cv.wait_for(lk, 10s, [&] { return messageReceived; }));
 
     // Now create alice2
     auto aliceArchive = std::filesystem::current_path().string() + "/alice.gz";
@@ -263,7 +265,7 @@ SyncHistoryTest::testCreateConversationWithMessagesThenAddDevice()
     alice2Id = Manager::instance().addAccount(details);
 
     // Check if conversation is ready
-    CPPUNIT_ASSERT(cv.wait_for(lk, std::chrono::seconds(60), [&]() { return conversationReady; }));
+    CPPUNIT_ASSERT(cv.wait_for(lk, 60s, [&]() { return conversationReady; }));
     std::vector<std::map<std::string, std::string>> messages;
     confHandlers.insert(DRing::exportable_callback<DRing::ConversationSignal::ConversationLoaded>(
         [&](uint32_t,
@@ -277,7 +279,7 @@ SyncHistoryTest::testCreateConversationWithMessagesThenAddDevice()
         }));
     DRing::registerSignalHandlers(confHandlers);
     DRing::loadConversationMessages(alice2Id, convId, "", 0);
-    cv.wait_for(lk, std::chrono::seconds(30));
+    cv.wait_for(lk, 30s);
     DRing::unregisterSignalHandlers();
     confHandlers.clear();
 
@@ -297,17 +299,17 @@ SyncHistoryTest::testCreateMultipleConversationThenAddDevice()
     DRing::sendMessage(aliceId, convId, std::string("Message 1"), "");
     DRing::sendMessage(aliceId, convId, std::string("Message 2"), "");
     DRing::sendMessage(aliceId, convId, std::string("Message 3"), "");
-    std::this_thread::sleep_for(std::chrono::seconds(1));
+    std::this_thread::sleep_for(1s);
     auto convId2 = DRing::startConversation(aliceId);
     DRing::sendMessage(aliceId, convId2, std::string("Message 1"), "");
     DRing::sendMessage(aliceId, convId2, std::string("Message 2"), "");
     DRing::sendMessage(aliceId, convId2, std::string("Message 3"), "");
-    std::this_thread::sleep_for(std::chrono::seconds(1));
+    std::this_thread::sleep_for(1s);
     auto convId3 = DRing::startConversation(aliceId);
     DRing::sendMessage(aliceId, convId3, std::string("Message 1"), "");
     DRing::sendMessage(aliceId, convId3, std::string("Message 2"), "");
     DRing::sendMessage(aliceId, convId3, std::string("Message 3"), "");
-    std::this_thread::sleep_for(std::chrono::seconds(1));
+    std::this_thread::sleep_for(1s);
     auto convId4 = DRing::startConversation(aliceId);
     DRing::sendMessage(aliceId, convId4, std::string("Message 1"), "");
     DRing::sendMessage(aliceId, convId4, std::string("Message 2"), "");
@@ -342,8 +344,7 @@ SyncHistoryTest::testCreateMultipleConversationThenAddDevice()
     confHandlers.clear();
 
     // Check if conversation is ready
-    CPPUNIT_ASSERT(
-        cv.wait_for(lk, std::chrono::seconds(60), [&]() { return conversationReady == 4; }));
+    CPPUNIT_ASSERT(cv.wait_for(lk, 60s, [&]() { return conversationReady == 4; }));
     DRing::unregisterSignalHandlers();
 }
 
@@ -391,8 +392,7 @@ SyncHistoryTest::testReceivesInviteThenAddDevice()
 
     memberEvent = false;
     DRing::addConversationMember(bobId, convId, uri);
-    CPPUNIT_ASSERT(
-        cv.wait_for(lk, std::chrono::seconds(30), [&] { return memberEvent && requestReceived; }));
+    CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return memberEvent && requestReceived; }));
     DRing::unregisterSignalHandlers();
     confHandlers.clear();
 
@@ -420,7 +420,7 @@ SyncHistoryTest::testReceivesInviteThenAddDevice()
             }));
     DRing::registerSignalHandlers(confHandlers);
 
-    CPPUNIT_ASSERT(cv.wait_for(lk, std::chrono::seconds(30), [&] { return requestReceived; }));
+    CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return requestReceived; }));
     DRing::unregisterSignalHandlers();
 }
 
@@ -477,11 +477,9 @@ SyncHistoryTest::testRemoveConversationOnAllDevices()
     DRing::registerSignalHandlers(confHandlers);
 
     alice2Id = Manager::instance().addAccount(details);
-    CPPUNIT_ASSERT(cv.wait_for(lk, std::chrono::seconds(60), [&] {
-        return alice2Ready && conversationReady;
-    }));
+    CPPUNIT_ASSERT(cv.wait_for(lk, 60s, [&] { return alice2Ready && conversationReady; }));
     DRing::removeConversation(aliceId, convId);
-    CPPUNIT_ASSERT(cv.wait_for(lk, std::chrono::seconds(30), [&] { return conversationRemoved; }));
+    CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return conversationRemoved; }));
 
     DRing::unregisterSignalHandlers();
 }
@@ -489,7 +487,6 @@ SyncHistoryTest::testRemoveConversationOnAllDevices()
 void
 SyncHistoryTest::testSyncCreateAccountExportDeleteReimportOldBackup()
 {
-    std::this_thread::sleep_for(std::chrono::seconds(10));
     auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
     auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
     auto bobUri = bobAccount->getUsername();
@@ -553,17 +550,17 @@ SyncHistoryTest::testSyncCreateAccountExportDeleteReimportOldBackup()
     DRing::registerSignalHandlers(confHandlers);
 
     DRing::addConversationMember(aliceId, convId, bobUri);
-    CPPUNIT_ASSERT(cv.wait_for(lk, std::chrono::seconds(30), [&]() { return requestReceived; }));
+    CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return requestReceived; }));
 
     DRing::acceptConversationRequest(bobId, convId);
-    CPPUNIT_ASSERT(cv.wait_for(lk, std::chrono::seconds(30), [&]() { return conversationReady; }));
+    CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return conversationReady; }));
 
     // Wait that alice sees Bob
-    cv.wait_for(lk, std::chrono::seconds(30), [&]() { return messageAliceReceived == 1; });
+    cv.wait_for(lk, 30s, [&]() { return messageAliceReceived == 1; });
 
     // disable account (same as removed)
     Manager::instance().sendRegister(aliceId, false);
-    std::this_thread::sleep_for(std::chrono::seconds(5));
+    std::this_thread::sleep_for(5s);
 
     std::map<std::string, std::string> details = DRing::getAccountTemplate("RING");
     details[ConfProperties::TYPE] = "RING";
@@ -576,18 +573,18 @@ SyncHistoryTest::testSyncCreateAccountExportDeleteReimportOldBackup()
     requestReceived = false;
     conversationReady = false;
     alice2Id = Manager::instance().addAccount(details);
-    CPPUNIT_ASSERT(cv.wait_for(lk, std::chrono::seconds(30), [&] { return alice2Ready; }));
+    CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return alice2Ready; }));
 
     // This will trigger a conversation request. Cause alice2 can't know first conversation
     DRing::sendMessage(bobId, convId, std::string("hi"), "");
-    CPPUNIT_ASSERT(cv.wait_for(lk, std::chrono::seconds(30), [&] { return requestReceived; }));
+    CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return requestReceived; }));
 
     DRing::acceptConversationRequest(alice2Id, convId);
-    CPPUNIT_ASSERT(cv.wait_for(lk, std::chrono::seconds(30), [&] { return conversationReady; }));
+    CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return conversationReady; }));
 
     messageBobReceived = 0;
     DRing::sendMessage(alice2Id, convId, std::string("hi"), "");
-    cv.wait_for(lk, std::chrono::seconds(30), [&]() { return messageBobReceived == 1; });
+    cv.wait_for(lk, 30s, [&]() { return messageBobReceived == 1; });
     DRing::unregisterSignalHandlers();
 }
 
@@ -661,16 +658,16 @@ SyncHistoryTest::testSyncCreateAccountExportDeleteReimportWithConvId()
     DRing::registerSignalHandlers(confHandlers);
 
     DRing::addConversationMember(aliceId, convId, bobUri);
-    CPPUNIT_ASSERT(cv.wait_for(lk, std::chrono::seconds(30), [&]() { return requestReceived; }));
+    CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return requestReceived; }));
 
     DRing::acceptConversationRequest(bobId, convId);
-    CPPUNIT_ASSERT(cv.wait_for(lk, std::chrono::seconds(30), [&]() { return conversationReady; }));
+    CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return conversationReady; }));
 
     // We need to track presence to know when to sync
     bobAccount->trackBuddyPresence(aliceUri, true);
 
     // Wait that alice sees Bob
-    cv.wait_for(lk, std::chrono::seconds(30), [&]() { return memberAddGenerated; });
+    cv.wait_for(lk, 30s, [&]() { return memberAddGenerated; });
 
     // Backup alice after startConversation with member
     auto aliceArchive = std::filesystem::current_path().string() + "/alice.gz";
@@ -678,7 +675,7 @@ SyncHistoryTest::testSyncCreateAccountExportDeleteReimportWithConvId()
 
     // disable account (same as removed)
     Manager::instance().sendRegister(aliceId, false);
-    std::this_thread::sleep_for(std::chrono::seconds(5));
+    std::this_thread::sleep_for(5s);
 
     std::map<std::string, std::string> details = DRing::getAccountTemplate("RING");
     details[ConfProperties::TYPE] = "RING";
@@ -692,12 +689,12 @@ SyncHistoryTest::testSyncCreateAccountExportDeleteReimportWithConvId()
     conversationReady = false;
     alice2Id = Manager::instance().addAccount(details);
     // Should retrieve conversation, no need for action as the convInfos is in the archive
-    CPPUNIT_ASSERT(cv.wait_for(lk, std::chrono::seconds(30), [&] { return alice2Ready; }));
-    CPPUNIT_ASSERT(cv.wait_for(lk, std::chrono::seconds(30), [&] { return conversationReady; }));
+    CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return alice2Ready; }));
+    CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return conversationReady; }));
 
     messageBobReceived = 0;
     DRing::sendMessage(alice2Id, convId, std::string("hi"), "");
-    cv.wait_for(lk, std::chrono::seconds(30), [&]() { return messageBobReceived == 1; });
+    cv.wait_for(lk, 30s, [&]() { return messageBobReceived == 1; });
     DRing::unregisterSignalHandlers();
 }
 
@@ -764,7 +761,7 @@ SyncHistoryTest::testSyncCreateAccountExportDeleteReimportWithConvReq()
     DRing::registerSignalHandlers(confHandlers);
 
     DRing::addConversationMember(bobId, convId, aliceUri);
-    CPPUNIT_ASSERT(cv.wait_for(lk, std::chrono::seconds(30), [&]() { return requestReceived; }));
+    CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return requestReceived; }));
 
     // Backup alice after startConversation with member
     auto aliceArchive = std::filesystem::current_path().string() + "/alice.gz";
@@ -772,7 +769,7 @@ SyncHistoryTest::testSyncCreateAccountExportDeleteReimportWithConvReq()
 
     // disable account (same as removed)
     Manager::instance().sendRegister(aliceId, false);
-    std::this_thread::sleep_for(std::chrono::seconds(5));
+    std::this_thread::sleep_for(5s);
 
     std::map<std::string, std::string> details = DRing::getAccountTemplate("RING");
     details[ConfProperties::TYPE] = "RING";
@@ -784,13 +781,12 @@ SyncHistoryTest::testSyncCreateAccountExportDeleteReimportWithConvReq()
     details[ConfProperties::ARCHIVE_PATH] = aliceArchive;
     conversationReady = false;
     alice2Id = Manager::instance().addAccount(details);
-    CPPUNIT_ASSERT(cv.wait_for(lk, std::chrono::seconds(30), [&] { return alice2Ready; }));
+    CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return alice2Ready; }));
 
     // Should get the same request as before.
     DRing::acceptConversationRequest(alice2Id, convId);
-    CPPUNIT_ASSERT(cv.wait_for(lk, std::chrono::seconds(30), [&]() {
-        return conversationReady && messageBobReceived == 1;
-    }));
+    CPPUNIT_ASSERT(
+        cv.wait_for(lk, 30s, [&]() { return conversationReady && messageBobReceived == 1; }));
     DRing::unregisterSignalHandlers();
 }
 
@@ -828,7 +824,7 @@ SyncHistoryTest::testSyncOneToOne()
     DRing::registerSignalHandlers(confHandlers);
 
     aliceAccount->addContact(bobAccount->getUsername());
-    CPPUNIT_ASSERT(cv.wait_for(lk, std::chrono::seconds(30), [&] { return !convId.empty(); }));
+    CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return !convId.empty(); }));
 
     // Now create alice2
     auto aliceArchive = std::filesystem::current_path().string() + "/alice.gz";
@@ -843,9 +839,7 @@ SyncHistoryTest::testSyncOneToOne()
     details[ConfProperties::ARCHIVE_PATH] = aliceArchive;
 
     alice2Id = Manager::instance().addAccount(details);
-    CPPUNIT_ASSERT(cv.wait_for(lk, std::chrono::seconds(30), [&] {
-        return alice2Ready && conversationReady;
-    }));
+    CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return alice2Ready && conversationReady; }));
     DRing::unregisterSignalHandlers();
 }
 
@@ -883,7 +877,7 @@ SyncHistoryTest::testConversationRequestRemoved()
     DRing::registerSignalHandlers(confHandlers);
 
     DRing::addConversationMember(bobId, convId, uri);
-    CPPUNIT_ASSERT(cv.wait_for(lk, std::chrono::seconds(30), [&] { return requestReceived; }));
+    CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return requestReceived; }));
     DRing::unregisterSignalHandlers();
     confHandlers.clear();
 
@@ -923,13 +917,11 @@ SyncHistoryTest::testConversationRequestRemoved()
             }));
     DRing::registerSignalHandlers(confHandlers);
 
-    CPPUNIT_ASSERT(cv.wait_for(lk, std::chrono::seconds(30), [&] { return requestReceived; }));
+    CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return requestReceived; }));
     // Now decline trust request, this should trigger ConversationRequestDeclined both sides for Alice
     DRing::declineConversationRequest(aliceId, convId);
 
-    CPPUNIT_ASSERT(cv.wait_for(lk, std::chrono::seconds(30), [&] {
-        return requestDeclined && requestDeclined2;
-    }));
+    CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return requestDeclined && requestDeclined2; }));
 
     DRing::unregisterSignalHandlers();
 }
@@ -1018,10 +1010,10 @@ END:VCARD";
     DRing::registerSignalHandlers(confHandlers);
     aliceAccount->addContact(bobUri);
     aliceAccount->sendTrustRequest(bobUri, {});
-    CPPUNIT_ASSERT(cv.wait_for(lk, std::chrono::seconds(30), [&]() { return requestReceived; }));
+    CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return requestReceived; }));
 
     CPPUNIT_ASSERT(bobAccount->acceptTrustRequest(aliceUri));
-    CPPUNIT_ASSERT(cv.wait_for(lk, std::chrono::seconds(30), [&]() {
+    CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() {
         return conversationReady && bobProfileReceived && aliceProfileReceived;
     }));
     CPPUNIT_ASSERT(fileutils::isFile(bobDest));
@@ -1038,12 +1030,244 @@ END:VCARD";
     bobProfileReceived = false, aliceProfileReceived = false;
     alice2Id = Manager::instance().addAccount(details);
 
-    CPPUNIT_ASSERT(cv.wait_for(lk, std::chrono::seconds(60), [&] {
-        return aliceProfileReceived && bobProfileReceived;
-    }));
+    CPPUNIT_ASSERT(cv.wait_for(lk, 60s, [&] { return aliceProfileReceived && bobProfileReceived; }));
     DRing::unregisterSignalHandlers();
 }
 
+void
+SyncHistoryTest::testLastInteractionAfterClone()
+{
+    auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
+    auto aliceUri = aliceAccount->getUsername();
+    auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
+    auto bobUri = bobAccount->getUsername();
+    std::string convId;
+
+    std::mutex mtx;
+    std::unique_lock<std::mutex> lk {mtx};
+    std::condition_variable cv;
+    std::map<std::string, std::shared_ptr<DRing::CallbackWrapperBase>> confHandlers;
+    auto messageReceived = false;
+    std::string msgId = "";
+    confHandlers.insert(DRing::exportable_callback<DRing::ConversationSignal::MessageReceived>(
+        [&](const std::string& /* accountId */,
+            const std::string& /* conversationId */,
+            std::map<std::string, std::string> message) {
+            messageReceived = true;
+            msgId = message["id"];
+            cv.notify_one();
+        }));
+    auto conversationReady = false;
+    confHandlers.insert(DRing::exportable_callback<DRing::ConversationSignal::ConversationReady>(
+        [&](const std::string& accountId, const std::string& conversationId) {
+            if (accountId == bobId || accountId == alice2Id) {
+                convId = conversationId;
+                conversationReady = true;
+            }
+            cv.notify_one();
+        }));
+    auto requestReceived = false;
+    confHandlers.insert(
+        DRing::exportable_callback<DRing::ConversationSignal::ConversationRequestReceived>(
+            [&](const std::string& accountId,
+                const std::string& /*conversationId*/,
+                std::map<std::string, std::string> /*metadatas*/) {
+                if (accountId == bobId)
+                    requestReceived = true;
+                cv.notify_one();
+            }));
+    auto messageDisplayed = false;
+    confHandlers.insert(
+        DRing::exportable_callback<DRing::ConfigurationSignal::AccountMessageStatusChanged>(
+            [&](const std::string& /* accountId */,
+                const std::string& /* conversationId */,
+                const std::string& /* username */,
+                const std::string& /* msgId */,
+                int status) {
+                if (status == 3)
+                    messageDisplayed = true;
+                cv.notify_one();
+            }));
+    DRing::registerSignalHandlers(confHandlers);
+    confHandlers.clear();
+
+    aliceAccount->addContact(bobUri);
+    aliceAccount->sendTrustRequest(bobUri, {});
+    CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return requestReceived; }));
+
+    CPPUNIT_ASSERT(bobAccount->acceptTrustRequest(aliceUri));
+    CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return conversationReady; }));
+
+    // Start conversation
+    messageReceived = false;
+    DRing::sendMessage(bobId, convId, std::string("Message 1"), "");
+    CPPUNIT_ASSERT(cv.wait_for(lk, 10s, [&] { return messageReceived; }));
+    messageReceived = false;
+    DRing::sendMessage(bobId, convId, std::string("Message 2"), "");
+    CPPUNIT_ASSERT(cv.wait_for(lk, 10s, [&] { return messageReceived; }));
+    messageReceived = false;
+    DRing::sendMessage(bobId, convId, std::string("Message 3"), "");
+    CPPUNIT_ASSERT(cv.wait_for(lk, 10s, [&] { return messageReceived; }));
+
+    messageDisplayed = false;
+    DRing::setMessageDisplayed(aliceId, "swarm:" + convId, msgId, 3);
+    CPPUNIT_ASSERT(cv.wait_for(lk, 10s, [&] { return messageDisplayed; }));
+
+    // Now create alice2
+    conversationReady = false;
+    auto aliceArchive = std::filesystem::current_path().string() + "/alice.gz";
+    aliceAccount->exportArchive(aliceArchive);
+    std::map<std::string, std::string> details = DRing::getAccountTemplate("RING");
+    details[ConfProperties::TYPE] = "RING";
+    details[ConfProperties::DISPLAYNAME] = "ALICE2";
+    details[ConfProperties::ALIAS] = "ALICE2";
+    details[ConfProperties::UPNP_ENABLED] = "true";
+    details[ConfProperties::ARCHIVE_PASSWORD] = "";
+    details[ConfProperties::ARCHIVE_PIN] = "";
+    details[ConfProperties::ARCHIVE_PATH] = aliceArchive;
+    alice2Id = Manager::instance().addAccount(details);
+
+    // Check if conversation is ready
+    CPPUNIT_ASSERT(cv.wait_for(lk, 60s, [&]() { return conversationReady; }));
+    // Check that last displayed is synched
+    auto membersInfos = DRing::getConversationMembers(alice2Id, convId);
+    CPPUNIT_ASSERT(std::find_if(membersInfos.begin(),
+                                membersInfos.end(),
+                                [&](auto infos) {
+                                    return infos["uri"] == aliceUri
+                                           && infos["lastDisplayed"] == msgId;
+                                })
+                   != membersInfos.end());
+}
+
+void
+SyncHistoryTest::testLastInteractionAfterSomeMessages()
+{
+    auto aliceAccount = Manager::instance().getAccount<JamiAccount>(aliceId);
+    auto aliceUri = aliceAccount->getUsername();
+    auto bobAccount = Manager::instance().getAccount<JamiAccount>(bobId);
+    auto bobUri = bobAccount->getUsername();
+    std::string convId;
+
+    std::mutex mtx;
+    std::unique_lock<std::mutex> lk {mtx};
+    std::condition_variable cv;
+    std::map<std::string, std::shared_ptr<DRing::CallbackWrapperBase>> confHandlers;
+    auto messageReceived = false;
+    std::string msgId = "";
+    confHandlers.insert(DRing::exportable_callback<DRing::ConversationSignal::MessageReceived>(
+        [&](const std::string& /* accountId */,
+            const std::string& /* conversationId */,
+            std::map<std::string, std::string> message) {
+            messageReceived = true;
+            msgId = message["id"];
+            cv.notify_one();
+        }));
+    auto conversationReady = false, conversationAlice2Ready = false;
+    confHandlers.insert(DRing::exportable_callback<DRing::ConversationSignal::ConversationReady>(
+        [&](const std::string& accountId, const std::string& conversationId) {
+            if (accountId == bobId) {
+                convId = conversationId;
+                conversationReady = true;
+            } else if (accountId == alice2Id) {
+                conversationAlice2Ready = true;
+            }
+            cv.notify_one();
+        }));
+    auto requestReceived = false;
+    confHandlers.insert(
+        DRing::exportable_callback<DRing::ConversationSignal::ConversationRequestReceived>(
+            [&](const std::string& accountId,
+                const std::string& /*conversationId*/,
+                std::map<std::string, std::string> /*metadatas*/) {
+                if (accountId == bobId)
+                    requestReceived = true;
+                cv.notify_one();
+            }));
+    auto messageDisplayed = false;
+    confHandlers.insert(
+        DRing::exportable_callback<DRing::ConfigurationSignal::AccountMessageStatusChanged>(
+            [&](const std::string& /* accountId */,
+                const std::string& /* conversationId */,
+                const std::string& /* username */,
+                const std::string& /* msgId */,
+                int status) {
+                if (status == 3)
+                    messageDisplayed = true;
+                cv.notify_one();
+            }));
+    auto alice2Ready = false, alice2Stopped = false;
+    confHandlers.insert(
+        DRing::exportable_callback<DRing::ConfigurationSignal::VolatileDetailsChanged>(
+            [&](const std::string& accountId, const std::map<std::string, std::string>& details) {
+                if (alice2Id != accountId) {
+                    return;
+                }
+                alice2Ready = details.at(DRing::Account::VolatileProperties::DEVICE_ANNOUNCED)
+                              == "true";
+                auto daemonStatus = details.at(DRing::Account::ConfProperties::Registration::STATUS);
+                if (daemonStatus == "UNREGISTERED")
+                    alice2Stopped = true;
+                cv.notify_one();
+            }));
+    DRing::registerSignalHandlers(confHandlers);
+    confHandlers.clear();
+
+    // Creates alice2
+    auto aliceArchive = std::filesystem::current_path().string() + "/alice.gz";
+    aliceAccount->exportArchive(aliceArchive);
+    std::map<std::string, std::string> details = DRing::getAccountTemplate("RING");
+    details[ConfProperties::TYPE] = "RING";
+    details[ConfProperties::DISPLAYNAME] = "ALICE2";
+    details[ConfProperties::ALIAS] = "ALICE2";
+    details[ConfProperties::UPNP_ENABLED] = "true";
+    details[ConfProperties::ARCHIVE_PASSWORD] = "";
+    details[ConfProperties::ARCHIVE_PIN] = "";
+    details[ConfProperties::ARCHIVE_PATH] = aliceArchive;
+    alice2Id = Manager::instance().addAccount(details);
+
+    aliceAccount->addContact(bobUri);
+    aliceAccount->sendTrustRequest(bobUri, {});
+    CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return requestReceived; }));
+
+    CPPUNIT_ASSERT(bobAccount->acceptTrustRequest(aliceUri));
+    CPPUNIT_ASSERT(
+        cv.wait_for(lk, 30s, [&]() { return conversationReady && conversationAlice2Ready; }));
+
+    // Disable alice 2
+    Manager::instance().sendRegister(aliceId, false);
+    CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return alice2Stopped; }));
+
+    // Start conversation
+    messageReceived = false;
+    DRing::sendMessage(bobId, convId, std::string("Message 1"), "");
+    CPPUNIT_ASSERT(cv.wait_for(lk, 10s, [&] { return messageReceived; }));
+    messageReceived = false;
+    DRing::sendMessage(bobId, convId, std::string("Message 2"), "");
+    CPPUNIT_ASSERT(cv.wait_for(lk, 10s, [&] { return messageReceived; }));
+    messageReceived = false;
+    DRing::sendMessage(bobId, convId, std::string("Message 3"), "");
+    CPPUNIT_ASSERT(cv.wait_for(lk, 10s, [&] { return messageReceived; }));
+
+    messageDisplayed = false;
+    auto displayedId = msgId;
+    DRing::setMessageDisplayed(aliceId, "swarm:" + convId, displayedId, 3);
+    CPPUNIT_ASSERT(cv.wait_for(lk, 10s, [&] { return messageDisplayed; }));
+
+    // Now restart alice2
+    messageDisplayed = false;
+    Manager::instance().sendRegister(aliceId, true);
+    CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&] { return messageDisplayed; }));
+    auto membersInfos = DRing::getConversationMembers(alice2Id, convId);
+    CPPUNIT_ASSERT(std::find_if(membersInfos.begin(),
+                                membersInfos.end(),
+                                [&](auto infos) {
+                                    return infos["uri"] == aliceUri
+                                           && infos["lastDisplayed"] == displayedId;
+                                })
+                   != membersInfos.end());
+}
+
 } // namespace test
 } // namespace jami