Treat read markers as item in the model

This commit is contained in:
Carl Schwan
2021-05-21 17:46:34 +00:00
parent 0bc51627bb
commit b5edfc909e
4 changed files with 139 additions and 49 deletions

View File

@@ -19,6 +19,7 @@
#include <QTimeZone>
#include <KLocalizedString>
#include <KFormat>
#include "utils.h"
@@ -34,7 +35,6 @@ QHash<int, QByteArray> MessageEventModel::roleNames() const
roles[ContentRole] = "content";
roles[ContentTypeRole] = "contentType";
roles[HighlightRole] = "isHighlighted";
roles[ReadMarkerRole] = "readMarker";
roles[SpecialMarksRole] = "marks";
roles[LongOperationRole] = "progressInfo";
roles[AnnotationRole] = "annotation";
@@ -90,6 +90,7 @@ void MessageEventModel::setRoom(NeoChatRoom *room)
m_currentRoom = room;
if (room) {
m_lastReadEventIndex = QPersistentModelIndex(QModelIndex());
room->setDisplayed();
if (m_currentRoom->timelineSize() < 10) {
room->getPreviousContent(50);
@@ -141,6 +142,10 @@ void MessageEventModel::setRoom(NeoChatRoom *room)
});
connect(m_currentRoom, &Room::addedMessages, this, [=](int lowest, int biggest) {
endInsertRows();
if (!m_lastReadEventIndex.isValid()) {
// no read marker, so see if we need to create one.
moveReadMarker(QString(), m_currentRoom->readMarkerEventId());
}
if (biggest < m_currentRoom->maxTimelineIndex()) {
auto rowBelowInserted = m_currentRoom->maxTimelineIndex() - biggest + timelineBaseIndex() - 1;
refreshEventRoles(rowBelowInserted, {ShowAuthorRole});
@@ -170,9 +175,6 @@ void MessageEventModel::setRoom(NeoChatRoom *room)
}
refreshRow(timelineBaseIndex()); // Refresh the looks
refreshLastUserEvents(0);
if (m_currentRoom->timelineSize() > 1) { // Refresh above
refreshEventRoles(timelineBaseIndex() + 1, {ReadMarkerRole});
}
if (timelineBaseIndex() > 0) { // Refresh below, see #312
refreshEventRoles(timelineBaseIndex() - 1, {ShowAuthorRole});
}
@@ -182,10 +184,7 @@ void MessageEventModel::setRoom(NeoChatRoom *room)
beginRemoveRows({}, i, i);
});
connect(m_currentRoom, &Room::pendingEventDiscarded, this, &MessageEventModel::endRemoveRows);
connect(m_currentRoom, &Room::readMarkerMoved, this, [this] {
refreshEventRoles(std::exchange(lastReadEventId, m_currentRoom->readMarkerEventId()), {ReadMarkerRole});
refreshEventRoles(lastReadEventId, {ReadMarkerRole});
});
connect(m_currentRoom, &Room::readMarkerMoved, this, &MessageEventModel::moveReadMarker);
connect(m_currentRoom, &Room::replacedEvent, this, [this](const RoomEvent *newEvent) {
refreshLastUserEvents(refreshEvent(newEvent->id()) - timelineBaseIndex());
});
@@ -199,10 +198,6 @@ void MessageEventModel::setRoom(NeoChatRoom *room)
connect(m_currentRoom, &Room::fileTransferCompleted, this, &MessageEventModel::refreshEvent);
connect(m_currentRoom, &Room::fileTransferFailed, this, &MessageEventModel::refreshEvent);
connect(m_currentRoom, &Room::fileTransferCancelled, this, &MessageEventModel::refreshEvent);
connect(m_currentRoom, &Room::readMarkerForUserMoved, this, [=](User *, const QString &fromEventId, const QString &toEventId) {
refreshEventRoles(fromEventId, {UserMarkerRole});
refreshEventRoles(toEventId, {UserMarkerRole});
});
connect(m_currentRoom->connection(), &Connection::ignoredUsersListChanged, this, [=] {
beginResetModel();
endResetModel();
@@ -235,6 +230,43 @@ void MessageEventModel::refreshEventRoles(int row, const QVector<int> &roles)
Q_EMIT dataChanged(idx, idx, roles);
}
void MessageEventModel::moveReadMarker(const QString &fromEventId, const QString &toEventId)
{
const auto timelineIt = m_currentRoom->findInTimeline(toEventId);
if (timelineIt == m_currentRoom->timelineEdge()) {
return;
}
int newRow = int(timelineIt - m_currentRoom->messageEvents().rbegin()) + timelineBaseIndex();
if (!m_lastReadEventIndex.isValid()) {
// Not valid index means we don't display any marker yet, in this case
// we create the new index and insert the row in case the read marker
// need to be displayed.
if (newRow > timelineBaseIndex()) {
// The user didn't read all the messages yet.
beginInsertRows({}, newRow, newRow);
m_lastReadEventIndex = QPersistentModelIndex(index(newRow, 0));
endInsertRows();
return;
}
// The user read all the messages and we didn't display any read marker yet
// => do nothing
return;
}
if (newRow <= timelineBaseIndex()) {
// The user read all the messages => remove read marker
beginRemoveRows({}, m_lastReadEventIndex.row(), m_lastReadEventIndex.row());
m_lastReadEventIndex = QModelIndex();
endRemoveRows();
return;
}
// The user didn't read all the messages yet but moved the reader marker.
beginMoveRows({}, m_lastReadEventIndex.row(), m_lastReadEventIndex.row(), {}, newRow);
m_lastReadEventIndex = QPersistentModelIndex(index(newRow, 0));
endMoveRows();
}
int MessageEventModel::refreshEventRoles(const QString &id, const QVector<int> &roles)
{
// On 64-bit platforms, difference_type for std containers is long long
@@ -327,7 +359,15 @@ int MessageEventModel::rowCount(const QModelIndex &parent) const
if (!m_currentRoom || parent.isValid()) {
return 0;
}
return m_currentRoom->timelineSize();
const auto firstIt = m_currentRoom->messageEvents().crbegin();
if (firstIt != m_currentRoom->messageEvents().crend()) {
const auto &firstEvt = **firstIt;
return m_currentRoom->timelineSize() + (lastReadEventId != firstEvt.id() ? 1 : 0);
} else {
return m_currentRoom->timelineSize();
}
}
inline QVariantMap userAtEvent(NeoChatUser *user, NeoChatRoom *room, const RoomEvent &evt)
@@ -354,7 +394,22 @@ QVariant MessageEventModel::data(const QModelIndex &idx, int role) const
};
bool isPending = row < timelineBaseIndex();
const auto timelineIt = m_currentRoom->messageEvents().crbegin() + std::max(0, row - timelineBaseIndex());
if (m_lastReadEventIndex.row() == row) {
switch(role) {
case EventTypeRole:
return QStringLiteral("readMarker");
case TimeRole:
{
const QDateTime eventDate = data(index(m_lastReadEventIndex.row() + 1, 0), TimeRole).toDateTime();
const KFormat format;
return format.formatRelativeDateTime(eventDate, QLocale::ShortFormat);
}
}
return {};
}
const auto timelineIt = m_currentRoom->messageEvents().crbegin() + std::max(0, row - timelineBaseIndex() - (m_lastReadEventIndex.isValid() && m_lastReadEventIndex.row() < row ? 1 : 0));
const auto pendingIt = m_currentRoom->pendingEvents().crbegin() + std::min(row, timelineBaseIndex());
const auto &evt = isPending ? **pendingIt : **timelineIt;
@@ -457,10 +512,6 @@ QVariant MessageEventModel::data(const QModelIndex &idx, int role) const
return m_currentRoom->isEventHighlighted(&evt);
}
if (role == ReadMarkerRole) {
return evt.id() == lastReadEventId && row > timelineBaseIndex();
}
if (role == SpecialMarksRole) {
if (isPending) {
return pendingIt->deliveryStatus();