diff --git a/Linphone/core/chat/message/EventLogProxy.cpp b/Linphone/core/chat/message/EventLogProxy.cpp index 26d69fa5..8d8fb96a 100644 --- a/Linphone/core/chat/message/EventLogProxy.cpp +++ b/Linphone/core/chat/message/EventLogProxy.cpp @@ -44,6 +44,7 @@ void EventLogProxy::setSourceModel(QAbstractItemModel *model) { connect(newEventLogList, &EventLogList::eventChanged, this, &EventLogProxy::eventChanged); connect(newEventLogList, &EventLogList::eventInserted, this, [this, newEventLogList](int index, EventLogGui *event) { + invalidate(); if (index != -1) { index = dynamic_cast(sourceModel()) ->mapFromSource(newEventLogList->index(index, 0)) @@ -53,7 +54,7 @@ void EventLogProxy::setSourceModel(QAbstractItemModel *model) { emit eventInserted(index, event); }); } - setSourceModels(new SortFilterList(model, Qt::AscendingOrder)); + setSourceModels(new SortFilterList(model, Qt::DescendingOrder)); sort(0); } @@ -88,10 +89,10 @@ int EventLogProxy::findFirstUnreadIndex() { if (mMaxDisplayItems <= listIndex) setMaxDisplayItems(listIndex + mDisplayItemsStep); return listIndex; } else { - return std::max(0, getCount() - 1); + return 0; } } - return std::max(0, getCount() - 1); + return 0; } void EventLogProxy::markIndexAsRead(int proxyIndex) { diff --git a/Linphone/data/languages/de.ts b/Linphone/data/languages/de.ts index 88fce5a2..fe95b0e3 100644 --- a/Linphone/data/languages/de.ts +++ b/Linphone/data/languages/de.ts @@ -2072,45 +2072,45 @@ Error ChatMessagesListView - - + + popup_info_find_message_title Find message - + info_popup_no_result_message No result found - + info_popup_first_result_message First result reached - + info_popup_last_result_message Last result reached - + chat_message_list_encrypted_header_title End to end encrypted chat - + chat_message_list_encrypted_header_message Les messages de cette conversation sont chiffrés de bout en bout. Seul votre correspondant peut les déchiffrer. - + chat_message_is_writing_info %1 is writing… diff --git a/Linphone/data/languages/en.ts b/Linphone/data/languages/en.ts index 19e973de..4b822290 100644 --- a/Linphone/data/languages/en.ts +++ b/Linphone/data/languages/en.ts @@ -2034,38 +2034,38 @@ Error ChatMessagesListView - - + + popup_info_find_message_title Find message Find message - + info_popup_no_result_message No result found No result found - + info_popup_first_result_message First result reached First result reached - + info_popup_last_result_message Last result reached Last result reached - + chat_message_list_encrypted_header_title End to end encrypted chat End to end encrypted chat - + chat_message_list_encrypted_header_message Les messages de cette conversation sont chiffrés de bout en bout. Seul votre correspondant peut les déchiffrer. @@ -2073,7 +2073,7 @@ Error Only your correspondent can decrypt them. - + chat_message_is_writing_info %1 is writing… %1 is writing… diff --git a/Linphone/data/languages/fr_FR.ts b/Linphone/data/languages/fr_FR.ts index fba62c0a..a7160862 100644 --- a/Linphone/data/languages/fr_FR.ts +++ b/Linphone/data/languages/fr_FR.ts @@ -2034,38 +2034,38 @@ Error ChatMessagesListView - - + + popup_info_find_message_title Find message Trouver un message - + info_popup_no_result_message No result found Aucun résultat trouvé - + info_popup_first_result_message First result reached Premier résultat atteint - + info_popup_last_result_message Last result reached Dernier résultat atteint - + chat_message_list_encrypted_header_title End to end encrypted chat Conversation chiffrée de bout en bout - + chat_message_list_encrypted_header_message Les messages de cette conversation sont chiffrés de bout en bout. Seul votre correspondant peut les déchiffrer. @@ -2073,7 +2073,7 @@ Error en bout. Seul votre correspondant peut les déchiffrer. - + chat_message_is_writing_info %1 is writing… %1 est en train d'écrire… diff --git a/Linphone/view/Control/Display/Chat/ChatMessagesListView.qml b/Linphone/view/Control/Display/Chat/ChatMessagesListView.qml index d5f00539..a6116d3c 100644 --- a/Linphone/view/Control/Display/Chat/ChatMessagesListView.qml +++ b/Linphone/view/Control/Display/Chat/ChatMessagesListView.qml @@ -13,6 +13,7 @@ ListView { property ChatGui chat property color backgroundColor property bool lastItemVisible: false + verticalLayoutDirection: ListView.BottomToTop signal showReactionsForMessageRequested(ChatMessageGui chatMessage) signal showImdnStatusForMessageRequested(ChatMessageGui chatMessage) signal replyToMessageRequested(ChatMessageGui chatMessage) @@ -57,7 +58,7 @@ ListView { Component.onCompleted: { Qt.callLater(function() { var index = eventLogProxy.findFirstUnreadIndex() - positionViewAtIndex(index, ListView.End) + positionViewAtIndex(index, ListView.Beginning) eventLogProxy.markIndexAsRead(index) }) } @@ -81,43 +82,46 @@ ListView { anchors.rightMargin: Math.round(18 * DefaultStyle.dp) onClicked: { var index = eventLogProxy.findFirstUnreadIndex() - mainItem.positionViewAtIndex(index, ListView.End) + mainItem.positionViewAtIndex(index, ListView.Beginning) eventLogProxy.markIndexAsRead(index) } } onAtYEndChanged: if (atYEnd) { + chat.core.lMarkAsRead() + } + onAtYBeginningChanged: if (atYBeginning) { if (eventLogProxy.haveMore) eventLogProxy.displayMore() - else chat.core.lMarkAsRead() } model: EventLogProxy { id: eventLogProxy chatGui: mainItem.chat - // scroll when in view and message inserted filterText: mainItem.filterText initialDisplayItems: 10 - displayItemsStep: 3 * initialDisplayItems / 2 onEventInserted: (index, gui) => { if (!mainItem.visible) return - if(mainItem.lastItemVisible) mainItem.positionViewAtIndex(index, ListView.End) + if(mainItem.lastItemVisible) mainItem.positionViewAtIndex(index, ListView.Beginning) } onModelReset: Qt.callLater(function() { var index = eventLogProxy.findFirstUnreadIndex() - positionViewAtIndex(index, ListView.End) + positionViewAtIndex(index, ListView.Beginning) eventLogProxy.markIndexAsRead(index) }) } - header: Item { - visible: mainItem.chat && mainItem.chat.core.isEncrypted - height: visible ? headerMessage.height + Math.round(50 * DefaultStyle.dp) : Math.round(30 * DefaultStyle.dp) + footer: Item { + visible: mainItem.chat && mainItem.chat.core.isEncrypted && !eventLogProxy.haveMore + height: visible ? headerMessage.height + headerMessage.topMargin + headerMessage.bottomMargin : Math.round(30 * DefaultStyle.dp) width: headerMessage.width anchors.horizontalCenter: parent.horizontalCenter Control.Control { id: headerMessage - anchors.topMargin: Math.round(30 * DefaultStyle.dp) + property int topMargin: mainItem.contentHeight > mainItem.height ? Math.round(30 * DefaultStyle.dp) : Math.round(50 * DefaultStyle.dp) + property int bottomMargin: Math.round(30 * DefaultStyle.dp) + anchors.topMargin: topMargin + anchors.bottomMargin: bottomMargin anchors.top: parent.top padding: Math.round(10 * DefaultStyle.dp) background: Rectangle { @@ -159,118 +163,12 @@ ListView { } } } - - - delegate: DelegateChooser { - role: "eventType" - - DelegateChoice { - roleValue: "chatMessage" - 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) - chatMessage: modelData.core.chatMessageGui - onIsFullyVisibleChanged: { - if (index === mainItem.count - 1) { - mainItem.lastItemVisible = isFullyVisible - } - } - Component.onCompleted: if (index === mainItem.count - 1) mainItem.lastItemVisible = isFullyVisible - chat: mainItem.chat - searchedTextPart: mainItem.filterText - maxWidth: Math.round(mainItem.width * (3/4)) - width: mainItem.width - property var previousIndex: index - 1 - property ChatMessageGui nextChatMessage: index >= (mainItem.count - 1) - ? null - : eventLogProxy.getEventAtIndex(index+1) - ? eventLogProxy.getEventAtIndex(index+1).core.chatMessageGui - : null - property var previousFromAddress: eventLogProxy.getEventAtIndex(index-1)?.core.chatMessage?.fromAddress - backgroundColor: isRemoteMessage ? DefaultStyle.main2_100 : DefaultStyle.main1_100 - isFirstMessage: !previousFromAddress || previousFromAddress !== chatMessage.core.fromAddress - anchors.right: !isRemoteMessage && parent - ? parent.right - : undefined - - onMessageDeletionRequested: chatMessage.core.lDelete() - onShowReactionsForMessageRequested: mainItem.showReactionsForMessageRequested(chatMessage) - onShowImdnStatusForMessageRequested: mainItem.showImdnStatusForMessageRequested(chatMessage) - onReplyToMessageRequested: mainItem.replyToMessageRequested(chatMessage) - onForwardMessageRequested: mainItem.forwardMessageRequested(chatMessage) - onEndOfVoiceRecordingReached: { - if (nextChatMessage && nextChatMessage.core.isVoiceRecording) mainItem.requestAutoPlayVoiceRecording(index + 1) - } - Connections { - target: mainItem - function onRequestHighlight(indexToHighlight) { - if (indexToHighlight === index) { - requestHighlight() - } - } - function onRequestAutoPlayVoiceRecording(indexToPlay) { - if (indexToPlay === index) { - chatMessageDelegate.requestAutoPlayVoiceRecording() - } - } - } - } - } - - DelegateChoice { - roleValue: "event" - 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 { - 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) + ephemeralEventItem.height - EphemeralEvent { - id: ephemeralEventItem - anchors.top: parent.top - anchors.topMargin: showTopMargin ? 30 : 0 * DefaultStyle.dp - eventLogGui: modelData - } - } - } - } - - footerPositioning: ListView.OverlayFooter - footer: Control.Control { + footerPositioning: ListView.PullBackFooter + headerPositioning: ListView.OverlayHeader + header: Control.Control { visible: composeLayout.composingName !== "" && composeLayout.composingName !== undefined width: mainItem.width + // height: visible ? contentItem.implicitHeight + topPadding + bottomPadding : 0 z: mainItem.z + 2 topPadding: Math.round(5 * DefaultStyle.dp) bottomPadding: Math.round(5 * DefaultStyle.dp) @@ -297,4 +195,112 @@ ListView { } } } + + delegate: DelegateChooser { + role: "eventType" + + DelegateChoice { + roleValue: "chatMessage" + 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) + chatMessage: modelData.core.chatMessageGui + onIsFullyVisibleChanged: { + if (index === 0) { + mainItem.lastItemVisible = isFullyVisible + } + } + Component.onCompleted: { + if (index === 0) mainItem.lastItemVisible = isFullyVisible + } + chat: mainItem.chat + searchedTextPart: mainItem.filterText + maxWidth: Math.round(mainItem.width * (3/4)) + width: mainItem.width + property var previousIndex: index - 1 + property ChatMessageGui nextChatMessage: index <= 0 + ? null + : eventLogProxy.getEventAtIndex(index-1) + ? eventLogProxy.getEventAtIndex(index-1).core.chatMessageGui + : null + property var previousFromAddress: eventLogProxy.getEventAtIndex(index+1)?.core.chatMessage?.fromAddress + backgroundColor: isRemoteMessage ? DefaultStyle.main2_100 : DefaultStyle.main1_100 + isFirstMessage: !previousFromAddress || previousFromAddress !== chatMessage.core.fromAddress + anchors.right: !isRemoteMessage && parent + ? parent.right + : undefined + + onMessageDeletionRequested: chatMessage.core.lDelete() + onShowReactionsForMessageRequested: mainItem.showReactionsForMessageRequested(chatMessage) + onShowImdnStatusForMessageRequested: mainItem.showImdnStatusForMessageRequested(chatMessage) + onReplyToMessageRequested: mainItem.replyToMessageRequested(chatMessage) + onForwardMessageRequested: mainItem.forwardMessageRequested(chatMessage) + onEndOfVoiceRecordingReached: { + if (nextChatMessage && nextChatMessage.core.isVoiceRecording) mainItem.requestAutoPlayVoiceRecording(index - 1) + } + Connections { + target: mainItem + function onRequestHighlight(indexToHighlight) { + if (indexToHighlight === index) { + requestHighlight() + } + } + function onRequestAutoPlayVoiceRecording(indexToPlay) { + if (indexToPlay === index) { + chatMessageDelegate.requestAutoPlayVoiceRecording() + } + } + } + } + } + + DelegateChoice { + roleValue: "event" + 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 === 0) { + mainItem.lastItemVisible = isFullyVisible + } + } + property bool showTopMargin: !header.visible && index == 0 + width: mainItem.width + height: (showTopMargin ? 30 * DefaultStyle.dp : 0) + 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 { + 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 === 0) { + mainItem.lastItemVisible = isFullyVisible + } + } + property bool showTopMargin: !header.visible && index == 0 + width: mainItem.width + //height: 40 * DefaultStyle.dp + height: (showTopMargin ? 30 : 0 * DefaultStyle.dp) + ephemeralEventItem.height + EphemeralEvent { + id: ephemeralEventItem + anchors.top: parent.top + anchors.topMargin: showTopMargin ? 30 : 0 * DefaultStyle.dp + eventLogGui: modelData + } + } + } + } }