diff options
-rw-r--r-- | CMakeLists.txt | 2 | ||||
-rw-r--r-- | connection.cpp | 2 | ||||
-rw-r--r-- | connection.h | 5 | ||||
-rw-r--r-- | events/event.cpp | 148 | ||||
-rw-r--r-- | events/event.h | 103 | ||||
-rw-r--r-- | events/receiptevent.cpp | 40 | ||||
-rw-r--r-- | events/receiptevent.h | 35 | ||||
-rw-r--r-- | events/roomaliasesevent.cpp | 42 | ||||
-rw-r--r-- | events/roomaliasesevent.h | 14 | ||||
-rw-r--r-- | events/roomcanonicalaliasevent.cpp | 32 | ||||
-rw-r--r-- | events/roomcanonicalaliasevent.h | 17 | ||||
-rw-r--r-- | events/roommemberevent.cpp | 77 | ||||
-rw-r--r-- | events/roommemberevent.h | 29 | ||||
-rw-r--r-- | events/roommessageevent.cpp | 121 | ||||
-rw-r--r-- | events/roommessageevent.h | 67 | ||||
-rw-r--r-- | events/roomnameevent.cpp | 30 | ||||
-rw-r--r-- | events/roomnameevent.h | 17 | ||||
-rw-r--r-- | events/roomtopicevent.cpp | 29 | ||||
-rw-r--r-- | events/roomtopicevent.h | 17 | ||||
-rw-r--r-- | events/typingevent.cpp | 42 | ||||
-rw-r--r-- | events/typingevent.h | 12 | ||||
-rw-r--r-- | examples/qmc-example.cpp | 11 | ||||
-rw-r--r-- | jobs/roommessagesjob.cpp | 9 | ||||
-rw-r--r-- | jobs/roommessagesjob.h | 2 | ||||
-rw-r--r-- | jobs/syncjob.cpp | 6 | ||||
-rw-r--r-- | jobs/syncjob.h | 55 | ||||
-rw-r--r-- | room.cpp | 201 | ||||
-rw-r--r-- | room.h | 24 |
28 files changed, 470 insertions, 719 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 11cf015d..ad7c5a34 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -50,7 +50,6 @@ set(libqmatrixclient_SRCS logging.cpp room.cpp user.cpp - state.cpp settings.cpp events/event.cpp events/roommessageevent.cpp @@ -61,7 +60,6 @@ set(libqmatrixclient_SRCS events/roomtopicevent.cpp events/typingevent.cpp events/receiptevent.cpp - events/unknownevent.cpp jobs/basejob.cpp jobs/checkauthmethods.cpp jobs/passwordlogin.cpp diff --git a/connection.cpp b/connection.cpp index f9e2e7ae..56628a07 100644 --- a/connection.cpp +++ b/connection.cpp @@ -191,7 +191,7 @@ void Connection::postMessage(Room* room, const QString& type, const QString& mes callApi<PostMessageJob>(room->id(), type, message); } -PostReceiptJob* Connection::postReceipt(Room* room, Event* event) const +PostReceiptJob* Connection::postReceipt(Room* room, RoomEvent* event) const { return callApi<PostReceiptJob>(room->id(), event->id()); } diff --git a/connection.h b/connection.h index 08184d0d..f0b097fd 100644 --- a/connection.h +++ b/connection.h @@ -26,7 +26,7 @@ namespace QMatrixClient { class Room; class User; - class Event; + class RoomEvent; class ConnectionPrivate; class ConnectionData; @@ -61,7 +61,8 @@ namespace QMatrixClient Q_INVOKABLE virtual void postMessage(Room* room, const QString& type, const QString& message) const; /** @deprecated Use callApi<PostReceiptJob>() or Room::postReceipt() instead */ - Q_INVOKABLE virtual PostReceiptJob* postReceipt( Room* room, Event* event ) const; + Q_INVOKABLE virtual PostReceiptJob* postReceipt(Room* room, + RoomEvent* event) const; Q_INVOKABLE virtual JoinRoomJob* joinRoom(const QString& roomAlias); /** @deprecated Use callApi<LeaveRoomJob>() or Room::leaveRoom() instead */ Q_INVOKABLE virtual void leaveRoom( Room* room ); diff --git a/events/event.cpp b/events/event.cpp index 5df816fe..bd7e1b03 100644 --- a/events/event.cpp +++ b/events/event.cpp @@ -18,9 +18,6 @@ #include "event.h" -#include <QtCore/QJsonArray> -#include <QtCore/QJsonDocument> - #include "roommessageevent.h" #include "roomnameevent.h" #include "roomaliasesevent.h" @@ -31,120 +28,101 @@ #include "receiptevent.h" #include "unknownevent.h" #include "logging.h" -#include "util.h" - -using namespace QMatrixClient; - -class Event::Private -{ - public: - EventType type; - QString id; - QDateTime timestamp; - QString roomId; - QString senderId; - QString originalJson; -}; -Event::Event(EventType type) - : d(new Private) -{ - d->type = type; -} +#include <QtCore/QJsonDocument> -Event::~Event() -{ - delete d; -} +using namespace QMatrixClient; -EventType Event::type() const +Event::Event(Type type, const QJsonObject& rep) + : _type(type), _originalJson(rep) { - return d->type; + if (!rep.contains("content")) + { + qCWarning(EVENTS) << "Event without 'content' node"; + qCWarning(EVENTS) << formatJson << rep; + } } -QString Event::id() const +QByteArray Event::originalJson() const { - return d->id; + return QJsonDocument(_originalJson).toJson(); } -QDateTime Event::timestamp() const +QDateTime Event::toTimestamp(const QJsonValue& v) { - return d->timestamp; + Q_ASSERT(v.isDouble()); + return QDateTime::fromMSecsSinceEpoch( + static_cast<long long int>(v.toDouble()), Qt::UTC); } -QString Event::roomId() const +QStringList Event::toStringList(const QJsonValue& v) { - return d->roomId; -} + Q_ASSERT(v.isArray()); -QString Event::senderId() const -{ - return d->senderId; + QStringList l; + for( const QJsonValue& e : v.toArray() ) + l.push_back(e.toString()); + return l; } -QString Event::originalJson() const +const QJsonObject Event::contentJson() const { - return d->originalJson; + return _originalJson["content"].toObject(); } template <typename EventT> -EventT* make(const QJsonObject& obj) +EventT* make(const QJsonObject& o) { - return EventT::fromJson(obj); + return new EventT(o); } Event* Event::fromJson(const QJsonObject& obj) { + // Check more specific event types first + if (auto e = RoomEvent::fromJson(obj)) + return e; + return dispatch<Event*>(obj).to(obj["type"].toString(), - "m.room.message", &make<RoomMessageEvent>, - "m.room.name", &make<RoomNameEvent>, - "m.room.aliases", &make<RoomAliasesEvent>, - "m.room.canonical_alias", &make<RoomCanonicalAliasEvent>, - "m.room.member", &make<RoomMemberEvent>, - "m.room.topic", &make<RoomTopicEvent>, - "m.typing", &make<TypingEvent>, - "m.receipt", &make<ReceiptEvent>, - /* Insert new event types BEFORE this line */ - &make<UnknownEvent> - ); + "m.typing", make<TypingEvent>, + "m.receipt", make<ReceiptEvent>, + /* Insert new event types (except room events) BEFORE this line */ + nullptr + ); } -bool Event::parseJson(const QJsonObject& obj) +RoomEvent::RoomEvent(Type type, const QJsonObject& rep) + : Event(type, rep), _id(rep["event_id"].toString()) + , _serverTimestamp(toTimestamp(rep["origin_server_ts"])) + , _roomId(rep["room_id"].toString()) + , _senderId(rep["sender"].toString()) { - d->originalJson = QString::fromUtf8(QJsonDocument(obj).toJson()); - d->id = obj.value("event_id").toString(); - d->roomId = obj.value("room_id").toString(); - d->senderId = obj.value("sender").toString(); - bool correct = (d->type != EventType::Unknown); - if ( d->type != EventType::Typing && - d->type != EventType::Receipt ) + if (_id.isEmpty()) { - if (d->id.isEmpty()) - { - correct = false; - qCDebug(EVENTS) << "Event: can't find event_id; event dump follows"; - qCDebug(EVENTS) << formatJson << obj; - } - if( obj.contains("origin_server_ts") ) - { - d->timestamp = QDateTime::fromMSecsSinceEpoch( - static_cast<qint64>(obj.value("origin_server_ts").toDouble()), Qt::UTC ); - } - else if (d->type != EventType::Unknown) - { - correct = false; - qCDebug(EVENTS) << "Event: can't find ts; event dump follows"; - qCDebug(EVENTS) << formatJson << obj; - } + qCWarning(EVENTS) << "Can't find event_id in a room event"; + qCWarning(EVENTS) << formatJson << rep; + } + if (!rep.contains("origin_server_ts")) + { + qCWarning(EVENTS) << "Event: can't find server timestamp in a room event"; + qCWarning(EVENTS) << formatJson << rep; + } + if (_senderId.isEmpty()) + { + qCWarning(EVENTS) << "user_id not found in a room event"; + qCWarning(EVENTS) << formatJson << rep; } - return correct; } -Events QMatrixClient::eventsFromJson(const QJsonArray& json) +RoomEvent* RoomEvent::fromJson(const QJsonObject& obj) { - Events evs; - evs.reserve(json.size()); - for (auto event: json) - evs.push_back(Event::fromJson(event.toObject())); - return evs; + return dispatch<RoomEvent*>(obj).to(obj["type"].toString(), + "m.room.message", make<RoomMessageEvent>, + "m.room.name", make<RoomNameEvent>, + "m.room.aliases", make<RoomAliasesEvent>, + "m.room.canonical_alias", make<RoomCanonicalAliasEvent>, + "m.room.member", make<RoomMemberEvent>, + "m.room.topic", make<RoomTopicEvent>, + /* Insert new ROOM event types BEFORE this line */ + nullptr + ); } diff --git a/events/event.h b/events/event.h index f60dfb64..fd2f6feb 100644 --- a/events/event.h +++ b/events/event.h @@ -21,43 +21,92 @@ #include <QtCore/QString> #include <QtCore/QDateTime> #include <QtCore/QJsonObject> -#include <QtCore/QVector> +#include <QtCore/QJsonArray> -class QJsonArray; +#include "util.h" namespace QMatrixClient { - enum class EventType - { - RoomMessage, RoomName, RoomAliases, RoomCanonicalAlias, - RoomMember, RoomTopic, Typing, Receipt, Unknown - }; - class Event { + Q_GADGET public: - explicit Event(EventType type); - Event(Event&) = delete; - virtual ~Event(); - - EventType type() const; - QString id() const; - QDateTime timestamp() const; - QString roomId() const; - QString senderId() const; - // only for debug purposes! - QString originalJson() const; + enum class Type + { + RoomMessage, RoomName, RoomAliases, RoomCanonicalAlias, + RoomMember, RoomTopic, Typing, Receipt, Unknown + }; + + explicit Event(Type type, const QJsonObject& rep); + Event(const Event&) = delete; + + Type type() const { return _type; } + QByteArray originalJson() const; + + // Every event also has a "content" object but since its structure is + // different for different types, we're implementing it per-event type + // (and in most cases it will be a combination of other fields + // instead of "content" field). static Event* fromJson(const QJsonObject& obj); - + protected: - bool parseJson(const QJsonObject& obj); - + static QDateTime toTimestamp(const QJsonValue& v); + static QStringList toStringList(const QJsonValue& v); + + const QJsonObject contentJson() const; + private: - class Private; - Private* d; + Type _type; + QJsonObject _originalJson; + + REGISTER_ENUM(Type) }; - using Events = QVector<Event*>; + using EventType = Event::Type; + template <typename EventT> + using EventsBatch = std::vector<EventT*>; + using Events = EventsBatch<Event>; + + template <typename BaseEventT> + BaseEventT* makeEvent(const QJsonObject& obj) + { + if (auto e = BaseEventT::fromJson(obj)) + return e; + + return new BaseEventT(EventType::Unknown, obj); + } + + template <typename BaseEventT = Event, + typename BatchT = EventsBatch<BaseEventT> > + BatchT makeEvents(const QJsonArray& objs) + { + BatchT evs; + // The below line accommodates the difference in size types of + // STL and Qt containers. + evs.reserve(static_cast<typename BatchT::size_type>(objs.size())); + for (auto obj: objs) + evs.push_back(makeEvent<BaseEventT>(obj.toObject())); + return evs; + } - Events eventsFromJson(const QJsonArray& json); -} + class RoomEvent : public Event + { + public: + RoomEvent(Type type, const QJsonObject& rep); + + const QString& id() const { return _id; } + const QDateTime& timestamp() const { return _serverTimestamp; } + const QString& roomId() const { return _roomId; } + const QString& senderId() const { return _senderId; } + + // "Static override" of the one in Event + static RoomEvent* fromJson(const QJsonObject& obj); + + private: + QString _id; + QDateTime _serverTimestamp; + QString _roomId; + QString _senderId; + }; + using RoomEvents = EventsBatch<RoomEvent>; +} // namespace QMatrixClient diff --git a/events/receiptevent.cpp b/events/receiptevent.cpp index c163424f..e3478cf1 100644 --- a/events/receiptevent.cpp +++ b/events/receiptevent.cpp @@ -41,34 +41,13 @@ Example of a Receipt Event: using namespace QMatrixClient; -class ReceiptEvent::Private +ReceiptEvent::ReceiptEvent(const QJsonObject& obj) + : Event(Type::Receipt, obj) { - public: - EventsToReceipts eventsToReceipts; -}; + Q_ASSERT(obj["type"].toString() == jsonType); -ReceiptEvent::ReceiptEvent() - : Event(EventType::Receipt) - , d(new Private) -{ -} - -ReceiptEvent::~ReceiptEvent() -{ - delete d; -} - -EventsToReceipts ReceiptEvent::events() const -{ - return d->eventsToReceipts; -} - -ReceiptEvent* ReceiptEvent::fromJson(const QJsonObject& obj) -{ - ReceiptEvent* e = new ReceiptEvent(); - e->parseJson(obj); const QJsonObject contents = obj["content"].toObject(); - e->d->eventsToReceipts.reserve(contents.size()); + _eventsWithReceipts.reserve(static_cast<size_t>(contents.size())); for( auto eventIt = contents.begin(); eventIt != contents.end(); ++eventIt ) { if (eventIt.key().isEmpty()) @@ -78,15 +57,14 @@ ReceiptEvent* ReceiptEvent::fromJson(const QJsonObject& obj) continue; } const QJsonObject reads = eventIt.value().toObject().value("m.read").toObject(); - Receipts receipts; receipts.reserve(reads.size()); + std::vector<Receipt> receipts; + receipts.reserve(static_cast<size_t>(reads.size())); for( auto userIt = reads.begin(); userIt != reads.end(); ++userIt ) { const QJsonObject user = userIt.value().toObject(); - const auto time = QDateTime::fromMSecsSinceEpoch( - static_cast<qint64>(user["ts"].toDouble()), Qt::UTC ); - receipts.push_back({ userIt.key(), time }); + receipts.push_back({userIt.key(), toTimestamp(user["ts"])}); } - e->d->eventsToReceipts.push_back({ eventIt.key(), receipts }); + _eventsWithReceipts.push_back({eventIt.key(), receipts}); } - return e; } + diff --git a/events/receiptevent.h b/events/receiptevent.h index 40c0384f..1d280822 100644 --- a/events/receiptevent.h +++ b/events/receiptevent.h @@ -22,32 +22,29 @@ namespace QMatrixClient { - class Receipt + struct Receipt { - public: - QString userId; - QDateTime timestamp; + QString userId; + QDateTime timestamp; }; -} -Q_DECLARE_TYPEINFO(QMatrixClient::Receipt, Q_MOVABLE_TYPE); - -namespace QMatrixClient -{ - using Receipts = QVector<Receipt>; - using EventsToReceipts = QVector< QPair<QString, Receipts> >; + struct ReceiptsForEvent + { + QString evtId; + std::vector<Receipt> receipts; + }; + using EventsWithReceipts = std::vector<ReceiptsForEvent>; class ReceiptEvent: public Event { public: - ReceiptEvent(); - virtual ~ReceiptEvent(); + explicit ReceiptEvent(const QJsonObject& obj); - EventsToReceipts events() const; - - static ReceiptEvent* fromJson(const QJsonObject& obj); + EventsWithReceipts eventsWithReceipts() const + { return _eventsWithReceipts; } private: - class Private; - Private* d; + EventsWithReceipts _eventsWithReceipts; + + static constexpr const char * jsonType = "m.receipt"; }; -} +} // namespace QMatrixClient diff --git a/events/roomaliasesevent.cpp b/events/roomaliasesevent.cpp index ab414498..344b4367 100644 --- a/events/roomaliasesevent.cpp +++ b/events/roomaliasesevent.cpp @@ -34,44 +34,10 @@ #include "roomaliasesevent.h" -#include "logging.h" - -#include <QtCore/QJsonArray> - using namespace QMatrixClient; -class RoomAliasesEvent::Private -{ - public: - QStringList aliases; -}; - -RoomAliasesEvent::RoomAliasesEvent() - : Event(EventType::RoomAliases) - , d(new Private) -{ -} - -RoomAliasesEvent::~RoomAliasesEvent() -{ - delete d; -} - -QStringList RoomAliasesEvent::aliases() const -{ - return d->aliases; -} +RoomAliasesEvent::RoomAliasesEvent(const QJsonObject& obj) + : RoomEvent(Type::RoomAliases, obj) + , _aliases(toStringList(contentJson()["aliases"])) +{ } -RoomAliasesEvent* RoomAliasesEvent::fromJson(const QJsonObject& obj) -{ - RoomAliasesEvent* e = new RoomAliasesEvent(); - e->parseJson(obj); - const QJsonObject contents = obj.value("content").toObject(); - const QJsonArray aliases = contents.value("aliases").toArray(); - for( const QJsonValue& alias : aliases ) - { - e->d->aliases << alias.toString(); - } - qCDebug(EVENTS) << "RoomAliasesEvent:" << e->d->aliases; - return e; -} diff --git a/events/roomaliasesevent.h b/events/roomaliasesevent.h index 8f638be2..efafcb30 100644 --- a/events/roomaliasesevent.h +++ b/events/roomaliasesevent.h @@ -24,18 +24,14 @@ namespace QMatrixClient { - class RoomAliasesEvent: public Event + class RoomAliasesEvent: public RoomEvent { public: - RoomAliasesEvent(); - virtual ~RoomAliasesEvent(); + explicit RoomAliasesEvent(const QJsonObject& obj); - QStringList aliases() const; - - static RoomAliasesEvent* fromJson(const QJsonObject& obj); + QStringList aliases() const { return _aliases; } private: - class Private; - Private* d; + QStringList _aliases; }; -} +} // namespace QMatrixClient diff --git a/events/roomcanonicalaliasevent.cpp b/events/roomcanonicalaliasevent.cpp index d84c07fc..6884bc15 100644 --- a/events/roomcanonicalaliasevent.cpp +++ b/events/roomcanonicalaliasevent.cpp @@ -19,35 +19,3 @@ #include "roomcanonicalaliasevent.h" using namespace QMatrixClient; - -class RoomCanonicalAliasEvent::Private -{ - public: - QString alias; -}; - -RoomCanonicalAliasEvent::RoomCanonicalAliasEvent() - : Event(EventType::RoomCanonicalAlias) - , d(new Private) -{ -} - -RoomCanonicalAliasEvent::~RoomCanonicalAliasEvent() -{ - delete d; -} - -QString RoomCanonicalAliasEvent::alias() -{ - return d->alias; -} - -RoomCanonicalAliasEvent* RoomCanonicalAliasEvent::fromJson(const QJsonObject& obj) -{ - RoomCanonicalAliasEvent* e = new RoomCanonicalAliasEvent(); - e->parseJson(obj); - const QJsonObject contents = obj.value("content").toObject(); - e->d->alias = contents.value("alias").toString(); - return e; -} - diff --git a/events/roomcanonicalaliasevent.h b/events/roomcanonicalaliasevent.h index 87219be6..72620d74 100644 --- a/events/roomcanonicalaliasevent.h +++ b/events/roomcanonicalaliasevent.h @@ -22,18 +22,17 @@ namespace QMatrixClient { - class RoomCanonicalAliasEvent: public Event + class RoomCanonicalAliasEvent : public RoomEvent { public: - RoomCanonicalAliasEvent(); - virtual ~RoomCanonicalAliasEvent(); + explicit RoomCanonicalAliasEvent(const QJsonObject& obj) + : RoomEvent(Type::RoomCanonicalAlias, obj) + , _canonicalAlias(contentJson()["alias"].toString()) + { } - QString alias(); - - static RoomCanonicalAliasEvent* fromJson(const QJsonObject& obj); + QString alias() const { return _canonicalAlias; } private: - class Private; - Private* d; + QString _canonicalAlias; }; -} +} // namespace QMatrixClient diff --git a/events/roommemberevent.cpp b/events/roommemberevent.cpp index 51dbbbab..5973acc7 100644 --- a/events/roommemberevent.cpp +++ b/events/roommemberevent.cpp @@ -22,66 +22,19 @@ using namespace QMatrixClient; -class RoomMemberEvent::Private -{ - public: - MembershipType membership; - QString userId; - QString displayname; - QUrl avatarUrl; -}; - -RoomMemberEvent::RoomMemberEvent() - : Event(EventType::RoomMember) - , d(new Private) -{ -} - -RoomMemberEvent::~RoomMemberEvent() -{ - delete d; -} - -MembershipType RoomMemberEvent::membership() const -{ - return d->membership; -} - -QString RoomMemberEvent::userId() const -{ - return d->userId; -} - -QString RoomMemberEvent::displayName() const -{ - return d->displayname; -} - -QUrl RoomMemberEvent::avatarUrl() const -{ - return d->avatarUrl; -} - -RoomMemberEvent* RoomMemberEvent::fromJson(const QJsonObject& obj) -{ - RoomMemberEvent* e = new RoomMemberEvent(); - e->parseJson(obj); - e->d->userId = obj.value("state_key").toString(); - QJsonObject content = obj.value("content").toObject(); - e->d->displayname = content.value("displayname").toString(); - QString membershipString = content.value("membership").toString(); - if( membershipString == "invite" ) - e->d->membership = MembershipType::Invite; - else if( membershipString == "join" ) - e->d->membership = MembershipType::Join; - else if( membershipString == "knock" ) - e->d->membership = MembershipType::Knock; - else if( membershipString == "leave" ) - e->d->membership = MembershipType::Leave; - else if( membershipString == "ban" ) - e->d->membership = MembershipType::Ban; - else - qCDebug(EVENTS) << "Unknown MembershipType: " << membershipString; - e->d->avatarUrl = QUrl(content.value("avatar_url").toString()); - return e; +RoomMemberEvent::RoomMemberEvent(const QJsonObject& obj) + : RoomEvent(Type::RoomMember, obj), _userId(obj["state_key"].toString()) +{ + const auto contentObj = contentJson(); + _displayName = contentObj["displayname"].toString(); + _avatarUrl = contentObj["avatar_url"].toString(); + QString membershipString = contentObj["membership"].toString(); + const auto supportedStrings = { "invite", "join", "knock", "leave", "ban" }; + for (auto it = supportedStrings.begin(); it != supportedStrings.end(); ++it) + if (membershipString == *it) + { + _membership = MembershipType(it - supportedStrings.begin()); + return; + } + qCWarning(EVENTS) << "Unknown MembershipType: " << membershipString; } diff --git a/events/roommemberevent.h b/events/roommemberevent.h index a33c2982..9ebb75ee 100644 --- a/events/roommemberevent.h +++ b/events/roommemberevent.h @@ -24,23 +24,26 @@ namespace QMatrixClient { - enum class MembershipType {Invite, Join, Knock, Leave, Ban}; - - class RoomMemberEvent: public Event + class RoomMemberEvent: public RoomEvent { + Q_GADGET public: - RoomMemberEvent(); - virtual ~RoomMemberEvent(); + enum MembershipType : int {Invite = 0, Join, Knock, Leave, Ban}; - MembershipType membership() const; - QString userId() const; - QString displayName() const; - QUrl avatarUrl() const; + explicit RoomMemberEvent(const QJsonObject& obj); - static RoomMemberEvent* fromJson(const QJsonObject& obj); + MembershipType membership() const { return _membership; } + const QString& userId() const { return _userId; } + const QString& displayName() const { return _displayName; } + const QUrl& avatarUrl() const { return _avatarUrl; } private: - class Private; - Private* d; + MembershipType _membership; + QString _userId; + QString _displayName; + QUrl _avatarUrl; + + REGISTER_ENUM(MembershipType) }; -} + using MembershipType = RoomMemberEvent::MembershipType; +} // namespace QMatrixClient diff --git a/events/roommessageevent.cpp b/events/roommessageevent.cpp index d5e960a1..49bb4053 100644 --- a/events/roommessageevent.cpp +++ b/events/roommessageevent.cpp @@ -19,64 +19,15 @@ #include "roommessageevent.h" #include "logging.h" -#include "util.h" #include <QtCore/QMimeDatabase> using namespace QMatrixClient; - -class RoomMessageEvent::Private -{ - public: - Private() : msgtype(MessageEventType::Unknown), content(nullptr) {} - ~Private() { if (content) delete content; } - - QString userId; - MessageEventType msgtype; - QString plainBody; - MessageEventContent::Base* content; -}; - -RoomMessageEvent::RoomMessageEvent() - : Event(EventType::RoomMessage) - , d(new Private) -{ } - -RoomMessageEvent::~RoomMessageEvent() -{ - delete d; -} - -QString RoomMessageEvent::userId() const -{ - return d->userId; -} - -MessageEventType RoomMessageEvent::msgtype() const -{ - return d->msgtype; -} - -QString RoomMessageEvent::plainBody() const -{ - return d->plainBody; -} - -QString RoomMessageEvent::body() const -{ - return plainBody(); -} - using namespace MessageEventContent; -Base* RoomMessageEvent::content() const -{ - return d->content; -} - -using ContentPair = std::pair<MessageEventType, MessageEventContent::Base*>; +using ContentPair = std::pair<CType, Base*>; -template <MessageEventType EnumType, typename ContentT> +template <CType EnumType, typename ContentT> ContentPair make(const QJsonObject& json) { return { EnumType, new ContentT(json) }; @@ -91,59 +42,53 @@ ContentPair makeVideo(const QJsonObject& json) if (infoJson.contains("thumbnail_url")) { c->thumbnail = ImageInfo(infoJson["thumbnail_url"].toString(), - infoJson["thumbnail_info"].toObject()); + infoJson["thumbnail_info"].toObject()); } - return { MessageEventType::Video, c }; + return { CType::Video, c }; }; ContentPair makeUnknown(const QJsonObject& json) { qCDebug(EVENTS) << "RoomMessageEvent: couldn't resolve msgtype, JSON follows:"; qCDebug(EVENTS) << json; - return { MessageEventType::Unknown, new Base }; + return { CType::Unknown, new Base() }; } -RoomMessageEvent* RoomMessageEvent::fromJson(const QJsonObject& obj) +RoomMessageEvent::RoomMessageEvent(const QJsonObject& obj) + : RoomEvent(Type::RoomMessage, obj), _msgtype(CType::Unknown) + , _content(nullptr) { - RoomMessageEvent* e = new RoomMessageEvent(); - e->parseJson(obj); - if( obj.contains("sender") ) + const QJsonObject content = contentJson(); + if ( content.contains("msgtype") && content.contains("body") ) { - e->d->userId = obj.value("sender").toString(); - } else { - qCDebug(EVENTS) << "RoomMessageEvent: user_id not found"; + _plainBody = content["body"].toString(); + + auto factory = lookup(content["msgtype"].toString(), + "m.text", make<CType::Text, TextContent>, + "m.emote", make<CType::Emote, TextContent>, + "m.notice", make<CType::Notice, TextContent>, + "m.image", make<CType::Image, ImageContent>, + "m.file", make<CType::File, FileContent>, + "m.location", make<CType::Location, LocationContent>, + "m.video", makeVideo, + "m.audio", make<CType::Audio, AudioContent>, + // Insert new message types before this line + makeUnknown + ); + std::tie(_msgtype, _content) = factory(content); } - if( obj.contains("content") ) + else { - const QJsonObject content = obj["content"].toObject(); - if ( content.contains("msgtype") && content.contains("body") ) - { - e->d->plainBody = content["body"].toString(); - - auto delegate = lookup(content["msgtype"].toString(), - "m.text", &make<MessageEventType::Text, TextContent>, - "m.emote", &make<MessageEventType::Emote, TextContent>, - "m.notice", &make<MessageEventType::Notice, TextContent>, - "m.image", &make<MessageEventType::Image, ImageContent>, - "m.file", &make<MessageEventType::File, FileContent>, - "m.location", &make<MessageEventType::Location, LocationContent>, - "m.video", &makeVideo, - "m.audio", &make<MessageEventType::Audio, AudioContent>, - // Insert new message types before this line - &makeUnknown - ); - std::tie(e->d->msgtype, e->d->content) = delegate(content); - } - else - { - qCWarning(EVENTS) << "RoomMessageEvent(" << e->id() << "): no body or msgtype"; - qCDebug(EVENTS) << obj; - } + qCWarning(EVENTS) << "No body or msgtype in room message event"; + qCWarning(EVENTS) << formatJson << obj; } - return e; } -using namespace MessageEventContent; +RoomMessageEvent::~RoomMessageEvent() +{ + if (_content) + delete _content; +} TextContent::TextContent(const QJsonObject& json) { diff --git a/events/roommessageevent.h b/events/roommessageevent.h index 5d5336aa..6acaad6f 100644 --- a/events/roommessageevent.h +++ b/events/roommessageevent.h @@ -24,44 +24,45 @@ #include <QtCore/QMimeType> #include <QtCore/QSize> +#include <memory> + namespace QMatrixClient { - enum class MessageEventType - { - Text, Emote, Notice, Image, File, Location, Video, Audio, Unknown - }; - namespace MessageEventContent { - class Base { }; - } - - class RoomMessageEvent: public Event - { - public: - RoomMessageEvent(); - virtual ~RoomMessageEvent(); - - QString userId() const; - MessageEventType msgtype() const; + class Base + { + Q_GADGET + public: + enum class Type + { + Text, Emote, Notice, Image, File, Location, Video, Audio, Unknown + }; - QString plainBody() const; + virtual ~Base() = default; - /** - * Same as plainBody() for now; might change for "best-looking body" - * in the future. For richer contents, use content-specific data. - * - * @deprecated - */ - QString body() const; + REGISTER_ENUM(Type) + }; + using CType = Base::Type; + } // namespace MessageEventContent + using MessageEventType = MessageEventContent::CType; - MessageEventContent::Base* content() const; + class RoomMessageEvent: public RoomEvent + { + public: + explicit RoomMessageEvent(const QJsonObject& obj); + ~RoomMessageEvent(); - static RoomMessageEvent* fromJson( const QJsonObject& obj ); + const QString& userId() const { return _userId; } + MessageEventType msgtype() const { return _msgtype; } + const QString& plainBody() const { return _plainBody; } + const MessageEventContent::Base* content() const { return _content; } private: - class Private; - Private* d; + QString _userId; + MessageEventType _msgtype; + QString _plainBody; + MessageEventContent::Base* _content; }; namespace MessageEventContent @@ -73,7 +74,7 @@ namespace QMatrixClient class TextContent: public Base { public: - TextContent(const QJsonObject& json); + explicit TextContent(const QJsonObject& json); QMimeType mimeType; QString body; @@ -103,7 +104,7 @@ namespace QMatrixClient class ThumbnailedContent: public ContentInfoT { public: - ThumbnailedContent(const QJsonObject& json) + explicit ThumbnailedContent(const QJsonObject& json) : ContentInfoT(json["url"].toString(), json["info"].toObject()) , thumbnail(json["thumbnail_url"].toString(), json["thumbnail_info"].toObject()) @@ -118,7 +119,7 @@ namespace QMatrixClient class LocationContent: public Base { public: - LocationContent(const QJsonObject& json); + explicit LocationContent(const QJsonObject& json); QString geoUri; ImageInfo thumbnail; @@ -142,5 +143,5 @@ namespace QMatrixClient int duration; }; using AudioContent = ThumbnailedContent<AudioInfo>; - } -} + } // namespace MessageEventContent +} // namespace QMatrixClient diff --git a/events/roomnameevent.cpp b/events/roomnameevent.cpp index c94cb2c3..c202d17a 100644 --- a/events/roomnameevent.cpp +++ b/events/roomnameevent.cpp @@ -20,33 +20,3 @@ using namespace QMatrixClient;
-class RoomNameEvent::Private
-{
- public:
- QString name;
-};
-
-RoomNameEvent::RoomNameEvent() :
- Event(EventType::RoomName),
- d(new Private)
-{
-}
-
-RoomNameEvent::~RoomNameEvent()
-{
- delete d;
-}
-
-QString RoomNameEvent::name() const
-{
- return d->name;
-}
-
-RoomNameEvent* RoomNameEvent::fromJson(const QJsonObject& obj)
-{
- RoomNameEvent* e = new RoomNameEvent();
- e->parseJson(obj);
- const QJsonObject contents = obj.value("content").toObject();
- e->d->name = contents.value("name").toString();
- return e;
-}
diff --git a/events/roomnameevent.h b/events/roomnameevent.h index 8748c4be..bb823933 100644 --- a/events/roomnameevent.h +++ b/events/roomnameevent.h @@ -22,18 +22,17 @@ namespace QMatrixClient
{
- class RoomNameEvent : public Event
+ class RoomNameEvent : public RoomEvent
{
public:
- RoomNameEvent();
- virtual ~RoomNameEvent();
+ explicit RoomNameEvent(const QJsonObject& obj)
+ : RoomEvent(Type::RoomName, obj)
+ , _name(contentJson()["name"].toString())
+ { }
- QString name() const;
-
- static RoomNameEvent* fromJson(const QJsonObject& obj);
+ QString name() const { return _name; }
private:
- class Private;
- Private *d;
+ QString _name{};
};
-}
+} // namespace QMatrixClient
diff --git a/events/roomtopicevent.cpp b/events/roomtopicevent.cpp index 2e186c4b..26677e78 100644 --- a/events/roomtopicevent.cpp +++ b/events/roomtopicevent.cpp @@ -20,32 +20,3 @@ using namespace QMatrixClient; -class RoomTopicEvent::Private -{ - public: - QString topic; -}; - -RoomTopicEvent::RoomTopicEvent() - : Event(EventType::RoomTopic) - , d(new Private) -{ -} - -RoomTopicEvent::~RoomTopicEvent() -{ - delete d; -} - -QString RoomTopicEvent::topic() const -{ - return d->topic; -} - -RoomTopicEvent* RoomTopicEvent::fromJson(const QJsonObject& obj) -{ - auto e = new RoomTopicEvent(); - e->parseJson(obj); - e->d->topic = obj.value("content").toObject().value("topic").toString(); - return e; -} diff --git a/events/roomtopicevent.h b/events/roomtopicevent.h index 4b0a24b0..fb849afe 100644 --- a/events/roomtopicevent.h +++ b/events/roomtopicevent.h @@ -22,18 +22,17 @@ namespace QMatrixClient { - class RoomTopicEvent: public Event + class RoomTopicEvent: public RoomEvent { public: - RoomTopicEvent(); - virtual ~RoomTopicEvent(); + explicit RoomTopicEvent(const QJsonObject& obj) + : RoomEvent(Type::RoomTopic, obj) + , _topic(contentJson()["topic"].toString()) + { } - QString topic() const; - - static RoomTopicEvent* fromJson(const QJsonObject& obj); + QString topic() const { return _topic; } private: - class Private; - Private* d; + QString _topic; }; -} +} // namespace QMatrixClient diff --git a/events/typingevent.cpp b/events/typingevent.cpp index 009059af..a4d3bae4 100644 --- a/events/typingevent.cpp +++ b/events/typingevent.cpp @@ -18,43 +18,15 @@ #include "typingevent.h" -#include "logging.h" - -#include <QtCore/QJsonArray> - using namespace QMatrixClient; -class TypingEvent::Private -{ - public: - QStringList users; -}; - -TypingEvent::TypingEvent() - : Event(EventType::Typing) - , d( new Private ) -{ -} - -TypingEvent::~TypingEvent() +TypingEvent::TypingEvent(const QJsonObject& obj) + : Event(Type::Typing, obj) { - delete d; -} - -QStringList TypingEvent::users() -{ - return d->users; -} - -TypingEvent* TypingEvent::fromJson(const QJsonObject& obj) -{ - TypingEvent* e = new TypingEvent(); - e->parseJson(obj); - QJsonArray array = obj.value("content").toObject().value("user_ids").toArray(); + QJsonValue result; + result= contentJson()["user_ids"]; + QJsonArray array = result.toArray(); for( const QJsonValue& user: array ) - { - e->d->users << user.toString(); - } - qCDebug(EPHEMERAL) << "Typing:" << e->d->users; - return e; + _users.push_back(user.toString()); } + diff --git a/events/typingevent.h b/events/typingevent.h index da57a389..b12d224e 100644 --- a/events/typingevent.h +++ b/events/typingevent.h @@ -27,15 +27,11 @@ namespace QMatrixClient class TypingEvent: public Event { public: - TypingEvent(); - virtual ~TypingEvent(); + TypingEvent(const QJsonObject& obj); - QStringList users(); - - static TypingEvent* fromJson(const QJsonObject& obj); + QStringList users() const { return _users; } private: - class Private; - Private* d; + QStringList _users; }; -} +} // namespace QMatrixClient diff --git a/examples/qmc-example.cpp b/examples/qmc-example.cpp index 7b45a785..a6da6aba 100644 --- a/examples/qmc-example.cpp +++ b/examples/qmc-example.cpp @@ -1,12 +1,15 @@ -#include <iostream> -#include <QCoreApplication> #include "connection.h" #include "room.h" +#include <QCoreApplication> +#include <iostream> +#include <string> + using namespace QMatrixClient; using std::cout; using std::endl; +using std::string; void onNewRoom(Room* r) { @@ -16,11 +19,11 @@ void onNewRoom(Room* r) cout << " Name: " << r->name().toStdString() << endl; cout << " Canonical alias: " << r->canonicalAlias().toStdString() << endl; }); - QObject::connect(r, &Room::aboutToAddNewMessages, [=] (Events evs) { + QObject::connect(r, &Room::aboutToAddNewMessages, [=] (RoomEvents evs) { cout << "New events in room " << r->id().toStdString() << ":" << endl; for (auto e: evs) { - cout << e->originalJson().toStdString() << endl; + cout << string(e->originalJson()) << endl; } }); } diff --git a/jobs/roommessagesjob.cpp b/jobs/roommessagesjob.cpp index a48403c8..3e603a50 100644 --- a/jobs/roommessagesjob.cpp +++ b/jobs/roommessagesjob.cpp @@ -17,16 +17,15 @@ */ #include "roommessagesjob.h" -#include "../util.h" -#include <QtCore/QJsonArray> +#include "util.h" using namespace QMatrixClient; class RoomMessagesJob::Private { public: - Owning<Events> events; + Owning<RoomEvents> events; QString end; }; @@ -49,7 +48,7 @@ RoomMessagesJob::~RoomMessagesJob() delete d; } -Events RoomMessagesJob::releaseEvents() +RoomEvents RoomMessagesJob::releaseEvents() { return d->events.release(); } @@ -62,7 +61,7 @@ QString RoomMessagesJob::end() BaseJob::Status RoomMessagesJob::parseJson(const QJsonDocument& data) { QJsonObject obj = data.object(); - d->events.assign(eventsFromJson(obj.value("chunk").toArray())); + d->events.assign(makeEvents<RoomEvent>(obj.value("chunk").toArray())); d->end = obj.value("end").toString(); return Success; } diff --git a/jobs/roommessagesjob.h b/jobs/roommessagesjob.h index 2d15d9d4..a029c27c 100644 --- a/jobs/roommessagesjob.h +++ b/jobs/roommessagesjob.h @@ -34,7 +34,7 @@ namespace QMatrixClient FetchDirection dir = FetchDirection::Backward); virtual ~RoomMessagesJob(); - Events releaseEvents(); + RoomEvents releaseEvents(); QString end(); protected: diff --git a/jobs/syncjob.cpp b/jobs/syncjob.cpp index 5984128f..29ddc2e6 100644 --- a/jobs/syncjob.cpp +++ b/jobs/syncjob.cpp @@ -18,7 +18,6 @@ #include "syncjob.h" -#include <QtCore/QJsonArray> #include <QtCore/QElapsedTimer> using namespace QMatrixClient; @@ -96,11 +95,6 @@ BaseJob::Status SyncJob::parseJson(const QJsonDocument& data) return Success; } -void SyncRoomData::EventList::fromJson(const QJsonObject& roomContents) -{ - assign(eventsFromJson(roomContents[jsonKey].toObject()["events"].toArray())); -} - SyncRoomData::SyncRoomData(const QString& roomId_, JoinState joinState_, const QJsonObject& room_) : roomId(roomId_) diff --git a/jobs/syncjob.h b/jobs/syncjob.h index 48be9423..07824e23 100644 --- a/jobs/syncjob.h +++ b/jobs/syncjob.h @@ -20,39 +20,46 @@ #include "basejob.h" -#include "../joinstate.h" -#include "../events/event.h" +#include "joinstate.h" +#include "events/event.h" #include "util.h" namespace QMatrixClient { class SyncRoomData { - public: - class EventList : public Owning<Events> - { - private: - QString jsonKey; - public: - explicit EventList(QString k) : jsonKey(std::move(k)) { } - void fromJson(const QJsonObject& roomContents); - }; + public: + template <typename EventT> + class Batch : public Owning<EventsBatch<EventT>> + { + public: + explicit Batch(QString k) : jsonKey(std::move(k)) { } + void fromJson(const QJsonObject& roomContents) + { + this->assign(makeEvents<EventT>( + roomContents[jsonKey].toObject()["events"].toArray())); + } + + + private: + QString jsonKey; + }; - QString roomId; - JoinState joinState; - EventList state; - EventList timeline; - EventList ephemeral; - EventList accountData; - EventList inviteState; + QString roomId; + JoinState joinState; + Batch<RoomEvent> state; + Batch<RoomEvent> timeline; + Batch<Event> ephemeral; + Batch<Event> accountData; + Batch<Event> inviteState; - bool timelineLimited; - QString timelinePrevBatch; - int highlightCount; - int notificationCount; + bool timelineLimited; + QString timelinePrevBatch; + int highlightCount; + int notificationCount; - SyncRoomData(const QString& roomId, JoinState joinState_, - const QJsonObject& room_); + SyncRoomData(const QString& roomId, JoinState joinState_, + const QJsonObject& room_); }; } // namespace QMatrixClient Q_DECLARE_TYPEINFO(QMatrixClient::SyncRoomData, Q_MOVABLE_TYPE); @@ -96,18 +96,18 @@ class Room::Private void getPreviousContent(int limit = 10); - bool isEventNotable(const Event* e) const + bool isEventNotable(const RoomEvent* e) const { return e->senderId() != connection->userId() && e->type() == EventType::RoomMessage; } - void appendEvent(Event* e) + void appendEvent(RoomEvent* e) { insertEvent(e, timeline.end(), timeline.empty() ? 0 : q->maxTimelineIndex() + 1); } - void prependEvent(Event* e) + void prependEvent(RoomEvent* e) { insertEvent(e, timeline.begin(), timeline.empty() ? 0 : q->minTimelineIndex() - 1); @@ -116,7 +116,7 @@ class Room::Private /** * Removes events from the passed container that are already in the timeline */ - void dropDuplicateEvents(Events* events) const; + void dropDuplicateEvents(RoomEvents* events) const; void setLastReadEvent(User* u, const QString& eventId); rev_iter_pair_t promoteReadMarker(User* u, rev_iter_t newMarker); @@ -128,7 +128,7 @@ class Room::Private void insertMemberIntoMap(User* u); void removeMemberFromMap(const QString& username, User* u); - void insertEvent(Event* e, Timeline::iterator where, + void insertEvent(RoomEvent* e, Timeline::iterator where, TimelineItem::index_t index); }; @@ -394,10 +394,10 @@ void Room::Private::removeMemberFromMap(const QString& username, User* u) inline QByteArray makeErrorStr(const Event* e, const char* msg) { return QString("%1; event dump follows:\n%2") - .arg(msg, e->originalJson()).toUtf8(); + .arg(msg, QString(e->originalJson())).toUtf8(); } -void Room::Private::insertEvent(Event* e, Timeline::iterator where, +void Room::Private::insertEvent(RoomEvent* e, Timeline::iterator where, TimelineItem::index_t index) { Q_ASSERT_X(e, __FUNCTION__, "Attempt to add nullptr to timeline"); @@ -530,7 +530,7 @@ void Room::updateData(SyncRoomData&& data) qCDebug(PROFILER) << "*** Room::addNewMessageEvents():" << et.elapsed() << "ms"; et.restart(); - for( Event* ephemeralEvent: data.ephemeral ) + for( auto ephemeralEvent: data.ephemeral ) { processEphemeralEvent(ephemeralEvent); } @@ -586,12 +586,12 @@ void Room::leaveRoom() const connection()->callApi<LeaveRoomJob>(id()); } -void Room::Private::dropDuplicateEvents(Events* events) const +void Room::Private::dropDuplicateEvents(RoomEvents* events) const { // Collect all duplicate events at the end of the container auto dupsBegin = std::stable_partition(events->begin(), events->end(), - [&] (Event* e) { return !eventsIndex.contains(e->id()); }); + [&] (RoomEvent* e) { return !eventsIndex.contains(e->id()); }); // Dispose of those dups std::for_each(dupsBegin, events->end(), [] (Event* e) { delete e; }); events->erase(dupsBegin, events->end()); @@ -602,7 +602,7 @@ Connection* Room::connection() const return d->connection; } -void Room::addNewMessageEvents(Events events) +void Room::addNewMessageEvents(RoomEvents events) { d->dropDuplicateEvents(&events); if (events.empty()) @@ -612,9 +612,9 @@ void Room::addNewMessageEvents(Events events) emit addedMessages(); } -void Room::doAddNewMessageEvents(const Events& events) +void Room::doAddNewMessageEvents(const RoomEvents& events) { - Q_ASSERT(!events.isEmpty()); + Q_ASSERT(!events.empty()); Timeline::size_type newUnreadMessages = 0; for (auto e: events) @@ -647,7 +647,7 @@ void Room::doAddNewMessageEvents(const Events& events) } } -void Room::addHistoricalMessageEvents(Events events) +void Room::addHistoricalMessageEvents(RoomEvents events) { d->dropDuplicateEvents(&events); if (events.empty()) @@ -657,9 +657,9 @@ void Room::addHistoricalMessageEvents(Events events) emit addedMessages(); } -void Room::doAddHistoricalMessageEvents(const Events& events) +void Room::doAddHistoricalMessageEvents(const RoomEvents& events) { - Q_ASSERT(!events.isEmpty()); + Q_ASSERT(!events.empty()); // Historical messages arrive in newest-to-oldest order for (auto e: events) d->prependEvent(e); @@ -667,52 +667,57 @@ void Room::doAddHistoricalMessageEvents(const Events& events) << "past events; the oldest event is now" << d->timeline.front(); } -void Room::processStateEvents(const Events& events) +void Room::processStateEvents(const RoomEvents& events) { bool emitNamesChanged = false; for (auto event: events) { - if( event->type() == EventType::RoomName ) + switch (event->type()) { - auto nameEvent = static_cast<RoomNameEvent*>(event); - d->name = nameEvent->name(); - qCDebug(MAIN) << "room name:" << d->name; - emitNamesChanged = true; - } - if( event->type() == EventType::RoomAliases ) - { - auto aliasesEvent = static_cast<RoomAliasesEvent*>(event); - d->aliases = aliasesEvent->aliases(); - qCDebug(MAIN) << "room aliases:" << d->aliases; - emitNamesChanged = true; - } - if( event->type() == EventType::RoomCanonicalAlias ) - { - auto aliasEvent = static_cast<RoomCanonicalAliasEvent*>(event); - d->canonicalAlias = aliasEvent->alias(); - qCDebug(MAIN) << "room canonical alias:" << d->canonicalAlias; - emitNamesChanged = true; - } - if( event->type() == EventType::RoomTopic ) - { - auto topicEvent = static_cast<RoomTopicEvent*>(event); - d->topic = topicEvent->topic(); - emit topicChanged(); - } - if( event->type() == EventType::RoomMember ) - { - auto memberEvent = static_cast<RoomMemberEvent*>(event); - // Can't use d->member() below because the user may be not a member (yet) - User* u = d->connection->user(memberEvent->userId()); - u->processEvent(event); - if( memberEvent->membership() == MembershipType::Join ) - { - d->addMember(u); + case EventType::RoomName: { + auto nameEvent = static_cast<RoomNameEvent*>(event); + d->name = nameEvent->name(); + qCDebug(MAIN) << "Room name updated:" << d->name; + emitNamesChanged = true; + break; } - else if( memberEvent->membership() == MembershipType::Leave ) - { - d->removeMember(u); + case EventType::RoomAliases: { + auto aliasesEvent = static_cast<RoomAliasesEvent*>(event); + d->aliases = aliasesEvent->aliases(); + qCDebug(MAIN) << "Room aliases updated:" << d->aliases; + emitNamesChanged = true; + break; + } + case EventType::RoomCanonicalAlias: { + auto aliasEvent = static_cast<RoomCanonicalAliasEvent*>(event); + d->canonicalAlias = aliasEvent->alias(); + qCDebug(MAIN) << "Room canonical alias updated:" << d->canonicalAlias; + emitNamesChanged = true; + break; } + case EventType::RoomTopic: { + auto topicEvent = static_cast<RoomTopicEvent*>(event); + d->topic = topicEvent->topic(); + qCDebug(MAIN) << "Room topic updated:" << d->topic; + emit topicChanged(); + break; + } + case EventType::RoomMember: { + auto memberEvent = static_cast<RoomMemberEvent*>(event); + // Can't use d->member() below because the user may be not a member (yet) + User* u = d->connection->user(memberEvent->userId()); + u->processEvent(event); + if( memberEvent->membership() == MembershipType::Join ) + { + d->addMember(u); + } + else if( memberEvent->membership() == MembershipType::Leave ) + { + d->removeMember(u); + } + break; + } + default: /* Ignore events of other types */; } } if (emitNamesChanged) { @@ -723,53 +728,57 @@ void Room::processStateEvents(const Events& events) void Room::processEphemeralEvent(Event* event) { - if( event->type() == EventType::Typing ) - { - auto typingEvent = static_cast<TypingEvent*>(event); - d->usersTyping.clear(); - for( const QString& userId: typingEvent->users() ) - { - if (auto m = d->member(userId)) - d->usersTyping.append(m); - } - emit typingChanged(); - } - if( event->type() == EventType::Receipt ) + switch (event->type()) { - auto receiptEvent = static_cast<ReceiptEvent*>(event); - for( const auto &eventReceiptPair: receiptEvent->events() ) - { - const auto& eventId = eventReceiptPair.first; - const auto& receipts = eventReceiptPair.second; + case EventType::Typing: { + auto typingEvent = static_cast<TypingEvent*>(event); + d->usersTyping.clear(); + for( const QString& userId: typingEvent->users() ) { - if (receipts.size() == 1) - qCDebug(EPHEMERAL) << "Marking" << eventId - << "as read for" << receipts[0].userId; - else - qCDebug(EPHEMERAL) << "Marking" << eventId - << "as read for" - << receipts.size() << "users"; + if (auto m = d->member(userId)) + d->usersTyping.append(m); } - if (d->eventsIndex.contains(eventId)) - { - const auto newMarker = findInTimeline(eventId); - for( const Receipt& r: receipts ) - if (auto m = d->member(r.userId)) - d->promoteReadMarker(m, newMarker); - } else + emit typingChanged(); + break; + } + case EventType::Receipt: { + auto receiptEvent = static_cast<ReceiptEvent*>(event); + for( const auto &p: receiptEvent->eventsWithReceipts() ) { - qCDebug(EPHEMERAL) << "Event" << eventId - << "not found; saving read receipts anyway"; - // If the event is not found (most likely, because it's too old - // and hasn't been fetched from the server yet), but there is - // a previous marker for a user, keep the previous marker. - // Otherwise, blindly store the event id for this user. - for( const Receipt& r: receipts ) - if (auto m = d->member(r.userId)) - if (readMarker(m) == timelineEdge()) - d->setLastReadEvent(m, eventId); + { + if (p.receipts.size() == 1) + qCDebug(EPHEMERAL) << "Marking" << p.evtId + << "as read for" << p.receipts[0].userId; + else + qCDebug(EPHEMERAL) << "Marking" << p.evtId + << "as read for" + << p.receipts.size() << "users"; + } + if (d->eventsIndex.contains(p.evtId)) + { + const auto newMarker = findInTimeline(p.evtId); + for( const Receipt& r: p.receipts ) + if (auto m = d->member(r.userId)) + d->promoteReadMarker(m, newMarker); + } else + { + qCDebug(EPHEMERAL) << "Event" << p.evtId + << "not found; saving read receipts anyway"; + // If the event is not found (most likely, because it's too old + // and hasn't been fetched from the server yet), but there is + // a previous marker for a user, keep the previous marker. + // Otherwise, blindly store the event id for this user. + for( const Receipt& r: p.receipts ) + if (auto m = d->member(r.userId)) + if (readMarker(m) == timelineEdge()) + d->setLastReadEvent(m, p.evtId); + } } + break; } + default: + qCWarning(EPHEMERAL) << "Unexpected event type in 'ephemeral' batch:" + << event->type(); } } @@ -43,14 +43,14 @@ namespace QMatrixClient // a std:: container now using index_t = int; - TimelineItem(Event* e, index_t number) : evt(e), idx(number) { } + TimelineItem(RoomEvent* e, index_t number) : evt(e), idx(number) { } - Event* event() const { return evt.get(); } - Event* operator->() const { return event(); } //< Synonym for event() + RoomEvent* event() const { return evt.get(); } + RoomEvent* operator->() const { return event(); } //< Synonym for event() index_t index() const { return idx; } private: - std::unique_ptr<Event> evt; + std::unique_ptr<RoomEvent> evt; index_t idx; }; inline QDebug& operator<<(QDebug& d, const TimelineItem& ti) @@ -152,8 +152,8 @@ namespace QMatrixClient void userRenamed(User* user, QString oldName); signals: - void aboutToAddHistoricalMessages(const Events& events); - void aboutToAddNewMessages(const Events& events); + void aboutToAddHistoricalMessages(const RoomEvents& events); + void aboutToAddNewMessages(const RoomEvents& events); void addedMessages(); /** @@ -177,17 +177,17 @@ namespace QMatrixClient void unreadMessagesChanged(Room* room); protected: - virtual void doAddNewMessageEvents(const Events& events); - virtual void doAddHistoricalMessageEvents(const Events& events); - virtual void processStateEvents(const Events& events); + virtual void doAddNewMessageEvents(const RoomEvents& events); + virtual void doAddHistoricalMessageEvents(const RoomEvents& events); + virtual void processStateEvents(const RoomEvents& events); virtual void processEphemeralEvent(Event* event); private: class Private; Private* d; - void addNewMessageEvents(Events events); - void addHistoricalMessageEvents(Events events); + void addNewMessageEvents(RoomEvents events); + void addHistoricalMessageEvents(RoomEvents events); void markMessagesAsRead(rev_iter_t upToMarker); }; @@ -209,4 +209,4 @@ namespace QMatrixClient private: const Room* room; }; -} +} // namespace QMatrixClient |