aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--events/event.cpp23
-rw-r--r--events/event.h54
-rw-r--r--examples/qmc-example.cpp12
-rw-r--r--jobs/roommessagesjob.cpp6
-rw-r--r--jobs/roommessagesjob.h2
-rw-r--r--jobs/syncjob.h2
-rw-r--r--room.cpp244
-rw-r--r--room.h36
-rw-r--r--util.h47
9 files changed, 205 insertions, 221 deletions
diff --git a/events/event.cpp b/events/event.cpp
index 1e2d89f2..01f473ce 100644
--- a/events/event.cpp
+++ b/events/event.cpp
@@ -42,6 +42,8 @@ Event::Event(Type type, const QJsonObject& rep)
}
}
+Event::~Event() = default;
+
QByteArray Event::originalJson() const
{
return QJsonDocument(_originalJson).toJson();
@@ -72,14 +74,15 @@ inline BaseEventT* makeIfMatches(const QJsonObject& o, const QString& selector)
return makeIfMatches<BaseEventT, EventTs...>(o, selector);
}
-Event* Event::fromJson(const QJsonObject& obj)
+template <>
+EventPtr QMatrixClient::makeEvent<Event>(const QJsonObject& obj)
{
// Check more specific event types first
- if (auto e = RoomEvent::fromJson(obj))
- return e;
+ if (auto e = makeEvent<RoomEvent>(obj))
+ return EventPtr(move(e));
- return makeIfMatches<Event,
- TypingEvent, ReceiptEvent>(obj, obj["type"].toString());
+ return EventPtr { makeIfMatches<Event,
+ TypingEvent, ReceiptEvent>(obj, obj["type"].toString()) };
}
RoomEvent::RoomEvent(Event::Type type) : Event(type) { }
@@ -119,8 +122,7 @@ RoomEvent::RoomEvent(Type type, const QJsonObject& rep)
qCDebug(EVENTS) << "Event transactionId:" << _txnId;
}
-RoomEvent::~RoomEvent()
-{ /* Let QScopedPointer<RedactionEvent> do its job */ }
+RoomEvent::~RoomEvent() = default; // Let the smart pointer do its job
QString RoomEvent::redactionReason() const
{
@@ -133,11 +135,12 @@ void RoomEvent::addId(const QString& id)
_id = id;
}
-RoomEvent* RoomEvent::fromJson(const QJsonObject& obj)
+template <>
+RoomEventPtr QMatrixClient::makeEvent<RoomEvent>(const QJsonObject& obj)
{
- return makeIfMatches<RoomEvent,
+ return RoomEventPtr { makeIfMatches<RoomEvent,
RoomMessageEvent, RoomNameEvent, RoomAliasesEvent,
RoomCanonicalAliasEvent, RoomMemberEvent, RoomTopicEvent,
RoomAvatarEvent, EncryptionEvent, RedactionEvent>
- (obj, obj["type"].toString());
+ (obj, obj["type"].toString()) };
}
diff --git a/events/event.h b/events/event.h
index bd33bb50..2b18bb46 100644
--- a/events/event.h
+++ b/events/event.h
@@ -25,8 +25,21 @@
#include "util.h"
+#include <memory>
+
namespace QMatrixClient
{
+ template <typename EventT>
+ using event_ptr_tt = std::unique_ptr<EventT>;
+
+ /** Create an event with proper type from a JSON object
+ * Use this factory template to detect the type from the JSON object
+ * contents (the detected event type should derive from the template
+ * parameter type) and create an event object of that type.
+ */
+ template <typename EventT>
+ event_ptr_tt<EventT> makeEvent(const QJsonObject& obj);
+
class Event
{
Q_GADGET
@@ -64,12 +77,6 @@ namespace QMatrixClient
// (and in most cases it will be a combination of other fields
// instead of "content" field).
- /** Create an event with proper type from a JSON object
- * Use this factory to detect the type from the JSON object contents
- * and create an event object of that type.
- */
- static Event* fromJson(const QJsonObject& obj);
-
protected:
const QJsonObject contentJson() const;
@@ -82,6 +89,10 @@ namespace QMatrixClient
Q_PROPERTY(QJsonObject contentJson READ contentJson CONSTANT)
};
using EventType = Event::Type;
+ using EventPtr = event_ptr_tt<Event>;
+
+ template <>
+ EventPtr makeEvent<Event>(const QJsonObject& obj);
/**
* \brief A vector of pointers to events with deserialisation capabilities
@@ -94,7 +105,7 @@ namespace QMatrixClient
* \tparam EventT base type of all events in the vector
*/
template <typename EventT>
- class EventsBatch : public std::vector<EventT*>
+ class EventsBatch : public std::vector<event_ptr_tt<EventT>>
{
public:
/**
@@ -120,8 +131,10 @@ namespace QMatrixClient
for (auto objValue: objs)
{
const auto o = objValue.toObject();
- auto e = EventT::fromJson(o);
- this->push_back(e ? e : new EventT(EventType::Unknown, o));
+ auto e { makeEvent<EventT>(o) };
+ if (!e)
+ e.reset(new EventT(EventType::Unknown, o));
+ this->emplace_back(std::move(e));
}
}
};
@@ -151,10 +164,10 @@ namespace QMatrixClient
const QDateTime& timestamp() const { return _serverTimestamp; }
const QString& roomId() const { return _roomId; }
const QString& senderId() const { return _senderId; }
- bool isRedacted() const { return redactedBecause(); }
- RedactionEvent* redactedBecause() const
+ bool isRedacted() const { return bool(_redactedBecause); }
+ const RedactionEvent* redactedBecause() const
{
- return _redactedBecause.data();
+ return _redactedBecause.get();
}
QString redactionReason() const;
const QString& transactionId() const { return _txnId; }
@@ -180,18 +193,19 @@ namespace QMatrixClient
*/
void addId(const QString& id);
- // "Static override" of the one in Event
- static RoomEvent* fromJson(const QJsonObject& obj);
-
private:
QString _id;
QString _roomId;
QString _senderId;
QDateTime _serverTimestamp;
- QScopedPointer<RedactionEvent> _redactedBecause;
+ event_ptr_tt<RedactionEvent> _redactedBecause;
QString _txnId;
};
using RoomEvents = EventsBatch<RoomEvent>;
+ using RoomEventPtr = event_ptr_tt<RoomEvent>;
+
+ template <>
+ RoomEventPtr makeEvent<RoomEvent>(const QJsonObject& obj);
/**
* Conceptually similar to QStringView (but much more primitive), it's a
@@ -199,10 +213,10 @@ namespace QMatrixClient
* referring to the beginning and the end of a range in a RoomEvents
* container.
*/
- struct RoomEventsView
+ struct RoomEventsRange
{
- RoomEvents::const_iterator from;
- RoomEvents::const_iterator to;
+ RoomEvents::iterator from;
+ RoomEvents::iterator to;
RoomEvents::size_type size() const
{
@@ -212,6 +226,8 @@ namespace QMatrixClient
bool empty() const { return from == to; }
RoomEvents::const_iterator begin() const { return from; }
RoomEvents::const_iterator end() const { return to; }
+ RoomEvents::iterator begin() { return from; }
+ RoomEvents::iterator end() { return to; }
};
template <typename ContentT>
diff --git a/examples/qmc-example.cpp b/examples/qmc-example.cpp
index ff7944ce..dbb9912b 100644
--- a/examples/qmc-example.cpp
+++ b/examples/qmc-example.cpp
@@ -20,16 +20,16 @@ void onNewRoom(Room* r)
<< " Canonical alias: " << r->canonicalAlias().toStdString()
<< endl << endl;
});
- QObject::connect(r, &Room::aboutToAddNewMessages, [=] (RoomEventsView events) {
- cout << events.size() << " new event(s) in room "
+ QObject::connect(r, &Room::aboutToAddNewMessages, [=] (RoomEventsRange timeline) {
+ cout << timeline.size() << " new event(s) in room "
<< r->id().toStdString() << ":" << endl;
- for (auto e: events)
+ for (const auto& item: timeline)
{
cout << "From: "
- << r->roomMembername(e->senderId()).toStdString()
+ << r->roomMembername(item->senderId()).toStdString()
<< endl << "Timestamp:"
- << e->timestamp().toString().toStdString() << endl
- << "JSON:" << endl << string(e->originalJson()) << endl;
+ << item->timestamp().toString().toStdString() << endl
+ << "JSON:" << endl << string(item->originalJson()) << endl;
}
});
}
diff --git a/jobs/roommessagesjob.cpp b/jobs/roommessagesjob.cpp
index 9af1b3a6..e5568f17 100644
--- a/jobs/roommessagesjob.cpp
+++ b/jobs/roommessagesjob.cpp
@@ -23,7 +23,7 @@ using namespace QMatrixClient;
class RoomMessagesJob::Private
{
public:
- Owning<RoomEvents> events;
+ RoomEvents events;
QString end;
};
@@ -46,9 +46,9 @@ RoomMessagesJob::~RoomMessagesJob()
delete d;
}
-RoomEvents RoomMessagesJob::releaseEvents()
+RoomEvents&& RoomMessagesJob::releaseEvents()
{
- return d->events.release();
+ return move(d->events);
}
QString RoomMessagesJob::end() const
diff --git a/jobs/roommessagesjob.h b/jobs/roommessagesjob.h
index 9680d52c..7b3fd9c9 100644
--- a/jobs/roommessagesjob.h
+++ b/jobs/roommessagesjob.h
@@ -34,7 +34,7 @@ namespace QMatrixClient
FetchDirection dir = FetchDirection::Backward);
virtual ~RoomMessagesJob();
- RoomEvents releaseEvents();
+ RoomEvents&& releaseEvents();
QString end() const;
protected:
diff --git a/jobs/syncjob.h b/jobs/syncjob.h
index 08bd773e..e9288486 100644
--- a/jobs/syncjob.h
+++ b/jobs/syncjob.h
@@ -30,7 +30,7 @@ namespace QMatrixClient
{
public:
template <typename EventT>
- class Batch : public Owning<EventsBatch<EventT>>
+ class Batch : public EventsBatch<EventT>
{
public:
explicit Batch(QString k) : jsonKey(std::move(k)) { }
diff --git a/room.cpp b/room.cpp
index 1234c343..6c426de2 100644
--- a/room.cpp
+++ b/room.cpp
@@ -44,6 +44,7 @@
#include <array>
using namespace QMatrixClient;
+using namespace std::placeholders;
enum EventsPlacement : int { Older = -1, Newer = 1 };
@@ -101,20 +102,34 @@ class Room::Private
void getPreviousContent(int limit = 10);
- bool isEventNotable(const RoomEvent* e) const
+ bool isEventNotable(const TimelineItem& ti) const
{
- return !e->isRedacted() &&
- e->senderId() != connection->userId() &&
- e->type() == EventType::RoomMessage;
+ return !ti->isRedacted() &&
+ ti->senderId() != connection->userId() &&
+ ti->type() == EventType::RoomMessage;
}
- void insertEvents(RoomEventsView events, EventsPlacement placement);
+ void addNewMessageEvents(RoomEvents&& events);
+ void addHistoricalMessageEvents(RoomEvents&& events);
+
+ /**
+ * @brief Move events into the timeline
+ *
+ * Insert events into the timeline, either new or historical.
+ * Pointers in the original container become empty, the ownership
+ * is passed to the timeline container.
+ * @param events - the range of events to be inserted
+ * @param placement - position and direction of insertion: Older for
+ * historical messages, Newer for new ones
+ */
+ Timeline::size_type insertEvents(RoomEventsRange&& events,
+ EventsPlacement placement);
/**
* Removes events from the passed container that are already in the timeline
*/
void dropDuplicateEvents(RoomEvents* events) const;
- void checkUnreadMessages(RoomEventsView events);
+ void checkUnreadMessages(timeline_iter_t from);
void setLastReadEvent(User* u, const QString& eventId);
rev_iter_pair_t promoteReadMarker(User* u, rev_iter_t newMarker,
@@ -122,7 +137,13 @@ class Room::Private
void markMessagesAsRead(rev_iter_t upToMarker);
- void processRedaction(const RedactionEvent* redaction);
+ /**
+ * @brief Apply redaction to the timeline
+ *
+ * Tries to find an event in the timeline and redact it; deletes the
+ * redaction event whether the redacted event was found or not.
+ */
+ void processRedaction(RoomEventPtr redactionEvent);
QJsonObject toJson() const;
@@ -250,9 +271,8 @@ Room::Private::promoteReadMarker(User* u, Room::rev_iter_t newMarker,
setLastReadEvent(u, (*(eagerMarker - 1))->id());
if (isLocalUser(u) && unreadMessages)
{
- auto stillUnreadMessagesCount =
- count_if(eagerMarker, timeline.cend(),
- [=](const TimelineItem& ti) { return isEventNotable(ti.event()); });
+ auto stillUnreadMessagesCount = count_if(eagerMarker, timeline.cend(),
+ bind(&Room::Private::isEventNotable, this, _1));
if (stillUnreadMessagesCount == 0)
{
@@ -437,12 +457,13 @@ void Room::Private::removeMemberFromMap(const QString& username, User* u)
emit q->memberRenamed(formerNamesakes[0]);
}
-inline QByteArray makeErrorStr(const Event* e, QByteArray msg)
+inline QByteArray makeErrorStr(const Event& e, QByteArray msg)
{
- return msg.append("; event dump follows:\n").append(e->originalJson());
+ return msg.append("; event dump follows:\n").append(e.originalJson());
}
-void Room::Private::insertEvents(RoomEventsView events, EventsPlacement placement)
+Room::Timeline::size_type Room::Private::insertEvents(RoomEventsRange&& events,
+ EventsPlacement placement)
{
// Historical messages arrive in newest-to-oldest order, so the process for
// them is symmetric to the one for new messages.
@@ -450,22 +471,26 @@ void Room::Private::insertEvents(RoomEventsView events, EventsPlacement placemen
placement == Older ? timeline.front().index() :
timeline.back().index();
auto baseIndex = index;
- for (const auto e: events)
+ for (auto&& e: events)
{
+ const auto eId = e->id();
Q_ASSERT_X(e, __FUNCTION__, "Attempt to add nullptr to timeline");
- Q_ASSERT_X(!e->id().isEmpty(), __FUNCTION__,
- makeErrorStr(e, "Event with empty id cannot be in the timeline"));
- Q_ASSERT_X(!eventsIndex.contains(e->id()), __FUNCTION__,
- makeErrorStr(e, "Event is already in the timeline; "
+ Q_ASSERT_X(!eId.isEmpty(), __FUNCTION__,
+ makeErrorStr(*e,
+ "Event with empty id cannot be in the timeline"));
+ Q_ASSERT_X(!eventsIndex.contains(eId), __FUNCTION__,
+ makeErrorStr(*e, "Event is already in the timeline; "
"incoming events were not properly deduplicated"));
if (placement == Older)
- timeline.emplace_front(e, --index);
+ timeline.emplace_front(move(e), --index);
else
- timeline.emplace_back(e, ++index);
- eventsIndex.insert(e->id(), index);
- Q_ASSERT(q->findInTimeline(e->id())->event() == e);
+ timeline.emplace_back(move(e), ++index);
+ eventsIndex.insert(eId, index);
+ Q_ASSERT(q->findInTimeline(eId)->event()->id() == eId);
}
+ // Pointers in "events" are empty now, but events.size() didn't change
Q_ASSERT(int(events.size()) == (index - baseIndex) * int(placement));
+ return events.size();
}
void Room::Private::addMember(User *u)
@@ -578,15 +603,15 @@ void Room::updateData(SyncRoomData&& data)
<< et.elapsed() << "ms," << data.timeline.size() << "events";
et.restart();
- addNewMessageEvents(data.timeline.release());
+ d->addNewMessageEvents(move(data.timeline));
qCDebug(PROFILER) << "*** Room::addNewMessageEvents():"
<< et.elapsed() << "ms";
}
if (!data.ephemeral.empty())
{
et.restart();
- for( auto ephemeralEvent: data.ephemeral )
- processEphemeralEvent(ephemeralEvent);
+ for( auto&& ephemeralEvent: data.ephemeral )
+ processEphemeralEvent(move(ephemeralEvent));
qCDebug(PROFILER) << "*** Room::processEphemeralEvents():"
<< et.elapsed() << "ms";
}
@@ -638,7 +663,7 @@ void Room::Private::getPreviousContent(int limit)
connect( roomMessagesJob, &RoomMessagesJob::result, [=]() {
if( !roomMessagesJob->error() )
{
- q->addHistoricalMessageEvents(roomMessagesJob->releaseEvents());
+ addHistoricalMessageEvents(roomMessagesJob->releaseEvents());
prevBatch = roomMessagesJob->end();
}
roomMessagesJob = nullptr;
@@ -682,35 +707,35 @@ void Room::Private::dropDuplicateEvents(RoomEvents* events) const
if (events->empty())
return;
- // Collect all duplicate events at the end of the container
- auto dupsBegin =
- std::stable_partition(events->begin(), events->end(),
- [&] (RoomEvent* e) { return !eventsIndex.contains(e->id()); });
-
- if (dupsBegin != events->begin())
- {
- // Check the batch itself for dups
- auto eIt = events->begin();
- for (auto baseId = (*eIt)->id(); ++eIt < dupsBegin; baseId = (*eIt)->id())
- {
- dupsBegin =
- std::stable_partition(eIt, dupsBegin,
- [&] (const RoomEvent* e) { return e->id() != baseId; });
- }
- }
+ // Multiple-remove (by different criteria), single-erase
+ // 1. Check for duplicates against the timeline.
+ auto dupsBegin = remove_if(events->begin(), events->end(),
+ [&] (const RoomEventPtr& e)
+ { return eventsIndex.contains(e->id()); });
+
+ // 2. Check for duplicates within the batch if there are still events.
+ for (auto eIt = events->begin(); distance(eIt, dupsBegin) > 1; ++eIt)
+ dupsBegin = remove_if(eIt + 1, dupsBegin,
+ [&] (const RoomEventPtr& e)
+ { return e->id() == (*eIt)->id(); });
if (dupsBegin == events->end())
return;
qCDebug(EVENTS) << "Dropping" << distance(dupsBegin, events->end())
<< "duplicate event(s)";
- // Dispose of those dups
- std::for_each(dupsBegin, events->end(), [] (Event* e) { delete e; });
events->erase(dupsBegin, events->end());
}
-void Room::Private::processRedaction(const RedactionEvent* redaction)
+inline bool isRedaction(const RoomEventPtr& e)
{
- Q_ASSERT(redaction && redaction->type() == EventType::Redaction);
+ return e->type() == EventType::Redaction;
+}
+
+void Room::Private::processRedaction(RoomEventPtr redactionEvent)
+{
+ Q_ASSERT(redactionEvent && isRedaction(redactionEvent));
+ const auto& redaction =
+ static_cast<const RedactionEvent*>(redactionEvent.get());
const auto pIdx = eventsIndex.find(redaction->redactedEvent());
if (pIdx == eventsIndex.end())
@@ -778,11 +803,10 @@ void Room::Private::processRedaction(const RedactionEvent* redaction)
// Make a new event from the redacted JSON, exchange events,
// notify everyone and delete the old event
- auto oldEvent = ti.replaceEvent(RoomEvent::fromJson(originalJson));
- q->onRedaction(oldEvent, ti);
+ auto oldEvent { ti.replaceEvent(makeEvent<RoomEvent>(originalJson)) };
+ q->onRedaction(oldEvent.get(), ti.event());
qCDebug(MAIN) << "Redacted" << oldEvent->id() << "with" << redaction->id();
- emit q->replacedEvent(oldEvent, ti.event());
- delete oldEvent;
+ emit q->replacedEvent(ti.event(), oldEvent.get());
}
Connection* Room::connection() const
@@ -796,62 +820,55 @@ User* Room::localUser() const
return connection()->user();
}
-inline bool isRedaction(Event* e)
+void Room::Private::addNewMessageEvents(RoomEvents&& events)
{
- return e->type() == EventType::Redaction;
-}
-
-void Room::addNewMessageEvents(RoomEvents events)
-{
- auto timelineSize = d->timeline.size();
+ auto timelineSize = timeline.size();
- d->dropDuplicateEvents(&events);
+ dropDuplicateEvents(&events);
// We want to process redactions in the order of arrival (covering the
// case of one redaction superseding another one), hence stable partition.
const auto normalsBegin =
- std::stable_partition(events.begin(), events.end(), isRedaction);
- RoomEventsView redactions { events.begin(), normalsBegin },
+ stable_partition(events.begin(), events.end(), isRedaction);
+ RoomEventsRange redactions { events.begin(), normalsBegin },
normalEvents { normalsBegin, events.end() };
+
if (!normalEvents.empty())
+ emit q->aboutToAddNewMessages(normalEvents);
+ const auto insertedSize = insertEvents(std::move(normalEvents), Newer);
+ if (insertedSize > 0)
{
- emit aboutToAddNewMessages(normalEvents);
- doAddNewMessageEvents(normalEvents);
+ qCDebug(MAIN)
+ << "Room" << displayname << "received" << insertedSize
+ << "new events; the last event is now" << timeline.back();
+ q->onAddNewTimelineEvents(timeline.cend() - insertedSize);
}
- for (auto* r: redactions)
- d->processRedaction(static_cast<const RedactionEvent*>(r));
- if (!normalEvents.empty())
+ for (auto&& r: redactions)
+ processRedaction(move(r));
+ if (insertedSize > 0)
{
- d->checkUnreadMessages(normalEvents);
- emit addedMessages();
+ checkUnreadMessages(timeline.cend() - insertedSize);
+ emit q->addedMessages();
}
- Q_ASSERT(d->timeline.size() == timelineSize + normalEvents.size());
-}
-
-void Room::doAddNewMessageEvents(RoomEventsView events)
-{
- Q_ASSERT(!events.empty());
- d->insertEvents(events, Newer);
- qCDebug(MAIN)
- << "Room" << displayName() << "received" << events.size()
- << "new events; the last event is now" << d->timeline.back();
+ Q_ASSERT(timeline.size() == timelineSize + insertedSize);
}
-void Room::Private::checkUnreadMessages(RoomEventsView events)
+void Room::Private::checkUnreadMessages(timeline_iter_t from)
{
- auto newUnreadMessages =
- count_if(events.from, events.to,
- [=] (const RoomEvent* e) { return isEventNotable(e); });
+ Q_ASSERT(from < timeline.cend());
+ const auto newUnreadMessages = count_if(from, timeline.cend(),
+ bind(&Room::Private::isEventNotable, this, _1));
- // The first event in the batch defines whose read marker can possibly be
- // promoted any further over the same author's events newly arrived.
- // Others will need explicit read receipts 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.from)->senderId());
+ // The first event in the just-added batch (referred to by upTo.base())
+ // defines whose read marker can possibly be promoted any further over
+ // the same author's events newly arrived. Others will need explicit
+ // read receipts from the server (or, for the local user,
+ // markMessagesAsRead() invocation) to promote their read markers over
+ // the new message events.
+ auto firstWriter = connection->user((*from)->senderId());
if (q->readMarker(firstWriter) != timeline.crend())
{
- promoteReadMarker(firstWriter, q->findInTimeline((*events.from)->id()));
+ promoteReadMarker(firstWriter, q->findInTimeline((*from)->id()));
qCDebug(MAIN) << "Auto-promoted read marker for" << firstWriter->id()
<< "to" << *q->readMarker(firstWriter);
}
@@ -864,50 +881,45 @@ void Room::Private::checkUnreadMessages(RoomEventsView events)
}
}
-void Room::addHistoricalMessageEvents(RoomEvents events)
+void Room::Private::addHistoricalMessageEvents(RoomEvents&& events)
{
- auto timelineSize = d->timeline.size();
+ const auto timelineSize = timeline.size();
- d->dropDuplicateEvents(&events);
- auto redactionsBegin =
- std::remove_if(events.begin(), events.end(), isRedaction);
- RoomEventsView normalEvents { events.begin(), redactionsBegin };
+ dropDuplicateEvents(&events);
+ const auto redactionsBegin =
+ remove_if(events.begin(), events.end(), isRedaction);
+ RoomEventsRange normalEvents { events.begin(), redactionsBegin };
if (normalEvents.empty())
return;
- emit aboutToAddHistoricalMessages(normalEvents);
- doAddHistoricalMessageEvents(normalEvents);
- emit addedMessages();
-
- Q_ASSERT(d->timeline.size() == timelineSize + normalEvents.size());
-}
-
-void Room::doAddHistoricalMessageEvents(RoomEventsView events)
-{
- Q_ASSERT(!events.empty());
-
- const bool thereWasNoReadMarker = readMarker() == timelineEdge();
- d->insertEvents(events, Older);
+ emit q->aboutToAddHistoricalMessages(normalEvents);
+ const bool thereWasNoReadMarker = q->readMarker() == timeline.crend();
+ const auto insertedSize = insertEvents(std::move(normalEvents), Older);
// Catch a special case when the last read event id refers to an event
// that was outside the loaded timeline and has just arrived. Depending on
// other messages next to the last read one, we might need to promote
// the read marker and update unreadMessages flag.
- const auto curReadMarker = readMarker();
- if (thereWasNoReadMarker && curReadMarker != timelineEdge())
+ const auto curReadMarker = q->readMarker();
+ if (thereWasNoReadMarker && curReadMarker != timeline.crend())
{
qCDebug(MAIN) << "Discovered last read event in a historical batch";
- d->promoteReadMarker(localUser(), curReadMarker, true);
+ promoteReadMarker(q->localUser(), curReadMarker, true);
}
- qCDebug(MAIN) << "Room" << displayName() << "received" << events.size()
- << "past events; the oldest event is now" << d->timeline.front();
+ qCDebug(MAIN) << "Room" << displayname << "received" << insertedSize
+ << "past events; the oldest event is now" << timeline.front();
+ q->onAddHistoricalTimelineEvents(timeline.crend() - insertedSize);
+ emit q->addedMessages();
+
+ Q_ASSERT(timeline.size() == timelineSize + insertedSize);
}
void Room::processStateEvents(const RoomEvents& events)
{
bool emitNamesChanged = false;
- for (auto event: events)
+ for (const auto& e: events)
{
+ auto* event = e.get();
switch (event->type())
{
case EventType::RoomName: {
@@ -973,12 +985,12 @@ void Room::processStateEvents(const RoomEvents& events)
d->updateDisplayname();
}
-void Room::processEphemeralEvent(Event* event)
+void Room::processEphemeralEvent(EventPtr event)
{
switch (event->type())
{
case EventType::Typing: {
- auto typingEvent = static_cast<TypingEvent*>(event);
+ auto typingEvent = static_cast<TypingEvent*>(event.get());
d->usersTyping.clear();
for( const QString& userId: typingEvent->users() )
{
@@ -989,7 +1001,7 @@ void Room::processEphemeralEvent(Event* event)
break;
}
case EventType::Receipt: {
- auto receiptEvent = static_cast<ReceiptEvent*>(event);
+ auto receiptEvent = static_cast<ReceiptEvent*>(event.get());
for( const auto &p: receiptEvent->eventsWithReceipts() )
{
{
diff --git a/room.h b/room.h
index c0e041f6..08327917 100644
--- a/room.h
+++ b/room.h
@@ -45,25 +45,25 @@ namespace QMatrixClient
{
public:
// For compatibility with Qt containers, even though we use
- // a std:: container now
+ // a std:: container now for the room timeline
using index_t = int;
- TimelineItem(RoomEvent* e, index_t number) : evt(e), idx(number) { }
+ TimelineItem(RoomEventPtr&& e, index_t number)
+ : evt(move(e)), idx(number) { }
RoomEvent* event() const { return evt.get(); }
- RoomEvent* operator->() const { return event(); } //< Synonym for event()->
+ RoomEvent* operator->() const { return evt.operator->(); }
index_t index() const { return idx; }
// Used for event redaction
- RoomEvent* replaceEvent(RoomEvent* other)
+ RoomEventPtr replaceEvent(RoomEventPtr&& other)
{
- auto* old = evt.release();
- evt.reset(other);
- return old;
+ evt.swap(other);
+ return move(other);
}
private:
- std::unique_ptr<RoomEvent> evt;
+ RoomEventPtr evt;
index_t idx;
};
inline QDebug& operator<<(QDebug& d, const TimelineItem& ti)
@@ -88,6 +88,7 @@ namespace QMatrixClient
public:
using Timeline = std::deque<TimelineItem>;
using rev_iter_t = Timeline::const_reverse_iterator;
+ using timeline_iter_t = Timeline::const_iterator;
Room(Connection* connection, QString id, JoinState initialJoinState);
~Room() override;
@@ -188,8 +189,8 @@ namespace QMatrixClient
void markAllMessagesAsRead();
signals:
- void aboutToAddHistoricalMessages(RoomEventsView events);
- void aboutToAddNewMessages(RoomEventsView events);
+ void aboutToAddHistoricalMessages(RoomEventsRange events);
+ void aboutToAddNewMessages(RoomEventsRange events);
void addedMessages();
/**
@@ -212,21 +213,20 @@ namespace QMatrixClient
void lastReadEventChanged(User* user);
void readMarkerMoved();
void unreadMessagesChanged(Room* room);
- void replacedEvent(RoomEvent* before, RoomEvent* after);
+ void replacedEvent(const RoomEvent* newEvent,
+ const RoomEvent* oldEvent);
protected:
- virtual void doAddNewMessageEvents(RoomEventsView events);
- virtual void doAddHistoricalMessageEvents(RoomEventsView events);
virtual void processStateEvents(const RoomEvents& events);
- virtual void processEphemeralEvent(Event* event);
- virtual void onRedaction(RoomEvent*, TimelineItem&) { }
+ virtual void processEphemeralEvent(EventPtr event);
+ virtual void onAddNewTimelineEvents(timeline_iter_t from) { }
+ virtual void onAddHistoricalTimelineEvents(rev_iter_t from) { }
+ virtual void onRedaction(const RoomEvent* prevEvent,
+ const RoomEvent* after) { }
private:
class Private;
Private* d;
-
- void addNewMessageEvents(RoomEvents events);
- void addHistoricalMessageEvents(RoomEvents events);
};
class MemberSorter
diff --git a/util.h b/util.h
index 09c58087..65de0610 100644
--- a/util.h
+++ b/util.h
@@ -26,53 +26,6 @@
namespace QMatrixClient
{
/**
- * @brief A crude wrapper around a container of pointers that owns pointers
- * to contained objects
- *
- * Similar to vector<unique_ptr<>>, upon deletion, this wrapper
- * will delete all events contained in it. This wrapper can be used
- * over Qt containers, which are incompatible with unique_ptr and even
- * with QScopedPointer (which is the reason of its creation).
- */
- template <typename ContainerT>
- class Owning : public ContainerT
- {
- public:
- Owning() = default;
- Owning(const Owning&) = delete;
- Owning(Owning&&) = default;
- Owning& operator=(Owning&& other)
- {
- assign(other.release());
- return *this;
- }
-
- ~Owning() { cleanup(); }
-
- void assign(ContainerT&& other)
- {
- if (&other == this)
- return;
- cleanup();
- ContainerT::operator=(other);
- }
-
- /**
- * @brief returns the underlying container and releases the ownership
- *
- * Acts similar to unique_ptr::release.
- */
- ContainerT release()
- {
- ContainerT c;
- ContainerT::swap(c);
- return c;
- }
- private:
- void cleanup() { for (auto e: *this) delete e; }
- };
-
- /**
* @brief Lookup a value by a key in a varargs list
*
* This function template takes the value of its first argument (selector)