diff --git a/src/jamidht/conversation_module.cpp b/src/jamidht/conversation_module.cpp index ad75b4505..582954a8c 100644 --- a/src/jamidht/conversation_module.cpp +++ b/src/jamidht/conversation_module.cpp @@ -326,6 +326,14 @@ public: void rmConversationRequest(const std::string& id) { // conversationsRequestsMtx_ MUST BE LOCKED + auto it = conversationsRequests_.find(id); + if (it != conversationsRequests_.end()) { + auto& md = syncingMetadatas_[id]; + md = it->second.metadatas; + md["syncing"] = "true"; + md["created"] = std::to_string(it->second.received); + } + saveMetadatas(); conversationsRequests_.erase(id); saveConvRequests(); } @@ -385,6 +393,29 @@ public: void cloneConversationFrom(const std::string& conversationId, const std::string& uri, const std::string& oldConvId = ""); + + // While syncing, we do not want to lose metadata (avatar/title and mode) + std::map> syncingMetadatas_; + void saveMetadatas() { + auto path = fileutils::get_data_dir() / accountId_; + std::ofstream file(path / "syncingMetadatas", std::ios::trunc | std::ios::binary); + msgpack::pack(file, syncingMetadatas_); + } + + void loadMetadatas() { + try { + // read file + auto path = fileutils::get_data_dir() / accountId_; + std::lock_guard lock(dhtnet::fileutils::getFileLock(path / "syncingMetadatas")); + auto file = fileutils::loadFile("syncingMetadatas", path); + // load values + msgpack::unpacked result; + msgpack::unpack(result, (const char*) file.data(), file.size(), 0); + result.get().convert(syncingMetadatas_); + } catch (const std::exception& e) { + JAMI_WARNING("[ConversationModule] error loading syncingMetadatas_: {}", e.what()); + } + } }; ConversationModule::Impl::Impl(std::weak_ptr&& account, @@ -410,6 +441,7 @@ ConversationModule::Impl::Impl(std::weak_ptr&& account, username_ = info->accountId; } conversationsRequests_ = convRequests(accountId_); + loadMetadatas(); } void @@ -473,10 +505,8 @@ ConversationModule::Impl::cloneConversation(const std::string& deviceId, accountId_, conv->info.id, deviceId); - conv->info.created = std::time(nullptr); conv->info.members.emplace(username_); - if (peerUri != username_) - conv->info.members.emplace(peerUri); + conv->info.members.emplace(peerUri); addConvInfo(conv->info); } else { JAMI_DEBUG("[Account {}] Already have conversation {}", accountId_, conv->info.id); @@ -744,6 +774,8 @@ ConversationModule::Impl::handlePendingConversation(const std::string& conversat conversation->updatePreferences(preferences); if (!status.empty()) conversation->updateMessageStatus(status); + syncingMetadatas_.erase(conversationId); + saveMetadatas(); // Inform user that the conversation is ready emitSignal(accountId_, conversationId); @@ -839,6 +871,8 @@ ConversationModule::Impl::declineOtherConversationWith(const std::string& uri) n if (request.isOneToOne() && request.from == uri) { JAMI_WARNING("Decline conversation request ({}) from {}", id, uri); request.declined = std::time(nullptr); + syncingMetadatas_.erase(id); + saveMetadatas(); emitSignal(accountId_, id); } } @@ -1866,6 +1900,8 @@ ConversationModule::declineConversationRequest(const std::string& conversationId it->second.declined = std::time(nullptr); pimpl_->saveConvRequests(); } + pimpl_->syncingMetadatas_.erase(conversationId); + pimpl_->saveMetadatas(); emitSignal(pimpl_->accountId_, conversationId); pimpl_->needsSyncingCb_({}); @@ -2325,6 +2361,8 @@ ConversationModule::onSyncData(const SyncMsg& msg, pimpl_->accountId_, convId, deviceId); + pimpl_->syncingMetadatas_.erase(convId); + pimpl_->saveMetadatas(); emitSignal(pimpl_->accountId_, convId); continue; @@ -2549,10 +2587,22 @@ ConversationModule::conversationInfos(const std::string& conversationId) const } if (auto conv = pimpl_->getConversation(conversationId)) { std::lock_guard lk(conv->mtx); + std::map md; + { + auto syncingMetadatasIt = pimpl_->syncingMetadatas_.find(conversationId); + if (syncingMetadatasIt != pimpl_->syncingMetadatas_.end()) { + if (conv->conversation) { + pimpl_->syncingMetadatas_.erase(syncingMetadatasIt); + pimpl_->saveMetadatas(); + } else { + md = syncingMetadatasIt->second; + } + } + } if (conv->conversation) return conv->conversation->infos(); else - return {{"syncing", "true"}, {"created", std::to_string(conv->info.created)}}; + return md; } JAMI_ERROR("Conversation {:s} doesn't exist", conversationId); return {}; @@ -2647,6 +2697,8 @@ ConversationModule::removeContact(const std::string& uri, bool banned) ++it) { if (it->second.from == uri && !it->second.declined) { JAMI_DEBUG("Declining conversation request {:s} from {:s}", it->first, uri); + pimpl_->syncingMetadatas_.erase(it->first); + pimpl_->saveMetadatas(); emitSignal( pimpl_->accountId_, it->first); update = true; diff --git a/test/unitTest/conversation/conversationRequest.cpp b/test/unitTest/conversation/conversationRequest.cpp index c758992c1..e329ab882 100644 --- a/test/unitTest/conversation/conversationRequest.cpp +++ b/test/unitTest/conversation/conversationRequest.cpp @@ -95,6 +95,7 @@ public: void testAddConversationNoPresenceThenConnects(); void testRequestBigPayload(); void testBothRemoveReadd(); + void doNotLooseMetadata(); std::string aliceId; UserData aliceData; std::string bobId; @@ -137,6 +138,7 @@ private: CPPUNIT_TEST(testAddConversationNoPresenceThenConnects); CPPUNIT_TEST(testRequestBigPayload); CPPUNIT_TEST(testBothRemoveReadd); + CPPUNIT_TEST(doNotLooseMetadata); CPPUNIT_TEST_SUITE_END(); }; @@ -1148,6 +1150,40 @@ ConversationRequestTest::testBothRemoveReadd() return !aliceData.conversationId.empty() && bobData.conversationId == aliceData.conversationId; })); } +void +ConversationRequestTest::doNotLooseMetadata() +{ + std::cout << "\nRunning test: " << __func__ << std::endl; + connectSignals(); + + auto aliceAccount = Manager::instance().getAccount(aliceId); + auto bobAccount = Manager::instance().getAccount(bobId); + auto bobUri = bobAccount->getUsername(); + + libjami::startConversation(aliceId); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceData.conversationId != ""; })); + + auto aliceMsgSize = aliceData.messages.size(); + aliceAccount->convModule()->updateConversationInfos(aliceData.conversationId, {{"title", "My awesome swarm"}}); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { + return aliceMsgSize + 1 == aliceData.messages.size(); + })); + + libjami::addConversationMember(aliceId, aliceData.conversationId, bobUri); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return bobData.requestReceived; })); + + Manager::instance().sendRegister(aliceId, false); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return aliceData.stopped; })); + + libjami::acceptConversationRequest(bobId, aliceData.conversationId); + CPPUNIT_ASSERT(!cv.wait_for(lk, 10s, [&]() { return bobData.conversationId != ""; })); + + // Force reset + bobAccount->convModule()->loadConversations(); + auto infos = libjami::conversationInfos(bobId, aliceData.conversationId); + CPPUNIT_ASSERT(infos["syncing"] == "true"); + CPPUNIT_ASSERT(infos["title"] == "My awesome swarm"); +} } // namespace test } // namespace jami