diff --git a/bin/dbus/cx.ring.Ring.ConfigurationManager.xml b/bin/dbus/cx.ring.Ring.ConfigurationManager.xml index 19e2a1e2799edb8e3f94f7301b24dfb283657963..15fab637c9a19a7fa82ef13c990768594d79d120 100644 --- a/bin/dbus/cx.ring.Ring.ConfigurationManager.xml +++ b/bin/dbus/cx.ring.Ring.ConfigurationManager.xml @@ -1911,6 +1911,29 @@ </arg> </signal> + <signal name="conversationProfileUpdated" tp:name-for-bindings="conversationProfileUpdated"> + <tp:added version="13.4.0"/> + <tp:docstring> + Notify clients when a conversation got its profile changed. + </tp:docstring> + <arg type="s" name="account_id"> + <tp:docstring> + Account id related + </tp:docstring> + </arg> + <arg type="s" name="conversation_id"> + <tp:docstring> + Conversation id + </tp:docstring> + </arg> + <annotation name="org.qtproject.QtDBus.QtTypeName.Out2" value="MapStringString"/> + <arg type="a{ss}" name="profile"> + <tp:docstring> + The new profile + </tp:docstring> + </arg> + </signal> + <signal name="conversationRequestReceived" tp:name-for-bindings="conversationRequestReceived"> <tp:added version="10.0.0"/> <tp:docstring> diff --git a/bin/dbus/dbusclient.cpp b/bin/dbus/dbusclient.cpp index 03f08c1490d3e768cd870f0d7cfd2744ea26199b..164bd6155d32d8371c8b2cc487eace0e58d8d009 100644 --- a/bin/dbus/dbusclient.cpp +++ b/bin/dbus/dbusclient.cpp @@ -301,6 +301,8 @@ DBusClient::initLibrary(int flags) bind(&DBusConfigurationManager::conversationLoaded, confM, _1, _2, _3, _4)), exportable_callback<ConversationSignal::MessageReceived>( bind(&DBusConfigurationManager::messageReceived, confM, _1, _2, _3)), + exportable_callback<ConversationSignal::ConversationProfileUpdated>( + bind(&DBusConfigurationManager::conversationProfileUpdated, confM, _1, _2, _3)), exportable_callback<ConversationSignal::ConversationRequestReceived>( bind(&DBusConfigurationManager::conversationRequestReceived, confM, _1, _2, _3)), exportable_callback<ConversationSignal::ConversationRequestDeclined>( diff --git a/bin/jni/conversation.i b/bin/jni/conversation.i index 7f457906b4df4b79e246e96bac285d085b521772..2485823942f735d85c1e2cce59a1e705e4d12c66 100644 --- a/bin/jni/conversation.i +++ b/bin/jni/conversation.i @@ -27,6 +27,7 @@ public: virtual ~ConversationCallback(){} virtual void conversationLoaded(uint32_t /* id */, const std::string& /*accountId*/, const std::string& /* conversationId */, std::vector<std::map<std::string, std::string>> /*messages*/){} virtual void messageReceived(const std::string& /*accountId*/, const std::string& /* conversationId */, std::map<std::string, std::string> /*message*/){} + virtual void conversationProfileUpdated(const std::string& /*accountId*/, const std::string& /* conversationId */, std::map<std::string, std::string> /*profile*/){} virtual void conversationRequestReceived(const std::string& /*accountId*/, const std::string& /* conversationId */, std::map<std::string, std::string> /*metadatas*/){} virtual void conversationRequestDeclined(const std::string& /*accountId*/, const std::string& /* conversationId */){} virtual void conversationReady(const std::string& /*accountId*/, const std::string& /* conversationId */){} @@ -66,6 +67,7 @@ public: virtual ~ConversationCallback(){} virtual void conversationLoaded(uint32_t /* id */, const std::string& /*accountId*/, const std::string& /* conversationId */, std::vector<std::map<std::string, std::string>> /*messages*/){} virtual void messageReceived(const std::string& /*accountId*/, const std::string& /* conversationId */, std::map<std::string, std::string> /*message*/){} + virtual void conversationProfileUpdated(const std::string& /*accountId*/, const std::string& /* conversationId */, std::map<std::string, std::string> /*profile*/){} virtual void conversationRequestReceived(const std::string& /*accountId*/, const std::string& /* conversationId */, std::map<std::string, std::string> /*metadatas*/){} virtual void conversationRequestDeclined(const std::string& /*accountId*/, const std::string& /* conversationId */){} virtual void conversationReady(const std::string& /*accountId*/, const std::string& /* conversationId */){} diff --git a/bin/jni/jni_interface.i b/bin/jni/jni_interface.i index 89b05efa8251bf4a0001ea0095042a165331f7ce..80645527a225a48f3f524a017f2e6846d9c83bac 100644 --- a/bin/jni/jni_interface.i +++ b/bin/jni/jni_interface.i @@ -321,6 +321,7 @@ void init(ConfigurationCallback* confM, Callback* callM, PresenceCallback* presM const std::map<std::string, SharedCallback> conversationHandlers = { exportable_callback<ConversationSignal::ConversationLoaded>(bind(&ConversationCallback::conversationLoaded, convM, _1, _2, _3, _4)), exportable_callback<ConversationSignal::MessageReceived>(bind(&ConversationCallback::messageReceived, convM, _1, _2, _3)), + exportable_callback<ConversationSignal::ConversationProfileUpdated>(bind(&ConversationCallback::conversationProfileUpdated, convM, _1, _2, _3)), exportable_callback<ConversationSignal::ConversationRequestReceived>(bind(&ConversationCallback::conversationRequestReceived, convM, _1, _2, _3)), exportable_callback<ConversationSignal::ConversationRequestDeclined>(bind(&ConversationCallback::conversationRequestDeclined, convM, _1, _2)), exportable_callback<ConversationSignal::ConversationReady>(bind(&ConversationCallback::conversationReady, convM, _1, _2)), diff --git a/bin/nodejs/callback.h b/bin/nodejs/callback.h index 632d7603a4b77752b932bdbf7ad75252435e2519..f949639d8f9eb300a297a760c043f0ffd144c7fb 100644 --- a/bin/nodejs/callback.h +++ b/bin/nodejs/callback.h @@ -29,6 +29,7 @@ Persistent<Function> incomingCallCb; Persistent<Function> incomingCallWithMediaCb; Persistent<Function> conversationLoadedCb; Persistent<Function> messageReceivedCb; +Persistent<Function> conversationProfileUpdatedCb; Persistent<Function> conversationRequestReceivedCb; Persistent<Function> conversationRequestDeclinedCb; Persistent<Function> conversationReadyCb; @@ -88,6 +89,8 @@ getPresistentCb(std::string_view signal) return &conversationLoadedCb; else if (signal == "MessageReceived") return &messageReceivedCb; + else if (signal == "ConversationProfileUpdated") + return &conversationProfileUpdatedCb; else if (signal == "ConversationReady") return &conversationReadyCb; else if (signal == "ConversationRemoved") @@ -450,12 +453,10 @@ callStateChanged(const std::string& accountId, pendingSignals.emplace([accountId, callId, state, detail_code]() { Local<Function> func = Local<Function>::New(Isolate::GetCurrent(), callStateChangedCb); if (!func.IsEmpty()) { - SWIGV8_VALUE callback_args[] = { - V8_STRING_NEW_LOCAL(accountId), - V8_STRING_NEW_LOCAL(callId), - V8_STRING_NEW_LOCAL(state), - SWIGV8_INTEGER_NEW(detail_code) - }; + SWIGV8_VALUE callback_args[] = {V8_STRING_NEW_LOCAL(accountId), + V8_STRING_NEW_LOCAL(callId), + V8_STRING_NEW_LOCAL(state), + SWIGV8_INTEGER_NEW(detail_code)}; func->Call(SWIGV8_CURRENT_CONTEXT(), SWIGV8_NULL(), 4, callback_args); } }); @@ -472,11 +473,9 @@ mediaChangeRequested(const std::string& accountId, pendingSignals.emplace([accountId, callId, mediaList]() { Local<Function> func = Local<Function>::New(Isolate::GetCurrent(), mediaChangeRequestedCb); if (!func.IsEmpty()) { - SWIGV8_VALUE callback_args[] = { - V8_STRING_NEW_LOCAL(accountId), - V8_STRING_NEW_LOCAL(callId), - stringMapVecToJsMapArray(mediaList) - }; + SWIGV8_VALUE callback_args[] = {V8_STRING_NEW_LOCAL(accountId), + V8_STRING_NEW_LOCAL(callId), + stringMapVecToJsMapArray(mediaList)}; func->Call(SWIGV8_CURRENT_CONTEXT(), SWIGV8_NULL(), 3, callback_args); } }); diff --git a/bin/nodejs/conversation.i b/bin/nodejs/conversation.i index 9fd1319438e8eab169cfe0486a2cb3292a023ca4..dbc6107758b90b2a07dbd5d0acea8ce1129f82bd 100644 --- a/bin/nodejs/conversation.i +++ b/bin/nodejs/conversation.i @@ -27,6 +27,7 @@ public: virtual ~ConversationCallback(){} virtual void conversationLoaded(uint32_t /* id */, const std::string& /*accountId*/, const std::string& /* conversationId */, std::vector<std::map<std::string, std::string>> /*messages*/){} virtual void messageReceived(const std::string& /*accountId*/, const std::string& /* conversationId */, std::map<std::string, std::string> /*message*/){} + virtual void conversationProfileUpdated(const std::string& /*accountId*/, const std::string& /* conversationId */, std::map<std::string, std::string> /*profile*/){} virtual void conversationRequestReceived(const std::string& /*accountId*/, const std::string& /* conversationId */, std::map<std::string, std::string> /*metadatas*/){} virtual void conversationRequestDeclined(const std::string& /*accountId*/, const std::string& /* conversationId */){} virtual void conversationReady(const std::string& /*accountId*/, const std::string& /* conversationId */){} @@ -67,6 +68,7 @@ public: virtual ~ConversationCallback(){} virtual void conversationLoaded(uint32_t /* id */, const std::string& /*accountId*/, const std::string& /* conversationId */, std::vector<std::map<std::string, std::string>> /*messages*/){} virtual void messageReceived(const std::string& /*accountId*/, const std::string& /* conversationId */, std::map<std::string, std::string> /*message*/){} + virtual void conversationProfileUpdated(const std::string& /*accountId*/, const std::string& /* conversationId */, std::map<std::string, std::string> /*profile*/){} virtual void conversationRequestReceived(const std::string& /*accountId*/, const std::string& /* conversationId */, std::map<std::string, std::string> /*metadatas*/){} virtual void conversationRequestDeclined(const std::string& /*accountId*/, const std::string& /* conversationId */){} virtual void conversationReady(const std::string& /*accountId*/, const std::string& /* conversationId */){} diff --git a/bin/nodejs/nodejs_interface.i b/bin/nodejs/nodejs_interface.i index 0bc32bba1b4f1492f37e7d041979e3da8597cfec..7084f08444be4770d65935cadbdfa2dfac8b902b 100644 --- a/bin/nodejs/nodejs_interface.i +++ b/bin/nodejs/nodejs_interface.i @@ -147,6 +147,7 @@ void init(const SWIGV8_VALUE& funcMap){ const std::map<std::string, SharedCallback> conversationHandlers = { exportable_callback<ConversationSignal::ConversationLoaded>(bind(&conversationLoaded, _1, _2, _3, _4)), exportable_callback<ConversationSignal::MessageReceived>(bind(&messageReceived, _1, _2, _3)), + exportable_callback<ConversationSignal::ConversationProfileUpdated>(bind(&conversationProfileUpdated, _1, _2, _3)), exportable_callback<ConversationSignal::ConversationRequestReceived>(bind(&conversationRequestReceived, _1, _2, _3)), exportable_callback<ConversationSignal::ConversationRequestDeclined>(bind(&conversationRequestDeclined, _1, _2)), exportable_callback<ConversationSignal::ConversationReady>(bind(&conversationReady, _1, _2)), diff --git a/src/client/ring_signal.cpp b/src/client/ring_signal.cpp index 3b3d802ed0cee3ff0c2e78a7403cf8085c56b9c6..b31cc746f432fa2d3bd07be7fc76b762ec6e4e17 100644 --- a/src/client/ring_signal.cpp +++ b/src/client/ring_signal.cpp @@ -128,6 +128,7 @@ getSignalHandlers() /* Conversation */ exported_callback<DRing::ConversationSignal::ConversationLoaded>(), exported_callback<DRing::ConversationSignal::MessageReceived>(), + exported_callback<DRing::ConversationSignal::ConversationProfileUpdated>(), exported_callback<DRing::ConversationSignal::ConversationRequestReceived>(), exported_callback<DRing::ConversationSignal::ConversationRequestDeclined>(), exported_callback<DRing::ConversationSignal::ConversationReady>(), diff --git a/src/jami/conversation_interface.h b/src/jami/conversation_interface.h index 69a8ea5b7fec92868ed985991fa0b47942dc2bb2..06dcaf209dd47d5591f903f7f240690de0176252 100644 --- a/src/jami/conversation_interface.h +++ b/src/jami/conversation_interface.h @@ -92,6 +92,13 @@ struct DRING_PUBLIC ConversationSignal const std::string& /* conversationId */, std::map<std::string, std::string> /*message*/); }; + struct DRING_PUBLIC ConversationProfileUpdated + { + constexpr static const char* name = "ConversationProfileUpdated"; + using cb_type = void(const std::string& /*accountId*/, + const std::string& /* conversationId */, + std::map<std::string, std::string> /*profile*/); + }; struct DRING_PUBLIC ConversationRequestReceived { constexpr static const char* name = "ConversationRequestReceived"; diff --git a/src/jamidht/conversation.cpp b/src/jamidht/conversation.cpp index 10a19a554b601f874ddcac1c8098b7dd454f3c2f..fa4dd138258e80c9d544c84f50b389adc0758c2d 100644 --- a/src/jamidht/conversation.cpp +++ b/src/jamidht/conversation.cpp @@ -770,7 +770,7 @@ Conversation::sendMessage(std::string&& message, } void -Conversation::sendMessage(Json::Value&& value, const std::string& /*parent*/, OnDoneCb&& cb) +Conversation::sendMessage(Json::Value&& value, const std::string& parent, OnDoneCb&& cb) { dht::ThreadPool::io().run([w = weak(), value = std::move(value), cb = std::move(cb)] { if (auto sthis = w.lock()) { @@ -934,6 +934,7 @@ Conversation::pull(const std::string& deviceId, OnPullCb&& cb, std::string commi auto sthis_ = w.lock(); if (!sthis_) return; + auto& repo = sthis_->pimpl_->repository_; std::string deviceId, commitId; OnPullCb cb; @@ -973,8 +974,7 @@ Conversation::pull(const std::string& deviceId, OnPullCb&& cb, std::string commi it = itr.first; } // If recently fetched, the commit can already be there, so no need to do complex operations - if (commitId != "" - && sthis_->pimpl_->repository_->getCommit(commitId, false) != std::nullopt) { + if (commitId != "" && repo->getCommit(commitId, false) != std::nullopt) { cb(true); std::lock_guard<std::mutex> lk(sthis_->pimpl_->pullcbsMtx_); sthis_->pimpl_->fetchingRemotes_.erase(it); @@ -991,12 +991,25 @@ Conversation::pull(const std::string& deviceId, OnPullCb&& cb, std::string commi cb(false); continue; } + auto oldId = repo->getHead(); std::unique_lock<std::mutex> lk(sthis_->pimpl_->writeMtx_); auto newCommits = sthis_->mergeHistory(deviceId); sthis_->pimpl_->announce(newCommits); lk.unlock(); if (cb) cb(true); + // Announce if profile changed + auto newId = repo->getHead(); + if (oldId != newId) { + auto diffStats = repo->diffStats(newId, oldId); + auto changedFiles = repo->changedFiles(diffStats); + if (find(changedFiles.begin(), changedFiles.end(), "profile.vcf") + != changedFiles.end()) { + if (auto account = sthis_->pimpl_->account_.lock()) + emitSignal<DRing::ConversationSignal::ConversationProfileUpdated>( + account->getAccountID(), repo->id(), repo->infos()); + } + } } }); } @@ -1095,12 +1108,16 @@ Conversation::updateInfos(const std::map<std::string, std::string>& map, const O { dht::ThreadPool::io().run([w = weak(), map = std::move(map), cb = std::move(cb)] { if (auto sthis = w.lock()) { + auto& repo = sthis->pimpl_->repository_; std::unique_lock<std::mutex> lk(sthis->pimpl_->writeMtx_); - auto commit = sthis->pimpl_->repository_->updateInfos(map); + auto commit = repo->updateInfos(map); sthis->pimpl_->announce(commit); lk.unlock(); if (cb) cb(!commit.empty(), commit); + if (auto account = sthis->pimpl_->account_.lock()) + emitSignal<DRing::ConversationSignal::ConversationProfileUpdated>( + account->getAccountID(), repo->id(), repo->infos()); } }); } diff --git a/src/jamidht/conversationrepository.cpp b/src/jamidht/conversationrepository.cpp index fd6df772853a277a4de08878faabdb7c0d96b995..edd4adc03bcf67c39aeaeb1c8661f46c2d99314f 100644 --- a/src/jamidht/conversationrepository.cpp +++ b/src/jamidht/conversationrepository.cpp @@ -3573,4 +3573,19 @@ ConversationRepository::uriFromDevice(const std::string& deviceId) const return pimpl_->uriFromDevice(deviceId); } +std::string +ConversationRepository::getHead() const +{ + if (auto repo = pimpl_->repository()) { + git_oid commit_id; + if (git_reference_name_to_id(&commit_id, repo.get(), "HEAD") < 0) { + JAMI_ERR("Cannot get reference for HEAD"); + return {}; + } + if (auto commit_str = git_oid_tostr_s(&commit_id)) + return commit_str; + } + return {}; +} + } // namespace jami diff --git a/src/jamidht/conversationrepository.h b/src/jamidht/conversationrepository.h index 89b8c6d2c4a0dbcabec1e5920ed037e210c73177..f085287ee2248fd9303440602ce0f3e8adb86479 100644 --- a/src/jamidht/conversationrepository.h +++ b/src/jamidht/conversationrepository.h @@ -359,6 +359,11 @@ public: */ std::string uriFromDevice(const std::string& deviceId) const; + /** + * Get current HEAD hash + */ + std::string getHead() const; + private: ConversationRepository() = delete; class Impl; diff --git a/test/unitTest/conversation/conversation.cpp b/test/unitTest/conversation/conversation.cpp index 2d5925898839533ca7db5769a74127657e37cdad..a309c29cdd16bc2305c397c63b7ece7fd90c00fb 100644 --- a/test/unitTest/conversation/conversation.cpp +++ b/test/unitTest/conversation/conversation.cpp @@ -1920,8 +1920,6 @@ ConversationTest::testUpdateProfile() std::condition_variable cv; std::map<std::string, std::shared_ptr<DRing::CallbackWrapperBase>> confHandlers; auto messageBobReceived = 0, messageAliceReceived = 0; - bool requestReceived = false; - bool conversationReady = false; confHandlers.insert(DRing::exportable_callback<DRing::ConversationSignal::MessageReceived>( [&](const std::string& accountId, const std::string& /* conversationId */, @@ -1933,6 +1931,7 @@ ConversationTest::testUpdateProfile() } cv.notify_one(); })); + bool requestReceived = false; confHandlers.insert( DRing::exportable_callback<DRing::ConversationSignal::ConversationRequestReceived>( [&](const std::string& /*accountId*/, @@ -1941,6 +1940,7 @@ ConversationTest::testUpdateProfile() requestReceived = true; cv.notify_one(); })); + bool conversationReady = false; confHandlers.insert(DRing::exportable_callback<DRing::ConversationSignal::ConversationReady>( [&](const std::string& accountId, const std::string& /* conversationId */) { if (accountId == bobId) { @@ -1948,6 +1948,17 @@ ConversationTest::testUpdateProfile() cv.notify_one(); } })); + std::map<std::string, std::string> profileAlice, profileBob; + confHandlers.insert( + DRing::exportable_callback<DRing::ConversationSignal::ConversationProfileUpdated>( + [&](const auto& accountId, const auto& /* conversationId */, const auto& profile) { + if (accountId == aliceId) { + profileAlice = profile; + } else if (accountId == bobId) { + profileBob = profile; + } + cv.notify_one(); + })); DRing::registerSignalHandlers(confHandlers); auto convId = DRing::startConversation(aliceId); @@ -1962,11 +1973,18 @@ ConversationTest::testUpdateProfile() messageBobReceived = 0; aliceAccount->convModule()->updateConversationInfos(convId, {{"title", "My awesome swarm"}}); - CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { return messageBobReceived == 1; })); + CPPUNIT_ASSERT(cv.wait_for(lk, 30s, [&]() { + return messageBobReceived == 1 && !profileAlice.empty() && !profileBob.empty(); + })); auto infos = DRing::conversationInfos(bobId, convId); + // Verify that we have the same profile everywhere CPPUNIT_ASSERT(infos["title"] == "My awesome swarm"); + CPPUNIT_ASSERT(profileAlice["title"] == "My awesome swarm"); + CPPUNIT_ASSERT(profileBob["title"] == "My awesome swarm"); CPPUNIT_ASSERT(infos["description"].empty()); + CPPUNIT_ASSERT(profileAlice["description"].empty()); + CPPUNIT_ASSERT(profileBob["description"].empty()); DRing::unregisterSignalHandlers(); }