From f1ffe1e7a3e81c07a07a8416ce307e4413ec8fbc Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Sun, 1 Jul 2018 22:48:38 +0900 Subject: Event types system remade to be extensible There were two common points that had to be updated every time a new event is introduced: the EventType enumeration and one of 3 doMakeEvent<> specialisations. The new code has a template class, EventFactory<>, that uses a list of static factory methods to create events instead of typelists used in doMakeEvent<>(); the EventType enumeration is replaced with a namespace populated with constants as necessary. In general, EventType is considered a deprecated mechanism altogether; instead, a set of facilities is provided: is<>() to check if an event has a certain type (to replace comparison against an EventType value) and visit<>() to execute actions based on the event type (replacing switch statements over EventType values). Closes #129. --- lib/events/accountdataevents.h | 39 ++--- lib/events/directchatevent.cpp | 8 +- lib/events/directchatevent.h | 9 +- lib/events/event.cpp | 140 ++++++---------- lib/events/event.h | 349 ++++++++++++++++++++++++++++------------ lib/events/eventcontent.cpp | 25 +-- lib/events/eventcontent.h | 36 +---- lib/events/receiptevent.cpp | 12 +- lib/events/receiptevent.h | 6 +- lib/events/redactionevent.h | 17 +- lib/events/roomavatarevent.h | 10 +- lib/events/roommemberevent.cpp | 14 +- lib/events/roommemberevent.h | 23 +-- lib/events/roommessageevent.cpp | 88 ++++++---- lib/events/roommessageevent.h | 26 ++- lib/events/simplestateevents.h | 68 ++++++-- lib/events/typingevent.cpp | 8 +- lib/events/typingevent.h | 7 +- 18 files changed, 503 insertions(+), 382 deletions(-) (limited to 'lib/events') diff --git a/lib/events/accountdataevents.h b/lib/events/accountdataevents.h index 11667172..6d53d2aa 100644 --- a/lib/events/accountdataevents.h +++ b/lib/events/accountdataevents.h @@ -32,7 +32,7 @@ namespace QMatrixClient { TagRecord (QString order = {}) : order(std::move(order)) { } explicit TagRecord(const QJsonValue& jv) - : order(jv.toObject().value("order").toString()) + : order(jv.toObject().value("order"_ls).toString()) { } QString order; @@ -50,28 +50,29 @@ namespace QMatrixClient using TagsMap = QHash; -#define DEFINE_SIMPLE_EVENT(_Name, _TypeId, _EnumType, _ContentType, _ContentKey) \ +#define DEFINE_SIMPLE_EVENT(_Name, _TypeId, _ContentType, _ContentKey) \ class _Name : public Event \ { \ public: \ - static constexpr const char* typeId() { return _TypeId; } \ - explicit _Name(const QJsonObject& obj) \ - : Event((_EnumType), obj) \ - , _content(contentJson(), QStringLiteral(#_ContentKey)) \ + using content_type = _ContentType; \ + DEFINE_EVENT_TYPEID(_TypeId, _Name) \ + explicit _Name(QJsonObject obj) \ + : Event(typeId(), std::move(obj)) \ { } \ - template \ - explicit _Name(Ts&&... contentArgs) \ - : Event(_EnumType) \ - , _content(QStringLiteral(#_ContentKey), \ - std::forward(contentArgs)...) \ + explicit _Name(_ContentType content) \ + : Event(typeId(), matrixTypeId(), \ + QJsonObject { { QStringLiteral(#_ContentKey), \ + toJson(std::move(content)) } }) \ { } \ - const _ContentType& _ContentKey() const { return _content.value; } \ - QJsonObject toJson() const { return _content.toJson(); } \ - protected: \ - EventContent::SimpleContent<_ContentType> _content; \ - }; + auto _ContentKey() const \ + { return fromJson(contentJson()[#_ContentKey]); } \ + }; // End of macro + + DEFINE_SIMPLE_EVENT(TagEvent, "m.tag", TagsMap, tags) + DEFINE_SIMPLE_EVENT(ReadMarkerEvent, "m.fully_read", QString, event_id) + DEFINE_SIMPLE_EVENT(IgnoredUsersEvent, "m.ignored_user_list", + QSet, ignored_users) - DEFINE_SIMPLE_EVENT(TagEvent, "m.tag", EventType::Tag, TagsMap, tags) - DEFINE_SIMPLE_EVENT(ReadMarkerEvent, "m.fully_read", EventType::ReadMarker, - QString, event_id) + DEFINE_EVENTTYPE_ALIAS(Tag, TagEvent) + DEFINE_EVENTTYPE_ALIAS(ReadMarker, ReadMarkerEvent) } diff --git a/lib/events/directchatevent.cpp b/lib/events/directchatevent.cpp index 63d638a3..6f5d34f2 100644 --- a/lib/events/directchatevent.cpp +++ b/lib/events/directchatevent.cpp @@ -18,18 +18,12 @@ #include "directchatevent.h" -#include "converters.h" - using namespace QMatrixClient; -DirectChatEvent::DirectChatEvent(const QJsonObject& obj) - : Event(Type::DirectChat, obj) -{ } - QMultiHash DirectChatEvent::usersToDirectChats() const { QMultiHash result; - const auto json = contentJson(); + const auto& json = contentJson(); for (auto it = json.begin(); it != json.end(); ++it) { // Beware of range-for's over temporary returned from temporary diff --git a/lib/events/directchatevent.h b/lib/events/directchatevent.h index bd8f2d35..1d366721 100644 --- a/lib/events/directchatevent.h +++ b/lib/events/directchatevent.h @@ -25,10 +25,13 @@ namespace QMatrixClient class DirectChatEvent : public Event { public: - explicit DirectChatEvent(const QJsonObject& obj); + DEFINE_EVENT_TYPEID("m.direct", DirectChatEvent) - QMultiHash usersToDirectChats() const; + explicit DirectChatEvent(const QJsonObject& obj) + : Event(typeId(), obj) + { } - static constexpr const char* typeId() { return "m.direct"; } + QMultiHash usersToDirectChats() const; }; + DEFINE_EVENTTYPE_ALIAS(DirectChat, DirectChatEvent) } diff --git a/lib/events/event.cpp b/lib/events/event.cpp index 576e9426..3f507347 100644 --- a/lib/events/event.cpp +++ b/lib/events/event.cpp @@ -33,117 +33,93 @@ using namespace QMatrixClient; -Event::Event(Type type, const QJsonObject& rep) - : _type(type), _originalJson(rep) +event_type_t EventTypeRegistry::nextTypeId() { - if (!rep.contains("content") && - !rep.value("unsigned").toObject().contains("redacted_because")) + static event_type_t typeIndex = unknownTypeId(); + return ++typeIndex; +} + +Event::Event(Type type, const QJsonObject& json) + : _type(type), _json(json) +{ + if (!json.contains(ContentKeyL) && + !json.value(UnsignedKeyL).toObject().contains(RedactedCauseKeyL)) { qCWarning(EVENTS) << "Event without 'content' node"; - qCWarning(EVENTS) << formatJson << rep; + qCWarning(EVENTS) << formatJson << json; } } +Event::Event(Type type, event_mtype_t matrixType, const QJsonObject& contentJson) + : Event(type, basicEventJson(matrixType, contentJson)) +{ } + Event::~Event() = default; -QString Event::jsonType() const +QString Event::matrixType() const { - return originalJsonObject().value("type").toString(); + return fullJson()[TypeKeyL].toString(); } QByteArray Event::originalJson() const { - return QJsonDocument(_originalJson).toJson(); -} - -QJsonObject Event::originalJsonObject() const -{ - return _originalJson; + return QJsonDocument(_json).toJson(); } const QJsonObject Event::contentJson() const { - return _originalJson["content"].toObject(); -} - -template -inline event_ptr_tt makeIfMatches(const QJsonObject&, const QString&) -{ - return nullptr; + return fullJson()[ContentKeyL].toObject(); } -template -inline event_ptr_tt makeIfMatches(const QJsonObject& o, - const QString& selector) +const QJsonObject Event::unsignedJson() const { - if (selector == EventT::typeId()) - return _impl::create(o); - - return makeIfMatches(o, selector); + return fullJson()[UnsignedKeyL].toObject(); } -template <> -EventPtr _impl::doMakeEvent(const QJsonObject& obj) -{ - // Check more specific event types first - if (auto e = doMakeEvent(obj)) - return ptrCast(move(e)); - - return makeIfMatches( - obj, obj["type"].toString()); -} +[[gnu::unused]] static auto roomEventTypeInitialised = + EventTypeRegistry::chainFactories(); -RoomEvent::RoomEvent(Event::Type type) : Event(type) { } +RoomEvent::RoomEvent(Type type, event_mtype_t matrixType, + const QJsonObject& contentJson) + : Event(type, matrixType, contentJson) +{ } -RoomEvent::RoomEvent(Type type, const QJsonObject& rep) - : Event(type, rep) - , _id(rep["event_id"].toString()) +RoomEvent::RoomEvent(Type type, const QJsonObject& json) + : Event(type, json) { -// if (_id.isEmpty()) -// { -// qCWarning(EVENTS) << "Can't find event_id in a room event"; -// qCWarning(EVENTS) << formatJson << rep; -// } -// if (!rep.contains("origin_server_ts")) -// { -// qCWarning(EVENTS) << "Can't find server timestamp in a room event"; -// qCWarning(EVENTS) << formatJson << rep; -// } -// if (_senderId.isEmpty()) -// { -// qCWarning(EVENTS) << "Can't find sender in a room event"; -// qCWarning(EVENTS) << formatJson << rep; -// } - auto unsignedData = rep["unsigned"].toObject(); - auto redaction = unsignedData.value("redacted_because"); + const auto unsignedData = json[UnsignedKeyL].toObject(); + const auto redaction = unsignedData[RedactedCauseKeyL]; if (redaction.isObject()) { - _redactedBecause = _impl::create(redaction.toObject()); + _redactedBecause = makeEvent(redaction.toObject()); return; } - _txnId = unsignedData.value("transactionId").toString(); + _txnId = unsignedData.value("transactionId"_ls).toString(); if (!_txnId.isEmpty()) qCDebug(EVENTS) << "Event transactionId:" << _txnId; } RoomEvent::~RoomEvent() = default; // Let the smart pointer do its job +QString RoomEvent::id() const +{ + return fullJson()[EventIdKeyL].toString(); +} + QDateTime RoomEvent::timestamp() const { - return QMatrixClient::fromJson( - originalJsonObject().value("origin_server_ts")); + return QMatrixClient::fromJson(fullJson()["origin_server_ts"_ls]); } QString RoomEvent::roomId() const { - return originalJsonObject().value("room_id").toString(); + return fullJson()["room_id"_ls].toString(); } QString RoomEvent::senderId() const { - return originalJsonObject().value("sender").toString(); + return fullJson()["sender"_ls].toString(); } QString RoomEvent::redactionReason() const @@ -151,37 +127,17 @@ QString RoomEvent::redactionReason() const return isRedacted() ? _redactedBecause->reason() : QString{}; } -void RoomEvent::addId(const QString& id) +void RoomEvent::addId(const QString& newId) { - Q_ASSERT(_id.isEmpty()); Q_ASSERT(!id.isEmpty()); - _id = id; + Q_ASSERT(id().isEmpty()); Q_ASSERT(!newId.isEmpty()); + editJson().insert(EventIdKey, newId); } -template <> -RoomEventPtr _impl::doMakeEvent(const QJsonObject& obj) -{ - // Check more specific event types first - if (auto e = doMakeEvent(obj)) - return ptrCast(move(e)); - - return makeIfMatches(obj, obj["type"].toString()); -} +[[gnu::unused]] static auto stateEventTypeInitialised = + EventTypeRegistry::chainFactories(); bool StateEventBase::repeatsState() const { - auto contentJson = originalJsonObject().value("content"); - auto prevContentJson = originalJsonObject().value("unsigned") - .toObject().value("prev_content"); - return contentJson == prevContentJson; -} - -template<> -StateEventPtr _impl::doMakeEvent(const QJsonObject& obj) -{ - return makeIfMatches(obj, obj["type"].toString()); - + const auto prevContentJson = unsignedJson().value(PrevContentKeyL); + return fullJson().value(ContentKeyL) == prevContentJson; } diff --git a/lib/events/event.h b/lib/events/event.h index cbfa06ac..f8264baf 100644 --- a/lib/events/event.h +++ b/lib/events/event.h @@ -23,6 +23,8 @@ namespace QMatrixClient { + // === event_ptr_tt<> and type casting facilities === + template using event_ptr_tt = std::unique_ptr; @@ -44,106 +46,262 @@ namespace QMatrixClient return unique_ptr_cast(ptr); } - namespace _impl + // === Predefined types and JSON key names + + using event_type_t = uint; + using event_mtype_t = const char*; + + static const auto TypeKey = QStringLiteral("type"); + static const auto ContentKey = QStringLiteral("content"); + static const auto EventIdKey = QStringLiteral("event_id"); + static const auto TypeKeyL = "type"_ls; + static const auto ContentKeyL = "content"_ls; + static const auto EventIdKeyL = "event_id"_ls; + static const auto UnsignedKeyL = "unsigned"_ls; + static const auto RedactedCauseKeyL = "redacted_because"_ls; + static const auto PrevContentKeyL = "prev_content"_ls; + + // === Event factory === + + template + inline event_ptr_tt makeEvent(ArgTs&&... args) { - template - inline event_ptr_tt create(ArgTs&&... args) - { - return std::make_unique(std::forward(args)...); - } + return std::make_unique(std::forward(args)...); + } + + class EventTypeRegistry + { + public: + static constexpr event_type_t unknownTypeId() { return 0; } + static event_type_t nextTypeId(); + + /** Chain two type factories + * Adds the factory class of EventT2 (EventT2::factory_t) to + * the list in factory class of EventT1 (EventT1::factory_t) so + * that when EventT1::factory_t::make() is invoked, types of + * EventT2 factory are looked through as well. This is used + * to include RoomEvent types into the more general Event factory, + * and state event types into the RoomEvent factory. + */ + template + static auto chainFactories() + { + EventT1::factory_t::addFactory(&EventT2::factory_t::make); + return 0; + } + + /** Add a type to its default factory + * Adds a standard factory method (via makeEvent<>) for a given + * type to EventT::factory_t factory class so that it can be + * created dynamically from loadEvent<>(). + * + * \tparam EventT the type to enable dynamic creation of + * \return the registered type id + * \sa loadEvent, Event::type + */ + template + static auto addType() + { + EventT::factory_t::addFactory( + [] (const QJsonObject& json, const QString& jsonMatrixType) + { + return EventT::matrixTypeId() == jsonMatrixType + ? makeEvent(json) : nullptr; + }); + return nextTypeId(); + } + + template + static auto typeId() { return _typeId>; } + + private: + template + static const event_type_t _typeId; + }; + + template + const event_type_t EventTypeRegistry::_typeId = addType(); + + template + class EventFactory + { + public: + template + static void addFactory(FnT&& factory) + { + factories().emplace_back(std::forward(factory)); + } + + static event_ptr_tt make(const QJsonObject& json, + const QString& matrixType) + { + for (const auto& f: factories()) + if (auto e = f(json, matrixType)) + return e; + return makeEvent(EventTypeRegistry::unknownTypeId(), + json); + } + + private: + static auto& factories() + { + using inner_factory_tt = + std::function(const QJsonObject&, + const QString&)>; + static std::vector _factories {}; + return _factories; + } + }; + + template + inline QJsonObject basicEventJson(StrT matrixType, + const QJsonObject& content) + { + return { { TypeKey, std::forward(matrixType) }, + { ContentKey, content } }; + } + + /** 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 + inline event_ptr_tt loadEvent(const QJsonObject& fullJson) + { + return EventFactory + ::make(fullJson, fullJson[TypeKeyL].toString()); + } - template - inline event_ptr_tt doMakeEvent(const QJsonObject& obj) + /** Create an event from a type string and content JSON + * Use this factory template to resolve the C++ type from the Matrix + * type string in \p matrixType and create an event of that type that has + * its content part set to \p content. + */ + template + inline event_ptr_tt loadEvent(const QString& matrixType, + const QJsonObject& content) + { + return EventFactory + ::make(basicEventJson(matrixType, content), matrixType); + } + + template struct FromJson> + { + auto operator()(const QJsonValue& jv) const { - return create(obj); + return loadEvent(jv.toObject()); } - } + }; + + // === Event === class Event { Q_GADGET public: - enum class Type : quint16 - { - Unknown = 0, - Typing, Receipt, Tag, DirectChat, ReadMarker, - RoomEventBase = 0x1000, - RoomMessage = RoomEventBase + 1, - RoomEncryptedMessage, Redaction, - RoomStateEventBase = 0x1800, - RoomName = RoomStateEventBase + 1, - RoomAliases, RoomCanonicalAlias, RoomMember, RoomTopic, - RoomAvatar, RoomEncryption, RoomCreate, RoomJoinRules, - RoomPowerLevels, - Reserved = 0x2000 - }; - - explicit Event(Type type) : _type(type) { } - Event(Type type, const QJsonObject& rep); + using Type = event_type_t; + using factory_t = EventFactory; + + explicit Event(Type type, const QJsonObject& json); + explicit Event(Type type, event_mtype_t matrixType, + const QJsonObject& contentJson = {}); Event(const Event&) = delete; + Event(Event&&) = default; + Event& operator=(const Event&) = delete; + Event& operator=(Event&&) = delete; virtual ~Event(); Type type() const { return _type; } - QString jsonType() const; - bool isStateEvent() const - { - return (quint16(_type) & 0x1800) == 0x1800; - } + QString matrixType() const; QByteArray originalJson() const; - QJsonObject originalJsonObject() const; + QJsonObject originalJsonObject() const { return fullJson(); } + + const QJsonObject& fullJson() const { return _json; } // According to the CS API spec, 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). + // different types, we're implementing it per-event type. const QJsonObject contentJson() const; + const QJsonObject unsignedJson() const; + + virtual bool isStateEvent() const { return false; } + + template + bool is() const + { + const auto eventTypeId = EventTypeRegistry::typeId(); + return _type == eventTypeId; + } - virtual QJsonObject toJson() const { Q_ASSERT(false); return {}; } + protected: + QJsonObject& editJson() { return _json; } private: Type _type; - QJsonObject _originalJson; + QJsonObject _json; - REGISTER_ENUM(Type) Q_PROPERTY(Type type READ type CONSTANT) Q_PROPERTY(QJsonObject contentJson READ contentJson CONSTANT) }; - using EventType = Event::Type; using EventPtr = event_ptr_tt; - /** 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 - inline event_ptr_tt makeEvent(const QJsonObject& obj) + using EventsArray = std::vector>; + using Events = EventsArray; + + // This macro should be used in a public section of an event class to + // provide matrixTypeId() and typeId(). +#define DEFINE_EVENT_TYPEID(_Id, _Type) \ + static constexpr event_mtype_t matrixTypeId() { return _Id; } \ + static event_type_t typeId() { return EventTypeRegistry::typeId<_Type>(); } + + // This macro should be put after an event class definition to define an + // additional constant that can be used for an event type id. The constant + // will be inside EventType namespace. This is for back-compatibility, + // to support clients checking for EventType::ShortName (previously + // EventType was a typedef for an enumeration). New code should use + // either typeId() for a specific event type, or (better) casting methods + // defined in the very beginning of this file. +#define DEFINE_EVENTTYPE_ALIAS(_Id, _Type) \ + namespace EventType \ + { \ + [[deprecated("Use "#_Type"::typeId(), Event::is<>() or visit<>()")]] \ + static const auto _Id { _Type::typeId() }; \ + } // End of macro + + // === visit<>() === + + template + inline fn_return_t visit(const Event& event, FnT visitor) { - auto e = _impl::doMakeEvent(obj); - if (!e) - e = _impl::create(EventType::Unknown, obj); - return e; + using event_type = fn_arg_t; + if (event.is()) + return visitor(static_cast(event)); + return fn_return_t(); } - namespace _impl + template + inline auto visit(const Event& event, FnT visitor1, FnTs&&... visitors) { - template <> - EventPtr doMakeEvent(const QJsonObject& obj); + using event_type1 = fn_arg_t; + if (event.is()) + return visitor1(static_cast(event)); + + return visit(event, std::forward(visitors)...); } - template struct FromJson> + template + inline auto visit(const event_ptr_tt& eptr, FnTs&&... visitors) { - auto operator()(const QJsonValue& jv) const - { - return makeEvent(jv.toObject()); - } - }; + using return_type = decltype(visit(*eptr, visitors...)); + if (eptr) + return visit(*eptr, visitors...); + return return_type(); + } - template - using EventsArray = std::vector>; - using Events = EventsArray; + // === RoomEvent === class RedactionEvent; @@ -159,13 +317,16 @@ namespace QMatrixClient Q_PROPERTY(bool isRedacted READ isRedacted) Q_PROPERTY(QString transactionId READ transactionId) public: + using factory_t = EventFactory; + // RedactionEvent is an incomplete type here so we cannot inline - // constructors and destructors - explicit RoomEvent(Type type); - RoomEvent(Type type, const QJsonObject& rep); + // constructors and destructors and we cannot use 'using'. + RoomEvent(Type type, event_mtype_t matrixType, + const QJsonObject& contentJson = {}); + RoomEvent(Type type, const QJsonObject& json); ~RoomEvent() override; - QString id() const { return _id; } + QString id() const; QDateTime timestamp() const; QString roomId() const; QString senderId() const; @@ -196,10 +357,9 @@ namespace QMatrixClient * in the event. It's the responsibility of the code calling addId() * to notify clients that use Q_PROPERTY(id) about its change */ - void addId(const QString& id); + void addId(const QString& newId); private: - QString _id; event_ptr_tt _redactedBecause; QString _txnId; }; @@ -207,43 +367,30 @@ namespace QMatrixClient using RoomEvents = EventsArray; using RoomEventsRange = Range; - namespace _impl - { - template <> - RoomEventPtr doMakeEvent(const QJsonObject& obj); - } + // === State events === class StateEventBase: public RoomEvent { public: - explicit StateEventBase(Type type, const QJsonObject& obj) - : RoomEvent(obj.contains("state_key") ? type : Type::Unknown, - obj) - { } - explicit StateEventBase(Type type) - : RoomEvent(type) - { } + using factory_t = EventFactory; + + using RoomEvent::RoomEvent; ~StateEventBase() override = default; + bool isStateEvent() const override { return true; } virtual bool repeatsState() const; }; using StateEventPtr = event_ptr_tt; using StateEvents = EventsArray; - namespace _impl - { - template <> - StateEventPtr doMakeEvent(const QJsonObject& obj); - } - template struct Prev { template explicit Prev(const QJsonObject& unsignedJson, ContentParamTs&&... contentParams) - : senderId(unsignedJson.value("prev_sender").toString()) - , content(unsignedJson.value("prev_content").toObject(), + : senderId(unsignedJson.value("prev_sender"_ls).toString()) + , content(unsignedJson.value(PrevContentKeyL).toObject(), std::forward(contentParams)...) { } @@ -258,31 +405,33 @@ namespace QMatrixClient using content_type = ContentT; template - explicit StateEvent(Type type, const QJsonObject& obj, + explicit StateEvent(Type type, const QJsonObject& fullJson, ContentParamTs&&... contentParams) - : StateEventBase(type, obj) + : StateEventBase(type, fullJson) , _content(contentJson(), std::forward(contentParams)...) { - auto unsignedData = obj.value("unsigned").toObject(); - if (unsignedData.contains("prev_content")) + const auto& unsignedData = unsignedJson(); + if (unsignedData.contains(PrevContentKeyL)) _prev = std::make_unique>(unsignedData, - std::forward(contentParams)...); + std::forward(contentParams)...); } template - explicit StateEvent(Type type, ContentParamTs&&... contentParams) - : StateEventBase(type) + explicit StateEvent(Type type, event_mtype_t matrixType, + ContentParamTs&&... contentParams) + : StateEventBase(type, matrixType) , _content(std::forward(contentParams)...) - { } - - QJsonObject toJson() const override { return _content.toJson(); } + { + editJson().insert(ContentKey, _content.toJson()); + } const ContentT& content() const { return _content; } - /** @deprecated Use prevContent instead */ + [[deprecated("Use prevContent instead")]] const ContentT* prev_content() const { return prevContent(); } const ContentT* prevContent() const { return _prev ? &_prev->content : nullptr; } - QString prevSenderId() const { return _prev ? _prev->senderId : ""; } + QString prevSenderId() const + { return _prev ? _prev->senderId : QString(); } protected: ContentT _content; diff --git a/lib/events/eventcontent.cpp b/lib/events/eventcontent.cpp index f5974b46..a6b1c763 100644 --- a/lib/events/eventcontent.cpp +++ b/lib/events/eventcontent.cpp @@ -17,8 +17,8 @@ */ #include "eventcontent.h" +#include "util.h" -#include #include using namespace QMatrixClient::EventContent; @@ -39,9 +39,9 @@ FileInfo::FileInfo(const QUrl& u, int payloadSize, const QMimeType& mimeType, FileInfo::FileInfo(const QUrl& u, const QJsonObject& infoJson, const QString& originalFilename) : originalInfoJson(infoJson) - , mimeType(QMimeDatabase().mimeTypeForName(infoJson["mimetype"].toString())) + , mimeType(QMimeDatabase().mimeTypeForName(infoJson["mimetype"_ls].toString())) , url(u) - , payloadSize(infoJson["size"].toInt()) + , payloadSize(infoJson["size"_ls].toInt()) , originalName(originalFilename) { if (!mimeType.isValid()) @@ -51,8 +51,8 @@ FileInfo::FileInfo(const QUrl& u, const QJsonObject& infoJson, void FileInfo::fillInfoJson(QJsonObject* infoJson) const { Q_ASSERT(infoJson); - infoJson->insert("size", payloadSize); - infoJson->insert("mimetype", mimeType.name()); + infoJson->insert(QStringLiteral("size"), payloadSize); + infoJson->insert(QStringLiteral("mimetype"), mimeType.name()); } ImageInfo::ImageInfo(const QUrl& u, int fileSize, QMimeType mimeType, @@ -63,23 +63,24 @@ ImageInfo::ImageInfo(const QUrl& u, int fileSize, QMimeType mimeType, ImageInfo::ImageInfo(const QUrl& u, const QJsonObject& infoJson, const QString& originalFilename) : FileInfo(u, infoJson, originalFilename) - , imageSize(infoJson["w"].toInt(), infoJson["h"].toInt()) + , imageSize(infoJson["w"_ls].toInt(), infoJson["h"_ls].toInt()) { } void ImageInfo::fillInfoJson(QJsonObject* infoJson) const { FileInfo::fillInfoJson(infoJson); - infoJson->insert("w", imageSize.width()); - infoJson->insert("h", imageSize.height()); + infoJson->insert(QStringLiteral("w"), imageSize.width()); + infoJson->insert(QStringLiteral("h"), imageSize.height()); } Thumbnail::Thumbnail(const QJsonObject& infoJson) - : ImageInfo(infoJson["thumbnail_url"].toString(), - infoJson["thumbnail_info"].toObject()) + : ImageInfo(infoJson["thumbnail_url"_ls].toString(), + infoJson["thumbnail_info"_ls].toObject()) { } void Thumbnail::fillInfoJson(QJsonObject* infoJson) const { - infoJson->insert("thumbnail_url", url.toString()); - infoJson->insert("thumbnail_info", toInfoJson(*this)); + infoJson->insert(QStringLiteral("thumbnail_url"), url.toString()); + infoJson->insert(QStringLiteral("thumbnail_info"), + toInfoJson(*this)); } diff --git a/lib/events/eventcontent.h b/lib/events/eventcontent.h index 9d44aec0..91d7a8c8 100644 --- a/lib/events/eventcontent.h +++ b/lib/events/eventcontent.h @@ -21,14 +21,11 @@ // This file contains generic event content definitions, applicable to room // message events as well as other events (e.g., avatars). -#include "converters.h" - +#include #include #include #include -#include - namespace QMatrixClient { namespace EventContent @@ -58,37 +55,6 @@ namespace QMatrixClient virtual void fillJson(QJsonObject* o) const = 0; }; - template - class SimpleContent: public Base - { - public: - using value_type = T; - - // The constructor is templated to enable perfect forwarding - template - SimpleContent(QString keyName, TT&& value) - : value(std::forward(value)), key(std::move(keyName)) - { } - SimpleContent(const QJsonObject& json, QString keyName) - : Base(json) - , value(QMatrixClient::fromJson(json[keyName])) - , key(std::move(keyName)) - { } - - public: - T value; - - protected: - QString key; - - private: - void fillJson(QJsonObject* json) const override - { - Q_ASSERT(json); - json->insert(key, QMatrixClient::toJson(value)); - } - }; - // The below structures fairly follow CS spec 11.2.1.6. The overall // set of attributes for each content types is a superset of the spec // but specific aggregation structure is altered. See doc comments to diff --git a/lib/events/receiptevent.cpp b/lib/events/receiptevent.cpp index a12f4c05..3451a40e 100644 --- a/lib/events/receiptevent.cpp +++ b/lib/events/receiptevent.cpp @@ -35,17 +35,14 @@ Example of a Receipt Event: #include "receiptevent.h" -#include "converters.h" #include "logging.h" using namespace QMatrixClient; ReceiptEvent::ReceiptEvent(const QJsonObject& obj) - : Event(Type::Receipt, obj) + : Event(typeId(), obj) { - Q_ASSERT(obj["type"].toString() == typeId()); - - const QJsonObject contents = contentJson(); + const auto& contents = contentJson(); _eventsWithReceipts.reserve(contents.size()); for( auto eventIt = contents.begin(); eventIt != contents.end(); ++eventIt ) { @@ -55,14 +52,15 @@ ReceiptEvent::ReceiptEvent(const QJsonObject& obj) qCDebug(EPHEMERAL) << "ReceiptEvent content follows:\n" << contents; continue; } - const QJsonObject reads = eventIt.value().toObject().value("m.read").toObject(); + const QJsonObject reads = eventIt.value().toObject() + .value("m.read"_ls).toObject(); QVector receipts; receipts.reserve(reads.size()); for( auto userIt = reads.begin(); userIt != reads.end(); ++userIt ) { const QJsonObject user = userIt.value().toObject(); receipts.push_back({userIt.key(), - QMatrixClient::fromJson(user["ts"])}); + fromJson(user["ts"_ls])}); } _eventsWithReceipts.push_back({eventIt.key(), std::move(receipts)}); } diff --git a/lib/events/receiptevent.h b/lib/events/receiptevent.h index e8d3a65f..10b3f631 100644 --- a/lib/events/receiptevent.h +++ b/lib/events/receiptevent.h @@ -39,14 +39,14 @@ namespace QMatrixClient class ReceiptEvent: public Event { public: + DEFINE_EVENT_TYPEID("m.receipt", ReceiptEvent) explicit ReceiptEvent(const QJsonObject& obj); - EventsWithReceipts eventsWithReceipts() const + const EventsWithReceipts& eventsWithReceipts() const { return _eventsWithReceipts; } - static constexpr const char* typeId() { return "m.receipt"; } - private: EventsWithReceipts _eventsWithReceipts; }; + DEFINE_EVENTTYPE_ALIAS(Receipt, ReceiptEvent) } // namespace QMatrixClient diff --git a/lib/events/redactionevent.h b/lib/events/redactionevent.h index dad54788..8c6cbeff 100644 --- a/lib/events/redactionevent.h +++ b/lib/events/redactionevent.h @@ -25,19 +25,16 @@ namespace QMatrixClient class RedactionEvent : public RoomEvent { public: - static constexpr const char* typeId() { return "m.room.redaction"; } + DEFINE_EVENT_TYPEID("m.room.redaction", RedactionEvent) explicit RedactionEvent(const QJsonObject& obj) - : RoomEvent(Type::Redaction, obj) - , _redactedEvent(obj.value("redacts").toString()) - , _reason(contentJson().value("reason").toString()) + : RoomEvent(typeId(), obj) { } - const QString& redactedEvent() const { return _redactedEvent; } - const QString& reason() const { return _reason; } - - private: - QString _redactedEvent; - QString _reason; + QString redactedEvent() const + { return fullJson()["redacts"_ls].toString(); } + QString reason() const + { return contentJson()["reason"_ls].toString(); } }; + DEFINE_EVENTTYPE_ALIAS(Redaction, RedactionEvent) } // namespace QMatrixClient diff --git a/lib/events/roomavatarevent.h b/lib/events/roomavatarevent.h index 0e44ad7c..fe11807c 100644 --- a/lib/events/roomavatarevent.h +++ b/lib/events/roomavatarevent.h @@ -20,8 +20,6 @@ #include "event.h" -#include - #include "eventcontent.h" namespace QMatrixClient @@ -33,11 +31,11 @@ namespace QMatrixClient // without a thumbnail. But The Spec says there be thumbnails, and // we follow The Spec. public: + DEFINE_EVENT_TYPEID("m.room.avatar", RoomAvatarEvent) explicit RoomAvatarEvent(const QJsonObject& obj) - : StateEvent(Type::RoomAvatar, obj) + : StateEvent(typeId(), obj) { } - - static constexpr const char* typeId() { return "m.room.avatar"; } + QUrl url() const { return content().url; } }; - + DEFINE_EVENTTYPE_ALIAS(RoomAvatar, RoomAvatarEvent) } // namespace QMatrixClient diff --git a/lib/events/roommemberevent.cpp b/lib/events/roommemberevent.cpp index 76b003c2..3acce7cb 100644 --- a/lib/events/roommemberevent.cpp +++ b/lib/events/roommemberevent.cpp @@ -50,10 +50,10 @@ namespace QMatrixClient } MemberEventContent::MemberEventContent(const QJsonObject& json) - : membership(fromJson(json["membership"])) - , isDirect(json["is_direct"].toBool()) - , displayName(json["displayname"].toString()) - , avatarUrl(json["avatar_url"].toString()) + : membership(fromJson(json["membership"_ls])) + , isDirect(json["is_direct"_ls].toBool()) + , displayName(json["displayname"_ls].toString()) + , avatarUrl(json["avatar_url"_ls].toString()) { } void MemberEventContent::fillJson(QJsonObject* o) const @@ -62,8 +62,8 @@ void MemberEventContent::fillJson(QJsonObject* o) const Q_ASSERT_X(membership != MembershipType::Undefined, __FUNCTION__, "The key 'membership' must be explicit in MemberEventContent"); if (membership != MembershipType::Undefined) - o->insert("membership", membershipStrings[membership]); - o->insert("displayname", displayName); + o->insert(QStringLiteral("membership"), membershipStrings[membership]); + o->insert(QStringLiteral("displayname"), displayName); if (avatarUrl.isValid()) - o->insert("avatar_url", avatarUrl.toString()); + o->insert(QStringLiteral("avatar_url"), avatarUrl.toString()); } diff --git a/lib/events/roommemberevent.h b/lib/events/roommemberevent.h index 8e0cc0a4..943d5ac6 100644 --- a/lib/events/roommemberevent.h +++ b/lib/events/roommemberevent.h @@ -22,8 +22,6 @@ #include "eventcontent.h" -#include - namespace QMatrixClient { class MemberEventContent: public EventContent::Base @@ -36,6 +34,9 @@ namespace QMatrixClient : membership(mt) { } explicit MemberEventContent(const QJsonObject& json); + explicit MemberEventContent(const QJsonValue& jv) + : MemberEventContent(jv.toObject()) + { } MembershipType membership; bool isDirect = false; @@ -52,23 +53,26 @@ namespace QMatrixClient { Q_GADGET public: - static constexpr const char* typeId() { return "m.room.member"; } + DEFINE_EVENT_TYPEID("m.room.member", RoomMemberEvent) using MembershipType = MemberEventContent::MembershipType; - explicit RoomMemberEvent(Type type, const QJsonObject& obj) - : StateEvent(type, obj) + explicit RoomMemberEvent(const QJsonObject& obj) + : StateEvent(typeId(), obj) { } RoomMemberEvent(MemberEventContent&& c) - : StateEvent(Type::RoomMember, c) + : StateEvent(typeId(), matrixTypeId(), c.toJson()) { } - explicit RoomMemberEvent(const QJsonObject& obj) - : RoomMemberEvent(Type::RoomMember, obj) + + // This is a special constructor enabling RoomMemberEvent to be + // a base class for more specific member events. + RoomMemberEvent(Type type, const QJsonObject& fullJson) + : StateEvent(type, fullJson) { } MembershipType membership() const { return content().membership; } QString userId() const - { return originalJsonObject().value("state_key").toString(); } + { return fullJson()["state_key"_ls].toString(); } bool isDirect() const { return content().isDirect; } QString displayName() const { return content().displayName; } QUrl avatarUrl() const { return content().avatarUrl; } @@ -76,4 +80,5 @@ namespace QMatrixClient private: REGISTER_ENUM(MembershipType) }; + DEFINE_EVENTTYPE_ALIAS(RoomMember, RoomMemberEvent) } // namespace QMatrixClient diff --git a/lib/events/roommessageevent.cpp b/lib/events/roommessageevent.cpp index 1a4e74bf..e07054a4 100644 --- a/lib/events/roommessageevent.cpp +++ b/lib/events/roommessageevent.cpp @@ -35,7 +35,7 @@ TypedBase* make(const QJsonObject& json) struct MsgTypeDesc { - QString jsonType; + QString matrixType; MsgType enumType; TypedBase* (*maker)(const QJsonObject&); }; @@ -56,39 +56,56 @@ QString msgTypeToJson(MsgType enumType) auto it = std::find_if(msgTypes.begin(), msgTypes.end(), [=](const MsgTypeDesc& mtd) { return mtd.enumType == enumType; }); if (it != msgTypes.end()) - return it->jsonType; + return it->matrixType; return {}; } -MsgType jsonToMsgType(const QString& jsonType) +MsgType jsonToMsgType(const QString& matrixType) { auto it = std::find_if(msgTypes.begin(), msgTypes.end(), - [=](const MsgTypeDesc& mtd) { return mtd.jsonType == jsonType; }); + [=](const MsgTypeDesc& mtd) { return mtd.matrixType == matrixType; }); if (it != msgTypes.end()) return it->enumType; return MsgType::Unknown; } +inline QJsonObject toMsgJson(const QString& plainBody, const QString& jsonMsgType, + TypedBase* content) +{ + auto json = content ? content->toJson() : QJsonObject(); + json.insert(QStringLiteral("msgtype"), jsonMsgType); + json.insert(QStringLiteral("body"), plainBody); + return json; +} + +static const auto MsgTypeKey = "msgtype"_ls; +static const auto BodyKey = "body"_ls; + +RoomMessageEvent::RoomMessageEvent(const QString& plainBody, + const QString& jsonMsgType, TypedBase* content) + : RoomEvent(typeId(), matrixTypeId(), + toMsgJson(plainBody, jsonMsgType, content)) + , _content(content) +{ } + RoomMessageEvent::RoomMessageEvent(const QString& plainBody, MsgType msgType, TypedBase* content) : RoomMessageEvent(plainBody, msgTypeToJson(msgType), content) { } RoomMessageEvent::RoomMessageEvent(const QJsonObject& obj) - : RoomEvent(Type::RoomMessage, obj), _content(nullptr) + : RoomEvent(typeId(), obj), _content(nullptr) { if (isRedacted()) return; const QJsonObject content = contentJson(); - if ( content.contains("msgtype") && content.contains("body") ) + if ( content.contains(MsgTypeKey) && content.contains(BodyKey) ) { - _plainBody = content["body"].toString(); - - _msgtype = content["msgtype"].toString(); + auto msgtype = content[MsgTypeKey].toString(); for (const auto& mt: msgTypes) - if (mt.jsonType == _msgtype) + if (mt.matrixType == msgtype) _content.reset(mt.maker(content)); if (!_content) @@ -107,13 +124,25 @@ RoomMessageEvent::RoomMessageEvent(const QJsonObject& obj) RoomMessageEvent::MsgType RoomMessageEvent::msgtype() const { - return jsonToMsgType(_msgtype); + return jsonToMsgType(rawMsgtype()); +} + +QString RoomMessageEvent::rawMsgtype() const +{ + return contentJson()[MsgTypeKey].toString(); +} + +QString RoomMessageEvent::plainBody() const +{ + return contentJson()[BodyKey].toString(); } QMimeType RoomMessageEvent::mimeType() const { - return _content ? _content->type() : - QMimeDatabase().mimeTypeForName("text/plain"); + static const auto PlainTextMimeType = + QMimeDatabase().mimeTypeForName("text/plain"); + return _content ? _content->type() : PlainTextMimeType; + ; } bool RoomMessageEvent::hasTextContent() const @@ -133,14 +162,6 @@ bool RoomMessageEvent::hasThumbnail() const return content() && content()->thumbnailInfo(); } -QJsonObject RoomMessageEvent::toJson() const -{ - QJsonObject obj = _content ? _content->toJson() : QJsonObject(); - obj.insert("msgtype", msgTypeToJson(msgtype())); - obj.insert("body", plainBody()); - return obj; -} - TextContent::TextContent(const QString& text, const QString& contentType) : mimeType(QMimeDatabase().mimeTypeForName(contentType)), body(text) { } @@ -148,26 +169,29 @@ TextContent::TextContent(const QString& text, const QString& contentType) TextContent::TextContent(const QJsonObject& json) { QMimeDatabase db; + static const auto PlainTextMimeType = db.mimeTypeForName("text/plain"); + static const auto HtmlMimeType = db.mimeTypeForName("text/html"); // Special-casing the custom matrix.org's (actually, Riot's) way // of sending HTML messages. - if (json["format"].toString() == "org.matrix.custom.html") + if (json["format"_ls].toString() == "org.matrix.custom.html") { - mimeType = db.mimeTypeForName("text/html"); - body = json["formatted_body"].toString(); + mimeType = HtmlMimeType; + body = json["formatted_body"_ls].toString(); } else { // Falling back to plain text, as there's no standard way to describe // rich text in messages. - mimeType = db.mimeTypeForName("text/plain"); - body = json["body"].toString(); + mimeType = PlainTextMimeType; + body = json[BodyKey].toString(); } } void TextContent::fillJson(QJsonObject* json) const { Q_ASSERT(json); - json->insert("format", QStringLiteral("org.matrix.custom.html")); - json->insert("formatted_body", body); + json->insert(QStringLiteral("format"), + QStringLiteral("org.matrix.custom.html")); + json->insert(QStringLiteral("formatted_body"), body); } LocationContent::LocationContent(const QString& geoUri, const ImageInfo& thumbnail) @@ -176,8 +200,8 @@ LocationContent::LocationContent(const QString& geoUri, const ImageInfo& thumbna LocationContent::LocationContent(const QJsonObject& json) : TypedBase(json) - , geoUri(json["geo_uri"].toString()) - , thumbnail(json["info"].toObject()) + , geoUri(json["geo_uri"_ls].toString()) + , thumbnail(json["info"_ls].toObject()) { } QMimeType LocationContent::type() const @@ -188,6 +212,6 @@ QMimeType LocationContent::type() const void LocationContent::fillJson(QJsonObject* o) const { Q_ASSERT(o); - o->insert("geo_uri", geoUri); - o->insert("info", toInfoJson(thumbnail)); + o->insert(QStringLiteral("geo_uri"), geoUri); + o->insert(QStringLiteral("info"), toInfoJson(thumbnail)); } diff --git a/lib/events/roommessageevent.h b/lib/events/roommessageevent.h index 075d7188..3f3832d4 100644 --- a/lib/events/roommessageevent.h +++ b/lib/events/roommessageevent.h @@ -37,6 +37,8 @@ namespace QMatrixClient Q_PROPERTY(QMimeType mimeType READ mimeType STORED false CONSTANT) Q_PROPERTY(EventContent::TypedBase* content READ content CONSTANT) public: + DEFINE_EVENT_TYPEID("m.room.message", RoomMessageEvent) + enum class MsgType { Text, Emote, Notice, Image, File, Location, Video, Audio, Unknown @@ -44,18 +46,15 @@ namespace QMatrixClient RoomMessageEvent(const QString& plainBody, const QString& jsonMsgType, - EventContent::TypedBase* content = nullptr) - : RoomEvent(Type::RoomMessage) - , _msgtype(jsonMsgType), _plainBody(plainBody), _content(content) - { } + EventContent::TypedBase* content = nullptr); explicit RoomMessageEvent(const QString& plainBody, MsgType msgType = MsgType::Text, EventContent::TypedBase* content = nullptr); explicit RoomMessageEvent(const QJsonObject& obj); MsgType msgtype() const; - QString rawMsgtype() const { return _msgtype; } - const QString& plainBody() const { return _plainBody; } + QString rawMsgtype() const; + QString plainBody() const; EventContent::TypedBase* content() const { return _content.data(); } QMimeType mimeType() const; @@ -63,17 +62,12 @@ namespace QMatrixClient bool hasFileContent() const; bool hasThumbnail() const; - QJsonObject toJson() const override; - - static constexpr const char* typeId() { return "m.room.message"; } - private: - QString _msgtype; - QString _plainBody; QScopedPointer _content; REGISTER_ENUM(MsgType) }; + DEFINE_EVENTTYPE_ALIAS(RoomMessage, RoomMessageEvent) using MessageEventType = RoomMessageEvent::MsgType; namespace EventContent @@ -140,16 +134,16 @@ namespace QMatrixClient public: PlayableContent(const QJsonObject& json) : ContentT(json) - , duration(ContentT::originalInfoJson["duration"].toInt()) + , duration(ContentT::originalInfoJson["duration"_ls].toInt()) { } protected: void fillJson(QJsonObject* json) const override { ContentT::fillJson(json); - auto infoJson = json->take("info").toObject(); - infoJson.insert("duration", duration); - json->insert("info", infoJson); + auto infoJson = json->take("info"_ls).toObject(); + infoJson.insert(QStringLiteral("duration"), duration); + json->insert(QStringLiteral("info"), infoJson); } public: diff --git a/lib/events/simplestateevents.h b/lib/events/simplestateevents.h index d9f403e8..a117efb1 100644 --- a/lib/events/simplestateevents.h +++ b/lib/events/simplestateevents.h @@ -23,31 +23,69 @@ namespace QMatrixClient { -#define DEFINE_SIMPLE_STATE_EVENT(_Name, _TypeId, _EnumType, _ContentType, _ContentKey) \ - class _Name \ - : public StateEvent> \ + namespace EventContent + { + template + class SimpleContent: public Base + { + public: + using value_type = T; + + // The constructor is templated to enable perfect forwarding + template + SimpleContent(QString keyName, TT&& value) + : value(std::forward(value)), key(std::move(keyName)) + { } + SimpleContent(const QJsonObject& json, QString keyName) + : Base(json) + , value(QMatrixClient::fromJson(json[keyName])) + , key(std::move(keyName)) + { } + + public: + T value; + + protected: + QString key; + + private: + void fillJson(QJsonObject* json) const override + { + Q_ASSERT(json); + json->insert(key, QMatrixClient::toJson(value)); + } + }; + } // namespace EventContent + +#define DEFINE_SIMPLE_STATE_EVENT(_Name, _TypeId, _ContentType, _ContentKey) \ + class _Name : public StateEvent> \ { \ public: \ - static constexpr const char* typeId() { return _TypeId; } \ + using content_type = _ContentType; \ + DEFINE_EVENT_TYPEID(_TypeId, _Name) \ explicit _Name(const QJsonObject& obj) \ - : StateEvent(_EnumType, obj, QStringLiteral(#_ContentKey)) \ + : StateEvent(typeId(), obj, QStringLiteral(#_ContentKey)) \ { } \ template \ explicit _Name(T&& value) \ - : StateEvent(_EnumType, QStringLiteral(#_ContentKey), \ + : StateEvent(typeId(), matrixTypeId(), \ + QStringLiteral(#_ContentKey), \ std::forward(value)) \ { } \ - const _ContentType& _ContentKey() const { return content().value; } \ - }; + auto _ContentKey() const { return content().value; } \ + }; // End of macro - DEFINE_SIMPLE_STATE_EVENT(RoomNameEvent, "m.room.name", - Event::Type::RoomName, QString, name) + DEFINE_SIMPLE_STATE_EVENT(RoomNameEvent, "m.room.name", QString, name) + DEFINE_EVENTTYPE_ALIAS(RoomName, RoomNameEvent) DEFINE_SIMPLE_STATE_EVENT(RoomAliasesEvent, "m.room.aliases", - Event::Type::RoomAliases, QStringList, aliases) + QStringList, aliases) + DEFINE_EVENTTYPE_ALIAS(RoomAliases, RoomAliasesEvent) DEFINE_SIMPLE_STATE_EVENT(RoomCanonicalAliasEvent, "m.room.canonical_alias", - Event::Type::RoomCanonicalAlias, QString, alias) - DEFINE_SIMPLE_STATE_EVENT(RoomTopicEvent, "m.room.topic", - Event::Type::RoomTopic, QString, topic) + QString, alias) + DEFINE_EVENTTYPE_ALIAS(RoomCanonicalAlias, RoomCanonicalAliasEvent) + DEFINE_SIMPLE_STATE_EVENT(RoomTopicEvent, "m.room.topic", QString, topic) + DEFINE_EVENTTYPE_ALIAS(RoomTopic, RoomTopicEvent) DEFINE_SIMPLE_STATE_EVENT(EncryptionEvent, "m.room.encryption", - Event::Type::RoomEncryption, QString, algorithm) + QString, algorithm) + DEFINE_EVENTTYPE_ALIAS(RoomEncryption, EncryptionEvent) } // namespace QMatrixClient diff --git a/lib/events/typingevent.cpp b/lib/events/typingevent.cpp index a4d3bae4..c7b69e33 100644 --- a/lib/events/typingevent.cpp +++ b/lib/events/typingevent.cpp @@ -21,12 +21,10 @@ using namespace QMatrixClient; TypingEvent::TypingEvent(const QJsonObject& obj) - : Event(Type::Typing, obj) + : Event(typeId(), obj) { - QJsonValue result; - result= contentJson()["user_ids"]; - QJsonArray array = result.toArray(); - for( const QJsonValue& user: array ) + const auto& array = contentJson()["user_ids"_ls].toArray(); + for(const auto& user: array ) _users.push_back(user.toString()); } diff --git a/lib/events/typingevent.h b/lib/events/typingevent.h index 6ccbc1c8..0c4b350d 100644 --- a/lib/events/typingevent.h +++ b/lib/events/typingevent.h @@ -20,20 +20,19 @@ #include "event.h" -#include - namespace QMatrixClient { class TypingEvent: public Event { public: - static constexpr const char* typeId() { return "m.typing"; } + DEFINE_EVENT_TYPEID("m.typing", TypingEvent) TypingEvent(const QJsonObject& obj); - QStringList users() const { return _users; } + const QStringList& users() const { return _users; } private: QStringList _users; }; + DEFINE_EVENTTYPE_ALIAS(Typing, TypingEvent) } // namespace QMatrixClient -- cgit v1.2.3 From ed467d27b07781fdd2f7ddef043568954ce50b69 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Mon, 2 Jul 2018 13:39:33 +0900 Subject: Events: use a template structure instead of template variables; rearrange code into blocks A template member variable in it seemed to cause internal compiler error in MSVC 2017, let alone MSVC 2015... --- lib/events/accountdataevents.h | 6 +- lib/events/directchatevent.h | 1 + lib/events/event.cpp | 24 ++---- lib/events/event.h | 180 ++++++++++++++++++++++------------------- lib/events/receiptevent.h | 1 + lib/events/redactionevent.h | 1 + lib/events/roomavatarevent.h | 1 + lib/events/roommemberevent.h | 1 + lib/events/roommessageevent.h | 1 + lib/events/simplestateevents.h | 4 +- lib/events/typingevent.h | 1 + lib/room.cpp | 6 +- 12 files changed, 121 insertions(+), 106 deletions(-) (limited to 'lib/events') diff --git a/lib/events/accountdataevents.h b/lib/events/accountdataevents.h index 6d53d2aa..ed4373cd 100644 --- a/lib/events/accountdataevents.h +++ b/lib/events/accountdataevents.h @@ -65,8 +65,10 @@ namespace QMatrixClient toJson(std::move(content)) } }) \ { } \ auto _ContentKey() const \ - { return fromJson(contentJson()[#_ContentKey]); } \ - }; // End of macro + { return fromJson(contentJson()[#_ContentKey##_ls]); } \ + }; \ + REGISTER_EVENT_TYPE(_Name) \ + // End of macro DEFINE_SIMPLE_EVENT(TagEvent, "m.tag", TagsMap, tags) DEFINE_SIMPLE_EVENT(ReadMarkerEvent, "m.fully_read", QString, event_id) diff --git a/lib/events/directchatevent.h b/lib/events/directchatevent.h index 1d366721..7559796b 100644 --- a/lib/events/directchatevent.h +++ b/lib/events/directchatevent.h @@ -33,5 +33,6 @@ namespace QMatrixClient QMultiHash usersToDirectChats() const; }; + REGISTER_EVENT_TYPE(DirectChatEvent) DEFINE_EVENTTYPE_ALIAS(DirectChat, DirectChatEvent) } diff --git a/lib/events/event.cpp b/lib/events/event.cpp index 3f507347..447068af 100644 --- a/lib/events/event.cpp +++ b/lib/events/event.cpp @@ -18,14 +18,6 @@ #include "event.h" -#include "roommessageevent.h" -#include "simplestateevents.h" -#include "roommemberevent.h" -#include "roomavatarevent.h" -#include "typingevent.h" -#include "receiptevent.h" -#include "accountdataevents.h" -#include "directchatevent.h" #include "redactionevent.h" #include "logging.h" @@ -33,12 +25,6 @@ using namespace QMatrixClient; -event_type_t EventTypeRegistry::nextTypeId() -{ - static event_type_t typeIndex = unknownTypeId(); - return ++typeIndex; -} - Event::Event(Type type, const QJsonObject& json) : _type(type), _json(json) { @@ -77,7 +63,7 @@ const QJsonObject Event::unsignedJson() const } [[gnu::unused]] static auto roomEventTypeInitialised = - EventTypeRegistry::chainFactories(); + Event::factory_t::chainFactory(); RoomEvent::RoomEvent(Type type, event_mtype_t matrixType, const QJsonObject& contentJson) @@ -134,10 +120,16 @@ void RoomEvent::addId(const QString& newId) } [[gnu::unused]] static auto stateEventTypeInitialised = - EventTypeRegistry::chainFactories(); + RoomEvent::factory_t::chainFactory(); bool StateEventBase::repeatsState() const { const auto prevContentJson = unsignedJson().value(PrevContentKeyL); return fullJson().value(ContentKeyL) == prevContentJson; } + +event_type_t QMatrixClient::nextTypeId() +{ + static event_type_t _id = EventTypeTraits::id; + return ++_id; +} diff --git a/lib/events/event.h b/lib/events/event.h index f8264baf..89ba94ac 100644 --- a/lib/events/event.h +++ b/lib/events/event.h @@ -21,6 +21,8 @@ #include "converters.h" #include "util.h" +#include + namespace QMatrixClient { // === event_ptr_tt<> and type casting facilities === @@ -46,10 +48,7 @@ namespace QMatrixClient return unique_ptr_cast(ptr); } - // === Predefined types and JSON key names - - using event_type_t = uint; - using event_mtype_t = const char*; + // === Standard Matrix key names and basicEventJson() === static const auto TypeKey = QStringLiteral("type"); static const auto ContentKey = QStringLiteral("content"); @@ -61,19 +60,57 @@ namespace QMatrixClient static const auto RedactedCauseKeyL = "redacted_because"_ls; static const auto PrevContentKeyL = "prev_content"_ls; + // Minimal correct Matrix event JSON + template + inline QJsonObject basicEventJson(StrT matrixType, + const QJsonObject& content) + { + return { { TypeKey, std::forward(matrixType) }, + { ContentKey, content } }; + } + // === Event factory === + using event_type_t = size_t; + using event_mtype_t = const char*; + + template + struct EventTypeTraits + { + static const event_type_t id; + }; + + template <> + struct EventTypeTraits + { + static constexpr event_type_t id = 0; + }; + + event_type_t nextTypeId(); + + template + const event_type_t EventTypeTraits::id = nextTypeId(); + + template + inline event_type_t typeId() { return EventTypeTraits>::id; } + + inline event_type_t unknownEventTypeId() { return typeId(); } + template inline event_ptr_tt makeEvent(ArgTs&&... args) { return std::make_unique(std::forward(args)...); } - class EventTypeRegistry + template + class EventFactory { public: - static constexpr event_type_t unknownTypeId() { return 0; } - static event_type_t nextTypeId(); + template + static void addMethod(FnT&& method) + { + factories().emplace_back(std::forward(method)); + } /** Chain two type factories * Adds the factory class of EventT2 (EventT2::factory_t) to @@ -83,53 +120,11 @@ namespace QMatrixClient * to include RoomEvent types into the more general Event factory, * and state event types into the RoomEvent factory. */ - template - static auto chainFactories() - { - EventT1::factory_t::addFactory(&EventT2::factory_t::make); - return 0; - } - - /** Add a type to its default factory - * Adds a standard factory method (via makeEvent<>) for a given - * type to EventT::factory_t factory class so that it can be - * created dynamically from loadEvent<>(). - * - * \tparam EventT the type to enable dynamic creation of - * \return the registered type id - * \sa loadEvent, Event::type - */ template - static auto addType() + static auto chainFactory() { - EventT::factory_t::addFactory( - [] (const QJsonObject& json, const QString& jsonMatrixType) - { - return EventT::matrixTypeId() == jsonMatrixType - ? makeEvent(json) : nullptr; - }); - return nextTypeId(); - } - - template - static auto typeId() { return _typeId>; } - - private: - template - static const event_type_t _typeId; - }; - - template - const event_type_t EventTypeRegistry::_typeId = addType(); - - template - class EventFactory - { - public: - template - static void addFactory(FnT&& factory) - { - factories().emplace_back(std::forward(factory)); + addMethod(&EventT::factory_t::make); + return 0; } static event_ptr_tt make(const QJsonObject& json, @@ -138,8 +133,7 @@ namespace QMatrixClient for (const auto& f: factories()) if (auto e = f(json, matrixType)) return e; - return makeEvent(EventTypeRegistry::unknownTypeId(), - json); + return makeEvent(unknownEventTypeId(), json); } private: @@ -153,12 +147,24 @@ namespace QMatrixClient } }; - template - inline QJsonObject basicEventJson(StrT matrixType, - const QJsonObject& content) + /** Add a type to its default factory + * Adds a standard factory method (via makeEvent<>) for a given + * type to EventT::factory_t factory class so that it can be + * created dynamically from loadEvent<>(). + * + * \tparam EventT the type to enable dynamic creation of + * \return the registered type id + * \sa loadEvent, Event::type + */ + template + inline void setupFactory() { - return { { TypeKey, std::forward(matrixType) }, - { ContentKey, content } }; + EventT::factory_t::addMethod( + [] (const QJsonObject& json, const QString& jsonMatrixType) + { + return EventT::matrixTypeId() == jsonMatrixType + ? makeEvent(json) : nullptr; + }); } /** Create an event with proper type from a JSON object @@ -199,6 +205,8 @@ namespace QMatrixClient class Event { Q_GADGET + Q_PROPERTY(Type type READ type CONSTANT) + Q_PROPERTY(QJsonObject contentJson READ contentJson CONSTANT) public: using Type = event_type_t; using factory_t = EventFactory; @@ -228,22 +236,12 @@ namespace QMatrixClient virtual bool isStateEvent() const { return false; } - template - bool is() const - { - const auto eventTypeId = EventTypeRegistry::typeId(); - return _type == eventTypeId; - } - protected: QJsonObject& editJson() { return _json; } private: Type _type; QJsonObject _json; - - Q_PROPERTY(Type type READ type CONSTANT) - Q_PROPERTY(QJsonObject contentJson READ contentJson CONSTANT) }; using EventPtr = event_ptr_tt; @@ -251,33 +249,47 @@ namespace QMatrixClient using EventsArray = std::vector>; using Events = EventsArray; + // === Macros used with event class definitions === + // This macro should be used in a public section of an event class to // provide matrixTypeId() and typeId(). #define DEFINE_EVENT_TYPEID(_Id, _Type) \ static constexpr event_mtype_t matrixTypeId() { return _Id; } \ - static event_type_t typeId() { return EventTypeRegistry::typeId<_Type>(); } - - // This macro should be put after an event class definition to define an - // additional constant that can be used for an event type id. The constant - // will be inside EventType namespace. This is for back-compatibility, - // to support clients checking for EventType::ShortName (previously - // EventType was a typedef for an enumeration). New code should use - // either typeId() for a specific event type, or (better) casting methods - // defined in the very beginning of this file. + static auto typeId() { return QMatrixClient::typeId<_Type>(); } \ + // End of macro + + // This macro should be put after an event class definition (in .h or .cpp) + // to enable its deserialisation from a /sync and other + // polymorphic event arrays +#define REGISTER_EVENT_TYPE(_Type) \ + namespace { \ + [[gnu::unused]] \ + static const auto _factoryAdded##_Type = ( setupFactory<_Type>(), 0); \ + } \ + // End of macro + + // This macro provides constants in EventType:: namespace for + // back-compatibility with libQMatrixClient 0.3 event type system. #define DEFINE_EVENTTYPE_ALIAS(_Id, _Type) \ namespace EventType \ { \ - [[deprecated("Use "#_Type"::typeId(), Event::is<>() or visit<>()")]] \ - static const auto _Id { _Type::typeId() }; \ - } // End of macro + [[deprecated("Use typeId<>(), is<>() or visit<>()")]] \ + static const auto _Id = typeId<_Type>(); \ + } \ + // End of macro + + // === is<>() and visit<>() === + + template + inline bool is(const Event& e) { return e.type() == typeId(); } - // === visit<>() === + inline bool isUnknown(const Event& e) { return e.type() == unknownEventTypeId(); } template inline fn_return_t visit(const Event& event, FnT visitor) { using event_type = fn_arg_t; - if (event.is()) + if (is(event)) return visitor(static_cast(event)); return fn_return_t(); } @@ -286,7 +298,7 @@ namespace QMatrixClient inline auto visit(const Event& event, FnT visitor1, FnTs&&... visitors) { using event_type1 = fn_arg_t; - if (event.is()) + if (is(event)) return visitor1(static_cast(event)); return visit(event, std::forward(visitors)...); diff --git a/lib/events/receiptevent.h b/lib/events/receiptevent.h index 10b3f631..5237ba69 100644 --- a/lib/events/receiptevent.h +++ b/lib/events/receiptevent.h @@ -48,5 +48,6 @@ namespace QMatrixClient private: EventsWithReceipts _eventsWithReceipts; }; + REGISTER_EVENT_TYPE(ReceiptEvent) DEFINE_EVENTTYPE_ALIAS(Receipt, ReceiptEvent) } // namespace QMatrixClient diff --git a/lib/events/redactionevent.h b/lib/events/redactionevent.h index 8c6cbeff..64504d57 100644 --- a/lib/events/redactionevent.h +++ b/lib/events/redactionevent.h @@ -36,5 +36,6 @@ namespace QMatrixClient QString reason() const { return contentJson()["reason"_ls].toString(); } }; + REGISTER_EVENT_TYPE(RedactionEvent) DEFINE_EVENTTYPE_ALIAS(Redaction, RedactionEvent) } // namespace QMatrixClient diff --git a/lib/events/roomavatarevent.h b/lib/events/roomavatarevent.h index fe11807c..491861b1 100644 --- a/lib/events/roomavatarevent.h +++ b/lib/events/roomavatarevent.h @@ -37,5 +37,6 @@ namespace QMatrixClient { } QUrl url() const { return content().url; } }; + REGISTER_EVENT_TYPE(RoomAvatarEvent) DEFINE_EVENTTYPE_ALIAS(RoomAvatar, RoomAvatarEvent) } // namespace QMatrixClient diff --git a/lib/events/roommemberevent.h b/lib/events/roommemberevent.h index 943d5ac6..1ecd63d1 100644 --- a/lib/events/roommemberevent.h +++ b/lib/events/roommemberevent.h @@ -80,5 +80,6 @@ namespace QMatrixClient private: REGISTER_ENUM(MembershipType) }; + REGISTER_EVENT_TYPE(RoomMemberEvent) DEFINE_EVENTTYPE_ALIAS(RoomMember, RoomMemberEvent) } // namespace QMatrixClient diff --git a/lib/events/roommessageevent.h b/lib/events/roommessageevent.h index 3f3832d4..ccf30f96 100644 --- a/lib/events/roommessageevent.h +++ b/lib/events/roommessageevent.h @@ -67,6 +67,7 @@ namespace QMatrixClient REGISTER_ENUM(MsgType) }; + REGISTER_EVENT_TYPE(RoomMessageEvent) DEFINE_EVENTTYPE_ALIAS(RoomMessage, RoomMessageEvent) using MessageEventType = RoomMessageEvent::MsgType; diff --git a/lib/events/simplestateevents.h b/lib/events/simplestateevents.h index a117efb1..afd59478 100644 --- a/lib/events/simplestateevents.h +++ b/lib/events/simplestateevents.h @@ -73,7 +73,9 @@ namespace QMatrixClient std::forward(value)) \ { } \ auto _ContentKey() const { return content().value; } \ - }; // End of macro + }; \ + REGISTER_EVENT_TYPE(_Name) \ + // End of macro DEFINE_SIMPLE_STATE_EVENT(RoomNameEvent, "m.room.name", QString, name) DEFINE_EVENTTYPE_ALIAS(RoomName, RoomNameEvent) diff --git a/lib/events/typingevent.h b/lib/events/typingevent.h index 0c4b350d..27b668b4 100644 --- a/lib/events/typingevent.h +++ b/lib/events/typingevent.h @@ -34,5 +34,6 @@ namespace QMatrixClient private: QStringList _users; }; + REGISTER_EVENT_TYPE(TypingEvent) DEFINE_EVENTTYPE_ALIAS(Typing, TypingEvent) } // namespace QMatrixClient diff --git a/lib/room.cpp b/lib/room.cpp index 9f4b5a0e..080ad30d 100644 --- a/lib/room.cpp +++ b/lib/room.cpp @@ -168,7 +168,7 @@ class Room::Private { return !ti->isRedacted() && ti->senderId() != connection->userId() && - ti->is(); + is(*ti); } void addNewMessageEvents(RoomEvents&& events); @@ -725,7 +725,7 @@ const RoomMessageEvent* Room::Private::getEventWithFile(const QString& eventId) const { auto evtIt = q->findInTimeline(eventId); - if (evtIt != timeline.rend() && evtIt->event()->is()) + if (evtIt != timeline.rend() && is(**evtIt)) { auto* event = evtIt->viewAs(); if (event->hasFileContent()) @@ -1275,7 +1275,7 @@ void Room::Private::dropDuplicateEvents(RoomEvents& events) const inline bool isRedaction(const RoomEventPtr& e) { - return e && e->is(); + return e && is(*e); } void Room::Private::processRedaction(event_ptr_tt&& redaction) -- cgit v1.2.3 From 2cbb2a8c1be8bf08b1b835ffaa7bb0bee823e3a6 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Wed, 4 Jul 2018 19:46:08 +0900 Subject: Break down event.* into smaller files We now have event.*, roomevent.*, stateevent.* and eventloader.h. If you only use event leaf-classes (such as RoomMemberEvent) you shouldn't notice anything. --- CMakeLists.txt | 2 + lib/csapi/event_context.h | 2 +- lib/csapi/gtad.yaml | 8 +- lib/csapi/message_pagination.h | 2 +- lib/csapi/notifications.h | 2 +- lib/csapi/peeking_events.h | 2 +- lib/csapi/presence.h | 2 +- lib/csapi/rooms.h | 2 +- lib/csapi/search.h | 6 +- lib/events/accountdataevents.h | 1 + lib/events/directchatevent.cpp | 2 + lib/events/event.cpp | 79 ++----------------- lib/events/event.h | 174 +---------------------------------------- lib/events/eventloader.h | 57 ++++++++++++++ lib/events/receiptevent.cpp | 1 + lib/events/receiptevent.h | 1 + lib/events/roomevent.cpp | 82 +++++++++++++++++++ lib/events/roomevent.h | 89 +++++++++++++++++++++ lib/events/roommemberevent.cpp | 1 + lib/events/roommemberevent.h | 3 +- lib/events/roommessageevent.h | 3 +- lib/events/simplestateevents.h | 2 +- lib/events/stateevent.cpp | 30 +++++++ lib/events/stateevent.h | 92 ++++++++++++++++++++++ lib/events/typingevent.cpp | 2 + lib/jobs/syncjob.cpp | 2 + lib/jobs/syncjob.h | 2 +- libqmatrixclient.pri | 5 ++ 28 files changed, 391 insertions(+), 265 deletions(-) create mode 100644 lib/events/eventloader.h create mode 100644 lib/events/roomevent.cpp create mode 100644 lib/events/roomevent.h create mode 100644 lib/events/stateevent.cpp create mode 100644 lib/events/stateevent.h (limited to 'lib/events') diff --git a/CMakeLists.txt b/CMakeLists.txt index 54b69457..8b78b831 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -81,6 +81,8 @@ set(libqmatrixclient_SRCS lib/converters.cpp lib/util.cpp lib/events/event.cpp + lib/events/roomevent.cpp + lib/events/stateevent.cpp lib/events/eventcontent.cpp lib/events/roommessageevent.cpp lib/events/roommemberevent.cpp diff --git a/lib/csapi/event_context.h b/lib/csapi/event_context.h index 0470ba36..22c9cbc3 100644 --- a/lib/csapi/event_context.h +++ b/lib/csapi/event_context.h @@ -6,7 +6,7 @@ #include "jobs/basejob.h" -#include "events/event.h" +#include "events/eventloader.h" #include "converters.h" namespace QMatrixClient diff --git a/lib/csapi/gtad.yaml b/lib/csapi/gtad.yaml index 3d1b7e88..09344be5 100644 --- a/lib/csapi/gtad.yaml +++ b/lib/csapi/gtad.yaml @@ -62,11 +62,11 @@ analyzer: - +set: { moveOnly: } +on: - /state_event.yaml$/: - { type: StateEventPtr, imports: '"events/event.h"' } + { type: StateEventPtr, imports: '"events/eventloader.h"' } - /room_event.yaml$/: - { type: RoomEventPtr, imports: '"events/event.h"' } + { type: RoomEventPtr, imports: '"events/eventloader.h"' } - /event.yaml$/: - { type: EventPtr, imports: '"events/event.h"' } + { type: EventPtr, imports: '"events/eventloader.h"' } - /auth_data.yaml$/: *QJsonObject # GTAD 0.6 cannot cope with this one - /m\.room\.member$/: pass # This $ref is only used in an array, see below - //: *UseOmittable # Also apply "avoidCopy" to all other ref'ed types @@ -79,7 +79,7 @@ analyzer: +on: - /^Notification|Result$/: type: "std::vector<{{1}}>" - imports: '"events/event.h"' + imports: '"events/eventloader.h"' - /m\.room\.member$/: type: "EventsArray" imports: '"events/roommemberevent.h"' diff --git a/lib/csapi/message_pagination.h b/lib/csapi/message_pagination.h index 92b258ea..58900940 100644 --- a/lib/csapi/message_pagination.h +++ b/lib/csapi/message_pagination.h @@ -6,7 +6,7 @@ #include "jobs/basejob.h" -#include "events/event.h" +#include "events/eventloader.h" #include "converters.h" namespace QMatrixClient diff --git a/lib/csapi/notifications.h b/lib/csapi/notifications.h index 094fa3af..3698431d 100644 --- a/lib/csapi/notifications.h +++ b/lib/csapi/notifications.h @@ -6,7 +6,7 @@ #include "jobs/basejob.h" -#include "events/event.h" +#include "events/eventloader.h" #include "converters.h" #include #include diff --git a/lib/csapi/peeking_events.h b/lib/csapi/peeking_events.h index f8876bf1..5f2c4233 100644 --- a/lib/csapi/peeking_events.h +++ b/lib/csapi/peeking_events.h @@ -6,7 +6,7 @@ #include "jobs/basejob.h" -#include "events/event.h" +#include "events/eventloader.h" #include "converters.h" namespace QMatrixClient diff --git a/lib/csapi/presence.h b/lib/csapi/presence.h index 2def94ba..7d6665f3 100644 --- a/lib/csapi/presence.h +++ b/lib/csapi/presence.h @@ -6,7 +6,7 @@ #include "jobs/basejob.h" -#include "events/event.h" +#include "events/eventloader.h" #include "converters.h" namespace QMatrixClient diff --git a/lib/csapi/rooms.h b/lib/csapi/rooms.h index 459c6ad5..7d690ec8 100644 --- a/lib/csapi/rooms.h +++ b/lib/csapi/rooms.h @@ -6,8 +6,8 @@ #include "jobs/basejob.h" -#include "events/event.h" #include "events/roommemberevent.h" +#include "events/eventloader.h" #include #include "converters.h" diff --git a/lib/csapi/search.h b/lib/csapi/search.h index 206bacca..572ea6af 100644 --- a/lib/csapi/search.h +++ b/lib/csapi/search.h @@ -6,12 +6,12 @@ #include "jobs/basejob.h" -#include -#include #include -#include "events/event.h" #include "converters.h" #include +#include +#include +#include "events/eventloader.h" namespace QMatrixClient { diff --git a/lib/events/accountdataevents.h b/lib/events/accountdataevents.h index ed4373cd..671ed776 100644 --- a/lib/events/accountdataevents.h +++ b/lib/events/accountdataevents.h @@ -22,6 +22,7 @@ #include "event.h" #include "eventcontent.h" +#include "converters.h" namespace QMatrixClient { diff --git a/lib/events/directchatevent.cpp b/lib/events/directchatevent.cpp index 6f5d34f2..266d60d8 100644 --- a/lib/events/directchatevent.cpp +++ b/lib/events/directchatevent.cpp @@ -18,6 +18,8 @@ #include "directchatevent.h" +#include + using namespace QMatrixClient; QMultiHash DirectChatEvent::usersToDirectChats() const diff --git a/lib/events/event.cpp b/lib/events/event.cpp index 447068af..44bf79a1 100644 --- a/lib/events/event.cpp +++ b/lib/events/event.cpp @@ -18,13 +18,18 @@ #include "event.h" -#include "redactionevent.h" #include "logging.h" #include using namespace QMatrixClient; +event_type_t QMatrixClient::nextTypeId() +{ + static event_type_t _id = EventTypeTraits::id; + return ++_id; +} + Event::Event(Type type, const QJsonObject& json) : _type(type), _json(json) { @@ -61,75 +66,3 @@ const QJsonObject Event::unsignedJson() const { return fullJson()[UnsignedKeyL].toObject(); } - -[[gnu::unused]] static auto roomEventTypeInitialised = - Event::factory_t::chainFactory(); - -RoomEvent::RoomEvent(Type type, event_mtype_t matrixType, - const QJsonObject& contentJson) - : Event(type, matrixType, contentJson) -{ } - -RoomEvent::RoomEvent(Type type, const QJsonObject& json) - : Event(type, json) -{ - const auto unsignedData = json[UnsignedKeyL].toObject(); - const auto redaction = unsignedData[RedactedCauseKeyL]; - if (redaction.isObject()) - { - _redactedBecause = makeEvent(redaction.toObject()); - return; - } - - _txnId = unsignedData.value("transactionId"_ls).toString(); - if (!_txnId.isEmpty()) - qCDebug(EVENTS) << "Event transactionId:" << _txnId; -} - -RoomEvent::~RoomEvent() = default; // Let the smart pointer do its job - -QString RoomEvent::id() const -{ - return fullJson()[EventIdKeyL].toString(); -} - -QDateTime RoomEvent::timestamp() const -{ - return QMatrixClient::fromJson(fullJson()["origin_server_ts"_ls]); -} - -QString RoomEvent::roomId() const -{ - return fullJson()["room_id"_ls].toString(); -} - -QString RoomEvent::senderId() const -{ - return fullJson()["sender"_ls].toString(); -} - -QString RoomEvent::redactionReason() const -{ - return isRedacted() ? _redactedBecause->reason() : QString{}; -} - -void RoomEvent::addId(const QString& newId) -{ - Q_ASSERT(id().isEmpty()); Q_ASSERT(!newId.isEmpty()); - editJson().insert(EventIdKey, newId); -} - -[[gnu::unused]] static auto stateEventTypeInitialised = - RoomEvent::factory_t::chainFactory(); - -bool StateEventBase::repeatsState() const -{ - const auto prevContentJson = unsignedJson().value(PrevContentKeyL); - return fullJson().value(ContentKeyL) == prevContentJson; -} - -event_type_t QMatrixClient::nextTypeId() -{ - static event_type_t _id = EventTypeTraits::id; - return ++_id; -} diff --git a/lib/events/event.h b/lib/events/event.h index 89ba94ac..04384aa7 100644 --- a/lib/events/event.h +++ b/lib/events/event.h @@ -18,10 +18,9 @@ #pragma once -#include "converters.h" #include "util.h" -#include +#include namespace QMatrixClient { @@ -167,39 +166,6 @@ namespace QMatrixClient }); } - /** 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 - inline event_ptr_tt loadEvent(const QJsonObject& fullJson) - { - return EventFactory - ::make(fullJson, fullJson[TypeKeyL].toString()); - } - - /** Create an event from a type string and content JSON - * Use this factory template to resolve the C++ type from the Matrix - * type string in \p matrixType and create an event of that type that has - * its content part set to \p content. - */ - template - inline event_ptr_tt loadEvent(const QString& matrixType, - const QJsonObject& content) - { - return EventFactory - ::make(basicEventJson(matrixType, content), matrixType); - } - - template struct FromJson> - { - auto operator()(const QJsonValue& jv) const - { - return loadEvent(jv.toObject()); - } - }; - // === Event === class Event @@ -313,144 +279,6 @@ namespace QMatrixClient return return_type(); } - // === RoomEvent === - - class RedactionEvent; - - /** This class corresponds to m.room.* events */ - class RoomEvent : public Event - { - Q_GADGET - Q_PROPERTY(QString id READ id) - Q_PROPERTY(QDateTime timestamp READ timestamp CONSTANT) - Q_PROPERTY(QString roomId READ roomId CONSTANT) - Q_PROPERTY(QString senderId READ senderId CONSTANT) - Q_PROPERTY(QString redactionReason READ redactionReason) - Q_PROPERTY(bool isRedacted READ isRedacted) - Q_PROPERTY(QString transactionId READ transactionId) - public: - using factory_t = EventFactory; - - // RedactionEvent is an incomplete type here so we cannot inline - // constructors and destructors and we cannot use 'using'. - RoomEvent(Type type, event_mtype_t matrixType, - const QJsonObject& contentJson = {}); - RoomEvent(Type type, const QJsonObject& json); - ~RoomEvent() override; - - QString id() const; - QDateTime timestamp() const; - QString roomId() const; - QString senderId() const; - bool isRedacted() const { return bool(_redactedBecause); } - const event_ptr_tt& redactedBecause() const - { - return _redactedBecause; - } - QString redactionReason() const; - const QString& transactionId() const { return _txnId; } - - /** - * Sets the transaction id for locally created events. This should be - * done before the event is exposed to any code using the respective - * Q_PROPERTY. - * - * \param txnId - transaction id, normally obtained from - * Connection::generateTxnId() - */ - void setTransactionId(const QString& txnId) { _txnId = txnId; } - - /** - * Sets event id for locally created events - * - * When a new event is created locally, it has no server id yet. - * This function allows to add the id once the confirmation from - * the server is received. There should be no id set previously - * in the event. It's the responsibility of the code calling addId() - * to notify clients that use Q_PROPERTY(id) about its change - */ - void addId(const QString& newId); - - private: - event_ptr_tt _redactedBecause; - QString _txnId; - }; - using RoomEventPtr = event_ptr_tt; - using RoomEvents = EventsArray; - using RoomEventsRange = Range; - - // === State events === - - class StateEventBase: public RoomEvent - { - public: - using factory_t = EventFactory; - - using RoomEvent::RoomEvent; - ~StateEventBase() override = default; - - bool isStateEvent() const override { return true; } - virtual bool repeatsState() const; - }; - using StateEventPtr = event_ptr_tt; - using StateEvents = EventsArray; - - template - struct Prev - { - template - explicit Prev(const QJsonObject& unsignedJson, - ContentParamTs&&... contentParams) - : senderId(unsignedJson.value("prev_sender"_ls).toString()) - , content(unsignedJson.value(PrevContentKeyL).toObject(), - std::forward(contentParams)...) - { } - - QString senderId; - ContentT content; - }; - - template - class StateEvent: public StateEventBase - { - public: - using content_type = ContentT; - - template - explicit StateEvent(Type type, const QJsonObject& fullJson, - ContentParamTs&&... contentParams) - : StateEventBase(type, fullJson) - , _content(contentJson(), - std::forward(contentParams)...) - { - const auto& unsignedData = unsignedJson(); - if (unsignedData.contains(PrevContentKeyL)) - _prev = std::make_unique>(unsignedData, - std::forward(contentParams)...); - } - template - explicit StateEvent(Type type, event_mtype_t matrixType, - ContentParamTs&&... contentParams) - : StateEventBase(type, matrixType) - , _content(std::forward(contentParams)...) - { - editJson().insert(ContentKey, _content.toJson()); - } - - const ContentT& content() const { return _content; } - [[deprecated("Use prevContent instead")]] - const ContentT* prev_content() const { return prevContent(); } - const ContentT* prevContent() const - { return _prev ? &_prev->content : nullptr; } - QString prevSenderId() const - { return _prev ? _prev->senderId : QString(); } - - protected: - ContentT _content; - std::unique_ptr> _prev; - }; } // namespace QMatrixClient Q_DECLARE_METATYPE(QMatrixClient::Event*) -Q_DECLARE_METATYPE(QMatrixClient::RoomEvent*) Q_DECLARE_METATYPE(const QMatrixClient::Event*) -Q_DECLARE_METATYPE(const QMatrixClient::RoomEvent*) diff --git a/lib/events/eventloader.h b/lib/events/eventloader.h new file mode 100644 index 00000000..5f1e3b57 --- /dev/null +++ b/lib/events/eventloader.h @@ -0,0 +1,57 @@ +/****************************************************************************** +* Copyright (C) 2018 Kitsune Ral +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#pragma once + +#include "stateevent.h" +#include "converters.h" + +namespace QMatrixClient { + /** 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 + inline event_ptr_tt loadEvent(const QJsonObject& fullJson) + { + return EventFactory + ::make(fullJson, fullJson[TypeKeyL].toString()); + } + + /** Create an event from a type string and content JSON + * Use this factory template to resolve the C++ type from the Matrix + * type string in \p matrixType and create an event of that type that has + * its content part set to \p content. + */ + template + inline event_ptr_tt loadEvent(const QString& matrixType, + const QJsonObject& content) + { + return EventFactory + ::make(basicEventJson(matrixType, content), matrixType); + } + + template struct FromJson> + { + auto operator()(const QJsonValue& jv) const + { + return loadEvent(jv.toObject()); + } + }; +} // namespace QMatrixClient diff --git a/lib/events/receiptevent.cpp b/lib/events/receiptevent.cpp index 3451a40e..47e1398c 100644 --- a/lib/events/receiptevent.cpp +++ b/lib/events/receiptevent.cpp @@ -35,6 +35,7 @@ Example of a Receipt Event: #include "receiptevent.h" +#include "converters.h" #include "logging.h" using namespace QMatrixClient; diff --git a/lib/events/receiptevent.h b/lib/events/receiptevent.h index 5237ba69..c15a01c2 100644 --- a/lib/events/receiptevent.h +++ b/lib/events/receiptevent.h @@ -21,6 +21,7 @@ #include "event.h" #include +#include namespace QMatrixClient { diff --git a/lib/events/roomevent.cpp b/lib/events/roomevent.cpp new file mode 100644 index 00000000..3d09af8a --- /dev/null +++ b/lib/events/roomevent.cpp @@ -0,0 +1,82 @@ +/****************************************************************************** +* Copyright (C) 2018 Kitsune Ral +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "roomevent.h" + +#include "redactionevent.h" +#include "converters.h" +#include "logging.h" + +using namespace QMatrixClient; + +[[gnu::unused]] static auto roomEventTypeInitialised = + Event::factory_t::chainFactory(); + +RoomEvent::RoomEvent(Type type, event_mtype_t matrixType, + const QJsonObject& contentJson) + : Event(type, matrixType, contentJson) +{ } + +RoomEvent::RoomEvent(Type type, const QJsonObject& json) + : Event(type, json) +{ + const auto unsignedData = json[UnsignedKeyL].toObject(); + const auto redaction = unsignedData[RedactedCauseKeyL]; + if (redaction.isObject()) + { + _redactedBecause = makeEvent(redaction.toObject()); + return; + } + + _txnId = unsignedData.value("transactionId"_ls).toString(); + if (!_txnId.isEmpty()) + qCDebug(EVENTS) << "Event transactionId:" << _txnId; +} + +RoomEvent::~RoomEvent() = default; // Let the smart pointer do its job + +QString RoomEvent::id() const +{ + return fullJson()[EventIdKeyL].toString(); +} + +QDateTime RoomEvent::timestamp() const +{ + return QMatrixClient::fromJson(fullJson()["origin_server_ts"_ls]); +} + +QString RoomEvent::roomId() const +{ + return fullJson()["room_id"_ls].toString(); +} + +QString RoomEvent::senderId() const +{ + return fullJson()["sender"_ls].toString(); +} + +QString RoomEvent::redactionReason() const +{ + return isRedacted() ? _redactedBecause->reason() : QString{}; +} + +void RoomEvent::addId(const QString& newId) +{ + Q_ASSERT(id().isEmpty()); Q_ASSERT(!newId.isEmpty()); + editJson().insert(EventIdKey, newId); +} diff --git a/lib/events/roomevent.h b/lib/events/roomevent.h new file mode 100644 index 00000000..ff50a83d --- /dev/null +++ b/lib/events/roomevent.h @@ -0,0 +1,89 @@ +/****************************************************************************** +* Copyright (C) 2018 Kitsune Ral +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#pragma once + +#include "event.h" + +namespace QMatrixClient { + class RedactionEvent; + + /** This class corresponds to m.room.* events */ + class RoomEvent : public Event + { + Q_GADGET + Q_PROPERTY(QString id READ id) + Q_PROPERTY(QDateTime timestamp READ timestamp CONSTANT) + Q_PROPERTY(QString roomId READ roomId CONSTANT) + Q_PROPERTY(QString senderId READ senderId CONSTANT) + Q_PROPERTY(QString redactionReason READ redactionReason) + Q_PROPERTY(bool isRedacted READ isRedacted) + Q_PROPERTY(QString transactionId READ transactionId) + public: + using factory_t = EventFactory; + + // RedactionEvent is an incomplete type here so we cannot inline + // constructors and destructors and we cannot use 'using'. + RoomEvent(Type type, event_mtype_t matrixType, + const QJsonObject& contentJson = {}); + RoomEvent(Type type, const QJsonObject& json); + ~RoomEvent() override; + + QString id() const; + QDateTime timestamp() const; + QString roomId() const; + QString senderId() const; + bool isRedacted() const { return bool(_redactedBecause); } + const event_ptr_tt& redactedBecause() const + { + return _redactedBecause; + } + QString redactionReason() const; + const QString& transactionId() const { return _txnId; } + + /** + * Sets the transaction id for locally created events. This should be + * done before the event is exposed to any code using the respective + * Q_PROPERTY. + * + * \param txnId - transaction id, normally obtained from + * Connection::generateTxnId() + */ + void setTransactionId(const QString& txnId) { _txnId = txnId; } + + /** + * Sets event id for locally created events + * + * When a new event is created locally, it has no server id yet. + * This function allows to add the id once the confirmation from + * the server is received. There should be no id set previously + * in the event. It's the responsibility of the code calling addId() + * to notify clients that use Q_PROPERTY(id) about its change + */ + void addId(const QString& newId); + + private: + event_ptr_tt _redactedBecause; + QString _txnId; + }; + using RoomEventPtr = event_ptr_tt; + using RoomEvents = EventsArray; + using RoomEventsRange = Range; +} // namespace QMatrixClient +Q_DECLARE_METATYPE(QMatrixClient::RoomEvent*) +Q_DECLARE_METATYPE(const QMatrixClient::RoomEvent*) diff --git a/lib/events/roommemberevent.cpp b/lib/events/roommemberevent.cpp index 3acce7cb..79e4af2d 100644 --- a/lib/events/roommemberevent.cpp +++ b/lib/events/roommemberevent.cpp @@ -18,6 +18,7 @@ #include "roommemberevent.h" +#include "converters.h" #include "logging.h" #include diff --git a/lib/events/roommemberevent.h b/lib/events/roommemberevent.h index 1ecd63d1..f3e4f53a 100644 --- a/lib/events/roommemberevent.h +++ b/lib/events/roommemberevent.h @@ -18,8 +18,7 @@ #pragma once -#include "event.h" - +#include "stateevent.h" #include "eventcontent.h" namespace QMatrixClient diff --git a/lib/events/roommessageevent.h b/lib/events/roommessageevent.h index ccf30f96..4c29a93e 100644 --- a/lib/events/roommessageevent.h +++ b/lib/events/roommessageevent.h @@ -18,8 +18,7 @@ #pragma once -#include "event.h" - +#include "roomevent.h" #include "eventcontent.h" namespace QMatrixClient diff --git a/lib/events/simplestateevents.h b/lib/events/simplestateevents.h index afd59478..fa1ca8f4 100644 --- a/lib/events/simplestateevents.h +++ b/lib/events/simplestateevents.h @@ -18,7 +18,7 @@ #pragma once -#include "event.h" +#include "stateevent.h" #include "eventcontent.h" namespace QMatrixClient diff --git a/lib/events/stateevent.cpp b/lib/events/stateevent.cpp new file mode 100644 index 00000000..fd5d2642 --- /dev/null +++ b/lib/events/stateevent.cpp @@ -0,0 +1,30 @@ +/****************************************************************************** +* Copyright (C) 2018 Kitsune Ral +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#include "stateevent.h" + +using namespace QMatrixClient; + +[[gnu::unused]] static auto stateEventTypeInitialised = + RoomEvent::factory_t::chainFactory(); + +bool StateEventBase::repeatsState() const +{ + const auto prevContentJson = unsignedJson().value(PrevContentKeyL); + return fullJson().value(ContentKeyL) == prevContentJson; +} diff --git a/lib/events/stateevent.h b/lib/events/stateevent.h new file mode 100644 index 00000000..6032132e --- /dev/null +++ b/lib/events/stateevent.h @@ -0,0 +1,92 @@ +/****************************************************************************** +* Copyright (C) 2018 Kitsune Ral +* +* This library is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License as published by the Free Software Foundation; either +* version 2.1 of the License, or (at your option) any later version. +* +* This library is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +* Lesser General Public License for more details. +* +* You should have received a copy of the GNU Lesser General Public +* License along with this library; if not, write to the Free Software +* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +*/ + +#pragma once + +#include "roomevent.h" + +namespace QMatrixClient { + class StateEventBase: public RoomEvent + { + public: + using factory_t = EventFactory; + + using RoomEvent::RoomEvent; + ~StateEventBase() override = default; + + bool isStateEvent() const override { return true; } + virtual bool repeatsState() const; + }; + using StateEventPtr = event_ptr_tt; + using StateEvents = EventsArray; + + template + struct Prev + { + template + explicit Prev(const QJsonObject& unsignedJson, + ContentParamTs&&... contentParams) + : senderId(unsignedJson.value("prev_sender"_ls).toString()) + , content(unsignedJson.value(PrevContentKeyL).toObject(), + std::forward(contentParams)...) + { } + + QString senderId; + ContentT content; + }; + + template + class StateEvent: public StateEventBase + { + public: + using content_type = ContentT; + + template + explicit StateEvent(Type type, const QJsonObject& fullJson, + ContentParamTs&&... contentParams) + : StateEventBase(type, fullJson) + , _content(contentJson(), + std::forward(contentParams)...) + { + const auto& unsignedData = unsignedJson(); + if (unsignedData.contains(PrevContentKeyL)) + _prev = std::make_unique>(unsignedData, + std::forward(contentParams)...); + } + template + explicit StateEvent(Type type, event_mtype_t matrixType, + ContentParamTs&&... contentParams) + : StateEventBase(type, matrixType) + , _content(std::forward(contentParams)...) + { + editJson().insert(ContentKey, _content.toJson()); + } + + const ContentT& content() const { return _content; } + [[deprecated("Use prevContent instead")]] + const ContentT* prev_content() const { return prevContent(); } + const ContentT* prevContent() const + { return _prev ? &_prev->content : nullptr; } + QString prevSenderId() const + { return _prev ? _prev->senderId : QString(); } + + protected: + ContentT _content; + std::unique_ptr> _prev; + }; +} // namespace QMatrixClient diff --git a/lib/events/typingevent.cpp b/lib/events/typingevent.cpp index c7b69e33..0d39d1be 100644 --- a/lib/events/typingevent.cpp +++ b/lib/events/typingevent.cpp @@ -18,6 +18,8 @@ #include "typingevent.h" +#include + using namespace QMatrixClient; TypingEvent::TypingEvent(const QJsonObject& obj) diff --git a/lib/jobs/syncjob.cpp b/lib/jobs/syncjob.cpp index 7e5f2e0f..02690e6d 100644 --- a/lib/jobs/syncjob.cpp +++ b/lib/jobs/syncjob.cpp @@ -18,6 +18,8 @@ #include "syncjob.h" +#include "events/eventloader.h" + #include using namespace QMatrixClient; diff --git a/lib/jobs/syncjob.h b/lib/jobs/syncjob.h index ca30848e..6b9bedfa 100644 --- a/lib/jobs/syncjob.h +++ b/lib/jobs/syncjob.h @@ -21,7 +21,7 @@ #include "basejob.h" #include "joinstate.h" -#include "events/event.h" +#include "events/stateevent.h" #include "util.h" namespace QMatrixClient diff --git a/libqmatrixclient.pri b/libqmatrixclient.pri index e61b50c8..4354b3f1 100644 --- a/libqmatrixclient.pri +++ b/libqmatrixclient.pri @@ -18,6 +18,8 @@ HEADERS += \ $$SRCPATH/avatar.h \ $$SRCPATH/util.h \ $$SRCPATH/events/event.h \ + $$SRCPATH/events/roomevent.h \ + $$SRCPATH/events/stateevent.h \ $$SRCPATH/events/eventcontent.h \ $$SRCPATH/events/roommessageevent.h \ $$SRCPATH/events/simplestateevents.h \ @@ -28,6 +30,7 @@ HEADERS += \ $$SRCPATH/events/accountdataevents.h \ $$SRCPATH/events/directchatevent.h \ $$SRCPATH/events/redactionevent.h \ + $$SRCPATH/events/eventloader.h \ $$SRCPATH/jobs/requestdata.h \ $$SRCPATH/jobs/basejob.h \ $$SRCPATH/jobs/sendeventjob.h \ @@ -51,6 +54,8 @@ SOURCES += \ $$SRCPATH/avatar.cpp \ $$SRCPATH/util.cpp \ $$SRCPATH/events/event.cpp \ + $$SRCPATH/events/roomevent.cpp \ + $$SRCPATH/events/stateevent.cpp \ $$SRCPATH/events/eventcontent.cpp \ $$SRCPATH/events/roommessageevent.cpp \ $$SRCPATH/events/roommemberevent.cpp \ -- cgit v1.2.3 From 5d1dd53890611376873f6f959e206d5a56cfff70 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Wed, 4 Jul 2018 21:05:20 +0900 Subject: Add #include to fix FTBFS with qmake --- lib/events/roomevent.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'lib/events') diff --git a/lib/events/roomevent.h b/lib/events/roomevent.h index ff50a83d..d2bc6edc 100644 --- a/lib/events/roomevent.h +++ b/lib/events/roomevent.h @@ -20,6 +20,8 @@ #include "event.h" +#include + namespace QMatrixClient { class RedactionEvent; -- cgit v1.2.3