aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKitsune Ral <Kitsune-Ral@users.sf.net>2017-01-11 07:41:53 +0900
committerGitHub <noreply@github.com>2017-01-11 07:41:53 +0900
commitf37874c7e55239359d02b926a65cd62d17336522 (patch)
treec14ba3abd15c380dd6430240dba46c615fc5eee8
parent78cfe3c102b686c71a1da5cbe91a2ae42dc03825 (diff)
parentfb4127da9db32e5e18b14beaaabbee7adf9cc334 (diff)
downloadlibquotient-f37874c7e55239359d02b926a65cd62d17336522.tar.gz
libquotient-f37874c7e55239359d02b926a65cd62d17336522.zip
Merge pull request #51 from Fxrh/kitsune-traceable-events
Make events traceable
-rw-r--r--room.cpp127
-rw-r--r--room.h7
2 files changed, 55 insertions, 79 deletions
diff --git a/room.cpp b/room.cpp
index 325e81bc..650b2ff0 100644
--- a/room.cpp
+++ b/room.cpp
@@ -18,12 +18,11 @@
#include "room.h"
-#include <array>
-
#include <QtCore/QHash>
#include <QtCore/QJsonArray>
#include <QtCore/QStringBuilder> // for efficient string concats (operator%)
#include <QtCore/QDebug>
+#include <algorithm>
#include "connection.h"
#include "state.h"
@@ -60,7 +59,8 @@ class Room::Private
void updateDisplayname();
Connection* connection;
- Timeline messageEvents;
+ Timeline timeline;
+ QHash<QString, Timeline::const_iterator> eventsIndex;
QString id;
QStringList aliases;
QString canonicalAlias;
@@ -74,7 +74,7 @@ class Room::Private
members_map_t membersMap;
QList<User*> usersTyping;
QList<User*> membersLeft;
- QHash<User*, QString> lastReadEvent;
+ QHash<const User*, QString> lastReadEventIds;
QString prevBatch;
RoomMessagesJob* roomMessagesJob;
@@ -92,6 +92,9 @@ class Room::Private
void getPreviousContent();
bool isEventNotable(const Event* e) const;
+
+ Timeline::const_iterator readMarker(const User* user) const;
+
private:
QString calculateDisplayname() const;
QString roomNameFromMemberNames(const QList<User*>& userlist) const;
@@ -123,7 +126,7 @@ QString Room::id() const
const Room::Timeline& Room::messageEvents() const
{
- return d->messageEvents;
+ return d->timeline;
}
QString Room::name() const
@@ -165,9 +168,9 @@ void Room::setJoinState(JoinState state)
emit joinStateChanged(oldState, state);
}
-void Room::setLastReadEvent(User* user, QString eventId)
+void Room::setLastReadEvent(User* user, Event* event)
{
- d->lastReadEvent.insert(user, eventId);
+ d->lastReadEventIds.insert(user, event->id());
emit lastReadEventChanged(user);
if (user == d->connection->user())
emit readMarkerPromoted();
@@ -175,83 +178,64 @@ void Room::setLastReadEvent(User* user, QString eventId)
Room::Timeline::const_iterator Room::promoteReadMarker(User* u, QString eventId)
{
- QString prevLastReadId = lastReadEvent(u);
- int stillUnreadMessagesCount = 0;
- auto it = d->messageEvents.end();
- Event* targetEvent = nullptr;
- // Older Qt doesn't provide rbegin()/rend() for Qt containers
- while (it != d->messageEvents.begin())
- {
- --it;
- // Check that the new read event is not before the previously set - only
- // allow the read marker to move down the timeline, not up.
- if (prevLastReadId == (*it)->id())
- break;
+ if (d->timeline.empty())
+ return d->timeline.end();
- // Found the message to mark as read; if there are messages from
- // that user right below this one, automatically promote the marker
- // to them instead of this one; still return this one to save
- // markMessagesAsRead() from going through local messages over again.
- if (eventId == (*it)->id())
- {
- setLastReadEvent(u, (targetEvent ? targetEvent : *it)->id());
- break;
- }
+ auto newMarker = next(d->eventsIndex.value(eventId)); // After the event
+ {
+ auto prevMarker = d->readMarker(u);
+ if (prevMarker > newMarker)
+ return prevMarker;
+ }
- // If we are on a message from that user (or a series thereof),
- // remember it (or the end of the sequence) so that we could use it
- // in case when the event to promote the marker to is immediately
- // above the ones from that user.
- if ((*it)->senderId() == u->id())
- {
- if (!targetEvent)
- targetEvent = *it;
- }
- else
- targetEvent = nullptr;
+ using namespace std;
- // Detect events "notable" for the local user so that we can properly
- // set unreadMessages
- if (u == connection()->user())
- stillUnreadMessagesCount += d->isEventNotable(*it);
- }
+ // Try to auto-promote the read marker over the user's own messages.
+ auto eagerMarker = find_if(newMarker, d->timeline.cend(),
+ [=](Event* e) { return e->senderId() != u->id(); });
+ setLastReadEvent(u, *(prev(eagerMarker)));
- if( u == connection()->user() )
+ if (u == connection()->user() && d->unreadMessages)
{
- if (d->unreadMessages && stillUnreadMessagesCount == 0)
+ auto stillUnreadMessagesCount =
+ count_if(eagerMarker, d->timeline.cend(),
+ [=](Event* e) { return d->isEventNotable(e); });
+
+ if (stillUnreadMessagesCount == 0)
{
d->unreadMessages = false;
qDebug() << "Room" << displayName() << ": no more unread messages";
emit unreadMessagesChanged(this);
}
- if (stillUnreadMessagesCount > 0)
+ else
qDebug() << "Room" << displayName()
<< ": still" << stillUnreadMessagesCount << "unread message(s)";
}
- return it;
+
+ // Return newMarker, rather than eagerMarker, to save markMessagesAsRead()
+ // that calls this method from going back through knowingly-local messages.
+ return newMarker;
}
void Room::markMessagesAsRead(QString uptoEventId)
{
- if (d->messageEvents.empty())
+ if (d->timeline.empty())
return;
User* localUser = connection()->user();
- QString prevLastReadId = lastReadEvent(localUser);
+ auto prevReadMarker = d->readMarker(localUser);
auto last = promoteReadMarker(localUser, uptoEventId);
- // We shouldn't send read receipts for messages from the local user - so
+ // We shouldn't send read receipts for the local user's own messages - so
// shift back (if necessary) to the nearest message not from the local user
// or the so far last read message, whichever comes first.
- for (; (*last)->id() != prevLastReadId; --last)
+ while (last > prevReadMarker)
{
- if ((*last)->senderId() != connection()->userId())
+ if ((*--last)->senderId() != connection()->userId())
{
d->connection->postReceipt(this, *last);
break;
}
- if (last == messageEvents().begin())
- break;
}
}
@@ -266,14 +250,17 @@ bool Room::hasUnreadMessages()
return d->unreadMessages;
}
-QString Room::lastReadEvent(User* user) const
+Room::Timeline::const_iterator Room::Private::readMarker(const User* user) const
{
- return d->lastReadEvent.value(user);
+ auto lastReadId = lastReadEventIds.value(user);
+ return lastReadId.isEmpty() ?
+ eventsIndex.value(lastReadId) + 1 :
+ timeline.begin();
}
QString Room::readMarkerEventId() const
{
- return lastReadEvent(d->connection->user());
+ return d->lastReadEventIds.value(d->connection->user());
}
int Room::notificationCount() const
@@ -504,7 +491,6 @@ bool Room::Private::isEventNotable(const Event* e) const
void Room::doAddNewMessageEvents(const Events& events)
{
- d->messageEvents.reserve(d->messageEvents.size() + events.size());
Timeline::size_type newUnreadMessages = 0;
@@ -513,26 +499,15 @@ void Room::doAddNewMessageEvents(const Events& events)
// from the server (or, for the local user, markMessagesAsRead() invocation)
// to promote their read markers over the new message events.
User* firstWriter = connection()->user(events.front()->senderId());
- bool canAutoPromote = d->messageEvents.empty() ||
- lastReadEvent(firstWriter) == d->messageEvents.back()->id();
- Event* firstWriterSeriesEnd = canAutoPromote ? events.front() : nullptr;
+ bool canAutoPromote = d->readMarker(firstWriter) == d->timeline.end();
for (auto e: events)
{
- d->messageEvents.push_back(e);
-
+ d->eventsIndex.insert(e->id(), d->timeline.insert(d->timeline.end(), e));
newUnreadMessages += d->isEventNotable(e);
- if (firstWriterSeriesEnd)
- {
- if (e->senderId() != firstWriter->id())
- firstWriterSeriesEnd = e;
- else
- {
- setLastReadEvent(firstWriter, firstWriterSeriesEnd->id());
- firstWriterSeriesEnd = nullptr;
- }
- }
}
+ if (canAutoPromote)
+ promoteReadMarker(firstWriter, events.front()->id());
if( !d->unreadMessages && newUnreadMessages > 0)
{
@@ -554,8 +529,8 @@ void Room::addHistoricalMessageEvents(const Events& events)
void Room::doAddHistoricalMessageEvents(const Events& events)
{
// Historical messages arrive in newest-to-oldest order
- d->messageEvents.reserve(d->messageEvents.size() + events.size());
- std::copy(events.begin(), events.end(), std::front_inserter(d->messageEvents));
+ for (auto e: events)
+ d->eventsIndex.insert(e->id(), d->timeline.insert(d->timeline.begin(), e));
}
void Room::processStateEvents(const Events& events)
diff --git a/room.h b/room.h
index afa6f0ef..f8db6384 100644
--- a/room.h
+++ b/room.h
@@ -26,6 +26,8 @@
#include "jobs/syncjob.h"
#include "joinstate.h"
+#include <deque>
+
namespace QMatrixClient
{
class Event;
@@ -38,7 +40,7 @@ namespace QMatrixClient
Q_OBJECT
Q_PROPERTY(QString readMarkerEventId READ readMarkerEventId WRITE markMessagesAsRead NOTIFY readMarkerPromoted)
public:
- using Timeline = Owning<Events>;
+ using Timeline = Owning< std::deque<Event*> >;
Room(Connection* connection, QString id);
virtual ~Room();
@@ -70,7 +72,6 @@ namespace QMatrixClient
Q_INVOKABLE void updateData(SyncRoomData& data );
Q_INVOKABLE void setJoinState( JoinState state );
- Q_INVOKABLE QString lastReadEvent(User* user) const;
QString readMarkerEventId() const;
/**
* @brief Mark the event with uptoEventId as read
@@ -140,7 +141,7 @@ namespace QMatrixClient
void addNewMessageEvents(const Events& events);
void addHistoricalMessageEvents(const Events& events);
- void setLastReadEvent(User* user, QString eventId);
+ void setLastReadEvent(User* user, Event* event);
};
class MemberSorter