Fixes in chat messages (navigation with read button, sorting)

This commit is contained in:
Gaëlle Braud 2025-07-23 21:09:14 +00:00
parent 8ac30ed393
commit ecf79d530a
8 changed files with 40 additions and 36 deletions

View file

@ -382,13 +382,6 @@ QDateTime ChatMessageCore::getTimestamp() const {
return mTimestamp; return mTimestamp;
} }
void ChatMessageCore::setTimestamp(QDateTime timestamp) {
if (mTimestamp != timestamp) {
mTimestamp = timestamp;
emit timestampChanged(timestamp);
}
}
QString ChatMessageCore::getText() const { QString ChatMessageCore::getText() const {
return mText; return mText;
} }

View file

@ -77,7 +77,7 @@ class EventLogCore;
class ChatMessageCore : public QObject, public AbstractObject { class ChatMessageCore : public QObject, public AbstractObject {
Q_OBJECT Q_OBJECT
Q_PROPERTY(QDateTime timestamp READ getTimestamp WRITE setTimestamp NOTIFY timestampChanged) Q_PROPERTY(QDateTime timestamp READ getTimestamp CONSTANT)
Q_PROPERTY(QString text READ getText WRITE setText NOTIFY textChanged) Q_PROPERTY(QString text READ getText WRITE setText NOTIFY textChanged)
Q_PROPERTY(QString utf8Text MEMBER mUtf8Text CONSTANT) Q_PROPERTY(QString utf8Text MEMBER mUtf8Text CONSTANT)
Q_PROPERTY(bool hasTextContent MEMBER mHasTextContent CONSTANT) Q_PROPERTY(bool hasTextContent MEMBER mHasTextContent CONSTANT)
@ -119,9 +119,6 @@ public:
QList<ImdnStatus> computeDeliveryStatus(const std::shared_ptr<linphone::ChatMessage> &message); QList<ImdnStatus> computeDeliveryStatus(const std::shared_ptr<linphone::ChatMessage> &message);
QDateTime getTimestamp() const;
void setTimestamp(QDateTime timestamp);
QString getText() const; QString getText() const;
void setText(QString text); void setText(QString text);
@ -138,6 +135,7 @@ public:
bool isEphemeral() const; bool isEphemeral() const;
int getEphemeralDuration() const; int getEphemeralDuration() const;
void setEphemeralDuration(int duration); void setEphemeralDuration(int duration);
QDateTime getTimestamp() const;
bool hasFileContent() const; bool hasFileContent() const;
@ -168,7 +166,6 @@ public:
Q_INVOKABLE ChatMessageContentGui *getVoiceRecordingContent() const; Q_INVOKABLE ChatMessageContentGui *getVoiceRecordingContent() const;
signals: signals:
void timestampChanged(QDateTime timestamp);
void textChanged(QString text); void textChanged(QString text);
void utf8TextChanged(QString text); void utf8TextChanged(QString text);
void isReadChanged(bool read); void isReadChanged(bool read);

View file

@ -37,9 +37,11 @@ EventLogCore::EventLogCore(const std::shared_ptr<const linphone::EventLog> &even
App::getInstance()->mEngine->setObjectOwnership(this, QQmlEngine::CppOwnership); App::getInstance()->mEngine->setObjectOwnership(this, QQmlEngine::CppOwnership);
mEventLogType = LinphoneEnums::fromLinphone(eventLog->getType()); mEventLogType = LinphoneEnums::fromLinphone(eventLog->getType());
mTimestamp = QDateTime::fromMSecsSinceEpoch(eventLog->getCreationTime() * 1000); mTimestamp = QDateTime::fromMSecsSinceEpoch(eventLog->getCreationTime() * 1000);
if (eventLog->getChatMessage()) { auto chatmessage = eventLog->getChatMessage();
mChatMessageCore = ChatMessageCore::create(eventLog->getChatMessage()); if (chatmessage) {
mEventId = Utils::coreStringToAppString(eventLog->getChatMessage()->getMessageId()); mChatMessageCore = ChatMessageCore::create(chatmessage);
mEventId = Utils::coreStringToAppString(chatmessage->getMessageId());
mTimestamp = QDateTime::fromSecsSinceEpoch(chatmessage->getTime());
} else if (eventLog->getCallLog()) { } else if (eventLog->getCallLog()) {
mCallHistoryCore = CallHistoryCore::create(eventLog->getCallLog()); mCallHistoryCore = CallHistoryCore::create(eventLog->getCallLog());
mEventId = Utils::coreStringToAppString(eventLog->getCallLog()->getCallId()); mEventId = Utils::coreStringToAppString(eventLog->getCallLog()->getCallId());
@ -80,6 +82,10 @@ CallHistoryCore *EventLogCore::getCallHistoryCorePointer() {
return mCallHistoryCore.get(); return mCallHistoryCore.get();
} }
QDateTime EventLogCore::getTimestamp() const {
return mTimestamp;
}
// Events (other than ChatMessage and CallLog which are handled in their respective Core) // Events (other than ChatMessage and CallLog which are handled in their respective Core)
void EventLogCore::computeEvent(const std::shared_ptr<const linphone::EventLog> &eventLog) { void EventLogCore::computeEvent(const std::shared_ptr<const linphone::EventLog> &eventLog) {

View file

@ -47,7 +47,7 @@ class EventLogCore : public QObject, public AbstractObject {
Q_PROPERTY(bool important MEMBER mImportant CONSTANT) Q_PROPERTY(bool important MEMBER mImportant CONSTANT)
Q_PROPERTY(bool handled MEMBER mHandled CONSTANT) Q_PROPERTY(bool handled MEMBER mHandled CONSTANT)
Q_PROPERTY(QString eventDetails MEMBER mEventDetails CONSTANT) Q_PROPERTY(QString eventDetails MEMBER mEventDetails CONSTANT)
Q_PROPERTY(QDateTime timestamp MEMBER mTimestamp CONSTANT) Q_PROPERTY(QDateTime timestamp READ getTimestamp CONSTANT)
public: public:
static QSharedPointer<EventLogCore> create(const std::shared_ptr<const linphone::EventLog> &eventLog); static QSharedPointer<EventLogCore> create(const std::shared_ptr<const linphone::EventLog> &eventLog);
@ -58,6 +58,9 @@ public:
QSharedPointer<ChatMessageCore> getChatMessageCore(); QSharedPointer<ChatMessageCore> getChatMessageCore();
ChatMessageGui *getChatMessageGui(); ChatMessageGui *getChatMessageGui();
QSharedPointer<CallHistoryCore> getCallHistoryCore(); QSharedPointer<CallHistoryCore> getCallHistoryCore();
QDateTime getTimestamp() const;
bool isHandled() const { bool isHandled() const {
return mHandled; return mHandled;
} }

View file

@ -141,14 +141,14 @@ QVariant EventLogList::data(const QModelIndex &index, int role) const {
if (core->getChatMessageCore()) { if (core->getChatMessageCore()) {
switch (role) { switch (role) {
case Qt::DisplayRole: case Qt::DisplayRole:
return QVariant::fromValue(new ChatMessageGui(core->getChatMessageCore())); return QVariant::fromValue(new EventLogGui(core));
case Qt::DisplayRole + 1: case Qt::DisplayRole + 1:
return "chatMessage"; return "chatMessage";
} }
} else if (core->getCallHistoryCore()) { } else if (core->getCallHistoryCore()) {
switch (role) { switch (role) {
case Qt::DisplayRole: case Qt::DisplayRole:
return QVariant::fromValue(new CallHistoryGui(core->getCallHistoryCore())); return QVariant::fromValue(new EventLogGui(core));
case Qt::DisplayRole + 1: case Qt::DisplayRole + 1:
return "callLog"; return "callLog";
} }

View file

@ -53,7 +53,7 @@ void EventLogProxy::setSourceModel(QAbstractItemModel *model) {
emit eventInserted(index, event); emit eventInserted(index, event);
}); });
} }
setSourceModels(new SortFilterList(model)); setSourceModels(new SortFilterList(model, Qt::AscendingOrder));
sort(0); sort(0);
} }
@ -94,6 +94,11 @@ int EventLogProxy::findFirstUnreadIndex() {
return std::max(0, getCount() - 1); return std::max(0, getCount() - 1);
} }
void EventLogProxy::markIndexAsRead(int proxyIndex) {
auto event = getItemAt<SortFilterList, EventLogList, EventLogCore>(proxyIndex);
if (event && event->getChatMessageCore()) event->getChatMessageCore()->lMarkAsRead();
}
int EventLogProxy::findIndexCorrespondingToFilter(int startIndex, bool goingBackward) { int EventLogProxy::findIndexCorrespondingToFilter(int startIndex, bool goingBackward) {
auto filter = getFilterText(); auto filter = getFilterText();
if (filter.isEmpty()) return startIndex; if (filter.isEmpty()) return startIndex;
@ -117,8 +122,8 @@ bool EventLogProxy::SortFilterList::filterAcceptsRow(int sourceRow, const QModel
} }
bool EventLogProxy::SortFilterList::lessThan(const QModelIndex &sourceLeft, const QModelIndex &sourceRight) const { bool EventLogProxy::SortFilterList::lessThan(const QModelIndex &sourceLeft, const QModelIndex &sourceRight) const {
auto l = getItemAtSource<EventLogList, ChatMessageCore>(sourceLeft.row()); auto l = getItemAtSource<EventLogList, EventLogCore>(sourceLeft.row());
auto r = getItemAtSource<EventLogList, ChatMessageCore>(sourceRight.row()); auto r = getItemAtSource<EventLogList, EventLogCore>(sourceRight.row());
if (l && r) return l->getTimestamp() <= r->getTimestamp(); if (l && r) return l->getTimestamp() <= r->getTimestamp();
else return true; else return true;
} }

View file

@ -46,6 +46,7 @@ public:
Q_INVOKABLE EventLogGui *getEventAtIndex(int index); Q_INVOKABLE EventLogGui *getEventAtIndex(int index);
Q_INVOKABLE int findFirstUnreadIndex(); Q_INVOKABLE int findFirstUnreadIndex();
Q_INVOKABLE void markIndexAsRead(int proxyIndex);
Q_INVOKABLE int findIndexCorrespondingToFilter(int startIndex, bool goingBackward = false); Q_INVOKABLE int findIndexCorrespondingToFilter(int startIndex, bool goingBackward = false);
signals: signals:

View file

@ -5,6 +5,7 @@ import QtQuick.Controls.Basic as Control
import Qt.labs.qmlmodels import Qt.labs.qmlmodels
import Linphone import Linphone
import UtilsCpp import UtilsCpp
import 'qrc:/qt/qml/Linphone/view/Style/buttonStyle.js' as ButtonStyle
ListView { ListView {
id: mainItem id: mainItem
@ -57,6 +58,7 @@ ListView {
Qt.callLater(function() { Qt.callLater(function() {
var index = eventLogProxy.findFirstUnreadIndex() var index = eventLogProxy.findFirstUnreadIndex()
positionViewAtIndex(index, ListView.End) positionViewAtIndex(index, ListView.End)
eventLogProxy.markIndexAsRead(index)
}) })
} }
@ -73,18 +75,21 @@ ListView {
topPadding: Math.round(16 * DefaultStyle.dp) topPadding: Math.round(16 * DefaultStyle.dp)
bottomPadding: Math.round(16 * DefaultStyle.dp) bottomPadding: Math.round(16 * DefaultStyle.dp)
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
style: ButtonStyle.main
anchors.right: parent.right anchors.right: parent.right
anchors.bottomMargin: Math.round(18 * DefaultStyle.dp) anchors.bottomMargin: Math.round(18 * DefaultStyle.dp)
anchors.rightMargin: Math.round(18 * DefaultStyle.dp) anchors.rightMargin: Math.round(18 * DefaultStyle.dp)
onClicked: { onClicked: {
var index = eventLogProxy.findFirstUnreadIndex() var index = eventLogProxy.findFirstUnreadIndex()
mainItem.positionViewAtIndex(index, ListView.End) mainItem.positionViewAtIndex(index, ListView.End)
eventLogProxy.markIndexAsRead(index)
} }
} }
onAtYEndChanged: if (atYEnd) { onAtYEndChanged: if (atYEnd) {
if (eventLogProxy.haveMore) if (eventLogProxy.haveMore)
eventLogProxy.displayMore() eventLogProxy.displayMore()
else chat.core.lMarkAsRead()
} }
model: EventLogProxy { model: EventLogProxy {
@ -94,11 +99,12 @@ ListView {
filterText: mainItem.filterText filterText: mainItem.filterText
onEventInserted: (index, gui) => { onEventInserted: (index, gui) => {
if (!mainItem.visible) return if (!mainItem.visible) return
mainItem.positionViewAtIndex(index, ListView.End) if(mainItem.lastItemVisible) mainItem.positionViewAtIndex(index, ListView.End)
} }
onModelReset: Qt.callLater(function() { onModelReset: Qt.callLater(function() {
var index = eventLogProxy.findFirstUnreadIndex() var index = eventLogProxy.findFirstUnreadIndex()
positionViewAtIndex(index, ListView.End) positionViewAtIndex(index, ListView.End)
eventLogProxy.markIndexAsRead(index)
}) })
} }
@ -162,23 +168,16 @@ ListView {
id: chatMessageDelegate id: chatMessageDelegate
property int yoff: Math.round(chatMessageDelegate.y - mainItem.contentY) property int yoff: Math.round(chatMessageDelegate.y - mainItem.contentY)
property bool isFullyVisible: (yoff > mainItem.y && yoff + height < mainItem.y + mainItem.height) property bool isFullyVisible: (yoff > mainItem.y && yoff + height < mainItem.y + mainItem.height)
chatMessage: modelData.core.chatMessageGui
onIsFullyVisibleChanged: { onIsFullyVisibleChanged: {
if (index === mainItem.count - 1) { if (index === mainItem.count - 1) {
mainItem.lastItemVisible = isFullyVisible mainItem.lastItemVisible = isFullyVisible
} }
if (isFullyVisible)
modelData.core.lMarkAsRead()
} }
Component.onCompleted: if (index === mainItem.count - 1) mainItem.lastItemVisible = isFullyVisible Component.onCompleted: if (index === mainItem.count - 1) mainItem.lastItemVisible = isFullyVisible
chatMessage: modelData
chat: mainItem.chat chat: mainItem.chat
searchedTextPart: mainItem.filterText searchedTextPart: mainItem.filterText
maxWidth: Math.round(mainItem.width * (3/4)) maxWidth: Math.round(mainItem.width * (3/4))
onVisibleChanged: {
if (visible) {
modelData.core.lMarkAsRead()
}
}
width: mainItem.width width: mainItem.width
property var previousIndex: index - 1 property var previousIndex: index - 1
property ChatMessageGui nextChatMessage: index >= (mainItem.count - 1) property ChatMessageGui nextChatMessage: index >= (mainItem.count - 1)
@ -188,16 +187,16 @@ ListView {
: null : null
property var previousFromAddress: eventLogProxy.getEventAtIndex(index-1)?.core.chatMessage?.fromAddress property var previousFromAddress: eventLogProxy.getEventAtIndex(index-1)?.core.chatMessage?.fromAddress
backgroundColor: isRemoteMessage ? DefaultStyle.main2_100 : DefaultStyle.main1_100 backgroundColor: isRemoteMessage ? DefaultStyle.main2_100 : DefaultStyle.main1_100
isFirstMessage: !previousFromAddress || previousFromAddress !== modelData.core.fromAddress isFirstMessage: !previousFromAddress || previousFromAddress !== chatMessage.core.fromAddress
anchors.right: !isRemoteMessage && parent anchors.right: !isRemoteMessage && parent
? parent.right ? parent.right
: undefined : undefined
onMessageDeletionRequested: modelData.core.lDelete() onMessageDeletionRequested: chatMessage.core.lDelete()
onShowReactionsForMessageRequested: mainItem.showReactionsForMessageRequested(modelData) onShowReactionsForMessageRequested: mainItem.showReactionsForMessageRequested(chatMessage)
onShowImdnStatusForMessageRequested: mainItem.showImdnStatusForMessageRequested(modelData) onShowImdnStatusForMessageRequested: mainItem.showImdnStatusForMessageRequested(chatMessage)
onReplyToMessageRequested: mainItem.replyToMessageRequested(modelData) onReplyToMessageRequested: mainItem.replyToMessageRequested(chatMessage)
onForwardMessageRequested: mainItem.forwardMessageRequested(modelData) onForwardMessageRequested: mainItem.forwardMessageRequested(chatMessage)
onEndOfVoiceRecordingReached: { onEndOfVoiceRecordingReached: {
if (nextChatMessage && nextChatMessage.core.isVoiceRecording) mainItem.requestAutoPlayVoiceRecording(index + 1) if (nextChatMessage && nextChatMessage.core.isVoiceRecording) mainItem.requestAutoPlayVoiceRecording(index + 1)
} }