Skip to content

Commit

Permalink
More careful tracking of pending events; resending/discarding
Browse files Browse the repository at this point in the history
Closes #387. Closes #388. Closes #390.
  • Loading branch information
KitsuneRal committed Aug 3, 2018
1 parent 7f39835 commit 15d7f03
Show file tree
Hide file tree
Showing 6 changed files with 164 additions and 83 deletions.
162 changes: 96 additions & 66 deletions client/models/messageeventmodel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ enum EventRoles {
ReadMarkerRole,
SpecialMarksRole,
LongOperationRole,
AnnotationRole,
// For debugging
EventResolvedTypeRole,
};
Expand All @@ -66,6 +67,7 @@ QHash<int, QByteArray> MessageEventModel::roleNames() const
roles[ReadMarkerRole] = "readMarker";
roles[SpecialMarksRole] = "marks";
roles[LongOperationRole] = "progressInfo";
roles[AnnotationRole] = "annotation";
roles[EventResolvedTypeRole] = "eventResolvedType";
return roles;
}
Expand All @@ -74,8 +76,10 @@ MessageEventModel::MessageEventModel(QObject* parent)
: QAbstractListModel(parent)
, m_currentRoom(nullptr)
{
qmlRegisterType<QMatrixClient::FileTransferInfo>();
qRegisterMetaType<QMatrixClient::FileTransferInfo>();
using namespace QMatrixClient;
qmlRegisterType<FileTransferInfo>(); qRegisterMetaType<FileTransferInfo>();
qmlRegisterUncreatableType<EventStatus>("QMatrixClient", 1, 0, "EventStatus",
"EventStatus is not an creatable type");
}

void MessageEventModel::changeRoom(QuaternionRoom* room)
Expand All @@ -94,6 +98,7 @@ void MessageEventModel::changeRoom(QuaternionRoom* room)
if( room )
{
lastReadEventId = room->readMarkerEventId();

using namespace QMatrixClient;
connect(m_currentRoom, &Room::aboutToAddNewMessages, this,
[=](RoomEventsRange events)
Expand All @@ -106,19 +111,16 @@ void MessageEventModel::changeRoom(QuaternionRoom* room)
[=](RoomEventsRange events)
{
if (rowCount() > 0)
nextNewerRow = rowCount() - 1; // See #312
rowBelowInserted = rowCount() - 1; // See #312
beginInsertRows(QModelIndex(), rowCount(),
rowCount() + int(events.size()) - 1);
});
connect(m_currentRoom, &Room::addedMessages, this,
[=] {
if (nextNewerRow > -1)
{
const auto idx = index(nextNewerRow);
emit dataChanged(idx, idx);
nextNewerRow = -1;
}
endInsertRows();
if (rowBelowInserted > -1)
refreshEventRoles(rowBelowInserted,
{AboveAuthorRole, AboveSectionRole});
});
connect(m_currentRoom, &Room::pendingEventAboutToAdd, this,
[this] { beginInsertRows({}, 0, 0); });
Expand All @@ -127,31 +129,43 @@ void MessageEventModel::changeRoom(QuaternionRoom* room)
connect(m_currentRoom, &Room::pendingEventAboutToMerge, this,
[this] (RoomEvent*, int i)
{
const auto timelineBaseIdx =
int(m_currentRoom->pendingEvents().size());
if (i + 1 == timelineBaseIdx)
return; // No need to move anything
mergingEcho = true;
Q_ASSERT(beginMoveRows({}, i, i, {}, timelineBaseIdx));
if (i == 0)
return; // No need to move anything, just refresh

movingEvent = true;
// Reverse i because row 0 is bottommost in the model
const auto row = timelineBaseIndex() - i - 1;
Q_ASSERT(beginMoveRows({}, row, row,
{}, timelineBaseIndex()));
});
connect(m_currentRoom, &Room::pendingEventMerged, this,
[this] {
if (mergingEcho)
if (movingEvent)
{
endMoveRows();
mergingEcho = false;
movingEvent = false;
}
refreshEventRoles(int(m_currentRoom->pendingEvents().size()),
{ SpecialMarksRole });
refreshRow(timelineBaseIndex()); // Refresh the looks
if (m_currentRoom->timelineSize() > 1) // Refresh above
refreshEventRoles(timelineBaseIndex() + 1/*,
{ReadMarkerRole}*/);
if (timelineBaseIndex() > 0) // Refresh below, see #312
refreshEventRoles(timelineBaseIndex() - 1,
{AboveAuthorRole, AboveSectionRole});
});
connect(m_currentRoom, &Room::pendingEventChanged, this,
[this] (int i) { refreshEventRoles(i, { SpecialMarksRole }); });
connect(m_currentRoom, &Room::readMarkerMoved, this, [this] {
connect(m_currentRoom, &Room::pendingEventChanged,
this, &MessageEventModel::refreshRow);
connect(m_currentRoom, &Room::pendingEventAboutToDiscard,
this, [this] (int i) { 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});
m_currentRoom->readMarkerEventId())/*,
{ReadMarkerRole}*/);
refreshEventRoles(lastReadEventId/*, {ReadMarkerRole}*/);
});
connect(m_currentRoom, &Room::replacedEvent, this,
[this] (const RoomEvent* newEvent) {
Expand All @@ -174,11 +188,20 @@ void MessageEventModel::changeRoom(QuaternionRoom* room)

void MessageEventModel::refreshEvent(const QString& eventId)
{
refreshEventRoles(eventId, {});
refreshEventRoles(eventId);
}

void MessageEventModel::refreshEventRoles(const int row,
const QVector<int>& roles)
void MessageEventModel::refreshRow(int row)
{
refreshEventRoles(row);
}

int MessageEventModel::timelineBaseIndex() const
{
return m_currentRoom ? int(m_currentRoom->pendingEvents().size()) : 0;
}

void MessageEventModel::refreshEventRoles(int row, const QVector<int>& roles)
{
const auto idx = index(row);
emit dataChanged(idx, idx, roles);
Expand All @@ -188,8 +211,16 @@ void MessageEventModel::refreshEventRoles(const QString& eventId,
const QVector<int>& roles)
{
const auto it = m_currentRoom->findInTimeline(eventId);
if (it != m_currentRoom->timelineEdge())
refreshEventRoles(it - m_currentRoom->messageEvents().rbegin(), roles);
if (it == m_currentRoom->timelineEdge())
{
qWarning() << "Trying to refresh inexistent event:" << eventId;
return;
}
if (roles.empty() || roles.contains(ReadMarkerRole))
qDebug() << "Refreshing" << eventId << ", lREI" << lastReadEventId
<< "rMEI" << m_currentRoom->readMarkerEventId();
refreshEventRoles(it - m_currentRoom->messageEvents().rbegin()
+ timelineBaseIndex(), roles);
}

inline bool hasValidTimestamp(const QMatrixClient::TimelineItem& ti)
Expand Down Expand Up @@ -221,10 +252,9 @@ QDateTime MessageEventModel::makeMessageTimestamp(
return {};
}

QString MessageEventModel::makeDateString(
const QuaternionRoom::rev_iter_t& baseIt) const
QString MessageEventModel::renderDate(QDateTime timestamp) const
{
auto date = makeMessageTimestamp(baseIt).toLocalTime().date();
auto date = timestamp.toLocalTime().date();
if (QMatrixClient::SettingsGroup("UI")
.value("banner_human_friendly_date", true).toBool())
{
Expand All @@ -247,21 +277,21 @@ int MessageEventModel::rowCount(const QModelIndex& parent) const
return m_currentRoom->timelineSize();
}

QVariant MessageEventModel::data(const QModelIndex& index, int role) const
QVariant MessageEventModel::data(const QModelIndex& idx, int role) const
{
const auto row = index.row();
const auto row = idx.row();

if( !m_currentRoom || row < 0 ||
row >= int(m_currentRoom->pendingEvents().size()) +
m_currentRoom->timelineSize())
return {};

const auto timelineBaseIdx = int(m_currentRoom->pendingEvents().size());
bool isPending = row < timelineBaseIndex();
const auto timelineIt = m_currentRoom->messageEvents().crbegin() +
std::max(-1, row - timelineBaseIdx);
const auto& evt = row < timelineBaseIdx
? *m_currentRoom->pendingEvents()[size_t(row)]
: *timelineIt->event();
std::max(0, row - timelineBaseIndex());
const auto pendingIt = m_currentRoom->pendingEvents().crbegin() +
std::min(row, timelineBaseIndex());
const auto& evt = isPending ? *pendingIt->event() : *timelineIt->event();

using namespace QMatrixClient;
if( role == Qt::DisplayRole )
Expand Down Expand Up @@ -422,7 +452,7 @@ QVariant MessageEventModel::data(const QModelIndex& index, int role) const
if( role == AuthorRole )
{
// FIXME: It shouldn't be User, it should be its state "as of event"
return QVariant::fromValue(row < timelineBaseIdx
return QVariant::fromValue(isPending
? m_currentRoom->localUser()
: m_currentRoom->user(evt.senderId()));
}
Expand Down Expand Up @@ -463,21 +493,24 @@ QVariant MessageEventModel::data(const QModelIndex& index, int role) const
return m_currentRoom->isEventHighlighted(&evt);

if( role == ReadMarkerRole )
{
qDebug() << "ReadMarkerRole:" << evt.id() << "vs" << lastReadEventId;
return evt.id() == lastReadEventId;
}

if( role == SpecialMarksRole )
{
if (row < timelineBaseIdx)
return evt.id().isEmpty() ? "unsent" : "unsynced";
if (isPending)
return pendingIt->deliveryStatus();

if (evt.isStateEvent() &&
static_cast<const StateEventBase&>(evt).repeatsState())
return "noop";
return evt.isRedacted() ? "redacted" : "";
return EventStatus::Hidden;
return evt.isRedacted() ? EventStatus::Redacted : EventStatus::Normal;
}

if( role == EventIdRole )
return evt.id();
return !evt.id().isEmpty() ? evt.id() : evt.transactionId();

if( role == LongOperationRole )
{
Expand All @@ -487,28 +520,25 @@ QVariant MessageEventModel::data(const QModelIndex& index, int role) const
m_currentRoom->fileTransferInfo(e->id()));
}

if (row >= timelineBaseIdx - 1) // The timeline and the topmost unsynced
{
if( role == TimeRole )
return row < timelineBaseIdx ? QDateTime::currentDateTimeUtc()
: makeMessageTimestamp(timelineIt);

if( role == SectionRole )
return row < timelineBaseIdx ? tr("Today")
: makeDateString(timelineIt); // FIXME: move date rendering to QML
if( role == AnnotationRole )
if (isPending)
return pendingIt->annotation();

// FIXME: shouldn't be here, because #312
auto aboveEventIt = timelineIt + 1;
if (aboveEventIt != m_currentRoom->timelineEdge())
{
if( role == AboveSectionRole )
return makeDateString(aboveEventIt);

if( role == AboveAuthorRole )
return QVariant::fromValue(
m_currentRoom->user((*aboveEventIt)->senderId()));
}
if( role == TimeRole || role == SectionRole)
{
auto ts = isPending ? pendingIt->lastUpdated()
: makeMessageTimestamp(timelineIt);
return role == TimeRole ? QVariant(ts) : renderDate(ts);
}

return QVariant();
if (role == AboveSectionRole)
return data(index(row + 1), SectionRole);

if( role == AboveAuthorRole )
return isPending
? QVariant::fromValue(m_currentRoom->localUser())
: data(index(row + 1), AuthorRole);

return {};
}
17 changes: 10 additions & 7 deletions client/models/messageeventmodel.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,15 +37,18 @@ class MessageEventModel: public QAbstractListModel

private slots:
void refreshEvent(const QString& eventId);
void refreshRow(int row);

private:
QuaternionRoom* m_currentRoom;
QString lastReadEventId;
int nextNewerRow = -1;
bool mergingEcho = 0;

QDateTime makeMessageTimestamp(const QMatrixClient::Room::rev_iter_t& baseIt) const;
QString makeDateString(const QMatrixClient::Room::rev_iter_t& baseIt) const;
void refreshEventRoles(const int row, const QVector<int>& roles);
void refreshEventRoles(const QString& eventId, const QVector<int>& roles);
int rowBelowInserted = -1;
bool movingEvent = 0;

int timelineBaseIndex() const;
QDateTime makeMessageTimestamp(const QuaternionRoom::rev_iter_t& baseIt) const;
QString renderDate(QDateTime timestamp) const;
void refreshEventRoles(int row, const QVector<int>& roles = {});
void refreshEventRoles(const QString& eventId,
const QVector<int>& roles = {});
};
16 changes: 16 additions & 0 deletions client/qml/ActiveLabel.qml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import QtQuick 2.2
import QtQuick.Controls 1.4

Label {
signal clicked

font.italic: true
textFormat: Text.PlainText
MouseArea
{
anchors.fill: parent
cursorShape: Qt.PointingHandCursor
acceptedButtons: Qt.LeftButton
onClicked: parent.clicked
}
}
Loading

0 comments on commit 15d7f03

Please sign in to comment.