diff --git a/Linphone/core/chat/ChatCore.cpp b/Linphone/core/chat/ChatCore.cpp index 9ceea15d6..798c7365a 100644 --- a/Linphone/core/chat/ChatCore.cpp +++ b/Linphone/core/chat/ChatCore.cpp @@ -476,6 +476,14 @@ QList> ChatCore::getEventLogList() const { } void ChatCore::resetEventLogList(QList> list) { + for (auto &e : mEventLogList) { + disconnect(e.get()); + } + for (auto &e : list) { + if (auto message = e->getChatMessageCore()) { + connect(message.get(), &ChatMessageCore::isReadChanged, this, [this] { emit lUpdateUnreadCount(); }); + } + } mEventLogList = list; emit eventListChanged(); } @@ -484,6 +492,8 @@ void ChatCore::appendEventLogsToEventLogList(QList> int nbAdded = 0; for (auto &e : list) { if (mEventLogList.contains(e)) continue; + if (auto message = e->getChatMessageCore()) + connect(message.get(), &ChatMessageCore::isReadChanged, this, [this] { emit lUpdateUnreadCount(); }); mEventLogList.append(e); ++nbAdded; } @@ -496,6 +506,8 @@ void ChatCore::appendEventLogToEventLogList(QSharedPointer e) { return e->getEventLogId() == event->getEventLogId(); }); if (it == mEventLogList.end()) { + if (auto message = e->getChatMessageCore()) + connect(message.get(), &ChatMessageCore::isReadChanged, this, [this] { emit lUpdateUnreadCount(); }); mEventLogList.append(e); emit eventsInserted({e}); } @@ -505,6 +517,7 @@ void ChatCore::removeEventLogsFromEventLogList(QListgetChatMessageCore()) disconnect(message.get()); mEventLogList.removeAll(e); ++nbRemoved; } @@ -513,6 +526,9 @@ void ChatCore::removeEventLogsFromEventLogList(QList #include @@ -109,63 +110,60 @@ void ChatList::setSelf(QSharedPointer me) { mModelConnection->makeConnectToModel( &CoreModel::defaultAccountChanged, [this](std::shared_ptr core, std::shared_ptr account) { lUpdate(); }); - mModelConnection->makeConnectToModel( - &CoreModel::messageReceived, - [this](const std::shared_ptr &core, const std::shared_ptr &room, - const std::shared_ptr &message) { - auto chatCore = ChatCore::create(room); - mModelConnection->invokeToCore([this, chatCore] { - auto chatList = getSharedList(); - auto it = - std::find_if(chatList.begin(), chatList.end(), [chatCore](const QSharedPointer item) { - return item && chatCore && item->getModel() && chatCore->getModel() && - item->getModel()->getMonitor() == chatCore->getModel()->getMonitor(); - }); - if (it == chatList.end()) { - connectItem(chatCore); - add(chatCore); - emit chatAdded(); - } - }); - }); + auto addChatToList = [this](const std::shared_ptr &core, + const std::shared_ptr &room, + const std::shared_ptr &message) { + mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); + auto receiverAccount = ToolModel::findAccount(message->getToAddress()); + if (!receiverAccount) { + qWarning() << log().arg("Receiver account not found in account list, return"); + return; + } + auto receiverAddress = receiverAccount->getContactAddress(); + if (!receiverAddress) { + qWarning() << log().arg("Receiver account has no address, return"); + return; + } + auto defaultAddress = core->getDefaultAccount()->getContactAddress(); + if (!defaultAddress->weakEqual(receiverAddress)) { + qDebug() << log().arg("Receiver account is not the default one, do not add chat to list"); + return; + } + + auto chatCore = ChatCore::create(room); + mModelConnection->invokeToCore([this, chatCore] { + auto chatList = getSharedList(); + auto it = std::find_if(chatList.begin(), chatList.end(), [chatCore](const QSharedPointer item) { + return item && chatCore && item->getModel() && chatCore->getModel() && + item->getModel()->getMonitor() == chatCore->getModel()->getMonitor(); + }); + if (it == chatList.end()) { + connectItem(chatCore); + add(chatCore); + emit chatAdded(); + } + }); + }; + mModelConnection->makeConnectToModel(&CoreModel::messageReceived, + [this, addChatToList](const std::shared_ptr &core, + const std::shared_ptr &room, + const std::shared_ptr &message) { + addChatToList(core, room, message); + }); mModelConnection->makeConnectToModel( &CoreModel::messagesReceived, - [this](const std::shared_ptr &core, const std::shared_ptr &room, - const std::list> &messages) { - auto chatCore = ChatCore::create(room); - mModelConnection->invokeToCore([this, chatCore] { - auto chatList = getSharedList(); - auto it = - std::find_if(chatList.begin(), chatList.end(), [chatCore](const QSharedPointer item) { - return item && chatCore && item->getModel() && chatCore->getModel() && - item->getModel()->getMonitor() == chatCore->getModel()->getMonitor(); - }); - if (it == chatList.end()) { - connectItem(chatCore); - add(chatCore); - emit chatAdded(); - } - }); + [this, addChatToList](const std::shared_ptr &core, + const std::shared_ptr &room, + const std::list> &messages) { + addChatToList(core, room, messages.front()); }); mModelConnection->makeConnectToModel( &CoreModel::newMessageReaction, - [this](const std::shared_ptr &core, const std::shared_ptr &room, - const std::shared_ptr &message, - const std::shared_ptr &reaction) { - auto chatCore = ChatCore::create(room); - mModelConnection->invokeToCore([this, chatCore] { - auto chatList = getSharedList(); - auto it = - std::find_if(chatList.begin(), chatList.end(), [chatCore](const QSharedPointer item) { - return item && chatCore && item->getModel() && chatCore->getModel() && - item->getModel()->getMonitor() == chatCore->getModel()->getMonitor(); - }); - if (it == chatList.end()) { - connectItem(chatCore); - add(chatCore); - emit chatAdded(); - } - }); + [this, addChatToList](const std::shared_ptr &core, + const std::shared_ptr &room, + const std::shared_ptr &message, + const std::shared_ptr &reaction) { + addChatToList(core, room, message); }); connect(this, &ChatList::filterChanged, [this](QString filter) { diff --git a/Linphone/data/CMakeLists.txt b/Linphone/data/CMakeLists.txt index 9fa123e30..f4a9a7d66 100644 --- a/Linphone/data/CMakeLists.txt +++ b/Linphone/data/CMakeLists.txt @@ -1,7 +1,6 @@ list(APPEND _LINPHONEAPP_RC_FILES data/assistant/use-app-sip-account.rc data/assistant/create-app-sip-account.rc data/assistant/use-other-sip-account.rc - data/shaders/opacityMask.frag.qsb data/shaders/roundEffect.frag.qsb data/emoji/emoji.json ) diff --git a/Linphone/model/account/AccountModel.cpp b/Linphone/model/account/AccountModel.cpp index abb8b1679..6c0ee641e 100644 --- a/Linphone/model/account/AccountModel.cpp +++ b/Linphone/model/account/AccountModel.cpp @@ -45,8 +45,7 @@ AccountModel::AccountModel(const std::shared_ptr &account, QO }); connect(CoreModel::getInstance().get(), &CoreModel::unreadNotificationsChanged, this, [this]() { - emit unreadNotificationsChanged(0 /*mMonitor->getUnreadChatMessageCount()*/, - mMonitor->getMissedCallsCount()); // TODO + emit unreadNotificationsChanged(mMonitor->getUnreadChatMessageCount(), mMonitor->getMissedCallsCount()); }); connect(CoreModel::getInstance().get(), &CoreModel::accountRemoved, this, [this](const std::shared_ptr &core, const std::shared_ptr &account) { diff --git a/Linphone/view/Control/Display/Chat/ChatMessagesListView.qml b/Linphone/view/Control/Display/Chat/ChatMessagesListView.qml index ac377a817..d0a3afe51 100644 --- a/Linphone/view/Control/Display/Chat/ChatMessagesListView.qml +++ b/Linphone/view/Control/Display/Chat/ChatMessagesListView.qml @@ -14,26 +14,24 @@ ListView { spacing: Math.round(4 * DefaultStyle.dp) property ChatGui chat property color backgroundColor + property bool lastItemVisible: false signal showReactionsForMessageRequested(ChatMessageGui chatMessage) signal showImdnStatusForMessageRequested(ChatMessageGui chatMessage) signal replyToMessageRequested(ChatMessageGui chatMessage) Component.onCompleted: { - var index = eventLogProxy.findFirstUnreadIndex() - positionViewAtIndex(index, ListView.End) - var chatMessage = eventLogProxy.getEventAtIndex(index)?.core?.chatMessage - if (chatMessage && !chatMessage.isRead) chatMessage.lMarkAsRead() + Qt.callLater(function() { + var index = eventLogProxy.findFirstUnreadIndex() + positionViewAtIndex(index, ListView.End) + }) } onCountChanged: if (atYEnd) { - var index = eventLogProxy.findFirstUnreadIndex() - mainItem.positionViewAtIndex(index, ListView.End) - var chatMessage = eventLogProxy.getEventAtIndex(index)?.core?.chatMessage - if (chatMessage && !chatMessage.isRead) chatMessage.lMarkAsRead() + positionViewAtEnd() } - + Button { - visible: !mainItem.atYEnd + visible: !mainItem.lastItemVisible icon.source: AppIcons.downArrow leftPadding: Math.round(16 * DefaultStyle.dp) rightPadding: Math.round(16 * DefaultStyle.dp) @@ -46,11 +44,14 @@ ListView { onClicked: { var index = eventLogProxy.findFirstUnreadIndex() mainItem.positionViewAtIndex(index, ListView.End) - var chatMessage = eventLogProxy.getEventAtIndex(index)?.core?.chatMessage - if (chatMessage && !chatMessage.isRead) chatMessage.lMarkAsRead() } } + onAtYEndChanged: if (atYEnd) { + if (eventLogProxy.haveMore) + eventLogProxy.displayMore() + } + model: EventLogProxy { id: eventLogProxy chatGui: mainItem.chat @@ -58,8 +59,6 @@ ListView { onEventInserted: (index, gui) => { if (!mainItem.visible) return mainItem.positionViewAtIndex(index, ListView.End) - if (gui.core.chatMessage && !gui.core.chatMessage.isRead) - gui.core.chatMessage.lMarkAsRead() } } @@ -114,67 +113,92 @@ ListView { } - delegate: DelegateChooser { + delegate: DelegateChooser { role: "eventType" DelegateChoice { roleValue: "chatMessage" - delegate: - ChatMessage { - chatMessage: modelData - chat: mainItem.chat - maxWidth: Math.round(mainItem.width * (3/4)) - onVisibleChanged: { - if (visible && !modelData.core.isRead) modelData.core.lMarkAsRead() - } - width: mainItem.width - property var previousIndex: index - 1 - property var previousFromAddress: eventLogProxy.getEventAtIndex(index-1)?.core.chatMessage?.fromAddress - backgroundColor: isRemoteMessage ? DefaultStyle.main2_100 : DefaultStyle.main1_100 - isFirstMessage: !previousFromAddress || previousFromAddress !== modelData.core.fromAddress - anchors.right: !isRemoteMessage && parent - ? parent.right - : undefined + delegate: ChatMessage { + id: chatMessageDelegate + property int yoff: Math.round(chatMessageDelegate.y - mainItem.contentY) + property bool isFullyVisible: (yoff > mainItem.y && yoff + height < mainItem.y + mainItem.height) + onIsFullyVisibleChanged: { + if (index === mainItem.count - 1) { + mainItem.lastItemVisible = isFullyVisible + } + if (isFullyVisible) + modelData.core.lMarkAsRead() + } + chatMessage: modelData + chat: mainItem.chat + maxWidth: Math.round(mainItem.width * (3/4)) + onVisibleChanged: { + if (visible) { + modelData.core.lMarkAsRead() + } + } + width: mainItem.width + property var previousIndex: index - 1 + property var previousFromAddress: eventLogProxy.getEventAtIndex(index-1)?.core.chatMessage?.fromAddress + backgroundColor: isRemoteMessage ? DefaultStyle.main2_100 : DefaultStyle.main1_100 + isFirstMessage: !previousFromAddress || previousFromAddress !== modelData.core.fromAddress + anchors.right: !isRemoteMessage && parent + ? parent.right + : undefined - onMessageDeletionRequested: modelData.core.lDelete() - onShowReactionsForMessageRequested: mainItem.showReactionsForMessageRequested(modelData) - onShowImdnStatusForMessageRequested: mainItem.showImdnStatusForMessageRequested(modelData) - onReplyToMessageRequested: mainItem.replyToMessageRequested(modelData) - } + onMessageDeletionRequested: modelData.core.lDelete() + onShowReactionsForMessageRequested: mainItem.showReactionsForMessageRequested(modelData) + onShowImdnStatusForMessageRequested: mainItem.showImdnStatusForMessageRequested(modelData) + onReplyToMessageRequested: mainItem.replyToMessageRequested(modelData) + } } DelegateChoice { roleValue: "event" - delegate: - Item { - property bool showTopMargin: !header.visible && index == 0 - width: mainItem.width - height: (showTopMargin ? 30 : 0 * DefaultStyle.dp) + eventItem.implicitHeight - Event { - id: eventItem - anchors.top: parent.top - anchors.topMargin: showTopMargin ? 30 : 0 * DefaultStyle.dp - width: parent.width - eventLogGui: modelData - } - } + delegate: Item { + id: eventDelegate + property int yoff: Math.round(eventDelegate.y - mainItem.contentY) + property bool isFullyVisible: (yoff > mainItem.y && yoff + height < mainItem.y + mainItem.height) + onIsFullyVisibleChanged: { + if (index === mainItem.count - 1) { + mainItem.lastItemVisible = isFullyVisible + } + } + property bool showTopMargin: !header.visible && index == 0 + width: mainItem.width + height: (showTopMargin ? 30 : 0 * DefaultStyle.dp) + eventItem.implicitHeight + Event { + id: eventItem + anchors.top: parent.top + anchors.topMargin: showTopMargin ? 30 : 0 * DefaultStyle.dp + width: parent.width + eventLogGui: modelData + } + } } DelegateChoice { roleValue: "ephemeralEvent" - delegate: - Item { - property bool showTopMargin: !header.visible && index == 0 - width: mainItem.width - //height: 40 * DefaultStyle.dp - height: (showTopMargin ? 30 : 0 * DefaultStyle.dp) + eventItem.height - EphemeralEvent { - id: eventItem - anchors.top: parent.top - anchors.topMargin: showTopMargin ? 30 : 0 * DefaultStyle.dp - eventLogGui: modelData - } - } + delegate: Item { + id: ephemeralEventDelegate + property int yoff: Math.round(ephemeralEventDelegate.y - mainItem.contentY) + property bool isFullyVisible: (yoff > mainItem.y && yoff + height < mainItem.y + mainItem.height) + onIsFullyVisibleChanged: { + if (index === mainItem.count - 1) { + mainItem.lastItemVisible = isFullyVisible + } + } + property bool showTopMargin: !header.visible && index == 0 + width: mainItem.width + //height: 40 * DefaultStyle.dp + height: (showTopMargin ? 30 : 0 * DefaultStyle.dp) + eventItem.height + EphemeralEvent { + id: eventItem + anchors.top: parent.top + anchors.topMargin: showTopMargin ? 30 : 0 * DefaultStyle.dp + eventLogGui: modelData + } + } } } diff --git a/Linphone/view/Control/Display/Chat/ChatTextContent.qml b/Linphone/view/Control/Display/Chat/ChatTextContent.qml index 5dce1138a..ff16a86f5 100644 --- a/Linphone/view/Control/Display/Chat/ChatTextContent.qml +++ b/Linphone/view/Control/Display/Chat/ChatTextContent.qml @@ -27,7 +27,7 @@ TextEdit { property var encodeTextObj: visible ? UtilsCpp.encodeTextToQmlRichFormat(contentGui.core.utf8Text, {}, mainItem.chatGui) : '' - text: encodeTextObj ? encodeTextObj.value : "" + text: encodeTextObj && encodeTextObj.value || "" textFormat: Text.RichText // To supports links and imgs. wrapMode: TextEdit.Wrap @@ -43,11 +43,10 @@ TextEdit { } onSelectedTextChanged:{ if(selectedText != '') lastTextSelected = selectedText - // else { - // if(mouseArea.keepLastSelection) { - // mouseArea.keepLastSelection = false - // } - // } + } + onLinkHovered: { + if (hoveredLink !== "") UtilsCpp.setGlobalCursor(Qt.PointingHandCursor) + else UtilsCpp.restoreGlobalCursor() } onActiveFocusChanged: { if(activeFocus) { diff --git a/Linphone/view/Control/Display/Chat/FileView.qml b/Linphone/view/Control/Display/Chat/FileView.qml index d7bbf19f6..a2078b7a8 100644 --- a/Linphone/view/Control/Display/Chat/FileView.qml +++ b/Linphone/view/Control/Display/Chat/FileView.qml @@ -17,20 +17,23 @@ Item { property string thumbnail: contentGui && contentGui.core.thumbnail property string name: contentGui && contentGui.core.name property string filePath: contentGui && contentGui.core.filePath - property bool active: true - property real animationScale : FileViewStyle.animation.to - property alias imageScale: thumbnailProvider.scale property bool wasDownloaded: contentGui && contentGui.core.wasDownloaded property bool isAnimatedImage : contentGui && contentGui.core.wasDownloaded && UtilsCpp.isAnimatedImage(filePath) property bool haveThumbnail: contentGui && UtilsCpp.canHaveThumbnail(filePath) property int fileSize: contentGui ? contentGui.core.fileSize : 0 property bool isTransferring + property bool isVideo: UtilsCpp.isVideo(filePath) + property bool isImage: UtilsCpp.isImage(filePath) + property bool isPdf: UtilsCpp.isPdf(filePath) + property bool isThumbnail: isVideo || isImage || isPdf + property int overriddenWidth + property int overriddenHeight Connections { enabled: contentGui target: contentGui.core function onMsgStateChanged(state) { - isTransferring = state === LinphoneEnums.ChatMessageState.StateFileTransferInProgress + mainItem.isTransferring = state === LinphoneEnums.ChatMessageState.StateFileTransferInProgress || state === LinphoneEnums.ChatMessageState.StateInProgress } } diff --git a/Linphone/view/Control/Display/Contact/Contact.qml b/Linphone/view/Control/Display/Contact/Contact.qml index 6d8ce2b0c..cb0622017 100644 --- a/Linphone/view/Control/Display/Contact/Contact.qml +++ b/Linphone/view/Control/Display/Contact/Contact.qml @@ -145,7 +145,7 @@ Control.Control{ Layout.preferredHeight: Math.round(26 * DefaultStyle.dp) Layout.fillHeight: true Layout.leftMargin: Math.round(40 * DefaultStyle.dp) - visible: mainItem.account.core.unreadCallNotifications > 0 + visible: mainItem.account.core.unreadNotifications > 0 Rectangle{ id: unreadNotifications anchors.verticalCenter: parent.verticalCenter @@ -166,7 +166,7 @@ Control.Control{ fontSizeMode: Text.Fit font.pixelSize: Math.round(11 * DefaultStyle.dp) font.weight: Math.round(700 * DefaultStyle.dp) - text: mainItem.account.core.unreadCallNotifications >= 100 ? '99+' : mainItem.account.core.unreadCallNotifications + text: mainItem.account.core.unreadNotifications >= 100 ? '99+' : mainItem.account.core.unreadNotifications } } MultiEffect { diff --git a/Linphone/view/Control/Input/Chat/ChatDroppableTextArea.qml b/Linphone/view/Control/Input/Chat/ChatDroppableTextArea.qml index 21f194279..2c8fa27f7 100644 --- a/Linphone/view/Control/Input/Chat/ChatDroppableTextArea.qml +++ b/Linphone/view/Control/Input/Chat/ChatDroppableTextArea.qml @@ -58,7 +58,7 @@ Control.Control { // height: mainItem.height leftPadding: Math.round(15 * DefaultStyle.dp) rightPadding: Math.round(15 * DefaultStyle.dp) - topPadding: Math.round(24 * DefaultStyle.dp) + topPadding: Math.round(16 * DefaultStyle.dp) bottomPadding: Math.round(16 * DefaultStyle.dp) background: Rectangle { anchors.fill: parent @@ -67,6 +67,10 @@ Control.Control { contentItem: Control.StackView { id: sendingAreaStackView initialItem: textAreaComp + width: currentItem.width + onHeightChanged: { + mainItem.height = height + mainItem.topPadding + mainItem.bottomPadding + } Component { id: textAreaComp RowLayout { @@ -97,11 +101,16 @@ Control.Control { } } Control.Control { + id: sendingControl + onHeightChanged: { + sendingAreaStackView.height = height + } Layout.fillWidth: true - leftPadding: Math.round(15 * DefaultStyle.dp) - rightPadding: Math.round(15 * DefaultStyle.dp) - topPadding: Math.round(15 * DefaultStyle.dp) - bottomPadding: Math.round(15 * DefaultStyle.dp) + Layout.alignment: Qt.AlignCenter + leftPadding: Math.round(24 * DefaultStyle.dp) + rightPadding: Math.round(20 * DefaultStyle.dp) + topPadding: Math.round(12 * DefaultStyle.dp) + bottomPadding: Math.round(12 * DefaultStyle.dp) background: Rectangle { id: inputBackground anchors.fill: parent @@ -116,7 +125,7 @@ Control.Control { contentItem: RowLayout { Flickable { id: sendingAreaFlickable - Layout.preferredHeight: Math.min(Math.round(60 * DefaultStyle.dp), contentHeight) + Layout.preferredHeight: Math.min(Math.round(100 * DefaultStyle.dp), contentHeight) Layout.fillHeight: true Layout.fillWidth: true contentHeight: sendingTextArea.contentHeight @@ -224,6 +233,9 @@ Control.Control { } ChatAudioContent { id: voiceMessage + onHeightChanged: { + sendingAreaStackView.height = height + } recording: true Layout.fillWidth: true Layout.preferredHeight: Math.round(48 * DefaultStyle.dp) diff --git a/Linphone/view/Page/Form/Chat/SelectedChatView.qml b/Linphone/view/Page/Form/Chat/SelectedChatView.qml index b5a6114de..ab9346b1b 100644 --- a/Linphone/view/Page/Form/Chat/SelectedChatView.qml +++ b/Linphone/view/Page/Form/Chat/SelectedChatView.qml @@ -213,7 +213,7 @@ RowLayout { Control.Control { id: participantListPopup width: parent.width - height: Math.min(contentItem.height, Math.round(200 * DefaultStyle.dp)) + height: Math.min(participantInfoList.height, Math.round(200 * DefaultStyle.dp)) visible: false anchors.bottom: chatMessagesListView.bottom anchors.left: chatMessagesListView.left @@ -378,7 +378,7 @@ RowLayout { } ChatDroppableTextArea { id: messageSender - Control.SplitView.preferredHeight: mainItem.chat.core.isReadOnly ? 0 : Math.round(79 * DefaultStyle.dp) + Control.SplitView.preferredHeight: mainItem.chat.core.isReadOnly ? 0 : height Control.SplitView.minimumHeight: mainItem.chat.core.isReadOnly ? 0 : Math.round(79 * DefaultStyle.dp) chat: mainItem.chat onChatChanged: {