diff --git a/Linphone/core/App.cpp b/Linphone/core/App.cpp index be897a337..4acc3d1a9 100644 --- a/Linphone/core/App.cpp +++ b/Linphone/core/App.cpp @@ -52,11 +52,11 @@ #include "core/call/CallGui.hpp" #include "core/call/CallList.hpp" #include "core/call/CallProxy.hpp" -#include "core/chat/ChatProxy.hpp" -#include "core/chat/message/ChatMessageProxy.hpp" -#include "core/chat/message/ChatMessageList.hpp" -#include "core/chat/message/ChatMessageGui.hpp" #include "core/camera/CameraGui.hpp" +#include "core/chat/ChatProxy.hpp" +#include "core/chat/message/ChatMessageGui.hpp" +#include "core/chat/message/ChatMessageList.hpp" +#include "core/chat/message/ChatMessageProxy.hpp" #include "core/conference/ConferenceGui.hpp" #include "core/conference/ConferenceInfoGui.hpp" #include "core/conference/ConferenceInfoProxy.hpp" @@ -263,7 +263,7 @@ void App::setAutoStart(bool enabled) { // ----------------------------------------------------------------------------- App::App(int &argc, char *argv[]) -: SingleApplication(argc, argv, true, Mode::User | Mode::ExcludeAppPath | Mode::ExcludeAppVersion) { + : SingleApplication(argc, argv, true, Mode::User | Mode::ExcludeAppPath | Mode::ExcludeAppVersion) { // Do not use APPLICATION_NAME here. // The EXECUTABLE_NAME will be used in qt standard paths. It's our goal. QThread::currentThread()->setPriority(QThread::HighPriority); @@ -526,7 +526,7 @@ void App::initCore() { setLocale(settings->getConfigLocale()); setAutoStart(settings->getAutoStart()); setQuitOnLastWindowClosed(settings->getExitOnClose()); - } + } const QUrl url("qrc:/qt/qml/Linphone/view/Page/Window/Main/MainWindow.qml"); QObject::connect( mEngine, &QQmlApplicationEngine::objectCreated, this, @@ -577,28 +577,28 @@ void App::initLocale() { mLocale = QLocale(QLocale::English); if (!installLocale(*this, *mDefaultTranslatorCore, mLocale)) qFatal("Unable to install default translator."); -// if (installLocale(*this, *mTranslatorCore, getLocale())) { -// qDebug() << "installed locale" << getLocale().name(); -// return; -// } + // if (installLocale(*this, *mTranslatorCore, getLocale())) { + // qDebug() << "installed locale" << getLocale().name(); + // return; + // } - // Try to use system locale. - // #ifdef Q_OS_MACOS - // Use this workaround if there is still an issue about detecting wrong language from system on Mac. Qt doesn't use - // the current system language on QLocale::system(). So we need to get it from user settings and overwrite its - // Locale. - // QSettings settings; - // QString preferredLanguage = settings.value("AppleLanguages").toStringList().first(); - // QStringList qtLocale = QLocale::system().name().split('_'); - // if(qtLocale[0] != preferredLanguage){ - // qInfo() << "Override Qt language from " << qtLocale[0] << " to the preferred language : " << - // preferredLanguage; qtLocale[0] = preferredLanguage; - // } - // QLocale sysLocale = QLocale(qtLocale.join('_')); - // #else + // Try to use system locale. + // #ifdef Q_OS_MACOS + // Use this workaround if there is still an issue about detecting wrong language from system on Mac. Qt doesn't use + // the current system language on QLocale::system(). So we need to get it from user settings and overwrite its + // Locale. + // QSettings settings; + // QString preferredLanguage = settings.value("AppleLanguages").toStringList().first(); + // QStringList qtLocale = QLocale::system().name().split('_'); + // if(qtLocale[0] != preferredLanguage){ + // qInfo() << "Override Qt language from " << qtLocale[0] << " to the preferred language : " << + // preferredLanguage; qtLocale[0] = preferredLanguage; + // } + // QLocale sysLocale = QLocale(qtLocale.join('_')); + // #else QLocale sysLocale(QLocale::system().name()); // Use Locale from name because Qt has a bug where it didn't use the - // QLocale::language (aka : translator.language != locale.language) on - // Mac. #endif + // QLocale::language (aka : translator.language != locale.language) on + // Mac. #endif if (installLocale(*this, *mTranslatorCore, sysLocale)) { qDebug() << "installed sys locale" << sysLocale.name(); setLocale(sysLocale.name()); @@ -785,34 +785,36 @@ void App::createCommandParser() { //: "A free and open source SIP video-phone." mParser->setApplicationDescription(tr("application_description")); //: "Send an order to the application towards a command line" - mParser->addPositionalArgument("command", tr("command_line_arg_order").replace("%1", APPLICATION_NAME), "[command]"); + mParser->addPositionalArgument("command", tr("command_line_arg_order").replace("%1", APPLICATION_NAME), + "[command]"); mParser->addOptions({ - //: "Show this help" - {{"h", "help"}, tr("command_line_option_show_help")}, + //: "Show this help" + {{"h", "help"}, tr("command_line_option_show_help")}, //{"cli-help", tr("commandLineOptionCliHelp").replace("%1", APPLICATION_NAME)}, - //:"Show app version" - {{"v", "version"}, tr("command_line_option_show_app_version")}, + //:"Show app version" + {{"v", "version"}, tr("command_line_option_show_app_version")}, - //{"config", tr("command_line_option_config").replace("%1", EXECUTABLE_NAME), tr("command_line_option_config_arg")}, + //{"config", tr("command_line_option_config").replace("%1", EXECUTABLE_NAME), + // tr("command_line_option_config_arg")}, {"fetch-config", - //: "Specify the linphone configuration file to be fetched. It will be merged with the current configuration." - tr("command_line_option_config_to_fetch") - .replace("%1", EXECUTABLE_NAME), - //: "URL, path or file" - tr("command_line_option_config_to_fetch_arg")}, + //: "Specify the linphone configuration file to be fetched. It will be merged with the current configuration." + tr("command_line_option_config_to_fetch").replace("%1", EXECUTABLE_NAME), + //: "URL, path or file" + tr("command_line_option_config_to_fetch_arg")}, - //{{"c", "call"}, tr("command_line_option_call").replace("%1", EXECUTABLE_NAME), tr("command_line_option_call_arg")}, + //{{"c", "call"}, tr("command_line_option_call").replace("%1", EXECUTABLE_NAME), + // tr("command_line_option_call_arg")}, - {"minimized", tr("command_line_option_minimized")}, + {"minimized", tr("command_line_option_minimized")}, - //: "Log to stdout some debug information while running" - {{"V", "verbose"}, tr("command_line_option_log_to_stdout")}, + //: "Log to stdout some debug information while running" + {{"V", "verbose"}, tr("command_line_option_log_to_stdout")}, - //: "Print only logs from the application" - {"qt-logs-only", tr("command_line_option_print_app_logs_only")}, + //: "Print only logs from the application" + {"qt-logs-only", tr("command_line_option_print_app_logs_only")}, }); } // Should be call only at first start @@ -1174,7 +1176,7 @@ void App::setSysTrayIcon() { //: "Afficher" restoreAction->setText(visible ? tr("hide_action") : tr("show_action")); }; - setRestoreActionText(root->isVisible()); + setRestoreActionText(root ? root->isVisible() : false); connect(root, &QWindow::visibleChanged, restoreAction, setRestoreActionText); root->connect(restoreAction, &QAction::triggered, this, [this, restoreAction](bool checked) { diff --git a/Linphone/core/chat/ChatCore.cpp b/Linphone/core/chat/ChatCore.cpp index 266e7f016..7bd72b4a9 100644 --- a/Linphone/core/chat/ChatCore.cpp +++ b/Linphone/core/chat/ChatCore.cpp @@ -88,18 +88,80 @@ ChatCore::~ChatCore() { void ChatCore::setSelf(QSharedPointer me) { mChatModelConnection = SafeConnection::create(me, mChatModel); - // mChatModelConnection->makeConnectToCore(&ChatCore::lSetMicrophoneMuted, [this](bool isMuted) { - // mChatModelConnection->invokeToModel( - // [this, isMuted]() { mChatModel->setMicrophoneMuted(isMuted); }); - // }); + mChatModelConnection->makeConnectToCore(&ChatCore::lDeleteHistory, [this]() { + mChatModelConnection->invokeToModel([this]() { mChatModel->deleteHistory(); }); + }); + mChatModelConnection->makeConnectToModel(&ChatModel::historyDeleted, [this]() { + mChatModelConnection->invokeToCore([this]() { + qDebug() << log().arg("history deleted for chatRoom") << this; + clearMessagesList(); + Utils::showInformationPopup(tr("Supprimé"), tr("L'historique des messages a été supprimé."), true); + }); + }); + mChatModelConnection->makeConnectToCore(&ChatCore::lUpdateUnreadCount, [this]() { + mChatModelConnection->invokeToModel([this]() { + auto count = mChatModel->getUnreadMessagesCount(); + mChatModelConnection->invokeToCore([this, count] { setUnreadMessagesCount(count); }); + }); + }); + + mChatModelConnection->makeConnectToCore(&ChatCore::lDelete, [this]() { + mChatModelConnection->invokeToModel([this]() { mChatModel->deleteChatRoom(); }); + }); + mChatModelConnection->makeConnectToModel( + &ChatModel::deleted, [this]() { mChatModelConnection->invokeToCore([this]() { emit deleted(); }); }); + mChatModelConnection->makeConnectToModel(&ChatModel::chatMessageReceived, [this](const std::shared_ptr &chatRoom, const std::shared_ptr &eventLog) { if (mChatModel->getMonitor() != chatRoom) return; - qDebug() << "MESSAGE RECEIVED IN CHATROOM" << mChatModel->getTitle(); - // mChatModelConnection->invokeToCore([this, isMuted]() { - // setMicrophoneMuted(isMuted); }); + emit lUpdateLastMessage(); + emit lUpdateUnreadCount(); + auto message = eventLog->getChatMessage(); + qDebug() << "EVENT LOG RECEIVED IN CHATROOM" << mChatModel->getTitle(); + if (message) { + auto newMessage = ChatMessageCore::create(message); + mChatModelConnection->invokeToCore([this, newMessage]() { + qDebug() << log().arg("append message to chatRoom") << this; + appendMessageToMessageList(newMessage); + }); + } }); + mChatModelConnection->makeConnectToModel( + &ChatModel::chatMessagesReceived, [this](const std::shared_ptr &chatRoom, + const std::list> &chatMessages) { + if (mChatModel->getMonitor() != chatRoom) return; + emit lUpdateLastMessage(); + emit lUpdateUnreadCount(); + qDebug() << "EVENT LOGS RECEIVED IN CHATROOM" << mChatModel->getTitle(); + QList> list; + for (auto &m : chatMessages) { + auto message = m->getChatMessage(); + if (message) { + auto newMessage = ChatMessageCore::create(message); + list.push_back(newMessage); + } + } + mChatModelConnection->invokeToCore([this, list]() { + qDebug() << log().arg("append messages to chatRoom") << this; + appendMessagesToMessageList(list); + }); + }); + + mChatModelConnection->makeConnectToCore(&ChatCore::lMarkAsRead, [this]() { + mChatModelConnection->invokeToModel([this]() { mChatModel->markAsRead(); }); + }); + mChatModelConnection->makeConnectToModel(&ChatModel::messagesRead, [this]() { + auto unread = mChatModel->getUnreadMessagesCount(); + mChatModelConnection->invokeToCore([this, unread]() { setUnreadMessagesCount(unread); }); + }); + + mChatModelConnection->makeConnectToCore(&ChatCore::lUpdateLastMessage, [this]() { + mChatModelConnection->invokeToModel([this]() { + auto message = mChatModel->getLastMessageInHistory(); + mChatModelConnection->invokeToCore([this, message]() { setLastMessageInHistory(message); }); + }); + }); } QDateTime ChatCore::getLastUpdatedTime() const { @@ -175,6 +237,7 @@ void ChatCore::setUnreadMessagesCount(int count) { QList> ChatCore::getChatMessageList() const { return mChatMessageList; } + void ChatCore::resetChatMessageList(QList> list) { mChatMessageList = list; emit messageListChanged(); @@ -190,6 +253,12 @@ void ChatCore::appendMessagesToMessageList(QList if (nbAdded > 0) emit messageListChanged(); } +void ChatCore::appendMessageToMessageList(QSharedPointer message) { + if (mChatMessageList.contains(message)) return; + mChatMessageList.append(message); + emit messageListChanged(); +} + void ChatCore::removeMessagesFromMessageList(QList> list) { int nbRemoved = 0; for (auto &message : list) { @@ -201,6 +270,11 @@ void ChatCore::removeMessagesFromMessageList(QList 0) emit messageListChanged(); } +void ChatCore::clearMessagesList() { + mChatMessageList.clear(); + emit messageListChanged(); +} + std::shared_ptr ChatCore::getModel() const { return mChatModel; } \ No newline at end of file diff --git a/Linphone/core/chat/ChatCore.hpp b/Linphone/core/chat/ChatCore.hpp index 9d782d9fc..f76cd76b0 100644 --- a/Linphone/core/chat/ChatCore.hpp +++ b/Linphone/core/chat/ChatCore.hpp @@ -70,8 +70,10 @@ public: QList> getChatMessageList() const; void resetChatMessageList(QList> list); + void appendMessageToMessageList(QSharedPointer message); void appendMessagesToMessageList(QList> list); void removeMessagesFromMessageList(QList> list); + void clearMessagesList(); QString getAvatarUri() const; void setAvatarUri(QString avatarUri); @@ -86,6 +88,14 @@ signals: void unreadMessagesCountChanged(int count); void messageListChanged(); void avatarUriChanged(); + void deleted(); + + void lDeleteMessage(); + void lDelete(); + void lDeleteHistory(); + void lMarkAsRead(); + void lUpdateLastMessage(); + void lUpdateUnreadCount(); private: QString id; diff --git a/Linphone/core/chat/ChatList.cpp b/Linphone/core/chat/ChatList.cpp index 71330ea48..fcf575f08 100644 --- a/Linphone/core/chat/ChatList.cpp +++ b/Linphone/core/chat/ChatList.cpp @@ -67,6 +67,17 @@ void ChatList::setSelf(QSharedPointer me) { chats->push_back(model); } mModelConnection->invokeToCore([this, chats]() { + for (auto &chat : getSharedList()) { + if (chat) disconnect(chat.get(), &ChatCore::deleted, this, nullptr); + } + for (auto &chat : *chats) { + connect(chat.get(), &ChatCore::deleted, this, [this, chat] { + remove(chat); + // We cannot use countChanged here because it is called before mList + // really has removed the item, then emit specific signal + emit chatRemoved(chat ? new ChatGui(chat) : nullptr); + }); + } mustBeInMainThread(getClassName()); resetData(*chats); delete chats; @@ -78,7 +89,7 @@ void ChatList::setSelf(QSharedPointer me) { &CoreModel::chatRoomStateChanged, [this](const std::shared_ptr &core, const std::shared_ptr &chatRoom, linphone::ChatRoom::State state) { - // check account, filtre, puis ajout si c'est bon + // check account, filter, then add if ok if (chatRoom->getAccount() == core->getDefaultAccount()) { if (state == linphone::ChatRoom::State::Created) { auto list = getSharedList(); diff --git a/Linphone/core/chat/ChatList.hpp b/Linphone/core/chat/ChatList.hpp index ec2a9f9b6..79c4a6741 100644 --- a/Linphone/core/chat/ChatList.hpp +++ b/Linphone/core/chat/ChatList.hpp @@ -46,6 +46,7 @@ public: signals: void lUpdate(); void filterChanged(QString filter); + void chatRemoved(ChatGui *chat); private: QString mFilter; diff --git a/Linphone/core/chat/ChatProxy.cpp b/Linphone/core/chat/ChatProxy.cpp index 8578a3c6c..bcbc32dc8 100644 --- a/Linphone/core/chat/ChatProxy.cpp +++ b/Linphone/core/chat/ChatProxy.cpp @@ -42,6 +42,7 @@ void ChatProxy::setSourceModel(QAbstractItemModel *model) { if (newChatList) { connect(this, &ChatProxy::filterTextChanged, newChatList, [this, newChatList] { emit newChatList->filterChanged(getFilterText()); }); + connect(newChatList, &ChatList::chatRemoved, this, &ChatProxy::chatRemoved); } setSourceModels(new SortFilterList(model)); sort(0); diff --git a/Linphone/core/chat/ChatProxy.hpp b/Linphone/core/chat/ChatProxy.hpp index 152e84135..efb4dc2e1 100644 --- a/Linphone/core/chat/ChatProxy.hpp +++ b/Linphone/core/chat/ChatProxy.hpp @@ -41,6 +41,9 @@ public: Q_INVOKABLE int findChatIndex(ChatGui *chatGui); +signals: + void chatRemoved(ChatGui *chat); + protected: QSharedPointer mList; DECLARE_ABSTRACT_OBJECT diff --git a/Linphone/core/chat/message/ChatMessageCore.cpp b/Linphone/core/chat/message/ChatMessageCore.cpp index 41f3dd42a..b3c663445 100644 --- a/Linphone/core/chat/message/ChatMessageCore.cpp +++ b/Linphone/core/chat/message/ChatMessageCore.cpp @@ -20,6 +20,7 @@ #include "ChatMessageCore.hpp" #include "core/App.hpp" +#include "core/chat/ChatCore.hpp" #include "model/tool/ToolModel.hpp" DEFINE_ABSTRACT_OBJECT(ChatMessageCore) @@ -51,6 +52,13 @@ ChatMessageCore::~ChatMessageCore() { void ChatMessageCore::setSelf(QSharedPointer me) { mChatMessageModelConnection = SafeConnection::create(me, mChatMessageModel); + mChatMessageModelConnection->makeConnectToCore(&ChatMessageCore::lDelete, [this] { + mChatMessageModelConnection->invokeToModel([this] { mChatMessageModel->deleteMessageFromChatRoom(); }); + }); + mChatMessageModelConnection->makeConnectToModel(&ChatMessageModel::messageDeleted, [this]() { + Utils::showInformationPopup(tr("Supprimé"), tr("Message supprimé"), true); + emit deleted(); + }); } QDateTime ChatMessageCore::getTimestamp() const { @@ -93,3 +101,7 @@ void ChatMessageCore::setIsRemoteMessage(bool isRemote) { emit isRemoteMessageChanged(isRemote); } } + +std::shared_ptr ChatMessageCore::getModel() const { + return mChatMessageModel; +} \ No newline at end of file diff --git a/Linphone/core/chat/message/ChatMessageCore.hpp b/Linphone/core/chat/message/ChatMessageCore.hpp index 75375b60a..b37679814 100644 --- a/Linphone/core/chat/message/ChatMessageCore.hpp +++ b/Linphone/core/chat/message/ChatMessageCore.hpp @@ -29,6 +29,8 @@ #include +class ChatCore; + class ChatMessageCore : public QObject, public AbstractObject { Q_OBJECT Q_PROPERTY(QDateTime timestamp READ getTimestamp WRITE setTimestamp NOTIFY timestampChanged) @@ -55,14 +57,18 @@ public: bool isRemoteMessage() const; void setIsRemoteMessage(bool isRemote); + std::shared_ptr getModel() const; + signals: void timestampChanged(QDateTime timestamp); void textChanged(QString text); void isRemoteMessageChanged(bool isRemote); + void lDelete(); + void deleted(); + private: - DECLARE_ABSTRACT_OBJECT - QString mText; + DECLARE_ABSTRACT_OBJECT QString mText; QString mPeerAddress; QString mPeerName; QDateTime mTimestamp; diff --git a/Linphone/core/chat/message/ChatMessageList.cpp b/Linphone/core/chat/message/ChatMessageList.cpp index 8ca118944..502729076 100644 --- a/Linphone/core/chat/message/ChatMessageList.cpp +++ b/Linphone/core/chat/message/ChatMessageList.cpp @@ -21,9 +21,9 @@ #include "ChatMessageList.hpp" #include "ChatMessageCore.hpp" #include "ChatMessageGui.hpp" +#include "core/App.hpp" #include "core/chat/ChatCore.hpp" #include "core/chat/ChatGui.hpp" -#include "core/App.hpp" #include #include @@ -39,7 +39,8 @@ QSharedPointer ChatMessageList::create() { return model; } -QSharedPointer ChatMessageList::createChatMessageCore(const std::shared_ptr &chatMessage) { +QSharedPointer +ChatMessageList::createChatMessageCore(const std::shared_ptr &chatMessage) { auto chatMessageCore = ChatMessageCore::create(chatMessage); return chatMessageCore; } @@ -55,7 +56,7 @@ ChatMessageList::~ChatMessageList() { mModelConnection = nullptr; } -ChatGui* ChatMessageList::getChat() const { +ChatGui *ChatMessageList::getChat() const { if (mChatCore) return new ChatGui(mChatCore); else return nullptr; } @@ -66,12 +67,14 @@ QSharedPointer ChatMessageList::getChatCore() const { void ChatMessageList::setChatCore(QSharedPointer core) { if (mChatCore != core) { + if (mChatCore) disconnect(mChatCore.get(), &ChatCore::messageListChanged, this, nullptr); mChatCore = core; + if (mChatCore) connect(mChatCore.get(), &ChatCore::messageListChanged, this, &ChatMessageList::lUpdate); emit chatChanged(); } } -void ChatMessageList::setChatGui(ChatGui* chat) { +void ChatMessageList::setChatGui(ChatGui *chat) { auto chatCore = chat ? chat->mCore : nullptr; setChatCore(chatCore); } @@ -80,17 +83,17 @@ void ChatMessageList::setSelf(QSharedPointer me) { mModelConnection = SafeConnection::create(me, CoreModel::getInstance()); mModelConnection->makeConnectToCore(&ChatMessageList::lUpdate, [this]() { -// mModelConnection->invokeToModel([this]() { -// // Avoid copy to lambdas -// QList> *calls = new QList>(); -// mustBeInLinphoneThread(getClassName()); -// mModelConnection->invokeToCore([this, calls, currentCallCore]() { -// mustBeInMainThread(getClassName()); -// resetData(*calls); -// }); -// }); + for (auto &message : getSharedList()) { + if (message) disconnect(message.get(), &ChatMessageCore::deleted, this, nullptr); + } if (!mChatCore) return; auto messages = mChatCore->getChatMessageList(); + for (auto &message : messages) { + connect(message.get(), &ChatMessageCore::deleted, this, [this, message] { + emit mChatCore->lUpdateLastMessage(); + remove(message); + }); + } resetData(messages); }); @@ -104,6 +107,7 @@ void ChatMessageList::setSelf(QSharedPointer me) { QVariant ChatMessageList::data(const QModelIndex &index, int role) const { int row = index.row(); if (!index.isValid() || row < 0 || row >= mList.count()) return QVariant(); - if (role == Qt::DisplayRole) return QVariant::fromValue(new ChatMessageGui(mList[row].objectCast())); + if (role == Qt::DisplayRole) + return QVariant::fromValue(new ChatMessageGui(mList[row].objectCast())); return QVariant(); } diff --git a/Linphone/model/chat/ChatModel.cpp b/Linphone/model/chat/ChatModel.cpp index 700888ee5..37ddc61e7 100644 --- a/Linphone/model/chat/ChatModel.cpp +++ b/Linphone/model/chat/ChatModel.cpp @@ -103,6 +103,22 @@ QString ChatModel::getLastMessageInHistory(std::listgetUnreadMessagesCount(); } + +void ChatModel::markAsRead() { + mMonitor->markAsRead(); + emit messagesRead(); +} + +void ChatModel::deleteHistory() { + mMonitor->deleteHistory(); + emit historyDeleted(); +} + +void ChatModel::deleteChatRoom() { + CoreModel::getInstance()->getCore()->deleteChatRoom(mMonitor); + emit deleted(); +} + //---------------------------------------------------------------// void ChatModel::onIsComposingReceived(const std::shared_ptr &chatRoom, diff --git a/Linphone/model/chat/ChatModel.hpp b/Linphone/model/chat/ChatModel.hpp index 77fbc3e1b..96b832e2d 100644 --- a/Linphone/model/chat/ChatModel.hpp +++ b/Linphone/model/chat/ChatModel.hpp @@ -42,8 +42,16 @@ public: QString getPeerAddress() const; QString getLastMessageInHistory(std::list> startList = {}) const; int getUnreadMessagesCount() const; + void markAsRead(); std::list> getHistory() const; QString getIdentifier() const; + void deleteHistory(); + void deleteChatRoom(); + +signals: + void historyDeleted(); + void messagesRead(); + void deleted(); private: DECLARE_ABSTRACT_OBJECT diff --git a/Linphone/model/chat/message/ChatMessageModel.cpp b/Linphone/model/chat/message/ChatMessageModel.cpp index 2292bdfac..3649056fb 100644 --- a/Linphone/model/chat/message/ChatMessageModel.cpp +++ b/Linphone/model/chat/message/ChatMessageModel.cpp @@ -59,3 +59,11 @@ ChatMessageModel::onFileTransferSend(const std::shared_ptrgetChatRoom(); + if (chatRoom) { + chatRoom->deleteMessage(mMonitor); + emit messageDeleted(); + } +} diff --git a/Linphone/model/chat/message/ChatMessageModel.hpp b/Linphone/model/chat/message/ChatMessageModel.hpp index 2457b01ca..484eea0dc 100644 --- a/Linphone/model/chat/message/ChatMessageModel.hpp +++ b/Linphone/model/chat/message/ChatMessageModel.hpp @@ -42,6 +42,11 @@ public: QString getPeerAddress() const; + void deleteMessageFromChatRoom(); + +signals: + void messageDeleted(); + private: DECLARE_ABSTRACT_OBJECT virtual std::shared_ptr onFileTransferSend(const std::shared_ptr &message, diff --git a/Linphone/view/CMakeLists.txt b/Linphone/view/CMakeLists.txt index f0c859d36..a5e5a69ec 100644 --- a/Linphone/view/CMakeLists.txt +++ b/Linphone/view/CMakeLists.txt @@ -22,6 +22,7 @@ list(APPEND _LINPHONEAPP_QML_FILES view/Control/Button/Settings/SwitchSetting.qml view/Control/Container/Carousel.qml + view/Control/Container/DetailLayout.qml view/Control/Container/FormItemLayout.qml view/Control/Container/ScrollBar.qml view/Control/Container/Section.qml diff --git a/Linphone/view/Control/Container/Call/CallHistoryLayout.qml b/Linphone/view/Control/Container/Call/CallHistoryLayout.qml index 26d31e644..cd321c99f 100644 --- a/Linphone/view/Control/Container/Call/CallHistoryLayout.qml +++ b/Linphone/view/Control/Container/Call/CallHistoryLayout.qml @@ -12,6 +12,7 @@ ColumnLayout { spacing: Math.round(30 * DefaultStyle.dp) property var callHistoryGui + property var chatGui property FriendGui contact property var conferenceInfo: callHistoryGui?.core.conferenceInfo diff --git a/Linphone/view/Control/Container/DetailLayout.qml b/Linphone/view/Control/Container/DetailLayout.qml new file mode 100644 index 000000000..1bbaa6c84 --- /dev/null +++ b/Linphone/view/Control/Container/DetailLayout.qml @@ -0,0 +1,54 @@ +import QtQuick +import QtQuick.Effects +import QtQuick.Layouts +import QtQuick.Controls.Basic as Control +import Linphone +import UtilsCpp +import SettingsCpp +import 'qrc:/qt/qml/Linphone/view/Style/buttonStyle.js' as ButtonStyle + +ColumnLayout { + id: mainItem + spacing: Math.round(15 * DefaultStyle.dp) + property string label + property var icon + property alias content: contentControl.contentItem + signal titleIconClicked + RowLayout { + spacing: Math.round(10 * DefaultStyle.dp) + Text { + text: mainItem.label + color: DefaultStyle.main1_500_main + font { + pixelSize: Typography.h4.pixelSize + weight: Typography.h4.weight + } + } + RoundButton { + visible: mainItem.icon != undefined + icon.source: mainItem.icon + style: ButtonStyle.noBackgroundOrange + onClicked: mainItem.titleIconClicked() + } + Item { + Layout.fillWidth: true + } + RoundButton { + id: expandButton + style: ButtonStyle.noBackground + checkable: true + checked: true + icon.source: checked ? AppIcons.upArrow : AppIcons.downArrow + KeyNavigation.down: contentControl + } + } + RoundedPane { + id: contentControl + visible: expandButton.checked + Layout.fillWidth: true + leftPadding: Math.round(20 * DefaultStyle.dp) + rightPadding: Math.round(20 * DefaultStyle.dp) + topPadding: Math.round(17 * DefaultStyle.dp) + bottomPadding: Math.round(17 * DefaultStyle.dp) + } +} \ No newline at end of file diff --git a/Linphone/view/Control/Display/Chat/ChatListView.qml b/Linphone/view/Control/Display/Chat/ChatListView.qml index 7d03a1e9b..badad463b 100644 --- a/Linphone/view/Control/Display/Chat/ChatListView.qml +++ b/Linphone/view/Control/Display/Chat/ChatListView.qml @@ -31,13 +31,18 @@ ListView { } filterText: mainItem.searchText onFilterTextChanged: maxDisplayItems = initialDisplayItems - initialDisplayItems: Math.max( - 20, - 2 * mainItem.height / (Math.round(56 * DefaultStyle.dp))) - displayItemsStep: 3 * initialDisplayItems / 2 - onModelReset: { - mainItem.resultsReceived() - } + initialDisplayItems: Math.max( + 20, + 2 * mainItem.height / (Math.round(56 * DefaultStyle.dp))) + displayItemsStep: 3 * initialDisplayItems / 2 + onModelReset: { + mainItem.resultsReceived() + } + onChatRemoved: { + var indexToSelect = mainItem.currentIndex + mainItem.currentIndex = -1 + mainItem.currentIndex = indexToSelect + } } // flickDeceleration: 10000 spacing: Math.round(10 * DefaultStyle.dp) @@ -56,19 +61,15 @@ ListView { onActiveFocusChanged: if (activeFocus && currentIndex < 0 && count > 0) currentIndex = 0 - onCountChanged: { - if (currentIndex < 0 && count > 0) { - mainItem.currentIndex = 0 // Select first item after loading model - } - if (atYBeginning) - positionViewAtBeginning() // Stay at beginning - } onAtYEndChanged: { if (atYEnd && count > 0) { chatProxy.displayMore() } } + onCountChanged: { + if (count > 0 && currentIndex < 0) currentIndex = 0 + } //---------------------------------------------------------------- function moveToCurrentItem() { @@ -93,10 +94,6 @@ ListView { } // //---------------------------------------------------------------- - onVisibleChanged: { -// if (!visible) -// currentIndex = -1 - } BusyIndicator { anchors.horizontalCenter: mainItem.horizontalCenter @@ -218,8 +215,39 @@ ListView { //sourdine, éphémère, IMDN } } + PopupButton { + id: chatroomPopup + // z: 1 + popup.x: 0 + popup.padding: Math.round(10 * DefaultStyle.dp) + visible: mouseArea.containsMouse || hovered || popup.opened + enabled: visible + popup.contentItem: ColumnLayout { + IconLabelButton { + //: "Supprimer" + text: qsTr("chat_room_delete") + icon.source: AppIcons.trashCan + spacing: Math.round(10 * DefaultStyle.dp) + Layout.fillWidth: true + onClicked: { + mainWindow.showConfirmationLambdaPopup(qsTr("Supprimer le chat ?"), + qsTr("Le chat ainsi que tous ses messages seront supprimés. Souhaitez-vous continuer ?"), + "", + function(confirmed) { + if (confirmed) { + modelData.core.lDelete() + chatroomPopup.close() + } + }) + } + style: ButtonStyle.noBackgroundRed + } + } + } + } MouseArea { + id: mouseArea hoverEnabled: true anchors.fill: parent focus: true diff --git a/Linphone/view/Control/Display/Chat/ChatMessage.qml b/Linphone/view/Control/Display/Chat/ChatMessage.qml index 7868823e7..834ce91e0 100644 --- a/Linphone/view/Control/Display/Chat/ChatMessage.qml +++ b/Linphone/view/Control/Display/Chat/ChatMessage.qml @@ -16,10 +16,59 @@ Control.Control { property string imgUrl property string contentText + topPadding: Math.round(12 * DefaultStyle.dp) bottomPadding: Math.round(12 * DefaultStyle.dp) leftPadding: Math.round(18 * DefaultStyle.dp) rightPadding: Math.round(18 * DefaultStyle.dp) + + signal messageDeletionRequested() + + MouseArea { + anchors.fill: parent + acceptedButtons: Qt.RightButton + onClicked: (mouse) => { + console.log("message clicked") + if (mouse.button === Qt.RightButton) { + optionsMenu.x = mouse.x + optionsMenu.open() + } + } + } + Popup { + id: optionsMenu + background: Item { + anchors.fill: parent + Rectangle { + id: popupBackground + anchors.fill: parent + color: DefaultStyle.grey_0 + radius: Math.round(16 * DefaultStyle.dp) + } + MultiEffect { + source: popupBackground + anchors.fill: popupBackground + shadowEnabled: true + shadowBlur: 0.1 + shadowColor: DefaultStyle.grey_1000 + shadowOpacity: 0.4 + } + } + contentItem: ColumnLayout { + IconLabelButton { + //: "Supprimer" + text: qsTr("chat_message_delete") + icon.source: AppIcons.trashCan + spacing: Math.round(10 * DefaultStyle.dp) + Layout.fillWidth: true + onClicked: { + mainItem.messageDeletionRequested() + optionsMenu.close() + } + style: ButtonStyle.noBackgroundRed + } + } + } background: Item { anchors.fill: parent diff --git a/Linphone/view/Control/Display/Chat/ChatMessagesListView.qml b/Linphone/view/Control/Display/Chat/ChatMessagesListView.qml index 5a7c6d874..7118836fc 100644 --- a/Linphone/view/Control/Display/Chat/ChatMessagesListView.qml +++ b/Linphone/view/Control/Display/Chat/ChatMessagesListView.qml @@ -15,6 +15,8 @@ ListView { Component.onCompleted: positionViewAtEnd() + onCountChanged: positionViewAtEnd(); + model: ChatMessageProxy { chatGui: mainItem.chat } @@ -34,5 +36,7 @@ ListView { anchors.right: !isRemoteMessage && parent ? parent.right : undefined + + onMessageDeletionRequested: modelData.core.lDelete() } } diff --git a/Linphone/view/Page/Form/Chat/SelectedChatView.qml b/Linphone/view/Page/Form/Chat/SelectedChatView.qml index cd49f248c..ff816a6b1 100644 --- a/Linphone/view/Page/Form/Chat/SelectedChatView.qml +++ b/Linphone/view/Page/Form/Chat/SelectedChatView.qml @@ -15,6 +15,11 @@ RowLayout { property CallGui call property alias callHeaderContent: splitPanel.headerContent spacing: 0 + + onChatChanged: { + // TODO : call when all messages read after scroll to unread feature available + if (chat) chat.core.lMarkAsRead() + } MainRightPanel { id: splitPanel Layout.fillWidth: true @@ -62,9 +67,10 @@ RowLayout { BigButton { style: ButtonStyle.noBackground checkable: true + checkedImageColor: DefaultStyle.main1_500_main icon.source: AppIcons.info onCheckedChanged: { - + detailsPanel.visible = !detailsPanel.visible } } } @@ -184,8 +190,46 @@ RowLayout { color: DefaultStyle.grey_0 anchors.fill: parent } - contentItem: ColumnLayout { + contentItem: CallHistoryLayout { + chatGui: mainItem.chat + detailContent: ColumnLayout { + DetailLayout { + //: Other actions + label: qsTr("Autres actions") + content: ColumnLayout { + // IconLabelButton { + // Layout.fillWidth: true + // Layout.preferredHeight: Math.round(50 * DefaultStyle.dp) + // icon.source: AppIcons.signOut + // //: "Quitter la conversation" + // text: qsTr("Quitter la conversation") + // onClicked: { + // } + // style: ButtonStyle.noBackground + // } + IconLabelButton { + Layout.fillWidth: true + Layout.preferredHeight: Math.round(50 * DefaultStyle.dp) + icon.source: AppIcons.trashCan + //: "Supprimer l'historique" + text: qsTr("Supprimer l'historique") + onClicked: { + mainWindow.showConfirmationLambdaPopup(qsTr("Supprimer l'historique ?"), + qsTr("Tous les messages seront supprimés de la chatroom.Souhaitez-vous continuer ?"), + "", + function(confirmed) { + if (confirmed) { + mainItem.chat.core.lDeleteHistory() + } + }) + } + style: ButtonStyle.noBackgroundRed + } + } + } + Item {Layout.fillHeight: true} + } } } } diff --git a/Linphone/view/Page/Main/Chat/ChatPage.qml b/Linphone/view/Page/Main/Chat/ChatPage.qml index 5fc8897a9..d56c4264a 100644 --- a/Linphone/view/Page/Main/Chat/ChatPage.qml +++ b/Linphone/view/Page/Main/Chat/ChatPage.qml @@ -154,9 +154,6 @@ AbstractMainPage { onCurrentIndexChanged: { mainItem.selectedChatGui = model.getAt(currentIndex) } - onCountChanged: { - mainItem.selectedChatGui = model.getAt(currentIndex) - } Connections { target: mainItem @@ -185,9 +182,6 @@ AbstractMainPage { objectName: "newChatItem" width: parent?.width height: parent?.height - Control.StackView.onActivated: { - callContactsList.forceActiveFocus() - } ColumnLayout { anchors.fill: parent spacing: 0 diff --git a/Linphone/view/Style/AppIcons.qml b/Linphone/view/Style/AppIcons.qml index 796dabc4a..0067bd66b 100644 --- a/Linphone/view/Style/AppIcons.qml +++ b/Linphone/view/Style/AppIcons.qml @@ -92,6 +92,7 @@ QtObject { property string settings: "image://internal/gear.svg" property string clock: "image://internal/clock.svg" property string note: "image://internal/note.svg" + property string signOut: "image://internal/sign-out.svg" property string userRectangle: "image://internal/user-rectangle.svg" property string usersTwo: "image://internal/users.svg" property string globe: "image://internal/globe-hemisphere-west.svg" diff --git a/Linphone/view/Style/buttonStyle.js b/Linphone/view/Style/buttonStyle.js index a46df1a8f..b04b423ce 100644 --- a/Linphone/view/Style/buttonStyle.js +++ b/Linphone/view/Style/buttonStyle.js @@ -132,7 +132,7 @@ normal: "#00000000", hovered: "#00000000", pressed: "#00000000", - checked: Linphone.DefaultStyle.main1_500main + checked: "#00000000" }, text: { normal: Linphone.DefaultStyle.main2_600, @@ -144,7 +144,7 @@ normal: Linphone.DefaultStyle.main2_600, hovered: Linphone.DefaultStyle.main2_700, pressed: Linphone.DefaultStyle.main2_800, - checked: Linphone.DefaultStyle.main1_500main + checked: Linphone.DefaultStyle.main1_500main, } } @@ -163,7 +163,8 @@ image: { normal: Linphone.DefaultStyle.danger_500main, hovered: Linphone.DefaultStyle.danger_700, - pressed: Linphone.DefaultStyle.danger_900 + pressed: Linphone.DefaultStyle.danger_900, + checked: Linphone.DefaultStyle.danger_900 } }