diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8105d437a..2286aea46 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,14 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+## 5.2.0 - undefined
+
+### Fixed
+- Download path and emojis size settings
+- Mac emoji font.
+
+### Added
+- Chat reactions
## 5.1.2 - 2023-08-25
diff --git a/linphone-app/CMakeLists.txt b/linphone-app/CMakeLists.txt
index 96f83e7c7..fb89135ec 100644
--- a/linphone-app/CMakeLists.txt
+++ b/linphone-app/CMakeLists.txt
@@ -242,6 +242,9 @@ set(SOURCES
src/components/chat-events/ChatMessageListener.cpp
src/components/chat-events/ChatMessageModel.cpp
src/components/chat-events/ChatNoticeModel.cpp
+ src/components/chat-reaction/ChatReactionModel.cpp
+ src/components/chat-reaction/ChatReactionListModel.cpp
+ src/components/chat-reaction/ChatReactionProxyModel.cpp
src/components/chat-room/ChatRoomInitializer.cpp
src/components/chat-room/ChatRoomListener.cpp
src/components/chat-room/ChatRoomModel.cpp
@@ -384,6 +387,9 @@ set(HEADERS
src/components/chat-events/ChatMessageListener.hpp
src/components/chat-events/ChatMessageModel.hpp
src/components/chat-events/ChatNoticeModel.hpp
+ src/components/chat-reaction/ChatReactionModel.hpp
+ src/components/chat-reaction/ChatReactionListModel.hpp
+ src/components/chat-reaction/ChatReactionProxyModel.hpp
src/components/chat-room/ChatRoomInitializer.hpp
src/components/chat-room/ChatRoomListener.hpp
src/components/chat-room/ChatRoomModel.hpp
diff --git a/linphone-app/assets/languages/cs.ts b/linphone-app/assets/languages/cs.ts
index f2a2d0b48..863e7a246 100644
--- a/linphone-app/assets/languages/cs.ts
+++ b/linphone-app/assets/languages/cs.ts
@@ -758,6 +758,18 @@ Adresa URL není nakonfigurována.
+
+ ChatReactionsDetails
+
+ reactionsCount
+ "%1<br>reactions" : count of all chat reactions with a jump line between count and text.
+
+
+
+
+
+
+
ChatReplyMessage
diff --git a/linphone-app/assets/languages/da.ts b/linphone-app/assets/languages/da.ts
index e2a07bd4b..60a0e490a 100644
--- a/linphone-app/assets/languages/da.ts
+++ b/linphone-app/assets/languages/da.ts
@@ -752,6 +752,17 @@ Server url ikke konfigureret.
+
+ ChatReactionsDetails
+
+ reactionsCount
+ "%1<br>reactions" : count of all chat reactions with a jump line between count and text.
+
+
+
+
+
+
ChatReplyMessage
diff --git a/linphone-app/assets/languages/de.ts b/linphone-app/assets/languages/de.ts
index 87a4ac1c8..30c79fca1 100644
--- a/linphone-app/assets/languages/de.ts
+++ b/linphone-app/assets/languages/de.ts
@@ -752,6 +752,17 @@ Server URL ist nicht konfiguriert.
+
+ ChatReactionsDetails
+
+ reactionsCount
+ "%1<br>reactions" : count of all chat reactions with a jump line between count and text.
+
+
+
+
+
+
ChatReplyMessage
@@ -1596,7 +1607,7 @@ Server URL ist nicht konfiguriert.
incallPauseWarning
'You are currently out of the conference.' : Pause message in video conference.
-
+ Sie haben den Anruf unterbrochen.
incallPauseHint
diff --git a/linphone-app/assets/languages/en.ts b/linphone-app/assets/languages/en.ts
index fa04c0f46..c34a73c56 100644
--- a/linphone-app/assets/languages/en.ts
+++ b/linphone-app/assets/languages/en.ts
@@ -752,6 +752,17 @@ Server URL not configured.
+
+ ChatReactionsDetails
+
+ reactionsCount
+ "%1<br>reactions" : count of all chat reactions with a jump line between count and text.
+
+ %1<br>reaction
+ %1<br>reactions
+
+
+
ChatReplyMessage
diff --git a/linphone-app/assets/languages/es.ts b/linphone-app/assets/languages/es.ts
index 36182ccea..2a06a48e9 100644
--- a/linphone-app/assets/languages/es.ts
+++ b/linphone-app/assets/languages/es.ts
@@ -752,6 +752,17 @@ URL del servidor no configurada.
+
+ ChatReactionsDetails
+
+ reactionsCount
+ "%1<br>reactions" : count of all chat reactions with a jump line between count and text.
+
+
+
+
+
+
ChatReplyMessage
diff --git a/linphone-app/assets/languages/fr_FR.ts b/linphone-app/assets/languages/fr_FR.ts
index 9a77aa04a..72a46c6f3 100644
--- a/linphone-app/assets/languages/fr_FR.ts
+++ b/linphone-app/assets/languages/fr_FR.ts
@@ -752,6 +752,17 @@ URL du serveur non configurée.
+
+ ChatReactionsDetails
+
+ reactionsCount
+ "%1<br>reactions" : count of all chat reactions with a jump line between count and text.
+
+ %1<br>réaction
+ %1<br>réactions
+
+
+
ChatReplyMessage
diff --git a/linphone-app/assets/languages/hu.ts b/linphone-app/assets/languages/hu.ts
index cc3b1fd78..ed92ff8cf 100644
--- a/linphone-app/assets/languages/hu.ts
+++ b/linphone-app/assets/languages/hu.ts
@@ -746,6 +746,16 @@ A kiszolgáló URL-je nincs konfigurálva.
+
+ ChatReactionsDetails
+
+ reactionsCount
+ "%1<br>reactions" : count of all chat reactions with a jump line between count and text.
+
+
+
+
+
ChatReplyMessage
diff --git a/linphone-app/assets/languages/it.ts b/linphone-app/assets/languages/it.ts
index f1c502500..dd74dafea 100644
--- a/linphone-app/assets/languages/it.ts
+++ b/linphone-app/assets/languages/it.ts
@@ -752,6 +752,17 @@ URL del server non configurato.
+
+ ChatReactionsDetails
+
+ reactionsCount
+ "%1<br>reactions" : count of all chat reactions with a jump line between count and text.
+
+
+
+
+
+
ChatReplyMessage
diff --git a/linphone-app/assets/languages/ja.ts b/linphone-app/assets/languages/ja.ts
index d02d4b0c1..80ba15599 100644
--- a/linphone-app/assets/languages/ja.ts
+++ b/linphone-app/assets/languages/ja.ts
@@ -746,6 +746,16 @@
+
+ ChatReactionsDetails
+
+ reactionsCount
+ "%1<br>reactions" : count of all chat reactions with a jump line between count and text.
+
+
+
+
+
ChatReplyMessage
diff --git a/linphone-app/assets/languages/lt.ts b/linphone-app/assets/languages/lt.ts
index 5d5184fb5..6d7d9e0ac 100644
--- a/linphone-app/assets/languages/lt.ts
+++ b/linphone-app/assets/languages/lt.ts
@@ -758,6 +758,18 @@ Nesukonfigūruotas serverio url.
+
+ ChatReactionsDetails
+
+ reactionsCount
+ "%1<br>reactions" : count of all chat reactions with a jump line between count and text.
+
+
+
+
+
+
+
ChatReplyMessage
diff --git a/linphone-app/assets/languages/pt_BR.ts b/linphone-app/assets/languages/pt_BR.ts
index c6948365b..4d674c8b6 100644
--- a/linphone-app/assets/languages/pt_BR.ts
+++ b/linphone-app/assets/languages/pt_BR.ts
@@ -752,6 +752,17 @@ URL do servidor não configurado.
+
+ ChatReactionsDetails
+
+ reactionsCount
+ "%1<br>reactions" : count of all chat reactions with a jump line between count and text.
+
+
+
+
+
+
ChatReplyMessage
diff --git a/linphone-app/assets/languages/ru.ts b/linphone-app/assets/languages/ru.ts
index 4de374f52..c0ee72c9b 100644
--- a/linphone-app/assets/languages/ru.ts
+++ b/linphone-app/assets/languages/ru.ts
@@ -758,6 +758,18 @@
+
+ ChatReactionsDetails
+
+ reactionsCount
+ "%1<br>reactions" : count of all chat reactions with a jump line between count and text.
+
+
+
+
+
+
+
ChatReplyMessage
diff --git a/linphone-app/assets/languages/sv.ts b/linphone-app/assets/languages/sv.ts
index 7afff6c73..0b3f819f2 100644
--- a/linphone-app/assets/languages/sv.ts
+++ b/linphone-app/assets/languages/sv.ts
@@ -752,6 +752,17 @@ Serverwebbadressen är inte konfigurerad.
+
+ ChatReactionsDetails
+
+ reactionsCount
+ "%1<br>reactions" : count of all chat reactions with a jump line between count and text.
+
+
+
+
+
+
ChatReplyMessage
diff --git a/linphone-app/assets/languages/tr.ts b/linphone-app/assets/languages/tr.ts
index 432d87649..5af771560 100644
--- a/linphone-app/assets/languages/tr.ts
+++ b/linphone-app/assets/languages/tr.ts
@@ -746,6 +746,16 @@ Sunucu url'si yapılandırılmadı.
+
+ ChatReactionsDetails
+
+ reactionsCount
+ "%1<br>reactions" : count of all chat reactions with a jump line between count and text.
+
+
+
+
+
ChatReplyMessage
diff --git a/linphone-app/assets/languages/uk.ts b/linphone-app/assets/languages/uk.ts
index be6b43132..080872db0 100644
--- a/linphone-app/assets/languages/uk.ts
+++ b/linphone-app/assets/languages/uk.ts
@@ -758,6 +758,18 @@
+
+ ChatReactionsDetails
+
+ reactionsCount
+ "%1<br>reactions" : count of all chat reactions with a jump line between count and text.
+
+
+
+
+
+
+
ChatReplyMessage
diff --git a/linphone-app/assets/languages/zh_CN.ts b/linphone-app/assets/languages/zh_CN.ts
index 1de29a6ac..be907c51a 100644
--- a/linphone-app/assets/languages/zh_CN.ts
+++ b/linphone-app/assets/languages/zh_CN.ts
@@ -746,6 +746,16 @@
+
+ ChatReactionsDetails
+
+ reactionsCount
+ "%1<br>reactions" : count of all chat reactions with a jump line between count and text.
+
+
+
+
+
ChatReplyMessage
diff --git a/linphone-app/resources.qrc b/linphone-app/resources.qrc
index 74f8f8b24..282f0bab3 100644
--- a/linphone-app/resources.qrc
+++ b/linphone-app/resources.qrc
@@ -346,6 +346,7 @@
ui/modules/Linphone/Chat/ChatFilePreview.qml
ui/modules/Linphone/Chat/ChatForwardMessage.qml
ui/modules/Linphone/Chat/ChatMessagePreview.qml
+ ui/modules/Linphone/Chat/ChatReactionsDetails.qml
ui/modules/Linphone/Chat/ChatReplyMessage.qml
ui/modules/Linphone/Chat/ChatReplyPreview.qml
ui/modules/Linphone/Chat/ChatTextMessage.qml
@@ -403,6 +404,7 @@
ui/modules/Linphone/Styles/Chat/ChatFilePreviewStyle.qml
ui/modules/Linphone/Styles/Chat/ChatCalendarMessageStyle.qml
ui/modules/Linphone/Styles/Chat/ChatForwardMessageStyle.qml
+ ui/modules/Linphone/Styles/Chat/ChatReactionsDetailsStyle.qml
ui/modules/Linphone/Styles/Chat/ChatReplyMessageStyle.qml
ui/modules/Linphone/Styles/Codecs/CodecsViewerStyle.qml
ui/modules/Linphone/Styles/Contact/AvatarStyle.qml
diff --git a/linphone-app/src/app/App.cpp b/linphone-app/src/app/App.cpp
index 0289ce755..9dbe87445 100644
--- a/linphone-app/src/app/App.cpp
+++ b/linphone-app/src/app/App.cpp
@@ -722,6 +722,7 @@ void App::registerTypes () {
registerType("CallsListProxyModel");
registerType("Camera");
registerType("ChatRoomProxyModel");
+ registerType("ChatReactionProxyModel");
registerType("ConferenceHelperModel");
registerType("ConferenceProxyModel");
registerType("ConferenceInfoModel");
@@ -762,6 +763,7 @@ void App::registerTypes () {
registerUncreatableType("ChatCallModel");
registerUncreatableType("ChatMessageModel");
registerUncreatableType("ChatNoticeModel");
+ registerUncreatableType("ChatReactionListModel");
registerUncreatableType("ChatRoomModel");
registerUncreatableType("ColorModel");
registerUncreatableType("ImageModel");
diff --git a/linphone-app/src/app/proxyModel/ProxyAbstractListModel.hpp b/linphone-app/src/app/proxyModel/ProxyAbstractListModel.hpp
index 22254aea7..f490478a4 100644
--- a/linphone-app/src/app/proxyModel/ProxyAbstractListModel.hpp
+++ b/linphone-app/src/app/proxyModel/ProxyAbstractListModel.hpp
@@ -112,11 +112,11 @@ public:
return true;
}
- virtual void clearData(){
+ virtual void clearData() override{
mList.clear();
}
- virtual void resetData(){
+ virtual void resetData() override{
beginResetModel();
clearData();
endResetModel();
diff --git a/linphone-app/src/components/Components.hpp b/linphone-app/src/components/Components.hpp
index 68810b554..41c82275f 100644
--- a/linphone-app/src/components/Components.hpp
+++ b/linphone-app/src/components/Components.hpp
@@ -30,6 +30,7 @@
#include "components/chat-events/ChatCallModel.hpp"
#include "components/chat-events/ChatMessageModel.hpp"
#include "components/chat-events/ChatNoticeModel.hpp"
+#include "components/chat-reaction/ChatReactionProxyModel.hpp"
#include "chat-room/ChatRoomProxyModel.hpp"
#include "codecs/AudioCodecsModel.hpp"
#include "codecs/VideoCodecsModel.hpp"
diff --git a/linphone-app/src/components/chat-events/ChatMessageListener.cpp b/linphone-app/src/components/chat-events/ChatMessageListener.cpp
index 148205dfe..77ae497a6 100644
--- a/linphone-app/src/components/chat-events/ChatMessageListener.cpp
+++ b/linphone-app/src/components/chat-events/ChatMessageListener.cpp
@@ -49,6 +49,9 @@ void ChatMessageListener::onFileTransferProgressIndication (const std::shared_pt
void ChatMessageListener::onMsgStateChanged (const std::shared_ptr &message, linphone::ChatMessage::State state){
emit msgStateChanged(message, state);
}
+void ChatMessageListener::onNewMessageReaction(const std::shared_ptr & message, const std::shared_ptr & reaction) {
+ emit newMessageReaction(message, reaction);
+}
void ChatMessageListener::onParticipantImdnStateChanged(const std::shared_ptr & message, const std::shared_ptr & state){
emit participantImdnStateChanged(message, state);
}
diff --git a/linphone-app/src/components/chat-events/ChatMessageListener.hpp b/linphone-app/src/components/chat-events/ChatMessageListener.hpp
index f2c1520b0..29f40d149 100644
--- a/linphone-app/src/components/chat-events/ChatMessageListener.hpp
+++ b/linphone-app/src/components/chat-events/ChatMessageListener.hpp
@@ -41,6 +41,7 @@ public:
virtual std::shared_ptr onFileTransferSend(const std::shared_ptr & message, const std::shared_ptr & content, size_t offset, size_t size) override;
virtual void onFileTransferProgressIndication (const std::shared_ptr &message, const std::shared_ptr &, size_t offset, size_t) override;
virtual void onMsgStateChanged (const std::shared_ptr &message, linphone::ChatMessage::State state) override;
+ virtual void onNewMessageReaction(const std::shared_ptr & message, const std::shared_ptr & reaction) override;
virtual void onParticipantImdnStateChanged(const std::shared_ptr & message, const std::shared_ptr & state) override;
virtual void onEphemeralMessageTimerStarted(const std::shared_ptr & message) override;
virtual void onEphemeralMessageDeleted(const std::shared_ptr & message) override;
@@ -50,6 +51,7 @@ signals:
std::shared_ptr fileTransferSend (const std::shared_ptr &,const std::shared_ptr &,size_t,size_t);
void fileTransferProgressIndication (const std::shared_ptr &message, const std::shared_ptr &, size_t offset, size_t);
void msgStateChanged (const std::shared_ptr &message, linphone::ChatMessage::State state);
+ void newMessageReaction(const std::shared_ptr & message, const std::shared_ptr & reaction);
void participantImdnStateChanged(const std::shared_ptr & message, const std::shared_ptr & state);
void ephemeralMessageTimerStarted(const std::shared_ptr & message);
void ephemeralMessageDeleted(const std::shared_ptr & message);
diff --git a/linphone-app/src/components/chat-events/ChatMessageModel.cpp b/linphone-app/src/components/chat-events/ChatMessageModel.cpp
index 1f9a34636..2348b5740 100644
--- a/linphone-app/src/components/chat-events/ChatMessageModel.cpp
+++ b/linphone-app/src/components/chat-events/ChatMessageModel.cpp
@@ -38,6 +38,8 @@
#include "app/App.hpp"
#include "app/paths/Paths.hpp"
+#include "components/chat-reaction/ChatReactionModel.hpp"
+#include "components/chat-reaction/ChatReactionListModel.hpp"
#include "components/contact/ContactModel.hpp"
#include "components/contacts/ContactsListModel.hpp"
#include "components/content/ContentListModel.hpp"
@@ -61,6 +63,7 @@ void ChatMessageModel::connectTo(ChatMessageListener * listener){
connect(listener, &ChatMessageListener::fileTransferSend, this, &ChatMessageModel::onFileTransferSend);
connect(listener, &ChatMessageListener::fileTransferProgressIndication, this, &ChatMessageModel::onFileTransferProgressIndication);
connect(listener, &ChatMessageListener::msgStateChanged, this, &ChatMessageModel::onMsgStateChanged);
+ connect(listener, &ChatMessageListener::newMessageReaction, this, &ChatMessageModel::onNewMessageReaction);
connect(listener, &ChatMessageListener::participantImdnStateChanged, this, &ChatMessageModel::onParticipantImdnStateChanged);
connect(listener, &ChatMessageListener::ephemeralMessageTimerStarted, this, &ChatMessageModel::onEphemeralMessageTimerStarted);
connect(listener, &ChatMessageListener::ephemeralMessageDeleted, this, &ChatMessageModel::onEphemeralMessageDeleted);
@@ -95,6 +98,7 @@ ChatMessageModel::ChatMessageModel ( std::shared_ptr chat
mWasDownloaded = false;
mContentListModel = QSharedPointer::create(this);
+ mChatReactionListModel = QSharedPointer::create(this);
}
ChatMessageModel::~ChatMessageModel(){
@@ -188,6 +192,10 @@ QSharedPointer ChatMessageModel::getContents() const{
return mContentListModel;
}
+QSharedPointer ChatMessageModel::getChatReactions() const {
+ return mChatReactionListModel;
+}
+
bool ChatMessageModel::isReply() const{
return mChatMessage && mChatMessage->isReply();
}
@@ -247,6 +255,16 @@ void ChatMessageModel::resendMessage (){
}
}
+void ChatMessageModel::sendChatReaction(const QString& reaction){
+ auto chatReaction = mChatMessage->createReaction(Utils::appStringToCoreString(reaction));
+ if( mChatReactionListModel->exists(chatReaction)) {
+ chatReaction = mChatMessage->createReaction("");
+ return; // TODO : remove return when sending empty emoji will be supported.
+ }
+ chatReaction->send();
+ mChatReactionListModel->updateChatReaction(chatReaction);
+}
+
void ChatMessageModel::deleteEvent(){
if (mChatMessage && mChatMessage->getFileTransferInformation()) {
mChatMessage->cancelFileTransfer();
@@ -307,6 +325,11 @@ void ChatMessageModel::onMsgStateChanged (const std::shared_ptr & message, const std::shared_ptr & reaction){
+ mChatReactionListModel->updateChatReaction(reaction);
+}
+
void ChatMessageModel::onParticipantImdnStateChanged(const std::shared_ptr & message, const std::shared_ptr & state){
}
diff --git a/linphone-app/src/components/chat-events/ChatMessageModel.hpp b/linphone-app/src/components/chat-events/ChatMessageModel.hpp
index 88a61c1d0..bf51db599 100644
--- a/linphone-app/src/components/chat-events/ChatMessageModel.hpp
+++ b/linphone-app/src/components/chat-events/ChatMessageModel.hpp
@@ -33,6 +33,8 @@
class ChatMessageModel;
class ChatMessageListener;
+class ChatReactionModel;
+class ChatReactionListModel;
class ParticipantImdnStateProxyModel;
class ParticipantImdnStateListModel;
class ContentModel;
@@ -93,6 +95,7 @@ public:
Q_INVOKABLE ParticipantImdnStateProxyModel * getProxyImdnStates();
QSharedPointer getParticipantImdnStates() const;
QSharedPointer getContents() const;
+ QSharedPointer getChatReactions() const;
bool isReply() const;
ChatMessageModel * getReplyChatMessageModel() const;
@@ -110,6 +113,7 @@ public:
//----------------------------------------------------------------------------
Q_INVOKABLE void resendMessage ();
+ Q_INVOKABLE void sendChatReaction(const QString& reaction);
virtual void deleteEvent() override;
void updateFileTransferInformation();
@@ -121,6 +125,7 @@ public:
std::shared_ptr onFileTransferSend (const std::shared_ptr &,const std::shared_ptr &,size_t,size_t);
void onFileTransferProgressIndication (const std::shared_ptr &message, const std::shared_ptr &, size_t offset, size_t);
void onMsgStateChanged (const std::shared_ptr &message, linphone::ChatMessage::State state);
+ void onNewMessageReaction(const std::shared_ptr & message, const std::shared_ptr & reaction);
void onParticipantImdnStateChanged(const std::shared_ptr & message, const std::shared_ptr & state);
void onEphemeralMessageTimerStarted(const std::shared_ptr & message);
void onEphemeralMessageDeleted(const std::shared_ptr & message);
@@ -152,6 +157,7 @@ private:
QSharedPointer mFileTransfertContent;
QSharedPointer mParticipantImdnStateListModel;
QSharedPointer mReplyChatMessageModel;
+ QSharedPointer mChatReactionListModel;
QString mFromDisplayNameCache;
QString fromDisplayNameReplyMessage;
diff --git a/linphone-app/src/components/chat-reaction/ChatReactionListModel.cpp b/linphone-app/src/components/chat-reaction/ChatReactionListModel.cpp
new file mode 100644
index 000000000..e35d25e0d
--- /dev/null
+++ b/linphone-app/src/components/chat-reaction/ChatReactionListModel.cpp
@@ -0,0 +1,230 @@
+/*
+ * Copyright (c) 2010-2023 Belledonne Communications SARL.
+ *
+ * This file is part of linphone-desktop
+ * (see https://www.linphone.org).
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include "ChatReactionListModel.hpp"
+
+#include "ChatReactionModel.hpp"
+#include "components/chat-events/ChatMessageModel.hpp"
+#include "utils/Utils.hpp"
+
+// =============================================================================
+
+ChatReactionListModel::ChatReactionListModel (ChatMessageModel * message, QObject* parent) : ProxyAbstractListModel(parent) {
+ mParent = message;
+ setChatMessageModel(message);
+}
+
+void ChatReactionListModel::setChatMessageModel(ChatMessageModel * message) {
+ if(message){
+ auto reactions = message->getChatMessage()->getReactions();
+ mReactions.clear();
+ mBodies.clear();
+ for(auto reaction : reactions){
+ auto reactionModel = QSharedPointer::create(reaction);
+ auto body = reactionModel->getBody();
+ if(!body.isEmpty()) {
+ mReactions[reactionModel->getFromAddress()] = reactionModel;
+ mBodies[reactionModel->getBody()].push_back(reactionModel);
+ }
+ }
+ updateList();
+ }
+}
+
+int ChatReactionListModel::count(){
+ return mList.count();
+}
+
+int ChatReactionListModel::getChatReactionCount(const QString& emoji) const {
+ if(emoji.isEmpty())
+ return mReactions.size();
+ else if(mBodies.contains(emoji))
+ return mBodies[emoji].size();
+ else
+ return 0;
+}
+
+QSharedPointer ChatReactionListModel::add(std::shared_ptr reaction){
+ auto reactionModel = QSharedPointer::create(reaction);
+ //ProxyListModel::add(reactionModel);
+ emit chatReactionsChanged();
+ return reactionModel;
+}
+
+
+void ChatReactionListModel::remove(ChatReactionModel * model){/*
+ int count = 0;
+ for(auto it = mList.begin() ; it != mList.end() ; ++count, ++it) {
+ if( it->get() == model) {
+ removeRow(count, QModelIndex());
+ return;
+ }
+ }*/
+}
+
+void ChatReactionListModel::clear(){
+ resetData();
+}
+
+/*
+QSharedPointer ChatReactionListModel::getChatReactionModel(const std::shared_ptr& reaction){
+ for(auto item : mList){
+ auto c = item.objectCast();
+ if(c->get() == content)
+ return c;
+ }
+ if(content->isFileTransfer() || content->isFile() || content->isFileEncrypted()){
+ for(auto item : mList){// Content object can be different for file (like while data transfer)
+ auto c = item.objectCast();
+ if(c->getContent()->getFilePath() == content->getFilePath())
+ return c;
+ }
+ }
+ return nullptr;
+}
+*/
+void ChatReactionListModel::updateChatReaction(const std::shared_ptr& reaction) {
+ QString address = Utils::coreStringToAppString(reaction->getFromAddress()->asStringUriOnly());
+ auto itReaction = mReactions.find(address);
+ int oldReactionCount = mReactions.size();
+ if( itReaction == mReactions.end()) {// New
+ auto reactionModel = QSharedPointer::create(reaction);
+ auto body = reactionModel->getBody();
+ if(body.isEmpty()) {
+ mReactions.remove(reactionModel->getFromAddress());
+ // TODO: optimize remove
+ mBodies.clear();
+ for(auto it : mReactions)
+ mBodies[it->getBody()].push_back(it);
+ }else{
+ mReactions[reactionModel->getFromAddress()] = reactionModel;
+ mBodies[reactionModel->getBody()].push_back(reactionModel);
+ }
+ }else{// Update
+ (*itReaction)->setBody(Utils::coreStringToAppString(reaction->getBody()));
+ // TODO: optimize update with a swap
+ mBodies.clear();
+ for(auto it : mReactions)
+ mBodies[it->getBody()].push_back(it);
+ }
+ updateList();
+ if(oldReactionCount != mReactions.size())
+ emit chatReactionCountChanged();
+}
+void ChatReactionListModel::updateList(){
+ QList data;
+ if(mGroupBy == EMOJIES){
+ for(auto it = mBodies.begin() ; it != mBodies.end() ; ++it) {
+ QVariantMap emoji;
+ emoji["body"] = it.key();
+ emoji["reactionsCount"] = it->size();
+ data << emoji;
+ }
+ }else{
+ for(auto reaction : mReactions){
+ QVariantMap react;
+ react["reaction"] = QVariant::fromValue(reaction.get());
+ data << react;
+ }
+ }
+ resetData();
+ ProxyAbstractListModel::add(data);
+ emit chatReactionsChanged();
+}
+
+bool ChatReactionListModel::exists(std::shared_ptr reaction) const {
+ QString address = Utils::coreStringToAppString(reaction->getFromAddress()->asStringUriOnly());
+ auto itReaction = mReactions.find(address);
+ if(itReaction != mReactions.end())
+ return (*itReaction)->getBody() == Utils::coreStringToAppString(reaction->getBody());
+ return false;
+}
+
+void ChatReactionListModel::updateChatReaction(std::shared_ptr oldReaction, std::shared_ptr newReaction) {
+
+
+}
+void ChatReactionListModel::updateChatReaction(ChatMessageModel * messageModel) {
+
+}
+
+ChatReactionListModel::GROUP_BY_TYPE ChatReactionListModel::getGroupBy() const {
+ return mGroupBy;
+}
+
+void ChatReactionListModel::setGroupBy(ChatReactionListModel::GROUP_BY_TYPE mode) {
+ if( mGroupBy != mode ) {
+ mGroupBy = mode;
+ updateList();
+ emit groupByChanged();
+ }
+}
+
+
+/*
+
+void ContentListModel::updateContent(std::shared_ptr oldContent, std::shared_ptr newContent){
+ int row = 0;
+ for(auto content = mList.begin() ; content != mList.end() ; ++content, ++row){
+ auto contentModel = content->objectCast();
+ if( contentModel->getContent() == oldContent){
+ mList.replace(row, QSharedPointer::create(newContent, contentModel->getChatMessageModel()));
+ emit dataChanged(index(row,0), index(row,0));
+ return;
+ }
+ }
+}
+
+void ContentListModel::updateContents(ChatMessageModel * messageModel){
+ std::list> contents = messageModel->getChatMessage()->getContents() ;
+ int count = 0;
+ beginResetModel();
+ for(auto content : contents){
+ if( count >= mList.size()){// New content
+ mList.insert(count, QSharedPointer::create(content, messageModel));
+ }else if(mList.at(count).objectCast()->getContent() != content){ // This content is not at its place
+ int c = count + 1;
+ while( c < mList.size() && mList.at(c).objectCast()->getContent() != content)
+ ++c;
+ if( c < mList.size()){// Found => swap position
+#if QT_VERSION < QT_VERSION_CHECK(5, 13, 0)
+ mList.swap(count, c);
+#else
+ mList.swapItemsAt(count, c);
+#endif
+ }else{// content is new
+ mList.insert(count, QSharedPointer::create(content, messageModel));
+ }
+ }
+ ++count;
+ }
+ if(count < mList.size())// Remove all old contents
+ mList.erase(mList.begin()+count, mList.end());
+ endResetModel();
+}
+
+void ContentListModel::updateAllTransferData(){
+ emit updateTransferDataRequested();
+}
+
+void ContentListModel::downloaded(){
+ for(auto content : mList)
+ content.objectCast()->createThumbnail();
+}*/
diff --git a/linphone-app/src/components/chat-reaction/ChatReactionListModel.hpp b/linphone-app/src/components/chat-reaction/ChatReactionListModel.hpp
new file mode 100644
index 000000000..e94f28b4d
--- /dev/null
+++ b/linphone-app/src/components/chat-reaction/ChatReactionListModel.hpp
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2010-2023 Belledonne Communications SARL.
+ *
+ * This file is part of linphone-desktop
+ * (see https://www.linphone.org).
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#ifndef CHAT_REACTION_LIST_MODEL_H_
+#define CHAT_REACTION_LIST_MODEL_H_
+
+#include
+#include "app/proxyModel/ProxyAbstractListModel.hpp"
+#include
+
+class ChatReactionModel;
+class ChatMessageModel;
+
+class ChatReactionListModel : public ProxyAbstractListModel {
+ Q_OBJECT
+
+public:
+ typedef enum{
+ EMOJIES,
+ REACTIONS
+ }GROUP_BY_TYPE;
+ Q_ENUM(GROUP_BY_TYPE)
+
+ ChatReactionListModel (ChatMessageModel * message = nullptr, QObject * parent = nullptr);
+ void setChatMessageModel(ChatMessageModel * message);
+
+ int count();
+ int getChatReactionCount(const QString& emoji = "")const;
+
+ QSharedPointer add(std::shared_ptr reaction);
+ Q_INVOKABLE void remove(ChatReactionModel * model);
+
+ void clear();
+
+ ChatReactionListModel::GROUP_BY_TYPE getGroupBy() const;
+ void setGroupBy(ChatReactionListModel::GROUP_BY_TYPE mode);
+
+ //QSharedPointer getChatReactionModel(const std::shared_ptr& reaction);
+
+ bool exists(std::shared_ptr reaction) const;
+
+ void updateChatReaction(std::shared_ptr oldReaction, std::shared_ptr newReaction);
+ void updateChatReaction(const std::shared_ptr& reaction);
+ void updateChatReaction(ChatMessageModel * messageModel);
+
+ void updateList();
+signals:
+ void chatReactionsChanged();
+ void chatReactionCountChanged();
+ void groupByChanged();
+
+private:
+ ChatMessageModel * mParent;
+ QMap> mReactions;
+ QMap>> mBodies;
+ GROUP_BY_TYPE mGroupBy = EMOJIES;
+
+};
+
+Q_DECLARE_METATYPE(std::shared_ptr)
+
+#endif
diff --git a/linphone-app/src/components/chat-reaction/ChatReactionModel.cpp b/linphone-app/src/components/chat-reaction/ChatReactionModel.cpp
new file mode 100644
index 000000000..6d6150e33
--- /dev/null
+++ b/linphone-app/src/components/chat-reaction/ChatReactionModel.cpp
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2010-2023 Belledonne Communications SARL.
+ *
+ * This file is part of linphone-desktop
+ * (see https://www.linphone.org).
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include "ChatReactionModel.hpp"
+
+#include "app/App.hpp"
+#include "utils/Utils.hpp"
+
+#include
+
+ChatReactionModel::ChatReactionModel(const std::shared_ptr& reaction) {
+ App::getInstance()->getEngine()->setObjectOwnership(this, QQmlEngine::CppOwnership);// Avoid QML to destroy it when passing by Q_INVOKABLE
+ mBody = Utils::coreStringToAppString(reaction->getBody());
+ mFromAddress = Utils::coreStringToAppString(reaction->getFromAddress()->asStringUriOnly());
+}
+
+QString ChatReactionModel::getBody() const {
+ return mBody;
+}
+
+void ChatReactionModel::setBody(const QString& body) {
+ mBody = body;
+ emit bodyChanged();
+}
+
+QString ChatReactionModel::getFromAddress() const {
+ return mFromAddress;
+}
diff --git a/linphone-app/src/components/chat-reaction/ChatReactionModel.hpp b/linphone-app/src/components/chat-reaction/ChatReactionModel.hpp
new file mode 100644
index 000000000..4135446e4
--- /dev/null
+++ b/linphone-app/src/components/chat-reaction/ChatReactionModel.hpp
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2010-2023 Belledonne Communications SARL.
+ *
+ * This file is part of linphone-desktop
+ * (see https://www.linphone.org).
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#ifndef CHAT_REACTION_MODEL_H_
+#define CHAT_REACTION_MODEL_H_
+
+#include
+#include
+#include
+
+class ChatReactionModel : public QObject {
+
+ Q_OBJECT
+
+public:
+ ChatReactionModel(const std::shared_ptr& reaction);
+
+ Q_PROPERTY(QString body READ getBody WRITE setBody NOTIFY bodyChanged)
+ Q_PROPERTY(QString fromAddress READ getFromAddress CONSTANT)
+
+ QString getBody() const;
+ void setBody(const QString& body);
+
+ QString getFromAddress() const;
+
+signals:
+ void bodyChanged();
+
+private:
+ QString mBody;
+ QString mFromAddress;
+};
+
+Q_DECLARE_METATYPE(QSharedPointer)
+
+#endif
diff --git a/linphone-app/src/components/chat-reaction/ChatReactionProxyModel.cpp b/linphone-app/src/components/chat-reaction/ChatReactionProxyModel.cpp
new file mode 100644
index 000000000..f001f09f7
--- /dev/null
+++ b/linphone-app/src/components/chat-reaction/ChatReactionProxyModel.cpp
@@ -0,0 +1,167 @@
+/*
+ * Copyright (c) 2010-2023 Belledonne Communications SARL.
+ *
+ * This file is part of linphone-desktop
+ * (see https://www.linphone.org).
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#include "ChatReactionProxyModel.hpp"
+
+#include "ChatReactionListModel.hpp"
+#include "ChatReactionModel.hpp"
+
+// =============================================================================
+
+ChatReactionProxyModel::ChatReactionProxyModel (QObject * parent) : SortFilterProxyModel(parent){
+ mContents = QSharedPointer::create();
+ connect(mContents.get(), &ChatReactionListModel::chatReactionCountChanged, this, &ChatReactionProxyModel::chatReactionCountChanged);
+ connect(mContents.get(), &ChatReactionListModel::groupByChanged, this, &ChatReactionProxyModel::groupByChanged);
+ setSourceModel(mContents.get());
+ sort(0);
+}
+
+ChatMessageModel * ChatReactionProxyModel::getChatMessageModel() const{
+ return nullptr;
+}
+
+void ChatReactionProxyModel::setChatMessageModel(ChatMessageModel * message){
+ setChatMessageModel(message, ChatReactionListModel::GROUP_BY_TYPE::EMOJIES);
+}
+
+void ChatReactionProxyModel::setChatMessageModel(ChatMessageModel * message, ChatReactionListModel::GROUP_BY_TYPE groupByMode) {
+ if(message){
+ auto model = qobject_cast(sourceModel());
+ model->setChatMessageModel(message);
+ model->setGroupBy(groupByMode);
+ }
+ emit chatMessageModelChanged();
+ emit chatReactionCountChanged();
+}
+
+int ChatReactionProxyModel::getChatReactionCount() const {
+ auto model = qobject_cast(sourceModel());
+ if(model)
+ return model->getChatReactionCount();
+ else
+ return 0;
+}
+
+int ChatReactionProxyModel::getChatReactionCount(const QString& emoji) const {
+ auto model = qobject_cast(sourceModel());
+ if(model)
+ return model->getChatReactionCount(emoji);
+ else
+ return 0;
+}
+
+ChatReactionListModel::GROUP_BY_TYPE ChatReactionProxyModel::getGroupBy() const {
+ auto model = qobject_cast(sourceModel());
+ return model ? model->getGroupBy() : ChatReactionListModel::GROUP_BY_TYPE::EMOJIES;
+}
+
+void ChatReactionProxyModel::setGroupBy(ChatReactionListModel::GROUP_BY_TYPE mode) {
+ auto model = qobject_cast(sourceModel());
+ if(model)
+ model->setGroupBy(mode);
+}
+
+QString ChatReactionProxyModel::getFilter() const {
+ return mFilter;
+}
+
+void ChatReactionProxyModel::setFilter(const QString& filter) {
+ if(mFilter != filter) {
+ mFilter = filter;
+ emit filterChanged();
+ invalidate();
+ }
+}
+
+/*
+void ChatReactionProxyModel::setContentListModel(ContentListModel * model){
+ setSourceModel(model);
+ sort(0);
+ emit chatMessageModelChanged();
+}
+*/
+
+bool ChatReactionProxyModel::filterAcceptsRow (
+ int sourceRow,
+ const QModelIndex &sourceParent
+ ) const {
+
+ bool show = false;
+
+ if (mFilter.isEmpty())
+ show = true;
+ else{
+ auto model = qobject_cast(sourceModel());
+ QModelIndex index = sourceModel()->index(sourceRow, 0, QModelIndex());
+ auto reaction = sourceModel()->data(index).value();
+
+ if( model->getGroupBy() == ChatReactionListModel::GROUP_BY_TYPE::REACTIONS) {
+ if( mFilter == reaction["reaction"].value()->getBody())
+ show = true;
+ }
+ }
+ return show;
+}
+
+/*
+bool ChatReactionProxyModel::lessThan (const QModelIndex &left, const QModelIndex &right) const {
+ const ContentModel *contentA = sourceModel()->data(left).value();
+ const ContentModel *contentB = sourceModel()->data(right).value();
+ bool aIsForward = contentA->getChatMessageModel() && contentA->getChatMessageModel()->isForward();
+ bool aIsReply = contentA->getChatMessageModel() && contentA->getChatMessageModel()->isReply();
+ bool aIsVoiceRecording = contentA->isVoiceRecording();
+ bool aIsFile = contentA->isFile() || contentA->isFileEncrypted() || contentA->isFileTransfer();
+ bool aIsText = contentA->isText() ;
+ bool bIsForward = contentB->getChatMessageModel() && contentB->getChatMessageModel()->isForward();
+ bool bIsReply = contentB->getChatMessageModel() && contentB->getChatMessageModel()->isReply();
+ bool bIsVoiceRecording = contentB->isVoiceRecording();
+ bool bIsFile = contentB->isFile() || contentB->isFileEncrypted() || contentB->isFileTransfer();
+ bool bIsText = contentB->isText() ;
+
+ return !bIsForward && (aIsForward
+ || !bIsReply && (aIsReply
+ || !bIsVoiceRecording && (aIsVoiceRecording
+ || !bIsFile && (aIsFile
+ || aIsText && !bIsText
+ )
+ )
+ )
+ );
+}
+*/
+/*
+void ChatReactionProxyModel::remove(ContentModel * model){
+ qobject_cast(sourceModel())->remove(model);
+}
+
+void ChatReactionProxyModel::clear(){
+ qobject_cast(sourceModel())->clear();
+}
+
+ContentProxyModel::FilterContentType ChatReactionProxyModel::getFilter() const{
+ return mFilter;
+}
+void ChatReactionProxyModel::setFilter(const FilterContentType& contentType){
+ if(contentType != mFilter){
+ mFilter = contentType;
+ emit filterChanged();
+ invalidate();
+ }
+}*/
\ No newline at end of file
diff --git a/linphone-app/src/components/chat-reaction/ChatReactionProxyModel.hpp b/linphone-app/src/components/chat-reaction/ChatReactionProxyModel.hpp
new file mode 100644
index 000000000..76e3a001b
--- /dev/null
+++ b/linphone-app/src/components/chat-reaction/ChatReactionProxyModel.hpp
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2010-2023 Belledonne Communications SARL.
+ *
+ * This file is part of linphone-desktop
+ * (see https://www.linphone.org).
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+#ifndef CHAT_REACTION_PROXY_MODEL_H_
+#define CHAT_REACTION_PROXY_MODEL_H_
+
+#include "app/proxyModel/SortFilterProxyModel.hpp"
+#include "components/chat-events/ChatMessageModel.hpp"
+#include "ChatReactionListModel.hpp"
+
+// =============================================================================
+
+class ChatReactionProxyModel : public SortFilterProxyModel {
+ Q_OBJECT
+
+public:
+ ChatReactionProxyModel (QObject *parent = nullptr);
+ Q_PROPERTY(ChatMessageModel * chatMessageModel READ getChatMessageModel WRITE setChatMessageModel NOTIFY chatMessageModelChanged)
+ Q_PROPERTY(int reactionCount READ getChatReactionCount NOTIFY chatReactionCountChanged)
+ Q_PROPERTY(ChatReactionListModel::GROUP_BY_TYPE groupBy READ getGroupBy WRITE setGroupBy NOTIFY groupByChanged)
+ Q_PROPERTY(QString filter READ getFilter WRITE setFilter NOTIFY filterChanged)
+ /*
+ Q_PROPERTY(FilterContentType filter READ getFilter WRITE setFilter NOTIFY filterChanged)
+
+ enum FilterContentType {
+ All,
+ File,
+ Text,
+ Voice,
+ Conference,
+ Unknown
+ };
+ Q_ENUM(FilterContentType)
+ */
+ ChatMessageModel * getChatMessageModel() const;
+ void setChatMessageModel(ChatMessageModel * message);
+ Q_INVOKABLE void setChatMessageModel(ChatMessageModel * message, ChatReactionListModel::GROUP_BY_TYPE groupByMode);
+
+ int getChatReactionCount() const;
+ Q_INVOKABLE int getChatReactionCount(const QString& emoji) const;
+
+ ChatReactionListModel::GROUP_BY_TYPE getGroupBy() const;
+ void setGroupBy(ChatReactionListModel::GROUP_BY_TYPE mode);
+
+ QString getFilter() const;
+ void setFilter(const QString& filter);
+ //Q_INVOKABLE void setContentListModel(ContentListModel * model);
+ //Q_INVOKABLE void addFile(const QString& path);
+ //Q_INVOKABLE void remove(ContentModel * model);
+ //Q_INVOKABLE void clear();
+
+signals:
+ void chatMessageModelChanged();
+ void chatReactionCountChanged();
+ void groupByChanged();
+ void filterChanged();
+
+
+protected:
+ QSharedPointer mContents;
+ virtual bool filterAcceptsRow (int sourceRow, const QModelIndex &sourceParent) const override;
+ //virtual bool lessThan (const QModelIndex &left, const QModelIndex &right) const override;
+
+ //std::shared_ptr mContents;
+ QString mFilter = "";
+};
+
+
+#endif
diff --git a/linphone-app/src/components/chat-room/ChatRoomModel.cpp b/linphone-app/src/components/chat-room/ChatRoomModel.cpp
index 09d8163f0..46b4c02ee 100644
--- a/linphone-app/src/components/chat-room/ChatRoomModel.cpp
+++ b/linphone-app/src/components/chat-room/ChatRoomModel.cpp
@@ -867,24 +867,39 @@ void ChatRoomModel::updateNewMessageNotice(const int& count){
int ChatRoomModel::loadTillMessage(ChatMessageModel * message){
if( message){
- qDebug() << "Load history till message : " << message->getChatMessage()->getMessageId().c_str();
auto linphoneMessage = message->getChatMessage();
+ return loadTillMessage(message);
+ }else
+ return -1;
+}
+
+int ChatRoomModel::loadTillMessage(std::shared_ptr linphoneMessage){
+ if(linphoneMessage)
+ return loadTillMessageId(Utils::coreStringToAppString(linphoneMessage->getMessageId()));
+ else
+ return -1;
+}
+
+int ChatRoomModel::loadTillMessageId(const QString& messageId){
+ if(!messageId.isEmpty()){
+ std::string lMessageId = Utils::appStringToCoreString(messageId);
+ qDebug() << "Load history till message : " << messageId;
// First find on current list
- auto entry = std::find_if(mList.begin(), mList.end(), [linphoneMessage](const QSharedPointer& entry ){
+ auto entry = std::find_if(mList.begin(), mList.end(), [lMessageId](const QSharedPointer& entry ){
auto chatEventEntry = entry.objectCast();
- return chatEventEntry->mType == ChatRoomModel::EntryType::MessageEntry && chatEventEntry.objectCast()->getChatMessage() == linphoneMessage;
+ return chatEventEntry->mType == ChatRoomModel::EntryType::MessageEntry && chatEventEntry.objectCast()->getChatMessage()->getMessageId() == lMessageId;
});
// if not find, load more entries and find it in new entries.
if( entry == mList.end()){
mPostModelChangedEvents = false;
beginResetModel();
int newEntries = loadMoreEntries();
- while( newEntries > 0){// no more new entries
+ while( newEntries > 0){// at 0 = no more new entries
int entryCount = 0;
entry = mList.begin();
auto chatEventEntry = entry->objectCast();
while(entryCount < newEntries &&
- (chatEventEntry->mType != ChatRoomModel::EntryType::MessageEntry || chatEventEntry.objectCast()->getChatMessage() != linphoneMessage)
+ (chatEventEntry->mType != ChatRoomModel::EntryType::MessageEntry || chatEventEntry.objectCast()->getChatMessage()->getMessageId() != lMessageId)
){
++entryCount;
++entry;
@@ -892,36 +907,43 @@ int ChatRoomModel::loadTillMessage(ChatMessageModel * message){
chatEventEntry = entry->objectCast();
}
if( entryCount < newEntries){// We got it
- qDebug() << "Find message at " << entryCount << " after loading new entries";
+ qDebug() << "Find message at " << entryCount << " after loading new entries " << mEntriesLoading;
mPostModelChangedEvents = true;
endResetModel();
+ emit tillMessagesLoaded(entryCount);
return entryCount;
}else
newEntries = loadMoreEntries();// continue
}
mPostModelChangedEvents = true;
endResetModel();
+ emit tillMessagesLoaded(newEntries);
}else{
int entryCount = entry - mList.begin();
qDebug() << "Find message at " << entryCount;
+ emit tillMessagesLoaded(entryCount);
return entryCount;
}
- qWarning() << "Message has not been found in history";
}
+ qWarning() << "Message has not been found in history";
return -1;
}
+QSharedPointer ChatRoomModel::getChatMessageModel(const std::shared_ptr message) const {
+ auto entry = std::find_if(mList.begin(), mList.end(), [message](const QSharedPointer& entry ){
+ auto chatEventEntry = entry.objectCast();
+ return chatEventEntry->mType == ChatRoomModel::EntryType::MessageEntry && chatEventEntry.objectCast()->getChatMessage() == message;
+ });
+ return entry != mList.end() ? entry->objectCast(): nullptr;
+}
+
bool ChatRoomModel::isTerminated(const std::shared_ptr& chatRoom){
return chatRoom->getState() == linphone::ChatRoom::State::Terminated || chatRoom->getState() == linphone::ChatRoom::State::Deleted;
}
bool ChatRoomModel::exists(const std::shared_ptr message) const{
- auto entry = std::find_if(mList.begin(), mList.end(), [message](const QSharedPointer& entry ){
- auto chatEventEntry = entry.objectCast();
- return chatEventEntry->mType == ChatRoomModel::EntryType::MessageEntry && chatEventEntry.objectCast()->getChatMessage() == message;
- });
// if not find, load more entries and find it in new entries.
- return entry != mList.end();
+ return getChatMessageModel(message) != nullptr;
}
void ChatRoomModel::addBindingCall(){ // If a call is binding to this chat room, we avoid cleaning data (Add=+1, remove=-1)
diff --git a/linphone-app/src/components/chat-room/ChatRoomModel.hpp b/linphone-app/src/components/chat-room/ChatRoomModel.hpp
index c24f0a96c..7b8286b8f 100644
--- a/linphone-app/src/components/chat-room/ChatRoomModel.hpp
+++ b/linphone-app/src/components/chat-room/ChatRoomModel.hpp
@@ -184,7 +184,10 @@ public:
Q_INVOKABLE int loadMoreEntries(); // return new entries count
void onCallEnded(std::shared_ptr call);
void updateNewMessageNotice(const int& count);
+ QSharedPointer getChatMessageModel(const std::shared_ptr message) const;
Q_INVOKABLE int loadTillMessage(ChatMessageModel * message);// Load all entries till message and return its index. -1 if not found.
+ Q_INVOKABLE int loadTillMessageId(const QString& messageId);
+ int loadTillMessage(std::shared_ptr linphoneMessage);
static bool isTerminated(const std::shared_ptr& chatRoom);
bool exists(const std::shared_ptr message) const;
@@ -256,9 +259,11 @@ signals:
bool isRemoteComposingChanged ();
void entriesLoadingChanged(const bool& loading);
void moreEntriesLoaded(const int& count);
+ void tillMessagesLoaded(int messageIndex);
void allEntriesRemoved (QSharedPointer model);
void lastEntryRemoved ();
+ void displayMessageIdRequested(const QString& messageId);
void messageSent (const std::shared_ptr &message);
void messageReceived (const std::shared_ptr &message);
diff --git a/linphone-app/src/components/chat-room/ChatRoomProxyModel.cpp b/linphone-app/src/components/chat-room/ChatRoomProxyModel.cpp
index 47c7b8c5e..602961b7d 100644
--- a/linphone-app/src/components/chat-room/ChatRoomProxyModel.cpp
+++ b/linphone-app/src/components/chat-room/ChatRoomProxyModel.cpp
@@ -126,6 +126,11 @@ void ChatRoomProxyModel::loadMoreEntriesAsync(){
void ChatRoomProxyModel::onMoreEntriesLoaded(const int& count){
emit moreEntriesLoaded(count);
}
+
+void ChatRoomProxyModel::onTillMessagesLoaded(const int& index){
+ int messageIndex = mapFromSource(static_cast(sourceModel())->index(index, 0)).row();
+ emit moreEntriesLoaded(messageIndex);
+}
void ChatRoomProxyModel::loadMoreEntries() {
if(mChatRoomModel ) {
mChatRoomModel->loadMoreEntries();
@@ -283,7 +288,9 @@ void ChatRoomProxyModel::reload (ChatRoomModel *chatRoomModel) {
QObject::disconnect(ChatRoomModel, &ChatRoomModel::messageSent, this, &ChatRoomProxyModel::handleMessageSent);
QObject::disconnect(ChatRoomModel, &ChatRoomModel::markAsReadEnabledChanged, this, &ChatRoomProxyModel::markAsReadEnabledChanged);
QObject::disconnect(ChatRoomModel, &ChatRoomModel::moreEntriesLoaded, this, &ChatRoomProxyModel::onMoreEntriesLoaded);
+ QObject::disconnect(ChatRoomModel, &ChatRoomModel::tillMessagesLoaded, this, &ChatRoomProxyModel::onTillMessagesLoaded);
QObject::disconnect(ChatRoomModel, &ChatRoomModel::chatRoomDeleted, this, &ChatRoomProxyModel::chatRoomDeleted);
+ QObject::disconnect(ChatRoomModel, &ChatRoomModel::displayMessageIdRequested, this, &ChatRoomProxyModel::displayMessageIdRequested);
if(mIsCall)
mChatRoomModel->removeBindingCall();
}
@@ -300,7 +307,9 @@ void ChatRoomProxyModel::reload (ChatRoomModel *chatRoomModel) {
QObject::connect(ChatRoomModel, &ChatRoomModel::messageSent, this, &ChatRoomProxyModel::handleMessageSent);
QObject::connect(ChatRoomModel, &ChatRoomModel::markAsReadEnabledChanged, this, &ChatRoomProxyModel::markAsReadEnabledChanged);
QObject::connect(ChatRoomModel, &ChatRoomModel::moreEntriesLoaded, this, &ChatRoomProxyModel::onMoreEntriesLoaded);
+ QObject::connect(ChatRoomModel, &ChatRoomModel::tillMessagesLoaded, this, &ChatRoomProxyModel::onTillMessagesLoaded);
QObject::connect(ChatRoomModel, &ChatRoomModel::chatRoomDeleted, this, &ChatRoomProxyModel::chatRoomDeleted);
+ QObject::connect(ChatRoomModel, &ChatRoomModel::displayMessageIdRequested, this, &ChatRoomProxyModel::displayMessageIdRequested);
mChatRoomModel->initEntries();// This way, we don't load huge chat rooms (that lead to freeze GUI)
}
setSourceModel(mChatRoomModel.get());
@@ -336,9 +345,17 @@ int ChatRoomProxyModel::loadTillMessage(ChatMessageModel * message){
return messageIndex;
}
+int ChatRoomProxyModel::loadTillMessageId(const QString& messageId){
+ int messageIndex = mChatRoomModel->loadTillMessageId(messageId);
+ if( messageIndex>= 0 ) {
+ messageIndex = mapFromSource(static_cast(sourceModel())->index(messageIndex, 0)).row();
+ }
+ qDebug() << "Message index from chat room proxy : " << messageIndex;
+ return messageIndex;
+}
+
ChatRoomModel *ChatRoomProxyModel::getChatRoomModel () const{
return mChatRoomModel.get();
-
}
void ChatRoomProxyModel::setChatRoomModel (ChatRoomModel *chatRoomModel){
diff --git a/linphone-app/src/components/chat-room/ChatRoomProxyModel.hpp b/linphone-app/src/components/chat-room/ChatRoomProxyModel.hpp
index d5e3f647a..7fe6f9c59 100644
--- a/linphone-app/src/components/chat-room/ChatRoomProxyModel.hpp
+++ b/linphone-app/src/components/chat-room/ChatRoomProxyModel.hpp
@@ -74,9 +74,11 @@ public:
Q_INVOKABLE void resetMessageCount();
Q_INVOKABLE int loadTillMessage(ChatMessageModel * message);// Load all entries till message and return its index in displayed list (-1 if not found)
+ Q_INVOKABLE int loadTillMessageId(const QString& messageId);
public slots:
void onMoreEntriesLoaded(const int& count);
+ void onTillMessagesLoaded(const int& messageIndex);
signals:
void peerAddressChanged (const QString &peerAddress);
@@ -91,6 +93,8 @@ signals:
void chatRoomDeleted();
void moreEntriesLoaded (int n);
+ void tillMessagesLoaded(int messageIndex);
+ void displayMessageIdRequested(const QString& messageId);
void entryTypeFilterChanged (int type);
void filterTextChanged();
diff --git a/linphone-app/src/components/core/CoreHandlers.cpp b/linphone-app/src/components/core/CoreHandlers.cpp
index 14068247b..357a68a42 100644
--- a/linphone-app/src/components/core/CoreHandlers.cpp
+++ b/linphone-app/src/components/core/CoreHandlers.cpp
@@ -62,6 +62,7 @@ void CoreHandlers::connectTo(CoreListener * listener){
connect(listener, &CoreListener::logCollectionUploadProgressIndication, this, &CoreHandlers::onLogCollectionUploadProgressIndication);
connect(listener, &CoreListener::messageReceived, this, &CoreHandlers::onMessageReceived);
connect(listener, &CoreListener::messagesReceived, this, &CoreHandlers::onMessagesReceived);
+ connect(listener, &CoreListener::newMessageReaction, this, &CoreHandlers::onNewMessageReaction);
connect(listener, &CoreListener::notifyPresenceReceivedForUriOrTel, this, &CoreHandlers::onNotifyPresenceReceivedForUriOrTel);
connect(listener, &CoreListener::notifyPresenceReceived, this, &CoreHandlers::onNotifyPresenceReceived);
connect(listener, &CoreListener::qrcodeFound, this, &CoreHandlers::onQrcodeFound);
@@ -318,6 +319,56 @@ void CoreHandlers::onMessagesReceived (
}
}
+void CoreHandlers::onNewMessageReaction(const std::shared_ptr & core, const std::shared_ptr & chatRoom, const std::shared_ptr & message, const std::shared_ptr & reaction){
+ QList, std::shared_ptr >> reactionsToNotify;
+ CoreManager *coreManager = CoreManager::getInstance();
+ SettingsModel *settingsModel = coreManager->getSettingsModel();
+ const App *app = App::getInstance();
+ QStringList notNotifyReasons;
+ QSettings appSettings;
+
+ appSettings.beginGroup("chatrooms");
+
+ if( !message || CoreManager::getInstance()->getAccountSettingsModel()->findAccount(reaction->getFromAddress()))
+ return;
+ // 1. Do not notify if chat is not activated.
+ if (chatRoom->getCurrentParams()->getEncryptionBackend() == linphone::ChatRoom::EncryptionBackend::None && !settingsModel->getStandardChatEnabled()
+ || chatRoom->getCurrentParams()->getEncryptionBackend() != linphone::ChatRoom::EncryptionBackend::None && !settingsModel->getSecureChatEnabled())
+ return;
+
+ // 2. Do not notify if the chatroom's notification has been deactivated.
+ appSettings.beginGroup(ChatRoomModel::getChatRoomId(chatRoom));
+ if(!appSettings.value("notifications", true).toBool()){
+ appSettings.endGroup();
+ return;
+ }else{
+ appSettings.endGroup();
+ }
+ // 3. Notify with Notification popup.
+ if (coreManager->getSettingsModel()->getChatNotificationsEnabled()
+ && (!app->hasFocus() || !Utils::isMe(chatRoom->getLocalAddress())))
+ reactionsToNotify.push_back({message, reaction});
+ else{
+ notNotifyReasons.push_back(
+ "NotifEnabled=" + QString::number(coreManager->getSettingsModel()->getChatNotificationsEnabled())
+ +" focus=" +QString::number(app->hasFocus())
+ +" isMe=" +QString::number(Utils::isMe(chatRoom->getLocalAddress()))
+ );
+ }
+ if( reactionsToNotify.size() > 0)
+ app->getNotifier()->notifyReceivedReactions(reactionsToNotify);
+ else if( notNotifyReasons.size() > 0)
+ qInfo() << "Notification received but was not selected to popup. Reasons : \n" << notNotifyReasons.join("\n");
+ // 3. Notify with sound.
+ if( reactionsToNotify.size() > 0) {
+ if (!coreManager->getSettingsModel()->getChatNotificationsEnabled() || !settingsModel->getChatNotificationSoundEnabled())
+ return;
+
+ if ( !app->hasFocus() || !CoreManager::getInstance()->getTimelineListModel()->getChatRoomModel(chatRoom, false) )
+ core->playLocal(Utils::appStringToCoreString(settingsModel->getChatNotificationSoundPath()));
+ }
+}
+
void CoreHandlers::onNotifyPresenceReceivedForUriOrTel (
const shared_ptr &,
const shared_ptr &,
diff --git a/linphone-app/src/components/core/CoreHandlers.hpp b/linphone-app/src/components/core/CoreHandlers.hpp
index ca131d689..90f3be1d0 100644
--- a/linphone-app/src/components/core/CoreHandlers.hpp
+++ b/linphone-app/src/components/core/CoreHandlers.hpp
@@ -53,6 +53,7 @@ signals:
void isComposingChanged (const std::shared_ptr &chatRoom);
void logsUploadStateChanged (linphone::Core::LogCollectionUploadState state, const std::string &info);
void messagesReceived (const std::list> &messages);
+ void newMessageReaction(const std::shared_ptr & chatRoom, const std::shared_ptr & message, const std::shared_ptr & reaction);
void presenceReceived (const QString &sipAddress, const std::shared_ptr &presenceModel);
void presenceStatusReceived(std::shared_ptr contact);
void registrationStateChanged (const std::shared_ptr &account, linphone::RegistrationState state);
@@ -82,6 +83,7 @@ public slots:
void onLogCollectionUploadProgressIndication (const std::shared_ptr &lc,size_t offset,size_t total);
void onMessageReceived (const std::shared_ptr &core,const std::shared_ptr &room,const std::shared_ptr &message);
void onMessagesReceived (const std::shared_ptr &core,const std::shared_ptr &room,const std::list> &messages);
+ void onNewMessageReaction(const std::shared_ptr & core, const std::shared_ptr & chatRoom, const std::shared_ptr & message, const std::shared_ptr & reaction);
void onNotifyPresenceReceivedForUriOrTel (const std::shared_ptr &core,const std::shared_ptr &linphoneFriend,const std::string &uriOrTel,const std::shared_ptr &presenceModel);
void onNotifyPresenceReceived (const std::shared_ptr &core,const std::shared_ptr &linphoneFriend);
void onQrcodeFound(const std::shared_ptr & core, const std::string & result);
diff --git a/linphone-app/src/components/core/CoreListener.cpp b/linphone-app/src/components/core/CoreListener.cpp
index 5a7154635..87ad06770 100644
--- a/linphone-app/src/components/core/CoreListener.cpp
+++ b/linphone-app/src/components/core/CoreListener.cpp
@@ -81,6 +81,9 @@ void CoreListener::onMessageReceived (const std::shared_ptr &cor
void CoreListener::onMessagesReceived (const std::shared_ptr &core,const std::shared_ptr &room,const std::list> &messages){
emit messagesReceived (core,room,messages);
}
+void CoreListener::onNewMessageReaction(const std::shared_ptr & core, const std::shared_ptr & chatRoom, const std::shared_ptr & message, const std::shared_ptr & reaction){
+ emit newMessageReaction (core,chatRoom,message, reaction);
+}
void CoreListener::onNotifyPresenceReceivedForUriOrTel (const std::shared_ptr &core,const std::shared_ptr &linphoneFriend,const std::string &uriOrTel,const std::shared_ptr &presenceModel){
emit notifyPresenceReceivedForUriOrTel (core,linphoneFriend,uriOrTel,presenceModel);
}
diff --git a/linphone-app/src/components/core/CoreListener.hpp b/linphone-app/src/components/core/CoreListener.hpp
index 6bc8c6db4..705c17c4a 100644
--- a/linphone-app/src/components/core/CoreListener.hpp
+++ b/linphone-app/src/components/core/CoreListener.hpp
@@ -52,6 +52,7 @@ public:
virtual void onLogCollectionUploadProgressIndication (const std::shared_ptr &lc,size_t offset,size_t total) override;
virtual void onMessageReceived (const std::shared_ptr &core,const std::shared_ptr &room,const std::shared_ptr &message) override;
virtual void onMessagesReceived (const std::shared_ptr &core,const std::shared_ptr &room,const std::list> &messages) override;
+ virtual void onNewMessageReaction(const std::shared_ptr & core, const std::shared_ptr & chatRoom, const std::shared_ptr & message, const std::shared_ptr & reaction) override;
virtual void onNotifyPresenceReceivedForUriOrTel (const std::shared_ptr &core,const std::shared_ptr &linphoneFriend,const std::string &uriOrTel,const std::shared_ptr &presenceModel) override;
virtual void onNotifyPresenceReceived (const std::shared_ptr &core,const std::shared_ptr &linphoneFriend) override;
virtual void onQrcodeFound(const std::shared_ptr & core, const std::string & result) override;
@@ -78,6 +79,7 @@ signals:
void logCollectionUploadProgressIndication (const std::shared_ptr &lc,size_t offset,size_t total);
void messageReceived (const std::shared_ptr &core,const std::shared_ptr &room,const std::shared_ptr &message);
void messagesReceived (const std::shared_ptr &core,const std::shared_ptr &room,const std::list> &messages);
+ void newMessageReaction(const std::shared_ptr & core, const std::shared_ptr & chatRoom, const std::shared_ptr & message, const std::shared_ptr & reaction);
void notifyPresenceReceivedForUriOrTel (const std::shared_ptr &core,const std::shared_ptr &linphoneFriend,const std::string &uriOrTel,const std::shared_ptr &presenceModel);
void notifyPresenceReceived (const std::shared_ptr &core,const std::shared_ptr &linphoneFriend);
void qrcodeFound(const std::shared_ptr & core, const std::string & result);
diff --git a/linphone-app/src/components/notifier/Notifier.cpp b/linphone-app/src/components/notifier/Notifier.cpp
index ab8856624..7937f7dfa 100644
--- a/linphone-app/src/components/notifier/Notifier.cpp
+++ b/linphone-app/src/components/notifier/Notifier.cpp
@@ -28,6 +28,7 @@
#include "app/App.hpp"
#include "components/call/CallModel.hpp"
+#include "components/chat-events/ChatMessageModel.hpp"
#include "components/core/CoreManager.hpp"
#include "components/timeline/TimelineModel.hpp"
#include "components/timeline/TimelineListModel.hpp"
@@ -312,6 +313,49 @@ void Notifier::notifyReceivedMessages (const list, std::shared_ptr>> &reactions) {
+ QVariantMap map;
+ QString txt;
+
+ if( reactions.size() > 0){
+ ChatMessageModel *redirection = nullptr;
+ QPair,std::shared_ptr> reaction = reactions.front();
+ shared_ptr message = reaction.first;
+ shared_ptr chatRoom(message->getChatRoom());
+ auto timelineModel = CoreManager::getInstance()->getTimelineListModel()->getTimeline(chatRoom, true);
+ map["messageId"] = Utils::coreStringToAppString(message->getMessageId());
+ if( reactions.size() == 1){
+ QString messageTxt;
+ auto fileContent = message->getFileTransferInformation();
+ if(!fileContent ){
+ foreach(auto content, message->getContents()){
+ if(content->isText())
+ messageTxt += content->getUtf8Text().c_str();
+ }
+ }else if( fileContent->isVoiceRecording())
+ messageTxt += "Voice message";
+ else
+ messageTxt += "File";
+ if(messageTxt.isEmpty() && message->hasConferenceInvitationContent())
+ messageTxt += "Conference invitation";
+ txt = QString("Has reacted by %2 to: %3").arg(Utils::coreStringToAppString(reaction.second->getBody())).arg(messageTxt);
+
+ }else
+ txt = "New message reactions received";
+ map["message"] = txt;
+
+ map["timelineModel"].setValue(timelineModel.get());
+ if( reactions.size() == 1) {// Display only sender on mono message.
+ map["peerAddress"] = Utils::coreStringToAppString(reaction.second->getFromAddress()->asStringUriOnly());
+ map["fullPeerAddress"] = Utils::coreStringToAppString(reaction.second->getFromAddress()->asString());
+ }
+ map["localAddress"] = Utils::coreStringToAppString(chatRoom->getLocalAddress()->asStringUriOnly());
+ map["fullLocalAddress"] = Utils::coreStringToAppString(chatRoom->getLocalAddress()->asString());
+ map["window"].setValue(App::getInstance()->getMainWindow());
+ CREATE_NOTIFICATION(Notifier::ReceivedMessage, map)
+ }
+}
+
void Notifier::notifyReceivedFileMessage (const shared_ptr &message, const shared_ptr &content) {
QVariantMap map;
shared_ptr chatRoom(message->getChatRoom());
diff --git a/linphone-app/src/components/notifier/Notifier.hpp b/linphone-app/src/components/notifier/Notifier.hpp
index 3748accd7..655c41ef8 100644
--- a/linphone-app/src/components/notifier/Notifier.hpp
+++ b/linphone-app/src/components/notifier/Notifier.hpp
@@ -55,6 +55,7 @@ public:
};
void notifyReceivedMessages (const std::list