From a7ba374b8f7e61b3fa5dbf4269278164a56a34af Mon Sep 17 00:00:00 2001 From: Gaelle Braud Date: Mon, 24 Nov 2025 18:37:42 +0100 Subject: [PATCH] improve chat messages list view fix auto scroll when receiving new message #LINQT-2154 --- Linphone/core/chat/message/EventLogList.cpp | 55 ++++-- Linphone/core/chat/message/EventLogList.hpp | 9 +- Linphone/core/chat/message/EventLogProxy.cpp | 171 ++++++++++++------ Linphone/core/chat/message/EventLogProxy.hpp | 46 ++++- Linphone/data/languages/de.ts | 58 +++--- Linphone/data/languages/en.ts | 58 +++--- Linphone/data/languages/fr.ts | 58 +++--- .../Display/Chat/ChatMessagesListView.qml | 55 +++--- .../view/Page/Form/Chat/SelectedChatView.qml | 4 +- 9 files changed, 304 insertions(+), 210 deletions(-) diff --git a/Linphone/core/chat/message/EventLogList.cpp b/Linphone/core/chat/message/EventLogList.cpp index 1fd0d3d13..16831dfaf 100644 --- a/Linphone/core/chat/message/EventLogList.cpp +++ b/Linphone/core/chat/message/EventLogList.cpp @@ -96,10 +96,12 @@ void EventLogList::setChatCore(QSharedPointer core) { [event](const QSharedPointer item) { return item == event; }); if (it == eventsList.end()) { connectItem(event); - add(event); + prepend(event); int index; get(event.get(), &index); - emit eventInserted(index, new EventLogGui(event)); + if (event->getChatMessageCore() && !event->getChatMessageCore()->isRemoteMessage()) { + emit eventInsertedByUser(index); + } } } }); @@ -122,6 +124,13 @@ void EventLogList::setDisplayItemsStep(int displayItemsStep) { } } +void EventLogList::markIndexAsRead(int index) { + if (index < mList.count()) { + auto eventLog = mList[index].objectCast(); + if (eventLog && eventLog->getChatMessageCore()) eventLog->getChatMessageCore()->lMarkAsRead(); + } +} + void EventLogList::displayMore() { auto loadMoreItems = [this] { if (!mChatCore) return; @@ -139,7 +148,7 @@ void EventLogList::displayMore() { QList> *events = new QList>(); for (auto it : linphoneLogs) { auto model = EventLogCore::create(it, chatModel->getMonitor()); - events->push_back(model); + if (it->getChatMessage() || model->isHandled()) events->push_front(model); } mCoreModelConnection->invokeToCore([this, events] { int currentCount = mList.count(); @@ -147,8 +156,8 @@ void EventLogList::displayMore() { for (int i = events->size() - 1; i >= 0; --i) { const auto &ev = events->at(i); connectItem(ev); - prepend(ev); } + add(*events); } }); }); @@ -166,8 +175,10 @@ void EventLogList::displayMore() { void EventLogList::loadMessagesUpTo(std::shared_ptr event) { mustBeInLinphoneThread(log().arg(Q_FUNC_INFO)); - auto oldestEventLoaded = getAt(0); - auto linOldest = std::const_pointer_cast(oldestEventLoaded->getModel()->getEventLog()); + auto oldestEventLoaded = mList.count() > 0 ? getAt(mList.count() - 1) : nullptr; + auto linOldest = oldestEventLoaded + ? std::const_pointer_cast(oldestEventLoaded->getModel()->getEventLog()) + : nullptr; auto chatModel = mChatCore->getModel(); assert(chatModel); if (!chatModel) return; @@ -179,45 +190,52 @@ void EventLogList::loadMessagesUpTo(std::shared_ptr event) { const auto &linChatRoom = chatModel->getMonitor(); for (const auto &it : beforeEvents) { auto model = EventLogCore::create(it, linChatRoom); - events->push_back(model); + if (it->getChatMessage() || model->isHandled()) events->push_front(model); } for (const auto &it : linphoneLogs) { auto model = EventLogCore::create(it, linChatRoom); - events->push_back(model); + if (it->getChatMessage() || model->isHandled()) events->push_front(model); } mCoreModelConnection->invokeToCore([this, events, event] { for (const auto &e : *events) { connectItem(e); - add(e); } + add(*events); emit messagesLoadedUpTo(event); }); } int EventLogList::findFirstUnreadIndex() { auto eventList = getSharedList(); - auto it = std::find_if(eventList.begin(), eventList.end(), [](const QSharedPointer item) { - return item->getChatMessageCore() && !item->getChatMessageCore()->isRead(); + auto it = std::find_if(eventList.rbegin(), eventList.rend(), [](const QSharedPointer item) { + auto chatmessage = item->getChatMessageCore(); + return chatmessage && !chatmessage->isRead(); }); - return it == eventList.end() ? -1 : std::distance(eventList.begin(), it); + return it == eventList.rend() ? -1 : std::distance(it, eventList.rend()) - 1; } -void EventLogList::findChatMessageWithFilter(QString filter, - QSharedPointer startEvent, - bool forward, - bool isFirstResearch) { +void EventLogList::findChatMessageWithFilter(QString filter, int startIndex, bool forward, bool isFirstResearch) { if (mChatCore) { if (isFirstResearch) mLastFoundResult.reset(); auto chatModel = mChatCore->getModel(); + auto startEvent = + startIndex >= 0 && startIndex < mList.count() ? mList[startIndex].objectCast() : nullptr; + lInfo() << log().arg("searching event starting from index") << startIndex << "| event :" + << (startEvent && startEvent->getChatMessageCore() ? startEvent->getChatMessageCore()->getText() + : "null") + << "| filter :" << filter; auto startEventModel = startEvent ? startEvent->getModel() : nullptr; mCoreModelConnection->invokeToModel([this, chatModel, startEventModel, filter, forward, isFirstResearch] { auto linStartEvent = startEventModel ? startEventModel->getEventLog() : nullptr; auto eventLog = chatModel->searchMessageByText(filter, linStartEvent, forward); - if (!eventLog) + if (!eventLog) { // event not found, search in the entire history + lInfo() << log().arg("not found, search in entire history"); auto eventLog = chatModel->searchMessageByText(filter, nullptr, forward); + } int index = -1; if (eventLog) { + lInfo() << log().arg("event with filter found") << eventLog.get(); auto eventList = getSharedList(); auto it = std::find_if(eventList.begin(), eventList.end(), [eventLog](const QSharedPointer item) { @@ -245,6 +263,7 @@ void EventLogList::findChatMessageWithFilter(QString filter, loadMessagesUpTo(eventLog); } } else { + lInfo() << log().arg("event not found at all in history"); mCoreModelConnection->invokeToCore([this, index] { emit messageWithFilterFound(index); }); } }); @@ -288,7 +307,7 @@ void EventLogList::setSelf(QSharedPointer me) { QList> *events = new QList>(); for (auto it : linphoneLogs) { auto model = EventLogCore::create(it, chatModel->getMonitor()); - events->push_back(model); + if (it->getChatMessage() || model->isHandled()) events->push_front(model); } mCoreModelConnection->invokeToCore([this, events] { for (auto &event : *events) { diff --git a/Linphone/core/chat/message/EventLogList.hpp b/Linphone/core/chat/message/EventLogList.hpp index 679738185..deec5b88e 100644 --- a/Linphone/core/chat/message/EventLogList.hpp +++ b/Linphone/core/chat/message/EventLogList.hpp @@ -54,13 +54,12 @@ public: int findFirstUnreadIndex(); + void markIndexAsRead(int index); + void displayMore(); void setDisplayItemsStep(int displayItemsStep); - void findChatMessageWithFilter(QString filter, - QSharedPointer startEvent, - bool forward = true, - bool isFirstResearch = true); + void findChatMessageWithFilter(QString filter, int startIndex, bool forward = true, bool isFirstResearch = true); void setSelf(QSharedPointer me); virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; @@ -69,7 +68,7 @@ public: signals: void lUpdate(); void filterChanged(QString filter); - void eventInserted(int index, EventLogGui *message); + void eventInsertedByUser(int index); void messageWithFilterFound(int index); void listAboutToBeReset(); void chatGuiChanged(); diff --git a/Linphone/core/chat/message/EventLogProxy.cpp b/Linphone/core/chat/message/EventLogProxy.cpp index 5fddfdf38..a1e45850d 100644 --- a/Linphone/core/chat/message/EventLogProxy.cpp +++ b/Linphone/core/chat/message/EventLogProxy.cpp @@ -26,7 +26,7 @@ DEFINE_ABSTRACT_OBJECT(EventLogProxy) -EventLogProxy::EventLogProxy(QObject *parent) : LimitProxy(parent) { +EventLogProxy::EventLogProxy(QObject *parent) : QSortFilterProxyModel(parent) { mList = EventLogList::create(); setSourceModel(mList.get()); } @@ -35,58 +35,41 @@ EventLogProxy::~EventLogProxy() { } void EventLogProxy::setSourceModel(QAbstractItemModel *model) { - auto oldEventLogList = getListModel(); + auto oldEventLogList = dynamic_cast(sourceModel()); if (oldEventLogList) { - disconnect(oldEventLogList, &EventLogList::listAboutToBeReset, this, nullptr); - disconnect(oldEventLogList, &EventLogList::chatGuiChanged, this, nullptr); disconnect(oldEventLogList, &EventLogList::displayItemsStepChanged, this, nullptr); - disconnect(oldEventLogList, &EventLogList::eventInserted, this, nullptr); disconnect(oldEventLogList, &EventLogList::messageWithFilterFound, this, nullptr); + disconnect(oldEventLogList, &EventLogList::eventInsertedByUser, this, nullptr); } auto newEventLogList = dynamic_cast(model); if (newEventLogList) { - connect(newEventLogList, &EventLogList::listAboutToBeReset, this, &EventLogProxy::listAboutToBeReset); - connect(newEventLogList, &EventLogList::chatGuiChanged, this, &EventLogProxy::chatGuiChanged); connect(this, &EventLogProxy::displayItemsStepChanged, newEventLogList, [this, newEventLogList] { newEventLogList->setDisplayItemsStep(mDisplayItemsStep); }); - connect(newEventLogList, &EventLogList::eventInserted, this, - [this, newEventLogList](int index, EventLogGui *event) { - invalidate(); - int proxyIndex = -1; - if (index != -1) { - proxyIndex = dynamic_cast(sourceModel()) - ->mapFromSource(newEventLogList->index(index, 0)) - .row(); - } - loadUntil(proxyIndex); - emit eventInserted(proxyIndex, event); - }); connect(newEventLogList, &EventLogList::messageWithFilterFound, this, [this, newEventLogList](int i) { - connect(this, &EventLogProxy::layoutChanged, newEventLogList, [this, i, newEventLogList] { - disconnect(this, &EventLogProxy::layoutChanged, newEventLogList, nullptr); - auto model = getListModel(); - int proxyIndex = - dynamic_cast(sourceModel())->mapFromSource(newEventLogList->index(i, 0)).row(); - if (i != -1) { - loadUntil(proxyIndex); - } - emit indexWithFilterFound(proxyIndex); - }); - invalidate(); + auto model = dynamic_cast(sourceModel()); + int proxyIndex = mapFromSource(newEventLogList->index(i, 0)).row(); + if (i != -1) { + loadUntil(proxyIndex); + } + emit indexWithFilterFound(proxyIndex); + }); + connect(newEventLogList, &EventLogList::eventInsertedByUser, this, [this, newEventLogList](int i) { + int proxyIndex = mapFromSource(newEventLogList->index(i, 0)).row(); + emit eventInsertedByUser(proxyIndex); }); } - setSourceModels(new SortFilterList(model, Qt::DescendingOrder)); - sort(0, Qt::DescendingOrder); + QSortFilterProxyModel::setSourceModel(model); } ChatGui *EventLogProxy::getChatGui() { - auto model = getListModel(); + auto model = dynamic_cast(sourceModel()); if (!mChatGui && model) mChatGui = model->getChat(); return mChatGui; } void EventLogProxy::setChatGui(ChatGui *chat) { - getListModel()->setChatGui(chat); + auto model = dynamic_cast(sourceModel()); + if (model) model->setChatGui(chat); } EventLogGui *EventLogProxy::getEventAtIndex(int i) { @@ -94,29 +77,86 @@ EventLogGui *EventLogProxy::getEventAtIndex(int i) { return eventCore == nullptr ? nullptr : new EventLogGui(eventCore); } +int EventLogProxy::getCount() const { + return rowCount(); +} + +int EventLogProxy::getInitialDisplayItems() const { + return mInitialDisplayItems; +} + +void EventLogProxy::setInitialDisplayItems(int initialItems) { + if (mInitialDisplayItems != initialItems) { + mInitialDisplayItems = initialItems; + if (getMaxDisplayItems() <= mInitialDisplayItems) setMaxDisplayItems(initialItems); + if (getDisplayItemsStep() <= 0) setDisplayItemsStep(initialItems); + emit initialDisplayItemsChanged(); + } +} + +int EventLogProxy::getDisplayCount(int listCount, int maxCount) { + return maxCount >= 0 ? qMin(listCount, maxCount) : listCount; +} + +int EventLogProxy::getDisplayCount(int listCount) const { + return getDisplayCount(listCount, mMaxDisplayItems); +} + QSharedPointer EventLogProxy::getEventCoreAtIndex(int i) { - return getItemAt(i); + auto model = dynamic_cast(sourceModel()); + if (model) { + return model->getAt(mapToSource(index(i, 0)).row()); + } + return nullptr; } void EventLogProxy::displayMore() { - auto model = getListModel(); + auto model = dynamic_cast(sourceModel()); if (model) { model->displayMore(); } } +int EventLogProxy::getMaxDisplayItems() const { + return mMaxDisplayItems; +} + +void EventLogProxy::setMaxDisplayItems(int maxItems) { + if (mMaxDisplayItems != maxItems) { + auto model = sourceModel(); + int modelCount = model ? model->rowCount() : 0; + int oldCount = getDisplayCount(modelCount); + mMaxDisplayItems = maxItems; + if (getInitialDisplayItems() > mMaxDisplayItems) setInitialDisplayItems(maxItems); + if (getDisplayItemsStep() <= 0) setDisplayItemsStep(maxItems); + emit maxDisplayItemsChanged(); + + if (model && getDisplayCount(modelCount) != oldCount) { + invalidate(); + } + } +} + +int EventLogProxy::getDisplayItemsStep() const { + return mDisplayItemsStep; +} + +void EventLogProxy::setDisplayItemsStep(int step) { + if (step > 0 && mDisplayItemsStep != step) { + mDisplayItemsStep = step; + emit displayItemsStepChanged(); + } +} void EventLogProxy::loadUntil(int index) { - auto confInfoList = getListModel(); if (mMaxDisplayItems < index) setMaxDisplayItems(index + mDisplayItemsStep); } int EventLogProxy::findFirstUnreadIndex() { - auto eventLogList = getListModel(); + auto eventLogList = dynamic_cast(sourceModel()); if (eventLogList) { auto listIndex = eventLogList->findFirstUnreadIndex(); if (listIndex != -1) { - listIndex = - dynamic_cast(sourceModel())->mapFromSource(eventLogList->index(listIndex, 0)).row(); + listIndex = mapFromSource(eventLogList->index(listIndex, 0)).row(); if (mMaxDisplayItems <= listIndex) setMaxDisplayItems(listIndex + mDisplayItemsStep); return listIndex; } else { @@ -126,32 +166,43 @@ int EventLogProxy::findFirstUnreadIndex() { return 0; } +QString EventLogProxy::getFilterText() const { + return mFilterText; +} + +void EventLogProxy::setFilterText(const QString &filter) { + if (mFilterText != filter) { +#if QT_VERSION >= QT_VERSION_CHECK(6, 10, 0) + beginFilterChange(); + mFilterText = filter; + endFilterChange(); +#else + mFilterText = filter; + invalidateFilter(); +#endif + emit filterTextChanged(); + } +} + +QSharedPointer EventLogProxy::getAt(int atIndex) const { + auto model = dynamic_cast(sourceModel()); + if (model) { + return model->getAt(mapToSource(index(atIndex, 0)).row()); + } + return nullptr; +} + void EventLogProxy::markIndexAsRead(int proxyIndex) { - auto event = getItemAt(proxyIndex); + auto event = getAt(proxyIndex); if (event && event->getChatMessageCore()) event->getChatMessageCore()->lMarkAsRead(); } void EventLogProxy::findIndexCorrespondingToFilter(int startIndex, bool forward, bool isFirstResearch) { auto filter = getFilterText(); if (filter.isEmpty()) return; - auto eventLogList = getListModel(); + auto eventLogList = dynamic_cast(sourceModel()); if (eventLogList) { - auto startEvent = mLastSearchStart; - if (!startEvent) { - startEvent = getItemAt(startIndex); - } - eventLogList->findChatMessageWithFilter(filter, startEvent, forward, isFirstResearch); + auto listIndex = mapToSource(index(startIndex, 0)).row(); + eventLogList->findChatMessageWithFilter(filter, listIndex, forward, isFirstResearch); } -} - -bool EventLogProxy::SortFilterList::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const { - auto l = getItemAtSource(sourceRow); - return l != nullptr; -} - -bool EventLogProxy::SortFilterList::lessThan(const QModelIndex &sourceLeft, const QModelIndex &sourceRight) const { - auto l = getItemAtSource(sourceLeft.row()); - auto r = getItemAtSource(sourceRight.row()); - if (l && r) return l->getTimestamp() < r->getTimestamp(); - return true; -} +} \ No newline at end of file diff --git a/Linphone/core/chat/message/EventLogProxy.hpp b/Linphone/core/chat/message/EventLogProxy.hpp index 738a39472..093c19543 100644 --- a/Linphone/core/chat/message/EventLogProxy.hpp +++ b/Linphone/core/chat/message/EventLogProxy.hpp @@ -22,19 +22,26 @@ #define EVENT_LIST_PROXY_H_ #include "EventLogList.hpp" -#include "core/proxy/LimitProxy.hpp" +// #include "core/proxy/LimitProxy.hpp" #include "tool/AbstractObject.hpp" +#include // ============================================================================= class ChatGui; -class EventLogProxy : public LimitProxy, public AbstractObject { +class EventLogProxy : public QSortFilterProxyModel, public AbstractObject { Q_OBJECT + Q_PROPERTY(int count READ getCount NOTIFY countChanged) Q_PROPERTY(ChatGui *chatGui READ getChatGui WRITE setChatGui NOTIFY chatGuiChanged) + Q_PROPERTY(int initialDisplayItems READ getInitialDisplayItems WRITE setInitialDisplayItems NOTIFY + initialDisplayItemsChanged) + Q_PROPERTY(int maxDisplayItems READ getMaxDisplayItems WRITE setMaxDisplayItems NOTIFY maxDisplayItemsChanged) + Q_PROPERTY(int displayItemsStep READ getDisplayItemsStep WRITE setDisplayItemsStep NOTIFY displayItemsStepChanged) + Q_PROPERTY(QString filterText READ getFilterText WRITE setFilterText NOTIFY filterTextChanged) public: - DECLARE_SORTFILTER_CLASS() + // DECLARE_SORTFILTER_CLASS() EventLogProxy(QObject *parent = Q_NULLPTR); ~EventLogProxy(); @@ -43,8 +50,27 @@ public: void setChatGui(ChatGui *chat); void setSourceModel(QAbstractItemModel *sourceModel) override; + virtual int getCount() const; + static int getDisplayCount(int listCount, int maxCount); + int getDisplayCount(int listCount) const; + int getInitialDisplayItems() const; + void setInitialDisplayItems(int initialItems); - Q_INVOKABLE void displayMore() override; + int getMaxDisplayItems() const; + void setMaxDisplayItems(int maxItems); + + int getDisplayItemsStep() const; + void setDisplayItemsStep(int step); + + QString getFilterText() const; + void setFilterText(const QString &filter); + + // bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override; + // bool lessThan(const QModelIndex &sourceLeft, const QModelIndex &sourceRight) const override; + + QSharedPointer getAt(int atIndex) const; + + Q_INVOKABLE void displayMore(); Q_INVOKABLE void loadUntil(int index); Q_INVOKABLE EventLogGui *getEventAtIndex(int i); QSharedPointer getEventCoreAtIndex(int i); @@ -53,15 +79,23 @@ public: Q_INVOKABLE void findIndexCorrespondingToFilter(int startIndex, bool forward = true, bool isFirstResearch = true); signals: - void eventInserted(int index, EventLogGui *message); + void eventInsertedByUser(int index); void indexWithFilterFound(int index); - void listAboutToBeReset(); void chatGuiChanged(); + void countChanged(); + void initialDisplayItemsChanged(); + void maxDisplayItemsChanged(); + void displayItemsStepChanged(); + void filterTextChanged(); protected: QSharedPointer mList; QSharedPointer mLastSearchStart; ChatGui *mChatGui = nullptr; + int mInitialDisplayItems = -1; + int mMaxDisplayItems = -1; + int mDisplayItemsStep = 5; + QString mFilterText; DECLARE_ABSTRACT_OBJECT }; diff --git a/Linphone/data/languages/de.ts b/Linphone/data/languages/de.ts index eb9185f1f..58f195e7e 100644 --- a/Linphone/data/languages/de.ts +++ b/Linphone/data/languages/de.ts @@ -2453,58 +2453,58 @@ 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 - + unencrypted_conversation_warning This conversation is not encrypted ! - + chat_message_list_encrypted_header_message Messages in this conversation are e2e encrypted. Only your correspondent can decrypt them. - + chat_message_list_not_encrypted_header_message Messages are not end to end encrypted, may sure you don't share any sensitive information ! - + chat_message_is_writing_info %1 is writing… @@ -3784,58 +3784,58 @@ Error EventLogCore - + conference_created_event - + conference_created_terminated - + conference_participant_added_event - + conference_participant_removed_event - - + + conference_security_event - + conference_ephemeral_message_enabled_event - + conference_ephemeral_message_lifetime_changed_event - + conference_ephemeral_message_disabled_event - + conference_subject_changed_event - + conference_participant_unset_admin_event - + conference_participant_set_admin_event @@ -5828,42 +5828,42 @@ Pour les activer dans un projet commercial, merci de nous contacter. SelectedChatView - + chat_view_group_call_toast_message Start a group call ? - + unencrypted_conversation_warning This conversation is not encrypted ! - + reply_to_label Reply to %1 - + shared_medias_title Shared medias - + shared_documents_title Shared documents - + forward_to_title Forward to… - + conversations_title Conversations Konversationen diff --git a/Linphone/data/languages/en.ts b/Linphone/data/languages/en.ts index 8d1d26009..0b6a92bdb 100644 --- a/Linphone/data/languages/en.ts +++ b/Linphone/data/languages/en.ts @@ -2425,44 +2425,44 @@ 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 - + unencrypted_conversation_warning This conversation is not encrypted ! This conversation is not encrypted ! - + chat_message_list_encrypted_header_message Messages in this conversation are e2e encrypted. Only your correspondent can decrypt them. @@ -2470,7 +2470,7 @@ Error Only your correspondent can decrypt them. - + chat_message_list_not_encrypted_header_message Messages are not end to end encrypted, may sure you don't share any sensitive information ! @@ -2478,7 +2478,7 @@ Only your correspondent can decrypt them. may sure you don't share any sensitive information ! - + chat_message_is_writing_info %1 is writing… %1 is writing… @@ -3703,59 +3703,59 @@ Only your correspondent can decrypt them. EventLogCore - + conference_created_event You have joined the group - + conference_created_terminated You have left the group - + conference_participant_added_event %1 has joined - + conference_participant_removed_event %1 is no longer in the conversation - + conference_participant_set_admin_event %1 is now an admin - + conference_participant_unset_admin_event %1 is no longer an admin - - + + conference_security_event Security level degraded by %1 - + conference_ephemeral_message_enabled_event Ephemeral messages enabled Expiration : %1 - + conference_ephemeral_message_disabled_event Ephemeral messages disabled - + conference_subject_changed_event New subject: %1 - + conference_ephemeral_message_lifetime_changed_event Ephemeral messages updated Expiration : %1 @@ -5723,42 +5723,42 @@ To enable them in a commercial project, please contact us. SelectedChatView - + chat_view_group_call_toast_message Start a group call ? - + unencrypted_conversation_warning This conversation is not encrypted ! This conversation is not encrypted ! - + reply_to_label Reply to %1 Reply to %1 - + shared_medias_title Shared medias Shared medias - + shared_documents_title Shared documents Shared documents - + forward_to_title Forward to… Froward to… - + conversations_title Conversations Conversations diff --git a/Linphone/data/languages/fr.ts b/Linphone/data/languages/fr.ts index 1f806520e..266e2190e 100644 --- a/Linphone/data/languages/fr.ts +++ b/Linphone/data/languages/fr.ts @@ -2425,44 +2425,44 @@ 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 - + unencrypted_conversation_warning This conversation is not encrypted ! Cette conversation n'est pas chiffrée ! - + chat_message_list_encrypted_header_message Messages in this conversation are e2e encrypted. Only your correspondent can decrypt them. @@ -2470,7 +2470,7 @@ Error en bout. Seul votre correspondant peut les déchiffrer. - + chat_message_list_not_encrypted_header_message Messages are not end to end encrypted, may sure you don't share any sensitive information ! @@ -2478,7 +2478,7 @@ en bout. Seul votre correspondant peut les déchiffrer. assurez-vous de ne pas partager d’informations sensibles ! - + chat_message_is_writing_info %1 is writing… %1 est en train d'écrire… @@ -3703,60 +3703,60 @@ en bout. Seul votre correspondant peut les déchiffrer. EventLogCore - + conference_created_event Vous avez rejoint le groupe - + conference_created_terminated Vous avez quitté le groupe - + conference_participant_added_event %1 a rejoint le groupe - + conference_participant_removed_event %1 ne fait plus partie du groupe - - + + conference_security_event Niveau de sécurité dégradé par %1 - + conference_ephemeral_message_enabled_event Messages éphémères activés Expiration : %1 - + conference_ephemeral_message_lifetime_changed_event Messages éphémères mis à jour Expiration : %1 - + conference_ephemeral_message_disabled_event Messages éphémères désactivés - + conference_subject_changed_event Nouveau sujet : %1 - + conference_participant_unset_admin_event %1 n'est plus admin - + conference_participant_set_admin_event %1 est maintenant admin @@ -5723,42 +5723,42 @@ Pour les activer dans un projet commercial, merci de nous contacter. SelectedChatView - + chat_view_group_call_toast_message Démarrer un appel de groupe ? - + unencrypted_conversation_warning This conversation is not encrypted ! Cette conversation n'est pas chiffrée ! - + reply_to_label Reply to %1 Réponse à %1 - + shared_medias_title Shared medias Médias partagés - + shared_documents_title Shared documents Documents partagés - + forward_to_title Forward to… Transférer à… - + conversations_title Conversations Conversations diff --git a/Linphone/view/Control/Display/Chat/ChatMessagesListView.qml b/Linphone/view/Control/Display/Chat/ChatMessagesListView.qml index 05acbf862..a152f0285 100644 --- a/Linphone/view/Control/Display/Chat/ChatMessagesListView.qml +++ b/Linphone/view/Control/Display/Chat/ChatMessagesListView.qml @@ -18,6 +18,7 @@ ListView { property real busyIndicatorSize: Utils.getSizeWithScreenRatio(60) property bool loading: false property bool isEncrypted: chat && chat.core.isEncrypted + highlightFollowsCurrentItem: false verticalLayoutDirection: ListView.BottomToTop signal showReactionsForMessageRequested(ChatMessageGui chatMessage) @@ -40,24 +41,16 @@ ListView { property bool searchForward: true onFindIndexWithFilter: (forward) => { searchForward = forward - eventLogProxy.findIndexCorrespondingToFilter(currentIndex, forward, false) - } - - Component.onCompleted: { - Qt.callLater(function() { - var index = eventLogProxy.findFirstUnreadIndex() - positionViewAtIndex(index, ListView.Beginning) - eventLogProxy.markIndexAsRead(index) - }) + eventLogProxy.findIndexCorrespondingToFilter(currentIndex, searchForward, false) } Button { visible: !mainItem.lastItemVisible icon.source: AppIcons.downArrow - leftPadding: Utils.getSizeWithScreenRatio(16) - rightPadding: Utils.getSizeWithScreenRatio(16) - topPadding: Utils.getSizeWithScreenRatio(16) - bottomPadding: Utils.getSizeWithScreenRatio(16) + leftPadding: Utils.getSizeWithScreenRatio(20) + rightPadding: Utils.getSizeWithScreenRatio(20) + topPadding: Utils.getSizeWithScreenRatio(20) + bottomPadding: Utils.getSizeWithScreenRatio(20) anchors.bottom: parent.bottom style: ButtonStyle.main anchors.right: parent.right @@ -65,20 +58,24 @@ ListView { anchors.rightMargin: Utils.getSizeWithScreenRatio(18) onClicked: { var index = eventLogProxy.findFirstUnreadIndex() - mainItem.positionViewAtIndex(index, ListView.Beginning) + mainItem.positionViewAtIndex(index, ListView.Contain) eventLogProxy.markIndexAsRead(index) } + UnreadNotification { + anchors.top: parent.top + anchors.right: parent.right + anchors.topMargin: Utils.getSizeWithScreenRatio(-5) + anchors.rightMargin: Utils.getSizeWithScreenRatio(-5) + unread: mainItem.chat?.core.unreadMessagesCount || 0 + } } - onAtYEndChanged: if (atYEnd && chat) { - chat.core.lMarkAsRead() - } - // Workaround : check if there is already items in the list - // so this function is only called when the user actually scroll - // the view till the begining onAtYBeginningChanged: if (atYBeginning && count !== 0) { eventLogProxy.displayMore() } + onAtYEndChanged: if (atYEnd && chat) { + chat.core.lMarkAsRead() + } model: EventLogProxy { id: eventLogProxy @@ -86,23 +83,18 @@ ListView { filterText: mainItem.filterText initialDisplayItems: 20 displayItemsStep: 20 - onEventInserted: (index, gui) => { - if (!mainItem.visible) return - if(mainItem.lastItemVisible) { - mainItem.positionViewAtIndex(index, ListView.Beginning) - markIndexAsRead(index) - } - } onModelAboutToBeReset: { loading = true } onModelReset: { loading = false var index = eventLogProxy.findFirstUnreadIndex() - positionViewAtIndex(index, ListView.Beginning) + mainItem.positionViewAtIndex(index, ListView.Contain) eventLogProxy.markIndexAsRead(index) } - onChatGuiChanged: forceLayout() + onEventInsertedByUser: (index) => { + mainItem.positionViewAtIndex(index, ListView.Beginning) + } onIndexWithFilterFound: (index) => { if (index !== -1) { currentIndex = index @@ -249,9 +241,10 @@ ListView { } } Component.onCompleted: { - if (index === 0) mainItem.lastItemVisible = isFullyVisible + if (index === 0) { + mainItem.lastItemVisible = isFullyVisible + } } - // onYChanged: if (index === 0) mainItem.lastItemVisible = isFullyVisible chat: mainItem.chat searchedTextPart: mainItem.filterText maxWidth: Math.round(mainItem.width * (3/4)) diff --git a/Linphone/view/Page/Form/Chat/SelectedChatView.qml b/Linphone/view/Page/Form/Chat/SelectedChatView.qml index 685cdf693..96cdd5c1c 100644 --- a/Linphone/view/Page/Form/Chat/SelectedChatView.qml +++ b/Linphone/view/Page/Form/Chat/SelectedChatView.qml @@ -27,13 +27,11 @@ FocusScope { signal groupCall() onActiveFocusChanged: if(activeFocus) { - console.log("selected has active focus, mark as read") - chat.core.lMarkAsRead() + if (chatMessagesListView.lastItemVisible) chat.core.lMarkAsRead() } MouseArea{ anchors.fill: parent onPressed: { - console.log("selected chat view pressed") forceActiveFocus() } }