diff options
Diffstat (limited to 'lib/events')
31 files changed, 1500 insertions, 1462 deletions
diff --git a/lib/events/accountdataevents.h b/lib/events/accountdataevents.h index 0cf2dc60..abab9867 100644 --- a/lib/events/accountdataevents.h +++ b/lib/events/accountdataevents.h @@ -24,76 +24,81 @@ #include "event.h" #include "eventcontent.h" -namespace QMatrixClient { - constexpr const char* FavouriteTag = "m.favourite"; - constexpr const char* LowPriorityTag = "m.lowpriority"; +namespace QMatrixClient +{ +constexpr const char* FavouriteTag = "m.favourite"; +constexpr const char* LowPriorityTag = "m.lowpriority"; - struct TagRecord { - using order_type = Omittable<float>; +struct TagRecord +{ + using order_type = Omittable<float>; - order_type order; + order_type order; - TagRecord(order_type order = none) : order(order) {} + TagRecord(order_type order = none) + : order(order) + {} - bool operator<(const TagRecord& other) const - { - // Per The Spec, rooms with no order should be after those with - // order - return !order.omitted() - && (other.order.omitted() - || order.value() < other.order.value()); - } - }; + bool operator<(const TagRecord& other) const + { + // Per The Spec, rooms with no order should be after those with order + return !order.omitted() + && (other.order.omitted() || order.value() < other.order.value()); + } +}; - template <> struct JsonObjectConverter<TagRecord> { - static void fillFrom(const QJsonObject& jo, TagRecord& rec) - { - // Parse a float both from JSON double and JSON string because - // libqmatrixclient previously used to use strings to store order. - const auto orderJv = jo.value("order"_ls); - if (orderJv.isDouble()) - rec.order = fromJson<float>(orderJv); - if (orderJv.isString()) { - bool ok; - rec.order = orderJv.toString().toFloat(&ok); - if (!ok) - rec.order = none; - } - } - static void dumpTo(QJsonObject& jo, const TagRecord& rec) - { - addParam<IfNotEmpty>(jo, QStringLiteral("order"), rec.order); +template <> +struct JsonObjectConverter<TagRecord> +{ + static void fillFrom(const QJsonObject& jo, TagRecord& rec) + { + // Parse a float both from JSON double and JSON string because + // libqmatrixclient previously used to use strings to store order. + const auto orderJv = jo.value("order"_ls); + if (orderJv.isDouble()) + rec.order = fromJson<float>(orderJv); + if (orderJv.isString()) { + bool ok; + rec.order = orderJv.toString().toFloat(&ok); + if (!ok) + rec.order = none; } - }; + } + static void dumpTo(QJsonObject& jo, const TagRecord& rec) + { + addParam<IfNotEmpty>(jo, QStringLiteral("order"), rec.order); + } +}; - using TagsMap = QHash<QString, TagRecord>; +using TagsMap = QHash<QString, TagRecord>; -#define DEFINE_SIMPLE_EVENT(_Name, _TypeId, _ContentType, _ContentKey) \ - class _Name : public Event \ - { \ - public: \ - using content_type = _ContentType; \ - DEFINE_EVENT_TYPEID(_TypeId, _Name) \ - explicit _Name(QJsonObject obj) : Event(typeId(), std::move(obj)) {} \ - explicit _Name(_ContentType content) \ - : Event(typeId(), matrixTypeId(), \ - QJsonObject { { QStringLiteral(#_ContentKey), \ - toJson(std::move(content)) } }) \ - { \ - } \ - auto _ContentKey() const \ - { \ - return fromJson<content_type>(contentJson()[#_ContentKey##_ls]); \ - } \ - }; \ - REGISTER_EVENT_TYPE(_Name) \ +#define DEFINE_SIMPLE_EVENT(_Name, _TypeId, _ContentType, _ContentKey) \ + class _Name : public Event \ + { \ + public: \ + using content_type = _ContentType; \ + DEFINE_EVENT_TYPEID(_TypeId, _Name) \ + explicit _Name(QJsonObject obj) \ + : Event(typeId(), std::move(obj)) \ + {} \ + explicit _Name(_ContentType content) \ + : Event(typeId(), matrixTypeId(), \ + QJsonObject { { QStringLiteral(#_ContentKey), \ + toJson(std::move(content)) } }) \ + {} \ + auto _ContentKey() const \ + { \ + return content<content_type>(#_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) - DEFINE_SIMPLE_EVENT(IgnoredUsersEvent, "m.ignored_user_list", QSet<QString>, - ignored_users) +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<QString>, + ignored_users) - DEFINE_EVENTTYPE_ALIAS(Tag, TagEvent) - DEFINE_EVENTTYPE_ALIAS(ReadMarker, ReadMarkerEvent) -} +DEFINE_EVENTTYPE_ALIAS(Tag, TagEvent) +DEFINE_EVENTTYPE_ALIAS(ReadMarker, ReadMarkerEvent) +} // namespace QMatrixClient diff --git a/lib/events/callanswerevent.cpp b/lib/events/callanswerevent.cpp index 91e164ad..7ab4a6fb 100644 --- a/lib/events/callanswerevent.cpp +++ b/lib/events/callanswerevent.cpp @@ -19,7 +19,6 @@ #include "callanswerevent.h" #include "event.h" - #include "logging.h" #include <QtCore/QJsonDocument> @@ -55,20 +54,18 @@ CallAnswerEvent::CallAnswerEvent(const QJsonObject& obj) CallAnswerEvent::CallAnswerEvent(const QString& callId, const int lifetime, const QString& sdp) - : CallEventBase(typeId(), matrixTypeId(), callId, 0, - { { QStringLiteral("lifetime"), lifetime }, - { QStringLiteral("answer"), - QJsonObject { { QStringLiteral("type"), - QStringLiteral("answer") }, - { QStringLiteral("sdp"), sdp } } } }) -{ -} + : CallEventBase( + typeId(), matrixTypeId(), callId, 0, + { { QStringLiteral("lifetime"), lifetime }, + { QStringLiteral("answer"), + QJsonObject { { QStringLiteral("type"), QStringLiteral("answer") }, + { QStringLiteral("sdp"), sdp } } } }) +{} CallAnswerEvent::CallAnswerEvent(const QString& callId, const QString& sdp) - : CallEventBase(typeId(), matrixTypeId(), callId, 0, - { { QStringLiteral("answer"), - QJsonObject { { QStringLiteral("type"), - QStringLiteral("answer") }, - { QStringLiteral("sdp"), sdp } } } }) -{ -} + : CallEventBase( + typeId(), matrixTypeId(), callId, 0, + { { QStringLiteral("answer"), + QJsonObject { { QStringLiteral("type"), QStringLiteral("answer") }, + { QStringLiteral("sdp"), sdp } } } }) +{} diff --git a/lib/events/callanswerevent.h b/lib/events/callanswerevent.h index f222803b..69662eb9 100644 --- a/lib/events/callanswerevent.h +++ b/lib/events/callanswerevent.h @@ -20,31 +20,29 @@ #include "roomevent.h" -namespace QMatrixClient { - class CallAnswerEvent : public CallEventBase - { - public: - DEFINE_EVENT_TYPEID("m.call.answer", CallAnswerEvent) +namespace QMatrixClient +{ +class CallAnswerEvent : public CallEventBase +{ +public: + DEFINE_EVENT_TYPEID("m.call.answer", CallAnswerEvent) - explicit CallAnswerEvent(const QJsonObject& obj); + explicit CallAnswerEvent(const QJsonObject& obj); - explicit CallAnswerEvent(const QString& callId, const int lifetime, - const QString& sdp); - explicit CallAnswerEvent(const QString& callId, const QString& sdp); + explicit CallAnswerEvent(const QString& callId, const int lifetime, + const QString& sdp); + explicit CallAnswerEvent(const QString& callId, const QString& sdp); - int lifetime() const - { - return content<int>("lifetime"_ls); - } // FIXME: Omittable<>? - QString sdp() const - { - return contentJson()["answer"_ls] - .toObject() - .value("sdp"_ls) - .toString(); - } - }; + int lifetime() const + { + return content<int>("lifetime"_ls); + } // FIXME: Omittable<>? + QString sdp() const + { + return contentJson()["answer"_ls].toObject().value("sdp"_ls).toString(); + } +}; - REGISTER_EVENT_TYPE(CallAnswerEvent) - DEFINE_EVENTTYPE_ALIAS(CallAnswer, CallAnswerEvent) +REGISTER_EVENT_TYPE(CallAnswerEvent) +DEFINE_EVENTTYPE_ALIAS(CallAnswer, CallAnswerEvent) } // namespace QMatrixClient diff --git a/lib/events/callcandidatesevent.h b/lib/events/callcandidatesevent.h index e66e0c09..1c12b800 100644 --- a/lib/events/callcandidatesevent.h +++ b/lib/events/callcandidatesevent.h @@ -20,30 +20,29 @@ #include "roomevent.h" -namespace QMatrixClient { - class CallCandidatesEvent : public CallEventBase - { - public: - DEFINE_EVENT_TYPEID("m.call.candidates", CallCandidatesEvent) +namespace QMatrixClient +{ +class CallCandidatesEvent : public CallEventBase +{ +public: + DEFINE_EVENT_TYPEID("m.call.candidates", CallCandidatesEvent) - explicit CallCandidatesEvent(const QJsonObject& obj) - : CallEventBase(typeId(), obj) - { - } + explicit CallCandidatesEvent(const QJsonObject& obj) + : CallEventBase(typeId(), obj) + {} - explicit CallCandidatesEvent(const QString& callId, - const QJsonArray& candidates) - : CallEventBase(typeId(), matrixTypeId(), callId, 0, - { { QStringLiteral("candidates"), candidates } }) - { - } + explicit CallCandidatesEvent(const QString& callId, + const QJsonArray& candidates) + : CallEventBase(typeId(), matrixTypeId(), callId, 0, + { { QStringLiteral("candidates"), candidates } }) + {} - QJsonArray candidates() const - { - return content<QJsonArray>("candidates"_ls); - } - }; + QJsonArray candidates() const + { + return content<QJsonArray>("candidates"_ls); + } +}; - REGISTER_EVENT_TYPE(CallCandidatesEvent) - DEFINE_EVENTTYPE_ALIAS(CallCandidates, CallCandidatesEvent) -} +REGISTER_EVENT_TYPE(CallCandidatesEvent) +DEFINE_EVENTTYPE_ALIAS(CallCandidates, CallCandidatesEvent) +} // namespace QMatrixClient diff --git a/lib/events/callhangupevent.cpp b/lib/events/callhangupevent.cpp index 80844f2d..2a4fd3da 100644 --- a/lib/events/callhangupevent.cpp +++ b/lib/events/callhangupevent.cpp @@ -19,7 +19,6 @@ #include "callhangupevent.h" #include "event.h" - #include "logging.h" #include <QtCore/QJsonDocument> @@ -50,5 +49,4 @@ CallHangupEvent::CallHangupEvent(const QJsonObject& obj) CallHangupEvent::CallHangupEvent(const QString& callId) : CallEventBase(typeId(), matrixTypeId(), callId, 0) -{ -} +{} diff --git a/lib/events/callhangupevent.h b/lib/events/callhangupevent.h index 3c3910be..0a5a3283 100644 --- a/lib/events/callhangupevent.h +++ b/lib/events/callhangupevent.h @@ -20,16 +20,17 @@ #include "roomevent.h" -namespace QMatrixClient { - class CallHangupEvent : public CallEventBase - { - public: - DEFINE_EVENT_TYPEID("m.call.hangup", CallHangupEvent) +namespace QMatrixClient +{ +class CallHangupEvent : public CallEventBase +{ +public: + DEFINE_EVENT_TYPEID("m.call.hangup", CallHangupEvent) - explicit CallHangupEvent(const QJsonObject& obj); - explicit CallHangupEvent(const QString& callId); - }; + explicit CallHangupEvent(const QJsonObject& obj); + explicit CallHangupEvent(const QString& callId); +}; - REGISTER_EVENT_TYPE(CallHangupEvent) - DEFINE_EVENTTYPE_ALIAS(CallHangup, CallHangupEvent) -} +REGISTER_EVENT_TYPE(CallHangupEvent) +DEFINE_EVENTTYPE_ALIAS(CallHangup, CallHangupEvent) +} // namespace QMatrixClient diff --git a/lib/events/callinviteevent.cpp b/lib/events/callinviteevent.cpp index 2459c093..f565fc3e 100644 --- a/lib/events/callinviteevent.cpp +++ b/lib/events/callinviteevent.cpp @@ -19,7 +19,6 @@ #include "callinviteevent.h" #include "event.h" - #include "logging.h" #include <QtCore/QJsonDocument> @@ -55,11 +54,10 @@ CallInviteEvent::CallInviteEvent(const QJsonObject& obj) CallInviteEvent::CallInviteEvent(const QString& callId, const int lifetime, const QString& sdp) - : CallEventBase(typeId(), matrixTypeId(), callId, lifetime, - { { QStringLiteral("lifetime"), lifetime }, - { QStringLiteral("offer"), - QJsonObject { { QStringLiteral("type"), - QStringLiteral("offer") }, - { QStringLiteral("sdp"), sdp } } } }) -{ -} + : CallEventBase( + typeId(), matrixTypeId(), callId, lifetime, + { { QStringLiteral("lifetime"), lifetime }, + { QStringLiteral("offer"), + QJsonObject { { QStringLiteral("type"), QStringLiteral("offer") }, + { QStringLiteral("sdp"), sdp } } } }) +{} diff --git a/lib/events/callinviteevent.h b/lib/events/callinviteevent.h index 911ccf96..4334ca5b 100644 --- a/lib/events/callinviteevent.h +++ b/lib/events/callinviteevent.h @@ -20,30 +20,28 @@ #include "roomevent.h" -namespace QMatrixClient { - class CallInviteEvent : public CallEventBase - { - public: - DEFINE_EVENT_TYPEID("m.call.invite", CallInviteEvent) +namespace QMatrixClient +{ +class CallInviteEvent : public CallEventBase +{ +public: + DEFINE_EVENT_TYPEID("m.call.invite", CallInviteEvent) - explicit CallInviteEvent(const QJsonObject& obj); + explicit CallInviteEvent(const QJsonObject& obj); - explicit CallInviteEvent(const QString& callId, const int lifetime, - const QString& sdp); + explicit CallInviteEvent(const QString& callId, const int lifetime, + const QString& sdp); - int lifetime() const - { - return content<int>("lifetime"_ls); - } // FIXME: Omittable<>? - QString sdp() const - { - return contentJson()["offer"_ls] - .toObject() - .value("sdp"_ls) - .toString(); - } - }; + int lifetime() const + { + return content<int>("lifetime"_ls); + } // FIXME: Omittable<>? + QString sdp() const + { + return contentJson()["offer"_ls].toObject().value("sdp"_ls).toString(); + } +}; - REGISTER_EVENT_TYPE(CallInviteEvent) - DEFINE_EVENTTYPE_ALIAS(CallInvite, CallInviteEvent) -} +REGISTER_EVENT_TYPE(CallInviteEvent) +DEFINE_EVENTTYPE_ALIAS(CallInvite, CallInviteEvent) +} // namespace QMatrixClient diff --git a/lib/events/directchatevent.h b/lib/events/directchatevent.h index 0d8b8f74..6b4a08ee 100644 --- a/lib/events/directchatevent.h +++ b/lib/events/directchatevent.h @@ -20,18 +20,19 @@ #include "event.h" -namespace QMatrixClient { - class DirectChatEvent : public Event - { - public: - DEFINE_EVENT_TYPEID("m.direct", DirectChatEvent) +namespace QMatrixClient +{ +class DirectChatEvent : public Event +{ +public: + DEFINE_EVENT_TYPEID("m.direct", DirectChatEvent) - explicit DirectChatEvent(const QJsonObject& obj) : Event(typeId(), obj) - { - } + explicit DirectChatEvent(const QJsonObject& obj) + : Event(typeId(), obj) + {} - QMultiHash<QString, QString> usersToDirectChats() const; - }; - REGISTER_EVENT_TYPE(DirectChatEvent) - DEFINE_EVENTTYPE_ALIAS(DirectChat, DirectChatEvent) -} + QMultiHash<QString, QString> usersToDirectChats() const; +}; +REGISTER_EVENT_TYPE(DirectChatEvent) +DEFINE_EVENTTYPE_ALIAS(DirectChat, DirectChatEvent) +} // namespace QMatrixClient diff --git a/lib/events/event.cpp b/lib/events/event.cpp index f44b1e5d..718a6602 100644 --- a/lib/events/event.cpp +++ b/lib/events/event.cpp @@ -38,10 +38,13 @@ event_type_t EventTypeRegistry::initializeTypeId(event_mtype_t matrixTypeId) QString EventTypeRegistry::getMatrixType(event_type_t typeId) { - return typeId < get().eventTypes.size() ? get().eventTypes[typeId] : ""; + return typeId < get().eventTypes.size() ? get().eventTypes[typeId] + : QString(); } -Event::Event(Type type, const QJsonObject& json) : _type(type), _json(json) +Event::Event(Type type, const QJsonObject& json) + : _type(type) + , _json(json) { if (!json.contains(ContentKeyL) && !json.value(UnsignedKeyL).toObject().contains(RedactedCauseKeyL)) { @@ -50,11 +53,9 @@ Event::Event(Type type, const QJsonObject& json) : _type(type), _json(json) } } -Event::Event(Type type, event_mtype_t matrixType, - const QJsonObject& contentJson) +Event::Event(Type type, event_mtype_t matrixType, const QJsonObject& contentJson) : Event(type, basicEventJson(matrixType, contentJson)) -{ -} +{} Event::~Event() = default; diff --git a/lib/events/event.h b/lib/events/event.h index a0f12b75..9dcec1ae 100644 --- a/lib/events/event.h +++ b/lib/events/event.h @@ -22,375 +22,396 @@ #include "logging.h" #ifdef ENABLE_EVENTTYPE_ALIAS -#define USE_EVENTTYPE_ALIAS 1 +# define USE_EVENTTYPE_ALIAS 1 #endif -namespace QMatrixClient { - // === event_ptr_tt<> and type casting facilities === - - template <typename EventT> using event_ptr_tt = std::unique_ptr<EventT>; +namespace QMatrixClient +{ +// === event_ptr_tt<> and type casting facilities === + +template <typename EventT> +using event_ptr_tt = std::unique_ptr<EventT>; + +/// Unwrap a plain pointer from a smart pointer +template <typename EventT> +inline EventT* rawPtr(const event_ptr_tt<EventT>& ptr) +{ + return ptr.get(); +} + +/// Unwrap a plain pointer and downcast it to the specified type +template <typename TargetEventT, typename EventT> +inline TargetEventT* weakPtrCast(const event_ptr_tt<EventT>& ptr) +{ + return static_cast<TargetEventT*>(rawPtr(ptr)); +} + +/// Re-wrap a smart pointer to base into a smart pointer to derived +template <typename TargetT, typename SourceT> +[[deprecated("Consider using eventCast() or visit() " + "instead")]] inline event_ptr_tt<TargetT> +ptrCast(event_ptr_tt<SourceT>&& ptr) +{ + return unique_ptr_cast<TargetT>(ptr); +} + +// === Standard Matrix key names and basicEventJson() === + +static const auto TypeKey = QStringLiteral("type"); +static const auto ContentKey = QStringLiteral("content"); +static const auto EventIdKey = QStringLiteral("event_id"); +static const auto UnsignedKey = QStringLiteral("unsigned"); +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; + +// Minimal correct Matrix event JSON +template <typename StrT> +inline QJsonObject basicEventJson(StrT matrixType, const QJsonObject& content) +{ + return { { TypeKey, std::forward<StrT>(matrixType) }, + { ContentKey, content } }; +} + +// === Event types and event types registry === + +using event_type_t = size_t; +using event_mtype_t = const char*; + +class EventTypeRegistry +{ +public: + ~EventTypeRegistry() = default; + + static event_type_t initializeTypeId(event_mtype_t matrixTypeId); template <typename EventT> - inline EventT* rawPtr(const event_ptr_tt<EventT>& ptr) // unwrap + static inline event_type_t initializeTypeId() { - return ptr.get(); + return initializeTypeId(EventT::matrixTypeId()); } - template <typename TargetEventT, typename EventT> - inline TargetEventT* weakPtrCast(const event_ptr_tt<EventT>& ptr) - { - return static_cast<TargetEventT*>(rawPtr(ptr)); - } + static QString getMatrixType(event_type_t typeId); - template <typename TargetT, typename SourceT> - inline event_ptr_tt<TargetT> ptrCast(event_ptr_tt<SourceT>&& ptr) - { - return unique_ptr_cast<TargetT>(ptr); - } +private: + EventTypeRegistry() = default; + Q_DISABLE_COPY(EventTypeRegistry) + DISABLE_MOVE(EventTypeRegistry) - // === Standard Matrix key names and basicEventJson() === - - static const auto TypeKey = QStringLiteral("type"); - static const auto ContentKey = QStringLiteral("content"); - static const auto EventIdKey = QStringLiteral("event_id"); - static const auto UnsignedKey = QStringLiteral("unsigned"); - 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; - - // Minimal correct Matrix event JSON - template <typename StrT> - inline QJsonObject basicEventJson(StrT matrixType, - const QJsonObject& content) + static EventTypeRegistry& get() { - return { { TypeKey, std::forward<StrT>(matrixType) }, - { ContentKey, content } }; + static EventTypeRegistry etr; + return etr; } - // === Event types and event types registry === + std::vector<event_mtype_t> eventTypes; +}; - using event_type_t = size_t; - using event_mtype_t = const char*; +template <> +inline event_type_t EventTypeRegistry::initializeTypeId<void>() +{ + return initializeTypeId(""); +} - class EventTypeRegistry +template <typename EventT> +struct EventTypeTraits +{ + static event_type_t id() { - public: - ~EventTypeRegistry() = default; - - static event_type_t initializeTypeId(event_mtype_t matrixTypeId); - - template <typename EventT> static inline event_type_t initializeTypeId() - { - return initializeTypeId(EventT::matrixTypeId()); - } - - static QString getMatrixType(event_type_t typeId); - - private: - EventTypeRegistry() = default; - Q_DISABLE_COPY(EventTypeRegistry) - DISABLE_MOVE(EventTypeRegistry) - - static EventTypeRegistry& get() - { - static EventTypeRegistry etr; - return etr; - } - - std::vector<event_mtype_t> eventTypes; - }; - - template <> inline event_type_t EventTypeRegistry::initializeTypeId<void>() - { - return initializeTypeId(""); + static const auto id = EventTypeRegistry::initializeTypeId<EventT>(); + return id; } - - template <typename EventT> struct EventTypeTraits { - static event_type_t id() - { - static const auto id = - EventTypeRegistry::initializeTypeId<EventT>(); - return id; - } - }; - - template <typename EventT> inline event_type_t typeId() +}; + +template <typename EventT> +inline event_type_t typeId() +{ + return EventTypeTraits<std::decay_t<EventT>>::id(); +} + +inline event_type_t unknownEventTypeId() { return typeId<void>(); } + +// === EventFactory === + +/** Create an event of arbitrary type from its arguments */ +template <typename EventT, typename... ArgTs> +inline event_ptr_tt<EventT> makeEvent(ArgTs&&... args) +{ + return std::make_unique<EventT>(std::forward<ArgTs>(args)...); +} + +template <typename BaseEventT> +class EventFactory +{ +public: + template <typename FnT> + static auto addMethod(FnT&& method) { - return EventTypeTraits<std::decay_t<EventT>>::id(); + factories().emplace_back(std::forward<FnT>(method)); + return 0; } - inline event_type_t unknownEventTypeId() { return typeId<void>(); } - - // === EventFactory === - - /** Create an event of arbitrary type from its arguments */ - template <typename EventT, typename... ArgTs> - inline event_ptr_tt<EventT> makeEvent(ArgTs&&... args) - { - return std::make_unique<EventT>(std::forward<ArgTs>(args)...); - } - - template <typename BaseEventT> class EventFactory - { - public: - template <typename FnT> static auto addMethod(FnT&& method) - { - factories().emplace_back(std::forward<FnT>(method)); - return 0; - } - - /** 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 <typename EventT> static auto chainFactory() - { - return addMethod(&EventT::factory_t::make); - } - - static event_ptr_tt<BaseEventT> make(const QJsonObject& json, - const QString& matrixType) - { - for (const auto& f : factories()) - if (auto e = f(json, matrixType)) - return e; - return nullptr; - } - - private: - static auto& factories() - { - using inner_factory_tt = std::function<event_ptr_tt<BaseEventT>( - const QJsonObject&, const QString&)>; - static std::vector<inner_factory_tt> _factories {}; - return _factories; - } - }; - - /** 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 + /** 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 <typename EventT> inline auto setupFactory() + template <typename EventT> + static auto chainFactory() { - qDebug(EVENTS) << "Adding factory method for" << EventT::matrixTypeId(); - return EventT::factory_t::addMethod( - [](const QJsonObject& json, const QString& jsonMatrixType) { - return EventT::matrixTypeId() == jsonMatrixType - ? makeEvent<EventT>(json) - : nullptr; - }); + return addMethod(&EventT::factory_t::make); } - template <typename EventT> inline auto registerEventType() + static event_ptr_tt<BaseEventT> make(const QJsonObject& json, + const QString& matrixType) { - // Initialise exactly once, even if this function is called twice for - // the same type (for whatever reason - you never know the ways of - // static initialisation is done). - static const auto _ = setupFactory<EventT>(); - return _; // Only to facilitate usage in static initialisation + for (const auto& f : factories()) + if (auto e = f(json, matrixType)) + return e; + return nullptr; } - // === Event === - - class Event +private: + static auto& factories() { - 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<Event>; - - explicit Event(Type type, const QJsonObject& json); - explicit Event(Type type, event_mtype_t matrixType, - const QJsonObject& contentJson = {}); - Q_DISABLE_COPY(Event) - Event(Event&&) = default; - Event& operator=(Event&&) = delete; - virtual ~Event(); - - Type type() const { return _type; } - QString matrixType() const; - QByteArray originalJson() 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. - - const QJsonObject contentJson() const; - const QJsonObject unsignedJson() const; - - template <typename T> T content(const QString& key) const - { - return fromJson<T>(contentJson()[key]); - } - - template <typename T> T content(const QLatin1String& key) const - { - return fromJson<T>(contentJson()[key]); - } - - friend QDebug operator<<(QDebug dbg, const Event& e) - { - QDebugStateSaver _dss { dbg }; - dbg.noquote().nospace() - << e.matrixType() << '(' << e.type() << "): "; - e.dumpTo(dbg); - return dbg; - } - - virtual bool isStateEvent() const { return false; } - virtual bool isCallEvent() const { return false; } - virtual void dumpTo(QDebug dbg) const; - - protected: - QJsonObject& editJson() { return _json; } - - private: - Type _type; - QJsonObject _json; - }; - using EventPtr = event_ptr_tt<Event>; - - template <typename EventT> - using EventsArray = std::vector<event_ptr_tt<EventT>>; - using Events = EventsArray<Event>; - - // === 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 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 = \ - registerEventType<_Type>(); \ - } \ - // End of macro - -#ifdef USE_EVENTTYPE_ALIAS - namespace EventType { - inline event_type_t logEventType(event_type_t id, const char* idName) - { - qDebug(EVENTS) << "Using id" << id << "for" << idName; - return id; - } + using inner_factory_tt = std::function<event_ptr_tt<BaseEventT>( + const QJsonObject&, const QString&)>; + static std::vector<inner_factory_tt> _factories {}; + return _factories; } +}; - // 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 is<>(), eventCast<>() or visit<>()")]] static const auto \ - _Id = logEventType(typeId<_Type>(), #_Id); \ - } \ - // End of macro -#else -#define DEFINE_EVENTTYPE_ALIAS(_Id, _Type) // Nothing -#endif - - // === is<>(), eventCast<>() and visit<>() === - - template <typename EventT> inline bool is(const Event& e) +/** 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 <typename EventT> +inline auto setupFactory() +{ + qDebug(EVENTS) << "Adding factory method for" << EventT::matrixTypeId(); + return EventT::factory_t::addMethod([](const QJsonObject& json, + const QString& jsonMatrixType) { + return EventT::matrixTypeId() == jsonMatrixType ? makeEvent<EventT>(json) + : nullptr; + }); +} + +template <typename EventT> +inline auto registerEventType() +{ + // Initialise exactly once, even if this function is called twice for + // the same type (for whatever reason - you never know the ways of + // static initialisation is done). + static const auto _ = setupFactory<EventT>(); + return _; // Only to facilitate usage in static initialisation +} + +// === Event === + +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<Event>; + + explicit Event(Type type, const QJsonObject& json); + explicit Event(Type type, event_mtype_t matrixType, + const QJsonObject& contentJson = {}); + Q_DISABLE_COPY(Event) + Event(Event&&) = default; + Event& operator=(Event&&) = delete; + virtual ~Event(); + + Type type() const { return _type; } + QString matrixType() const; + QByteArray originalJson() 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. + + const QJsonObject contentJson() const; + const QJsonObject unsignedJson() const; + + template <typename T> + T content(const QString& key) const { - return e.type() == typeId<EventT>(); + return fromJson<T>(contentJson()[key]); } - inline bool isUnknown(const Event& e) + template <typename T> + T content(const QLatin1String& key) const { - return e.type() == unknownEventTypeId(); + return fromJson<T>(contentJson()[key]); } - template <typename EventT, typename BasePtrT> - inline auto eventCast(const BasePtrT& eptr) - -> decltype(static_cast<EventT*>(&*eptr)) + friend QDebug operator<<(QDebug dbg, const Event& e) { - Q_ASSERT(eptr); - return is<std::decay_t<EventT>>(*eptr) ? static_cast<EventT*>(&*eptr) - : nullptr; + QDebugStateSaver _dss { dbg }; + dbg.noquote().nospace() << e.matrixType() << '(' << e.type() << "): "; + e.dumpTo(dbg); + return dbg; } - // A single generic catch-all visitor - template <typename BaseEventT, typename FnT> - inline auto visit(const BaseEventT& event, FnT&& visitor) - -> decltype(visitor(event)) - { - return visitor(event); - } + virtual bool isStateEvent() const { return false; } + virtual bool isCallEvent() const { return false; } + virtual void dumpTo(QDebug dbg) const; - template <typename T> constexpr auto is_event() - { - return std::is_base_of<Event, std::decay_t<T>>::value; - } +protected: + QJsonObject& editJson() { return _json; } - template <typename T, typename FnT> constexpr auto needs_cast() - { - return !std::is_convertible<T, fn_arg_t<FnT>>::value; - } +private: + Type _type; + QJsonObject _json; +}; +using EventPtr = event_ptr_tt<Event>; - // A single type-specific void visitor - template <typename BaseEventT, typename FnT> - inline std::enable_if_t<is_event<BaseEventT>() - && needs_cast<BaseEventT, FnT>() - && std::is_void<fn_return_t<FnT>>::value> - visit(const BaseEventT& event, FnT&& visitor) - { - using event_type = fn_arg_t<FnT>; - if (is<std::decay_t<event_type>>(event)) - visitor(static_cast<event_type>(event)); - } +template <typename EventT> +using EventsArray = std::vector<event_ptr_tt<EventT>>; +using Events = EventsArray<Event>; - // A single type-specific non-void visitor with an optional default value - template <typename BaseEventT, typename FnT> - inline std::enable_if_t< - is_event<BaseEventT>() && needs_cast<BaseEventT, FnT>(), - fn_return_t<FnT>> // non-voidness is guarded by defaultValue type - visit(const BaseEventT& event, FnT&& visitor, - fn_return_t<FnT>&& defaultValue = {}) - { - using event_type = fn_arg_t<FnT>; - if (is<std::decay_t<event_type>>(event)) - return visitor(static_cast<event_type>(event)); - return std::forward<fn_return_t<FnT>>(defaultValue); - } +// === 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 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 = \ + registerEventType<_Type>(); \ + } \ + // End of macro - // A chain of 2 or more visitors - template <typename BaseEventT, typename FnT1, typename FnT2, - typename... FnTs> - inline std::enable_if_t<is_event<BaseEventT>(), fn_return_t<FnT1>> - visit(const BaseEventT& event, FnT1&& visitor1, FnT2&& visitor2, - FnTs&&... visitors) +#ifdef USE_EVENTTYPE_ALIAS +namespace EventType +{ + inline event_type_t logEventType(event_type_t id, const char* idName) { - using event_type1 = fn_arg_t<FnT1>; - if (is<std::decay_t<event_type1>>(event)) - return visitor1(static_cast<event_type1&>(event)); - return visit(event, std::forward<FnT2>(visitor2), - std::forward<FnTs>(visitors)...); + qDebug(EVENTS) << "Using id" << id << "for" << idName; + return id; } +} // namespace EventType + +// 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 is<>(), eventCast<>() or " \ + "visit<>()")]] static const auto _Id = \ + logEventType(typeId<_Type>(), #_Id); \ + } \ + // End of macro +#else +# define DEFINE_EVENTTYPE_ALIAS(_Id, _Type) // Nothing +#endif + +// === is<>(), eventCast<>() and visit<>() === + +template <typename EventT> +inline bool is(const Event& e) +{ + return e.type() == typeId<EventT>(); +} + +inline bool isUnknown(const Event& e) +{ + return e.type() == unknownEventTypeId(); +} + +template <typename EventT, typename BasePtrT> +inline auto eventCast(const BasePtrT& eptr) + -> decltype(static_cast<EventT*>(&*eptr)) +{ + Q_ASSERT(eptr); + return is<std::decay_t<EventT>>(*eptr) ? static_cast<EventT*>(&*eptr) + : nullptr; +} + +// A single generic catch-all visitor +template <typename BaseEventT, typename FnT> +inline auto visit(const BaseEventT& event, FnT&& visitor) + -> decltype(visitor(event)) +{ + return visitor(event); +} + +template <typename T> +constexpr auto is_event() +{ + return std::is_base_of<Event, std::decay_t<T>>::value; +} + +template <typename T, typename FnT> +constexpr auto needs_cast() +{ + return !std::is_convertible<T, fn_arg_t<FnT>>::value; +} + +// A single type-specific void visitor +template <typename BaseEventT, typename FnT> +inline std::enable_if_t<is_event<BaseEventT>() && needs_cast<BaseEventT, FnT>() + && std::is_void<fn_return_t<FnT>>::value> +visit(const BaseEventT& event, FnT&& visitor) +{ + using event_type = fn_arg_t<FnT>; + if (is<std::decay_t<event_type>>(event)) + visitor(static_cast<event_type>(event)); +} + +// A single type-specific non-void visitor with an optional default value +template <typename BaseEventT, typename FnT> +inline std::enable_if_t<is_event<BaseEventT>() && needs_cast<BaseEventT, FnT>(), + fn_return_t<FnT>> // non-voidness is guarded by + // defaultValue type + visit(const BaseEventT& event, + FnT&& visitor, + fn_return_t<FnT>&& + defaultValue = {}) +{ + using event_type = fn_arg_t<FnT>; + if (is<std::decay_t<event_type>>(event)) + return visitor(static_cast<event_type>(event)); + return std::forward<fn_return_t<FnT>>(defaultValue); +} + +// A chain of 2 or more visitors +template <typename BaseEventT, typename FnT1, typename FnT2, typename... FnTs> +inline std::enable_if_t<is_event<BaseEventT>(), fn_return_t<FnT1>> +visit(const BaseEventT& event, FnT1&& visitor1, FnT2&& visitor2, + FnTs&&... visitors) +{ + using event_type1 = fn_arg_t<FnT1>; + if (is<std::decay_t<event_type1>>(event)) + return visitor1(static_cast<event_type1&>(event)); + return visit(event, std::forward<FnT2>(visitor2), + std::forward<FnTs>(visitors)...); +} } // namespace QMatrixClient Q_DECLARE_METATYPE(QMatrixClient::Event*) Q_DECLARE_METATYPE(const QMatrixClient::Event*) diff --git a/lib/events/eventcontent.cpp b/lib/events/eventcontent.cpp index cc31fea5..2b84c2b7 100644 --- a/lib/events/eventcontent.cpp +++ b/lib/events/eventcontent.cpp @@ -34,26 +34,31 @@ QJsonObject Base::toJson() const FileInfo::FileInfo(const QUrl& u, qint64 payloadSize, const QMimeType& mimeType, const QString& originalFilename) - : mimeType(mimeType), - url(u), - payloadSize(payloadSize), - originalName(originalFilename) -{ -} + : mimeType(mimeType) + , url(u) + , payloadSize(payloadSize) + , originalName(originalFilename) +{} FileInfo::FileInfo(const QUrl& u, const QJsonObject& infoJson, const QString& originalFilename) - : originalInfoJson(infoJson), - mimeType(QMimeDatabase().mimeTypeForName( - infoJson["mimetype"_ls].toString())), - url(u), - payloadSize(fromJson<qint64>(infoJson["size"_ls])), - originalName(originalFilename) + : originalInfoJson(infoJson) + , mimeType( + QMimeDatabase().mimeTypeForName(infoJson["mimetype"_ls].toString())) + , url(u) + , payloadSize(fromJson<qint64>(infoJson["size"_ls])) + , originalName(originalFilename) { if (!mimeType.isValid()) mimeType = QMimeDatabase().mimeTypeForData(QByteArray()); } +bool FileInfo::isValid() const +{ + return url.scheme() == "mxc" + && (url.authority() + url.path()).count('/') == 1; +} + void FileInfo::fillInfoJson(QJsonObject* infoJson) const { Q_ASSERT(infoJson); @@ -65,16 +70,15 @@ void FileInfo::fillInfoJson(QJsonObject* infoJson) const ImageInfo::ImageInfo(const QUrl& u, qint64 fileSize, QMimeType mimeType, const QSize& imageSize, const QString& originalFilename) - : FileInfo(u, fileSize, mimeType, originalFilename), imageSize(imageSize) -{ -} + : FileInfo(u, fileSize, mimeType, originalFilename) + , imageSize(imageSize) +{} ImageInfo::ImageInfo(const QUrl& u, const QJsonObject& infoJson, const QString& originalFilename) - : FileInfo(u, infoJson, originalFilename), - imageSize(infoJson["w"_ls].toInt(), infoJson["h"_ls].toInt()) -{ -} + : FileInfo(u, infoJson, originalFilename) + , imageSize(infoJson["w"_ls].toInt(), infoJson["h"_ls].toInt()) +{} void ImageInfo::fillInfoJson(QJsonObject* infoJson) const { @@ -88,8 +92,7 @@ void ImageInfo::fillInfoJson(QJsonObject* infoJson) const Thumbnail::Thumbnail(const QJsonObject& infoJson) : ImageInfo(infoJson["thumbnail_url"_ls].toString(), infoJson["thumbnail_info"_ls].toObject()) -{ -} +{} void Thumbnail::fillInfoJson(QJsonObject* infoJson) const { diff --git a/lib/events/eventcontent.h b/lib/events/eventcontent.h index 857e7369..d2b5e477 100644 --- a/lib/events/eventcontent.h +++ b/lib/events/eventcontent.h @@ -26,257 +26,265 @@ #include <QtCore/QSize> #include <QtCore/QUrl> -namespace QMatrixClient { - namespace EventContent { - /** - * A base class for all content types that can be stored - * in a RoomMessageEvent - * - * Each content type class should have a constructor taking - * a QJsonObject and override fillJson() with an implementation - * that will fill the target QJsonObject with stored values. It is - * assumed but not required that a content object can also be created - * from plain data. - */ - class Base - { - public: - explicit Base(QJsonObject o = {}) : originalJson(std::move(o)) {} - virtual ~Base() = default; +namespace QMatrixClient +{ +namespace EventContent +{ + /** + * A base class for all content types that can be stored + * in a RoomMessageEvent + * + * Each content type class should have a constructor taking + * a QJsonObject and override fillJson() with an implementation + * that will fill the target QJsonObject with stored values. It is + * assumed but not required that a content object can also be created + * from plain data. + */ + class Base + { + public: + explicit Base(QJsonObject o = {}) + : originalJson(std::move(o)) + {} + virtual ~Base() = default; + + // FIXME: make toJson() from converters.* work on base classes + QJsonObject toJson() const; - // FIXME: make toJson() from converters.* work on base classes - QJsonObject toJson() const; + public: + QJsonObject originalJson; - public: - QJsonObject originalJson; + protected: + virtual void fillJson(QJsonObject* o) const = 0; + }; - protected: - virtual void fillJson(QJsonObject* o) const = 0; - }; + // 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 + // each type for the list of available attributes. - // 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 - // each type for the list of available attributes. + // A quick classes inheritance structure follows: + // FileInfo + // FileContent : UrlBasedContent<FileInfo, Thumbnail> + // AudioContent : UrlBasedContent<FileInfo, Duration> + // ImageInfo : FileInfo + imageSize attribute + // ImageContent : UrlBasedContent<ImageInfo, Thumbnail> + // VideoContent : UrlBasedContent<ImageInfo, Thumbnail, Duration> - // A quick classes inheritance structure follows: - // FileInfo - // FileContent : UrlBasedContent<FileInfo, Thumbnail> - // AudioContent : UrlBasedContent<FileInfo, Duration> - // ImageInfo : FileInfo + imageSize attribute - // ImageContent : UrlBasedContent<ImageInfo, Thumbnail> - // VideoContent : UrlBasedContent<ImageInfo, Thumbnail, Duration> + /** + * A base/mixin class for structures representing an "info" object for + * some content types. These include most attachment types currently in + * the CS API spec. + * + * In order to use it in a content class, derive both from TypedBase + * (or Base) and from FileInfo (or its derivative, such as \p ImageInfo) + * and call fillInfoJson() to fill the "info" subobject. Make sure + * to pass an "info" part of JSON to FileInfo constructor, not the whole + * JSON content, as well as contents of "url" (or a similar key) and + * optionally "filename" node from the main JSON content. Assuming you + * don't do unusual things, you should use \p UrlBasedContent<> instead + * of doing multiple inheritance and overriding Base::fillJson() by hand. + * + * This class is not polymorphic. + */ + class FileInfo + { + public: + explicit FileInfo(const QUrl& u, qint64 payloadSize = -1, + const QMimeType& mimeType = {}, + const QString& originalFilename = {}); + FileInfo(const QUrl& u, const QJsonObject& infoJson, + const QString& originalFilename = {}); + + bool isValid() const; + + void fillInfoJson(QJsonObject* infoJson) const; /** - * A base/mixin class for structures representing an "info" object for - * some content types. These include most attachment types currently in - * the CS API spec. + * \brief Extract media id from the URL * - * In order to use it in a content class, derive both from TypedBase - * (or Base) and from FileInfo (or its derivative, such as \p ImageInfo) - * and call fillInfoJson() to fill the "info" subobject. Make sure - * to pass an "info" part of JSON to FileInfo constructor, not the whole - * JSON content, as well as contents of "url" (or a similar key) and - * optionally "filename" node from the main JSON content. Assuming you - * don't do unusual things, you should use \p UrlBasedContent<> instead - * of doing multiple inheritance and overriding Base::fillJson() by - * hand. - * - * This class is not polymorphic. + * This can be used, e.g., to construct a QML-facing image:// + * URI as follows: + * \code "image://provider/" + info.mediaId() \endcode */ - class FileInfo - { - public: - explicit FileInfo(const QUrl& u, qint64 payloadSize = -1, - const QMimeType& mimeType = {}, - const QString& originalFilename = {}); - FileInfo(const QUrl& u, const QJsonObject& infoJson, - const QString& originalFilename = {}); + QString mediaId() const { return url.authority() + url.path(); } - void fillInfoJson(QJsonObject* infoJson) const; + public: + QJsonObject originalInfoJson; + QMimeType mimeType; + QUrl url; + qint64 payloadSize; + QString originalName; + }; - /** - * \brief Extract media id from the URL - * - * This can be used, e.g., to construct a QML-facing image:// - * URI as follows: - * \code "image://provider/" + info.mediaId() \endcode - */ - QString mediaId() const { return url.authority() + url.path(); } + template <typename InfoT> + QJsonObject toInfoJson(const InfoT& info) + { + QJsonObject infoJson; + info.fillInfoJson(&infoJson); + return infoJson; + } - public: - QJsonObject originalInfoJson; - QMimeType mimeType; - QUrl url; - qint64 payloadSize; - QString originalName; - }; + /** + * A content info class for image content types: image, thumbnail, video + */ + class ImageInfo : public FileInfo + { + public: + explicit ImageInfo(const QUrl& u, qint64 fileSize = -1, + QMimeType mimeType = {}, const QSize& imageSize = {}, + const QString& originalFilename = {}); + ImageInfo(const QUrl& u, const QJsonObject& infoJson, + const QString& originalFilename = {}); - template <typename InfoT> QJsonObject toInfoJson(const InfoT& info) - { - QJsonObject infoJson; - info.fillInfoJson(&infoJson); - return infoJson; - } + void fillInfoJson(QJsonObject* infoJson) const; - /** - * A content info class for image content types: image, thumbnail, video - */ - class ImageInfo : public FileInfo - { - public: - explicit ImageInfo(const QUrl& u, qint64 fileSize = -1, - QMimeType mimeType = {}, - const QSize& imageSize = {}, - const QString& originalFilename = {}); - ImageInfo(const QUrl& u, const QJsonObject& infoJson, - const QString& originalFilename = {}); + public: + QSize imageSize; + }; - void fillInfoJson(QJsonObject* infoJson) const; - - public: - QSize imageSize; - }; + /** + * An auxiliary class for an info type that carries a thumbnail + * + * This class saves/loads a thumbnail to/from "info" subobject of + * the JSON representation of event content; namely, + * "info/thumbnail_url" and "info/thumbnail_info" fields are used. + */ + class Thumbnail : public ImageInfo + { + public: + Thumbnail() + : ImageInfo(QUrl()) + {} // To allow empty thumbnails + Thumbnail(const QJsonObject& infoJson); + Thumbnail(const ImageInfo& info) + : ImageInfo(info) + {} + using ImageInfo::ImageInfo; /** - * An auxiliary class for an info type that carries a thumbnail - * - * This class saves/loads a thumbnail to/from "info" subobject of - * the JSON representation of event content; namely, - * "info/thumbnail_url" and "info/thumbnail_info" fields are used. + * Writes thumbnail information to "thumbnail_info" subobject + * and thumbnail URL to "thumbnail_url" node inside "info". */ - class Thumbnail : public ImageInfo - { - public: - Thumbnail() : ImageInfo(QUrl()) {} // To allow empty thumbnails - Thumbnail(const QJsonObject& infoJson); - Thumbnail(const ImageInfo& info) : ImageInfo(info) {} - using ImageInfo::ImageInfo; + void fillInfoJson(QJsonObject* infoJson) const; + }; - /** - * Writes thumbnail information to "thumbnail_info" subobject - * and thumbnail URL to "thumbnail_url" node inside "info". - */ - void fillInfoJson(QJsonObject* infoJson) const; - }; + class TypedBase : public Base + { + public: + explicit TypedBase(const QJsonObject& o = {}) + : Base(o) + {} + virtual QMimeType type() const = 0; + virtual const FileInfo* fileInfo() const { return nullptr; } + virtual FileInfo* fileInfo() { return nullptr; } + virtual const Thumbnail* thumbnailInfo() const { return nullptr; } + }; - class TypedBase : public Base + /** + * A base class for content types that have a URL and additional info + * + * Types that derive from this class template take "url" and, + * optionally, "filename" values from the top-level JSON object and + * the rest of information from the "info" subobject, as defined by + * the parameter type. + * + * \tparam InfoT base info class + */ + template <class InfoT> + class UrlBasedContent : public TypedBase, public InfoT + { + public: + using InfoT::InfoT; + explicit UrlBasedContent(const QJsonObject& json) + : TypedBase(json) + , InfoT(json["url"].toString(), json["info"].toObject(), + json["filename"].toString()) { - public: - explicit TypedBase(const QJsonObject& o = {}) : Base(o) {} - virtual QMimeType type() const = 0; - virtual const FileInfo* fileInfo() const { return nullptr; } - virtual FileInfo* fileInfo() { return nullptr; } - virtual const Thumbnail* thumbnailInfo() const { return nullptr; } - }; - - /** - * A base class for content types that have a URL and additional info - * - * Types that derive from this class template take "url" and, - * optionally, "filename" values from the top-level JSON object and - * the rest of information from the "info" subobject, as defined by - * the parameter type. - * - * \tparam InfoT base info class - */ - template <class InfoT> - class UrlBasedContent : public TypedBase, public InfoT - { - public: - using InfoT::InfoT; - explicit UrlBasedContent(const QJsonObject& json) - : TypedBase(json), - InfoT(json["url"].toString(), json["info"].toObject(), - json["filename"].toString()) - { - // A small hack to facilitate links creation in QML. - originalJson.insert("mediaId", InfoT::mediaId()); - } + // A small hack to facilitate links creation in QML. + originalJson.insert("mediaId", InfoT::mediaId()); + } - QMimeType type() const override { return InfoT::mimeType; } - const FileInfo* fileInfo() const override { return this; } - FileInfo* fileInfo() override { return this; } + QMimeType type() const override { return InfoT::mimeType; } + const FileInfo* fileInfo() const override { return this; } + FileInfo* fileInfo() override { return this; } - protected: - void fillJson(QJsonObject* json) const override - { - Q_ASSERT(json); - json->insert("url", InfoT::url.toString()); - if (!InfoT::originalName.isEmpty()) - json->insert("filename", InfoT::originalName); - json->insert("info", toInfoJson<InfoT>(*this)); - } - }; + protected: + void fillJson(QJsonObject* json) const override + { + Q_ASSERT(json); + json->insert("url", InfoT::url.toString()); + if (!InfoT::originalName.isEmpty()) + json->insert("filename", InfoT::originalName); + json->insert("info", toInfoJson<InfoT>(*this)); + } + }; - template <typename InfoT> - class UrlWithThumbnailContent : public UrlBasedContent<InfoT> + template <typename InfoT> + class UrlWithThumbnailContent : public UrlBasedContent<InfoT> + { + public: + using UrlBasedContent<InfoT>::UrlBasedContent; + explicit UrlWithThumbnailContent(const QJsonObject& json) + : UrlBasedContent<InfoT>(json) + , thumbnail(InfoT::originalInfoJson) { - public: - using UrlBasedContent<InfoT>::UrlBasedContent; - explicit UrlWithThumbnailContent(const QJsonObject& json) - : UrlBasedContent<InfoT>(json), - thumbnail(InfoT::originalInfoJson) - { - // Another small hack, to simplify making a thumbnail link - UrlBasedContent<InfoT>::originalJson.insert( - "thumbnailMediaId", thumbnail.mediaId()); - } + // Another small hack, to simplify making a thumbnail link + UrlBasedContent<InfoT>::originalJson.insert("thumbnailMediaId", + thumbnail.mediaId()); + } - const Thumbnail* thumbnailInfo() const override - { - return &thumbnail; - } + const Thumbnail* thumbnailInfo() const override { return &thumbnail; } - public: - Thumbnail thumbnail; + public: + Thumbnail thumbnail; - protected: - void fillJson(QJsonObject* json) const override - { - UrlBasedContent<InfoT>::fillJson(json); - auto infoJson = json->take("info").toObject(); - thumbnail.fillInfoJson(&infoJson); - json->insert("info", infoJson); - } - }; + protected: + void fillJson(QJsonObject* json) const override + { + UrlBasedContent<InfoT>::fillJson(json); + auto infoJson = json->take("info").toObject(); + thumbnail.fillInfoJson(&infoJson); + json->insert("info", infoJson); + } + }; - /** - * Content class for m.image - * - * Available fields: - * - corresponding to the top-level JSON: - * - url - * - filename (extension to the spec) - * - corresponding to the "info" subobject: - * - payloadSize ("size" in JSON) - * - mimeType ("mimetype" in JSON) - * - imageSize (QSize for a combination of "h" and "w" in JSON) - * - thumbnail.url ("thumbnail_url" in JSON) - * - corresponding to the "info/thumbnail_info" subobject: contents of - * thumbnail field, in the same vein as for the main image: - * - payloadSize - * - mimeType - * - imageSize - */ - using ImageContent = UrlWithThumbnailContent<ImageInfo>; + /** + * Content class for m.image + * + * Available fields: + * - corresponding to the top-level JSON: + * - url + * - filename (extension to the spec) + * - corresponding to the "info" subobject: + * - payloadSize ("size" in JSON) + * - mimeType ("mimetype" in JSON) + * - imageSize (QSize for a combination of "h" and "w" in JSON) + * - thumbnail.url ("thumbnail_url" in JSON) + * - corresponding to the "info/thumbnail_info" subobject: contents of + * thumbnail field, in the same vein as for the main image: + * - payloadSize + * - mimeType + * - imageSize + */ + using ImageContent = UrlWithThumbnailContent<ImageInfo>; - /** - * Content class for m.file - * - * Available fields: - * - corresponding to the top-level JSON: - * - url - * - filename - * - corresponding to the "info" subobject: - * - payloadSize ("size" in JSON) - * - mimeType ("mimetype" in JSON) - * - thumbnail.url ("thumbnail_url" in JSON) - * - corresponding to the "info/thumbnail_info" subobject: - * - thumbnail.payloadSize - * - thumbnail.mimeType - * - thumbnail.imageSize (QSize for "h" and "w" in JSON) - */ - using FileContent = UrlWithThumbnailContent<FileInfo>; - } // namespace EventContent + /** + * Content class for m.file + * + * Available fields: + * - corresponding to the top-level JSON: + * - url + * - filename + * - corresponding to the "info" subobject: + * - payloadSize ("size" in JSON) + * - mimeType ("mimetype" in JSON) + * - thumbnail.url ("thumbnail_url" in JSON) + * - corresponding to the "info/thumbnail_info" subobject: + * - thumbnail.payloadSize + * - thumbnail.mimeType + * - thumbnail.imageSize (QSize for "h" and "w" in JSON) + */ + using FileContent = UrlWithThumbnailContent<FileInfo>; +} // namespace EventContent } // namespace QMatrixClient diff --git a/lib/events/eventloader.h b/lib/events/eventloader.h index a19a83b6..9c797701 100644 --- a/lib/events/eventloader.h +++ b/lib/events/eventloader.h @@ -20,51 +20,54 @@ #include "stateevent.h" -namespace QMatrixClient { - namespace _impl { - template <typename BaseEventT> - static inline auto loadEvent(const QJsonObject& json, - const QString& matrixType) - { - if (auto e = EventFactory<BaseEventT>::make(json, matrixType)) - return e; - return makeEvent<BaseEventT>(unknownEventTypeId(), json); - } - } - - /** 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. - */ +namespace QMatrixClient +{ +namespace _impl +{ template <typename BaseEventT> - inline event_ptr_tt<BaseEventT> loadEvent(const QJsonObject& fullJson) + static inline auto loadEvent(const QJsonObject& json, + const QString& matrixType) { - return _impl::loadEvent<BaseEventT>(fullJson, - fullJson[TypeKeyL].toString()); + if (auto e = EventFactory<BaseEventT>::make(json, matrixType)) + return e; + return makeEvent<BaseEventT>(unknownEventTypeId(), json); } +} // namespace _impl - /** 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 <typename BaseEventT> - inline event_ptr_tt<BaseEventT> loadEvent(const QString& matrixType, - const QJsonObject& 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 <typename BaseEventT> +inline event_ptr_tt<BaseEventT> loadEvent(const QJsonObject& fullJson) +{ + return _impl::loadEvent<BaseEventT>(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 <typename BaseEventT> +inline event_ptr_tt<BaseEventT> loadEvent(const QString& matrixType, + const QJsonObject& content) +{ + return _impl::loadEvent<BaseEventT>(basicEventJson(matrixType, content), + matrixType); +} + +template <typename EventT> +struct JsonConverter<event_ptr_tt<EventT>> +{ + static auto load(const QJsonValue& jv) { - return _impl::loadEvent<BaseEventT>(basicEventJson(matrixType, content), - matrixType); + return loadEvent<EventT>(jv.toObject()); } - - template <typename EventT> struct JsonConverter<event_ptr_tt<EventT>> { - static auto load(const QJsonValue& jv) - { - return loadEvent<EventT>(jv.toObject()); - } - static auto load(const QJsonDocument& jd) - { - return loadEvent<EventT>(jd.object()); - } - }; + static auto load(const QJsonDocument& jd) + { + return loadEvent<EventT>(jd.object()); + } +}; } // namespace QMatrixClient diff --git a/lib/events/receiptevent.cpp b/lib/events/receiptevent.cpp index 76c7b6e4..fcb8431b 100644 --- a/lib/events/receiptevent.cpp +++ b/lib/events/receiptevent.cpp @@ -40,26 +40,26 @@ Example of a Receipt Event: using namespace QMatrixClient; -ReceiptEvent::ReceiptEvent(const QJsonObject& obj) : Event(typeId(), obj) +ReceiptEvent::ReceiptEvent(const QJsonObject& obj) + : Event(typeId(), obj) { const auto& contents = contentJson(); _eventsWithReceipts.reserve(contents.size()); - for (auto eventIt = contents.begin(); eventIt != contents.end(); - ++eventIt) { + for (auto eventIt = contents.begin(); eventIt != contents.end(); ++eventIt) { if (eventIt.key().isEmpty()) { qCWarning(EPHEMERAL) - << "ReceiptEvent has an empty event id, skipping"; + << "ReceiptEvent has an empty event id, skipping"; qCDebug(EPHEMERAL) << "ReceiptEvent content follows:\n" << contents; continue; } const QJsonObject reads = - eventIt.value().toObject().value("m.read"_ls).toObject(); + eventIt.value().toObject().value("m.read"_ls).toObject(); QVector<Receipt> 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(), fromJson<QDateTime>(user["ts"_ls]) }); + { userIt.key(), fromJson<QDateTime>(user["ts"_ls]) }); } _eventsWithReceipts.push_back({ eventIt.key(), std::move(receipts) }); } diff --git a/lib/events/receiptevent.h b/lib/events/receiptevent.h index fca38bba..e8396670 100644 --- a/lib/events/receiptevent.h +++ b/lib/events/receiptevent.h @@ -23,31 +23,34 @@ #include <QtCore/QDateTime> #include <QtCore/QVector> -namespace QMatrixClient { - struct Receipt { - QString userId; - QDateTime timestamp; - }; - struct ReceiptsForEvent { - QString evtId; - QVector<Receipt> receipts; - }; - using EventsWithReceipts = QVector<ReceiptsForEvent>; +namespace QMatrixClient +{ +struct Receipt +{ + QString userId; + QDateTime timestamp; +}; +struct ReceiptsForEvent +{ + QString evtId; + QVector<Receipt> receipts; +}; +using EventsWithReceipts = QVector<ReceiptsForEvent>; - class ReceiptEvent : public Event - { - public: - DEFINE_EVENT_TYPEID("m.receipt", ReceiptEvent) - explicit ReceiptEvent(const QJsonObject& obj); +class ReceiptEvent : public Event +{ +public: + DEFINE_EVENT_TYPEID("m.receipt", ReceiptEvent) + explicit ReceiptEvent(const QJsonObject& obj); - const EventsWithReceipts& eventsWithReceipts() const - { - return _eventsWithReceipts; - } + const EventsWithReceipts& eventsWithReceipts() const + { + return _eventsWithReceipts; + } - private: - EventsWithReceipts _eventsWithReceipts; - }; - REGISTER_EVENT_TYPE(ReceiptEvent) - DEFINE_EVENTTYPE_ALIAS(Receipt, ReceiptEvent) +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 4187291c..a7dd9705 100644 --- a/lib/events/redactionevent.h +++ b/lib/events/redactionevent.h @@ -20,23 +20,23 @@ #include "roomevent.h" -namespace QMatrixClient { - class RedactionEvent : public RoomEvent - { - public: - DEFINE_EVENT_TYPEID("m.room.redaction", RedactionEvent) +namespace QMatrixClient +{ +class RedactionEvent : public RoomEvent +{ +public: + DEFINE_EVENT_TYPEID("m.room.redaction", RedactionEvent) - explicit RedactionEvent(const QJsonObject& obj) - : RoomEvent(typeId(), obj) - { - } + explicit RedactionEvent(const QJsonObject& obj) + : RoomEvent(typeId(), obj) + {} - QString redactedEvent() const - { - return fullJson()["redacts"_ls].toString(); - } - QString reason() const { return contentJson()["reason"_ls].toString(); } - }; - REGISTER_EVENT_TYPE(RedactionEvent) - DEFINE_EVENTTYPE_ALIAS(Redaction, RedactionEvent) + QString redactedEvent() const + { + return fullJson()["redacts"_ls].toString(); + } + 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 69cd1f9d..ee460339 100644 --- a/lib/events/roomavatarevent.h +++ b/lib/events/roomavatarevent.h @@ -21,21 +21,21 @@ #include "eventcontent.h" #include "stateevent.h" -namespace QMatrixClient { - class RoomAvatarEvent : public StateEvent<EventContent::ImageContent> - { - // It's a bit of an overkill to use a full-fledged ImageContent - // because in reality m.room.avatar usually only has a single URL, - // 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(typeId(), obj) - { - } - QUrl url() const { return content().url; } - }; - REGISTER_EVENT_TYPE(RoomAvatarEvent) - DEFINE_EVENTTYPE_ALIAS(RoomAvatar, RoomAvatarEvent) +namespace QMatrixClient +{ +class RoomAvatarEvent : public StateEvent<EventContent::ImageContent> +{ + // It's a bit of an overkill to use a full-fledged ImageContent + // because in reality m.room.avatar usually only has a single URL, + // 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(typeId(), obj) + {} + QUrl url() const { return content().url; } +}; +REGISTER_EVENT_TYPE(RoomAvatarEvent) +DEFINE_EVENTTYPE_ALIAS(RoomAvatar, RoomAvatarEvent) } // namespace QMatrixClient diff --git a/lib/events/roomcreateevent.h b/lib/events/roomcreateevent.h index fbfb33ca..17b86388 100644 --- a/lib/events/roomcreateevent.h +++ b/lib/events/roomcreateevent.h @@ -20,27 +20,30 @@ #include "stateevent.h" -namespace QMatrixClient { - class RoomCreateEvent : public StateEventBase - { - public: - DEFINE_EVENT_TYPEID("m.room.create", RoomCreateEvent) - - explicit RoomCreateEvent() : StateEventBase(typeId(), matrixTypeId()) {} - explicit RoomCreateEvent(const QJsonObject& obj) - : StateEventBase(typeId(), obj) - { - } +namespace QMatrixClient +{ +class RoomCreateEvent : public StateEventBase +{ +public: + DEFINE_EVENT_TYPEID("m.room.create", RoomCreateEvent) - struct Predecessor { - QString roomId; - QString eventId; - }; + explicit RoomCreateEvent() + : StateEventBase(typeId(), matrixTypeId()) + {} + explicit RoomCreateEvent(const QJsonObject& obj) + : StateEventBase(typeId(), obj) + {} - bool isFederated() const; - QString version() const; - Predecessor predecessor() const; - bool isUpgrade() const; + struct Predecessor + { + QString roomId; + QString eventId; }; - REGISTER_EVENT_TYPE(RoomCreateEvent) -} + + bool isFederated() const; + QString version() const; + Predecessor predecessor() const; + bool isUpgrade() const; +}; +REGISTER_EVENT_TYPE(RoomCreateEvent) +} // namespace QMatrixClient diff --git a/lib/events/roomevent.cpp b/lib/events/roomevent.cpp index 221a3a61..c28de559 100644 --- a/lib/events/roomevent.cpp +++ b/lib/events/roomevent.cpp @@ -25,15 +25,15 @@ using namespace QMatrixClient; [[gnu::unused]] static auto roomEventTypeInitialised = - Event::factory_t::chainFactory<RoomEvent>(); + Event::factory_t::chainFactory<RoomEvent>(); 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) +RoomEvent::RoomEvent(Type type, const QJsonObject& json) + : Event(type, json) { const auto unsignedData = json[UnsignedKeyL].toObject(); const auto redaction = unsignedData[RedactedCauseKeyL]; @@ -49,8 +49,7 @@ QString RoomEvent::id() const { return fullJson()[EventIdKeyL].toString(); } QDateTime RoomEvent::timestamp() const { - return QMatrixClient::fromJson<QDateTime>( - fullJson()["origin_server_ts"_ls]); + return QMatrixClient::fromJson<QDateTime>(fullJson()["origin_server_ts"_ls]); } QString RoomEvent::roomId() const @@ -108,8 +107,7 @@ CallEventBase::CallEventBase(Type type, event_mtype_t matrixType, const QJsonObject& contentJson) : RoomEvent(type, matrixType, makeCallContentJson(callId, version, contentJson)) -{ -} +{} CallEventBase::CallEventBase(Event::Type type, const QJsonObject& json) : RoomEvent(type, json) diff --git a/lib/events/roomevent.h b/lib/events/roomevent.h index 746e9173..42cd8fe4 100644 --- a/lib/events/roomevent.h +++ b/lib/events/roomevent.h @@ -22,85 +22,84 @@ #include <QtCore/QDateTime> -namespace QMatrixClient { - class RedactionEvent; +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 WRITE setTransactionId) - public: - using factory_t = EventFactory<RoomEvent>; +/** 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 WRITE setTransactionId) +public: + using factory_t = EventFactory<RoomEvent>; - // 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; + // 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<RedactionEvent>& redactedBecause() const - { - return _redactedBecause; - } - QString redactionReason() const; - QString transactionId() const; - QString stateKey() const; + QString id() const; + QDateTime timestamp() const; + QString roomId() const; + QString senderId() const; + bool isRedacted() const { return bool(_redactedBecause); } + const event_ptr_tt<RedactionEvent>& redactedBecause() const + { + return _redactedBecause; + } + QString redactionReason() const; + QString transactionId() const; + QString stateKey() const; - /** - * 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); + /** + * 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); - /** - * 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); + /** + * 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<RedactionEvent> _redactedBecause; - }; - using RoomEventPtr = event_ptr_tt<RoomEvent>; - using RoomEvents = EventsArray<RoomEvent>; - using RoomEventsRange = Range<RoomEvents>; +private: + event_ptr_tt<RedactionEvent> _redactedBecause; +}; +using RoomEventPtr = event_ptr_tt<RoomEvent>; +using RoomEvents = EventsArray<RoomEvent>; +using RoomEventsRange = Range<RoomEvents>; - class CallEventBase : public RoomEvent - { - public: - CallEventBase(Type type, event_mtype_t matrixType, - const QString& callId, int version, - const QJsonObject& contentJson = {}); - CallEventBase(Type type, const QJsonObject& json); - ~CallEventBase() override = default; - bool isCallEvent() const override { return true; } +class CallEventBase : public RoomEvent +{ +public: + CallEventBase(Type type, event_mtype_t matrixType, const QString& callId, + int version, const QJsonObject& contentJson = {}); + CallEventBase(Type type, const QJsonObject& json); + ~CallEventBase() override = default; + bool isCallEvent() const override { return true; } - QString callId() const { return content<QString>("call_id"_ls); } - int version() const { return content<int>("version"_ls); } - }; + QString callId() const { return content<QString>("call_id"_ls); } + int version() const { return content<int>("version"_ls); } +}; } // 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 53203873..e6292b73 100644 --- a/lib/events/roommemberevent.cpp +++ b/lib/events/roommemberevent.cpp @@ -28,31 +28,33 @@ static const std::array<QString, 5> membershipStrings = { QStringLiteral("leave"), QStringLiteral("ban") } }; -namespace QMatrixClient { - template <> struct JsonConverter<MembershipType> { - static MembershipType load(const QJsonValue& jv) - { - const auto& membershipString = jv.toString(); - for (auto it = membershipStrings.begin(); - it != membershipStrings.end(); ++it) - if (membershipString == *it) - return MembershipType(it - membershipStrings.begin()); +namespace QMatrixClient +{ +template <> +struct JsonConverter<MembershipType> +{ + static MembershipType load(const QJsonValue& jv) + { + const auto& membershipString = jv.toString(); + for (auto it = membershipStrings.begin(); it != membershipStrings.end(); + ++it) + if (membershipString == *it) + return MembershipType(it - membershipStrings.begin()); - qCWarning(EVENTS) << "Unknown MembershipType: " << membershipString; - return MembershipType::Undefined; - } - }; -} + qCWarning(EVENTS) << "Unknown MembershipType: " << membershipString; + return MembershipType::Undefined; + } +}; +} // namespace QMatrixClient using namespace QMatrixClient; MemberEventContent::MemberEventContent(const QJsonObject& json) - : membership(fromJson<MembershipType>(json["membership"_ls])), - isDirect(json["is_direct"_ls].toBool()), - displayName(json["displayname"_ls].toString()), - avatarUrl(json["avatar_url"_ls].toString()) -{ -} + : membership(fromJson<MembershipType>(json["membership"_ls])) + , isDirect(json["is_direct"_ls].toBool()) + , displayName(sanitized(json["displayname"_ls].toString())) + , avatarUrl(json["avatar_url"_ls].toString()) +{} void MemberEventContent::fillJson(QJsonObject* o) const { @@ -69,20 +71,20 @@ void MemberEventContent::fillJson(QJsonObject* o) const bool RoomMemberEvent::isInvite() const { return membership() == MembershipType::Invite - && (!prevContent() || prevContent()->membership != membership()); + && (!prevContent() || prevContent()->membership != membership()); } bool RoomMemberEvent::isJoin() const { return membership() == MembershipType::Join - && (!prevContent() || prevContent()->membership != membership()); + && (!prevContent() || prevContent()->membership != membership()); } bool RoomMemberEvent::isLeave() const { return membership() == MembershipType::Leave && prevContent() - && prevContent()->membership != membership() - && prevContent()->membership != MembershipType::Ban; + && prevContent()->membership != membership() + && prevContent()->membership != MembershipType::Ban; } bool RoomMemberEvent::isRename() const diff --git a/lib/events/roommemberevent.h b/lib/events/roommemberevent.h index 55f419d8..a837b026 100644 --- a/lib/events/roommemberevent.h +++ b/lib/events/roommemberevent.h @@ -21,92 +21,92 @@ #include "eventcontent.h" #include "stateevent.h" -namespace QMatrixClient { - class MemberEventContent : public EventContent::Base +namespace QMatrixClient +{ +class MemberEventContent : public EventContent::Base +{ +public: + enum MembershipType : size_t { - public: - enum MembershipType : size_t { - Invite = 0, - Join, - Knock, - Leave, - Ban, - Undefined - }; + Invite = 0, + Join, + Knock, + Leave, + Ban, + Undefined + }; - explicit MemberEventContent(MembershipType mt = Join) : membership(mt) - { - } - explicit MemberEventContent(const QJsonObject& json); + explicit MemberEventContent(MembershipType mt = Join) + : membership(mt) + {} + explicit MemberEventContent(const QJsonObject& json); - MembershipType membership; - bool isDirect = false; - QString displayName; - QUrl avatarUrl; + MembershipType membership; + bool isDirect = false; + QString displayName; + QUrl avatarUrl; - protected: - void fillJson(QJsonObject* o) const override; - }; +protected: + void fillJson(QJsonObject* o) const override; +}; - using MembershipType = MemberEventContent::MembershipType; +using MembershipType = MemberEventContent::MembershipType; - class RoomMemberEvent : public StateEvent<MemberEventContent> - { - Q_GADGET - public: - DEFINE_EVENT_TYPEID("m.room.member", RoomMemberEvent) +class RoomMemberEvent : public StateEvent<MemberEventContent> +{ + Q_GADGET +public: + DEFINE_EVENT_TYPEID("m.room.member", RoomMemberEvent) - using MembershipType = MemberEventContent::MembershipType; + using MembershipType = MemberEventContent::MembershipType; - explicit RoomMemberEvent(const QJsonObject& obj) - : StateEvent(typeId(), obj) - { - } - RoomMemberEvent(MemberEventContent&& c) - : StateEvent(typeId(), matrixTypeId(), c) - { - } + explicit RoomMemberEvent(const QJsonObject& obj) + : StateEvent(typeId(), obj) + {} + RoomMemberEvent(MemberEventContent&& c) + : StateEvent(typeId(), matrixTypeId(), c) + {} - /// A special constructor to create unknown RoomMemberEvents - /** - * This is needed in order to use RoomMemberEvent as a "base event - * class" in cases like GetMembersByRoomJob when RoomMemberEvents - * (rather than RoomEvents or StateEvents) are resolved from JSON. - * For such cases loadEvent<> requires an underlying class to be - * constructible with unknownTypeId() instead of its genuine id. - * Don't use it directly. - * \sa GetMembersByRoomJob, loadEvent, unknownTypeId - */ - RoomMemberEvent(Type type, const QJsonObject& fullJson) - : StateEvent(type, fullJson) - { - } + /// A special constructor to create unknown RoomMemberEvents + /** + * This is needed in order to use RoomMemberEvent as a "base event + * class" in cases like GetMembersByRoomJob when RoomMemberEvents + * (rather than RoomEvents or StateEvents) are resolved from JSON. + * For such cases loadEvent<> requires an underlying class to be + * constructible with unknownTypeId() instead of its genuine id. + * Don't use it directly. + * \sa GetMembersByRoomJob, loadEvent, unknownTypeId + */ + RoomMemberEvent(Type type, const QJsonObject& fullJson) + : StateEvent(type, fullJson) + {} - MembershipType membership() const { return content().membership; } - QString userId() const { return fullJson()["state_key"_ls].toString(); } - bool isDirect() const { return content().isDirect; } - QString displayName() const { return content().displayName; } - QUrl avatarUrl() const { return content().avatarUrl; } - bool isInvite() const; - bool isJoin() const; - bool isLeave() const; - bool isRename() const; - bool isAvatarUpdate() const; + MembershipType membership() const { return content().membership; } + QString userId() const { return fullJson()["state_key"_ls].toString(); } + bool isDirect() const { return content().isDirect; } + QString displayName() const { return content().displayName; } + QUrl avatarUrl() const { return content().avatarUrl; } + bool isInvite() const; + bool isJoin() const; + bool isLeave() const; + bool isRename() const; + bool isAvatarUpdate() const; - private: - REGISTER_ENUM(MembershipType) - }; +private: + REGISTER_ENUM(MembershipType) +}; - template <> class EventFactory<RoomMemberEvent> +template <> +class EventFactory<RoomMemberEvent> +{ +public: + static event_ptr_tt<RoomMemberEvent> make(const QJsonObject& json, + const QString&) { - public: - static event_ptr_tt<RoomMemberEvent> make(const QJsonObject& json, - const QString&) - { - return makeEvent<RoomMemberEvent>(json); - } - }; + return makeEvent<RoomMemberEvent>(json); + } +}; - REGISTER_EVENT_TYPE(RoomMemberEvent) - DEFINE_EVENTTYPE_ALIAS(RoomMember, RoomMemberEvent) +REGISTER_EVENT_TYPE(RoomMemberEvent) +DEFINE_EVENTTYPE_ALIAS(RoomMember, RoomMemberEvent) } // namespace QMatrixClient diff --git a/lib/events/roommessageevent.cpp b/lib/events/roommessageevent.cpp index 0af02eb0..c7f17303 100644 --- a/lib/events/roommessageevent.cpp +++ b/lib/events/roommessageevent.cpp @@ -40,19 +40,22 @@ static const auto NoticeTypeKey = "m.notice"; static const auto HtmlContentTypeId = QStringLiteral("org.matrix.custom.html"); -template <typename ContentT> TypedBase* make(const QJsonObject& json) +template <typename ContentT> +TypedBase* make(const QJsonObject& json) { return new ContentT(json); } -template <> TypedBase* make<TextContent>(const QJsonObject& json) +template <> +TypedBase* make<TextContent>(const QJsonObject& json) { return json.contains(FormattedBodyKey) || json.contains(RelatesToKey) - ? new TextContent(json) - : nullptr; + ? new TextContent(json) + : nullptr; } -struct MsgTypeDesc { +struct MsgTypeDesc +{ QString matrixType; MsgType enumType; TypedBase* (*maker)(const QJsonObject&); @@ -71,9 +74,10 @@ const std::vector<MsgTypeDesc> msgTypes = { QString msgTypeToJson(MsgType enumType) { - auto it = std::find_if( - msgTypes.begin(), msgTypes.end(), - [=](const MsgTypeDesc& mtd) { return mtd.enumType == enumType; }); + auto it = std::find_if(msgTypes.begin(), msgTypes.end(), + [=](const MsgTypeDesc& mtd) { + return mtd.enumType == enumType; + }); if (it != msgTypes.end()) return it->matrixType; @@ -112,16 +116,14 @@ RoomMessageEvent::RoomMessageEvent(const QString& plainBody, const QString& jsonMsgType, TypedBase* content) : RoomEvent(typeId(), matrixTypeId(), - assembleContentJson(plainBody, jsonMsgType, content)), - _content(content) -{ -} + assembleContentJson(plainBody, jsonMsgType, content)) + , _content(content) +{} RoomMessageEvent::RoomMessageEvent(const QString& plainBody, MsgType msgType, TypedBase* content) : RoomMessageEvent(plainBody, msgTypeToJson(msgType), content) -{ -} +{} TypedBase* contentFromFile(const QFileInfo& file, bool asGenericFile) { @@ -155,11 +157,11 @@ RoomMessageEvent::RoomMessageEvent(const QString& plainBody, asGenericFile ? QStringLiteral("m.file") : rawMsgTypeForFile(file), contentFromFile(file, asGenericFile)) -{ -} +{} RoomMessageEvent::RoomMessageEvent(const QJsonObject& obj) - : RoomEvent(typeId(), obj), _content(nullptr) + : RoomEvent(typeId(), obj) + , _content(nullptr) { if (isRedacted()) return; @@ -202,15 +204,15 @@ QString RoomMessageEvent::plainBody() const QMimeType RoomMessageEvent::mimeType() const { static const auto PlainTextMimeType = - QMimeDatabase().mimeTypeForName("text/plain"); + QMimeDatabase().mimeTypeForName("text/plain"); return _content ? _content->type() : PlainTextMimeType; } bool RoomMessageEvent::hasTextContent() const { return !content() - || (msgtype() == MsgType::Text || msgtype() == MsgType::Emote - || msgtype() == MsgType::Notice); + || (msgtype() == MsgType::Text || msgtype() == MsgType::Emote + || msgtype() == MsgType::Notice); } bool RoomMessageEvent::hasFileContent() const @@ -226,11 +228,12 @@ bool RoomMessageEvent::hasThumbnail() const QString rawMsgTypeForMimeType(const QMimeType& mimeType) { auto name = mimeType.name(); - return name.startsWith("image/") ? QStringLiteral("m.image") - : name.startsWith("video/") - ? QStringLiteral("m.video") - : name.startsWith("audio/") ? QStringLiteral("m.audio") - : QStringLiteral("m.file"); + return name.startsWith("image/") + ? QStringLiteral("m.image") + : name.startsWith("video/") + ? QStringLiteral("m.video") + : name.startsWith("audio/") ? QStringLiteral("m.audio") + : QStringLiteral("m.file"); } QString RoomMessageEvent::rawMsgTypeForUrl(const QUrl& url) @@ -245,9 +248,9 @@ QString RoomMessageEvent::rawMsgTypeForFile(const QFileInfo& fi) TextContent::TextContent(const QString& text, const QString& contentType, Omittable<RelatesTo> relatesTo) - : mimeType(QMimeDatabase().mimeTypeForName(contentType)), - body(text), - relatesTo(std::move(relatesTo)) + : mimeType(QMimeDatabase().mimeTypeForName(contentType)) + , body(text) + , relatesTo(std::move(relatesTo)) { if (contentType == HtmlContentTypeId) mimeType = QMimeDatabase().mimeTypeForName("text/html"); @@ -270,10 +273,8 @@ TextContent::TextContent(const QJsonObject& json) mimeType = PlainTextMimeType; body = json[BodyKey].toString(); } - const auto replyJson = json[RelatesToKey] - .toObject() - .value(RelatesTo::ReplyTypeId()) - .toObject(); + const auto replyJson = + json[RelatesToKey].toObject().value(RelatesTo::ReplyTypeId()).toObject(); if (!replyJson.isEmpty()) relatesTo = replyTo(fromJson<QString>(replyJson[EventIdKeyL])); } @@ -292,16 +293,15 @@ void TextContent::fillJson(QJsonObject* json) const LocationContent::LocationContent(const QString& geoUri, const Thumbnail& thumbnail) - : geoUri(geoUri), thumbnail(thumbnail) -{ -} + : geoUri(geoUri) + , thumbnail(thumbnail) +{} LocationContent::LocationContent(const QJsonObject& json) - : TypedBase(json), - geoUri(json["geo_uri"_ls].toString()), - thumbnail(json["info"_ls].toObject()) -{ -} + : TypedBase(json) + , geoUri(json["geo_uri"_ls].toString()) + , thumbnail(json["info"_ls].toObject()) +{} QMimeType LocationContent::type() const { diff --git a/lib/events/roommessageevent.h b/lib/events/roommessageevent.h index bd1b7c83..eabb21e3 100644 --- a/lib/events/roommessageevent.h +++ b/lib/events/roommessageevent.h @@ -23,205 +23,203 @@ class QFileInfo; -namespace QMatrixClient { - namespace MessageEventContent = EventContent; // Back-compatibility +namespace QMatrixClient +{ +namespace MessageEventContent = EventContent; // Back-compatibility + +/** + * The event class corresponding to m.room.message events + */ +class RoomMessageEvent : public RoomEvent +{ + Q_GADGET + Q_PROPERTY(QString msgType READ rawMsgtype CONSTANT) + Q_PROPERTY(QString plainBody READ plainBody CONSTANT) + 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 + }; + + RoomMessageEvent(const QString& plainBody, const QString& jsonMsgType, + EventContent::TypedBase* content = nullptr); + explicit RoomMessageEvent(const QString& plainBody, + MsgType msgType = MsgType::Text, + EventContent::TypedBase* content = nullptr); + explicit RoomMessageEvent(const QString& plainBody, const QFileInfo& file, + bool asGenericFile = false); + explicit RoomMessageEvent(const QJsonObject& obj); + + MsgType msgtype() const; + QString rawMsgtype() const; + QString plainBody() const; + EventContent::TypedBase* content() const { return _content.data(); } + template <typename VisitorT> + void editContent(VisitorT visitor) + { + visitor(*_content); + editJson()[ContentKeyL] = assembleContentJson(plainBody(), rawMsgtype(), + content()); + } + QMimeType mimeType() const; + bool hasTextContent() const; + bool hasFileContent() const; + bool hasThumbnail() const; + + static QString rawMsgTypeForUrl(const QUrl& url); + static QString rawMsgTypeForFile(const QFileInfo& fi); + +private: + QScopedPointer<EventContent::TypedBase> _content; + + static QJsonObject assembleContentJson(const QString& plainBody, + const QString& jsonMsgType, + EventContent::TypedBase* content); + + REGISTER_ENUM(MsgType) +}; +REGISTER_EVENT_TYPE(RoomMessageEvent) +DEFINE_EVENTTYPE_ALIAS(RoomMessage, RoomMessageEvent) +using MessageEventType = RoomMessageEvent::MsgType; + +namespace EventContent +{ + // Additional event content types + + struct RelatesTo + { + static constexpr const char* ReplyTypeId() { return "m.in_reply_to"; } + QString type; // The only supported relation so far + QString eventId; + }; + inline RelatesTo replyTo(QString eventId) + { + return { RelatesTo::ReplyTypeId(), std::move(eventId) }; + } /** - * The event class corresponding to m.room.message events + * Rich text content for m.text, m.emote, m.notice + * + * Available fields: mimeType, body. The body can be either rich text + * or plain text, depending on what mimeType specifies. */ - class RoomMessageEvent : public RoomEvent + class TextContent : public TypedBase { - Q_GADGET - Q_PROPERTY(QString msgType READ rawMsgtype CONSTANT) - Q_PROPERTY(QString plainBody READ plainBody CONSTANT) - 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 - }; - - RoomMessageEvent(const QString& plainBody, const QString& jsonMsgType, - EventContent::TypedBase* content = nullptr); - explicit RoomMessageEvent(const QString& plainBody, - MsgType msgType = MsgType::Text, - EventContent::TypedBase* content = nullptr); - explicit RoomMessageEvent(const QString& plainBody, - const QFileInfo& file, - bool asGenericFile = false); - explicit RoomMessageEvent(const QJsonObject& obj); - - MsgType msgtype() const; - QString rawMsgtype() const; - QString plainBody() const; - EventContent::TypedBase* content() const { return _content.data(); } - template <typename VisitorT> void editContent(VisitorT visitor) - { - visitor(*_content); - editJson()[ContentKeyL] = - assembleContentJson(plainBody(), rawMsgtype(), content()); - } - QMimeType mimeType() const; - bool hasTextContent() const; - bool hasFileContent() const; - bool hasThumbnail() const; - - static QString rawMsgTypeForUrl(const QUrl& url); - static QString rawMsgTypeForFile(const QFileInfo& fi); + public: + TextContent(const QString& text, const QString& contentType, + Omittable<RelatesTo> relatesTo = none); + explicit TextContent(const QJsonObject& json); - private: - QScopedPointer<EventContent::TypedBase> _content; + QMimeType type() const override { return mimeType; } - static QJsonObject - assembleContentJson(const QString& plainBody, - const QString& jsonMsgType, - EventContent::TypedBase* content); + QMimeType mimeType; + QString body; + Omittable<RelatesTo> relatesTo; - REGISTER_ENUM(MsgType) + protected: + void fillJson(QJsonObject* json) const override; }; - REGISTER_EVENT_TYPE(RoomMessageEvent) - DEFINE_EVENTTYPE_ALIAS(RoomMessage, RoomMessageEvent) - using MessageEventType = RoomMessageEvent::MsgType; - - namespace EventContent { - // Additional event content types - - struct RelatesTo { - static constexpr const char* ReplyTypeId() - { - return "m.in_reply_to"; - } - QString type; // The only supported relation so far - QString eventId; - }; - inline RelatesTo replyTo(QString eventId) - { - return { RelatesTo::ReplyTypeId(), std::move(eventId) }; - } - /** - * Rich text content for m.text, m.emote, m.notice - * - * Available fields: mimeType, body. The body can be either rich text - * or plain text, depending on what mimeType specifies. - */ - class TextContent : public TypedBase - { - public: - TextContent(const QString& text, const QString& contentType, - Omittable<RelatesTo> relatesTo = none); - explicit TextContent(const QJsonObject& json); - - QMimeType type() const override { return mimeType; } - - QMimeType mimeType; - QString body; - Omittable<RelatesTo> relatesTo; - - protected: - void fillJson(QJsonObject* json) const override; - }; - - /** - * Content class for m.location - * - * Available fields: - * - corresponding to the top-level JSON: - * - geoUri ("geo_uri" in JSON) - * - corresponding to the "info" subobject: - * - thumbnail.url ("thumbnail_url" in JSON) - * - corresponding to the "info/thumbnail_info" subobject: - * - thumbnail.payloadSize - * - thumbnail.mimeType - * - thumbnail.imageSize - */ - class LocationContent : public TypedBase - { - public: - LocationContent(const QString& geoUri, - const Thumbnail& thumbnail = {}); - explicit LocationContent(const QJsonObject& json); + /** + * Content class for m.location + * + * Available fields: + * - corresponding to the top-level JSON: + * - geoUri ("geo_uri" in JSON) + * - corresponding to the "info" subobject: + * - thumbnail.url ("thumbnail_url" in JSON) + * - corresponding to the "info/thumbnail_info" subobject: + * - thumbnail.payloadSize + * - thumbnail.mimeType + * - thumbnail.imageSize + */ + class LocationContent : public TypedBase + { + public: + LocationContent(const QString& geoUri, const Thumbnail& thumbnail = {}); + explicit LocationContent(const QJsonObject& json); - QMimeType type() const override; + QMimeType type() const override; - public: - QString geoUri; - Thumbnail thumbnail; + public: + QString geoUri; + Thumbnail thumbnail; - protected: - void fillJson(QJsonObject* o) const override; - }; + protected: + void fillJson(QJsonObject* o) const override; + }; - /** - * A base class for info types that include duration: audio and video - */ - template <typename ContentT> class PlayableContent : public ContentT + /** + * A base class for info types that include duration: audio and video + */ + template <typename ContentT> + class PlayableContent : public ContentT + { + public: + using ContentT::ContentT; + PlayableContent(const QJsonObject& json) + : ContentT(json) + , duration(ContentT::originalInfoJson["duration"_ls].toInt()) + {} + + protected: + void fillJson(QJsonObject* json) const override { - public: - using ContentT::ContentT; - PlayableContent(const QJsonObject& json) - : ContentT(json), - duration(ContentT::originalInfoJson["duration"_ls].toInt()) - { - } - - protected: - void fillJson(QJsonObject* json) const override - { - ContentT::fillJson(json); - auto infoJson = json->take("info"_ls).toObject(); - infoJson.insert(QStringLiteral("duration"), duration); - json->insert(QStringLiteral("info"), infoJson); - } - - public: - int duration; - }; - - /** - * Content class for m.video - * - * Available fields: - * - corresponding to the top-level JSON: - * - url - * - filename (extension to the CS API spec) - * - corresponding to the "info" subobject: - * - payloadSize ("size" in JSON) - * - mimeType ("mimetype" in JSON) - * - duration - * - imageSize (QSize for a combination of "h" and "w" in JSON) - * - thumbnail.url ("thumbnail_url" in JSON) - * - corresponding to the "info/thumbnail_info" subobject: contents of - * thumbnail field, in the same vein as for "info": - * - payloadSize - * - mimeType - * - imageSize - */ - using VideoContent = - PlayableContent<UrlWithThumbnailContent<ImageInfo>>; - - /** - * Content class for m.audio - * - * Available fields: - * - corresponding to the top-level JSON: - * - url - * - filename (extension to the CS API spec) - * - corresponding to the "info" subobject: - * - payloadSize ("size" in JSON) - * - mimeType ("mimetype" in JSON) - * - duration - */ - using AudioContent = PlayableContent<UrlBasedContent<FileInfo>>; - } // namespace EventContent + ContentT::fillJson(json); + auto infoJson = json->take("info"_ls).toObject(); + infoJson.insert(QStringLiteral("duration"), duration); + json->insert(QStringLiteral("info"), infoJson); + } + + public: + int duration; + }; + + /** + * Content class for m.video + * + * Available fields: + * - corresponding to the top-level JSON: + * - url + * - filename (extension to the CS API spec) + * - corresponding to the "info" subobject: + * - payloadSize ("size" in JSON) + * - mimeType ("mimetype" in JSON) + * - duration + * - imageSize (QSize for a combination of "h" and "w" in JSON) + * - thumbnail.url ("thumbnail_url" in JSON) + * - corresponding to the "info/thumbnail_info" subobject: contents of + * thumbnail field, in the same vein as for "info": + * - payloadSize + * - mimeType + * - imageSize + */ + using VideoContent = PlayableContent<UrlWithThumbnailContent<ImageInfo>>; + + /** + * Content class for m.audio + * + * Available fields: + * - corresponding to the top-level JSON: + * - url + * - filename (extension to the CS API spec) + * - corresponding to the "info" subobject: + * - payloadSize ("size" in JSON) + * - mimeType ("mimetype" in JSON) + * - duration + */ + using AudioContent = PlayableContent<UrlBasedContent<FileInfo>>; +} // namespace EventContent } // namespace QMatrixClient diff --git a/lib/events/roomtombstoneevent.h b/lib/events/roomtombstoneevent.h index 5b7ade76..aa9cb766 100644 --- a/lib/events/roomtombstoneevent.h +++ b/lib/events/roomtombstoneevent.h @@ -20,22 +20,22 @@ #include "stateevent.h" -namespace QMatrixClient { - class RoomTombstoneEvent : public StateEventBase - { - public: - DEFINE_EVENT_TYPEID("m.room.tombstone", RoomTombstoneEvent) +namespace QMatrixClient +{ +class RoomTombstoneEvent : public StateEventBase +{ +public: + DEFINE_EVENT_TYPEID("m.room.tombstone", RoomTombstoneEvent) - explicit RoomTombstoneEvent() : StateEventBase(typeId(), matrixTypeId()) - { - } - explicit RoomTombstoneEvent(const QJsonObject& obj) - : StateEventBase(typeId(), obj) - { - } + explicit RoomTombstoneEvent() + : StateEventBase(typeId(), matrixTypeId()) + {} + explicit RoomTombstoneEvent(const QJsonObject& obj) + : StateEventBase(typeId(), obj) + {} - QString serverMessage() const; - QString successorRoomId() const; - }; - REGISTER_EVENT_TYPE(RoomTombstoneEvent) -} + QString serverMessage() const; + QString successorRoomId() const; +}; +REGISTER_EVENT_TYPE(RoomTombstoneEvent) +} // namespace QMatrixClient diff --git a/lib/events/simplestateevents.h b/lib/events/simplestateevents.h index 430eacb0..7ad2efa6 100644 --- a/lib/events/simplestateevents.h +++ b/lib/events/simplestateevents.h @@ -18,74 +18,76 @@ #pragma once +#include "converters.h" #include "stateevent.h" -#include "converters.h" +namespace QMatrixClient +{ +namespace EventContent +{ + template <typename T> + class SimpleContent + { + public: + using value_type = T; -namespace QMatrixClient { - namespace EventContent { - template <typename T> class SimpleContent + // The constructor is templated to enable perfect forwarding + template <typename TT> + SimpleContent(QString keyName, TT&& value) + : value(std::forward<TT>(value)) + , key(std::move(keyName)) + {} + SimpleContent(const QJsonObject& json, QString keyName) + : value(fromJson<T>(json[keyName])) + , key(std::move(keyName)) + {} + QJsonObject toJson() const { - public: - using value_type = T; - - // The constructor is templated to enable perfect forwarding - template <typename TT> - SimpleContent(QString keyName, TT&& value) - : value(std::forward<TT>(value)), key(std::move(keyName)) - { - } - SimpleContent(const QJsonObject& json, QString keyName) - : value(fromJson<T>(json[keyName])), key(std::move(keyName)) - { - } - QJsonObject toJson() const - { - return { { key, QMatrixClient::toJson(value) } }; - } + return { { key, QMatrixClient::toJson(value) } }; + } - public: - T value; + public: + T value; - protected: - QString key; - }; - } // namespace EventContent + protected: + QString key; + }; +} // namespace EventContent #define DEFINE_SIMPLE_STATE_EVENT(_Name, _TypeId, _ValueType, _ContentKey) \ class _Name : public StateEvent<EventContent::SimpleContent<_ValueType>> \ { \ - public: \ + public: \ using value_type = content_type::value_type; \ DEFINE_EVENT_TYPEID(_TypeId, _Name) \ - explicit _Name() : _Name(value_type()) {} \ + explicit _Name() \ + : _Name(value_type()) \ + {} \ template <typename T> \ explicit _Name(T&& value) \ : StateEvent(typeId(), matrixTypeId(), \ QStringLiteral(#_ContentKey), std::forward<T>(value)) \ - { \ - } \ + {} \ explicit _Name(QJsonObject obj) \ : StateEvent(typeId(), std::move(obj), \ QStringLiteral(#_ContentKey)) \ - { \ - } \ + {} \ auto _ContentKey() const { return content().value; } \ }; \ REGISTER_EVENT_TYPE(_Name) \ // End of macro - DEFINE_SIMPLE_STATE_EVENT(RoomNameEvent, "m.room.name", QString, name) - DEFINE_EVENTTYPE_ALIAS(RoomName, RoomNameEvent) - DEFINE_SIMPLE_STATE_EVENT(RoomAliasesEvent, "m.room.aliases", QStringList, - aliases) - DEFINE_EVENTTYPE_ALIAS(RoomAliases, RoomAliasesEvent) - DEFINE_SIMPLE_STATE_EVENT(RoomCanonicalAliasEvent, "m.room.canonical_alias", - 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", QString, - algorithm) - DEFINE_EVENTTYPE_ALIAS(RoomEncryption, EncryptionEvent) +DEFINE_SIMPLE_STATE_EVENT(RoomNameEvent, "m.room.name", QString, name) +DEFINE_EVENTTYPE_ALIAS(RoomName, RoomNameEvent) +DEFINE_SIMPLE_STATE_EVENT(RoomAliasesEvent, "m.room.aliases", QStringList, + aliases) +DEFINE_EVENTTYPE_ALIAS(RoomAliases, RoomAliasesEvent) +DEFINE_SIMPLE_STATE_EVENT(RoomCanonicalAliasEvent, "m.room.canonical_alias", + 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", QString, + algorithm) +DEFINE_EVENTTYPE_ALIAS(RoomEncryption, EncryptionEvent) } // namespace QMatrixClient diff --git a/lib/events/stateevent.cpp b/lib/events/stateevent.cpp index f172edb6..7fea59a1 100644 --- a/lib/events/stateevent.cpp +++ b/lib/events/stateevent.cpp @@ -24,10 +24,9 @@ using namespace QMatrixClient; // StateEventBase itself can be instantiated if there's a state_key JSON key // but the event type is unknown. [[gnu::unused]] static auto stateEventTypeInitialised = - RoomEvent::factory_t::addMethod([](const QJsonObject& json, - const QString& matrixType) - -> StateEventPtr { - if (!json.contains("state_key")) + RoomEvent::factory_t::addMethod( + [](const QJsonObject& json, const QString& matrixType) -> StateEventPtr { + if (!json.contains("state_key"_ls)) return nullptr; if (auto e = StateEventBase::factory_t::make(json, matrixType)) @@ -53,7 +52,7 @@ void StateEventBase::dumpTo(QDebug dbg) const dbg << '<' << stateKey() << "> "; if (unsignedJson().contains(PrevContentKeyL)) dbg << QJsonDocument(unsignedJson()[PrevContentKeyL].toObject()) - .toJson(QJsonDocument::Compact) + .toJson(QJsonDocument::Compact) << " -> "; RoomEvent::dumpTo(dbg); } diff --git a/lib/events/stateevent.h b/lib/events/stateevent.h index 1a02646b..8a89c86c 100644 --- a/lib/events/stateevent.h +++ b/lib/events/stateevent.h @@ -20,110 +20,111 @@ #include "roomevent.h" -namespace QMatrixClient { - class StateEventBase : public RoomEvent - { - public: - using factory_t = EventFactory<StateEventBase>; +namespace QMatrixClient +{ +class StateEventBase : public RoomEvent +{ +public: + using factory_t = EventFactory<StateEventBase>; - using RoomEvent::RoomEvent; - ~StateEventBase() override = default; + using RoomEvent::RoomEvent; + ~StateEventBase() override = default; - bool isStateEvent() const override { return true; } - QString replacedState() const; - void dumpTo(QDebug dbg) const override; + bool isStateEvent() const override { return true; } + QString replacedState() const; + void dumpTo(QDebug dbg) const override; - virtual bool repeatsState() const; - }; - using StateEventPtr = event_ptr_tt<StateEventBase>; - using StateEvents = EventsArray<StateEventBase>; + virtual bool repeatsState() const; +}; +using StateEventPtr = event_ptr_tt<StateEventBase>; +using StateEvents = EventsArray<StateEventBase>; - template <> inline bool is<StateEventBase>(const Event& e) - { - return e.isStateEvent(); - } +template <> +inline bool is<StateEventBase>(const Event& e) +{ + return e.isStateEvent(); +} + +/** + * A combination of event type and state key uniquely identifies a piece + * of state in Matrix. + * \sa + * https://matrix.org/docs/spec/client_server/unstable.html#types-of-room-events + */ +using StateEventKey = QPair<QString, QString>; - /** - * A combination of event type and state key uniquely identifies a piece - * of state in Matrix. - * \sa - * https://matrix.org/docs/spec/client_server/unstable.html#types-of-room-events - */ - using StateEventKey = QPair<QString, QString>; +template <typename ContentT> +struct Prev +{ + template <typename... ContentParamTs> + explicit Prev(const QJsonObject& unsignedJson, + ContentParamTs&&... contentParams) + : senderId(unsignedJson.value("prev_sender"_ls).toString()) + , content(unsignedJson.value(PrevContentKeyL).toObject(), + std::forward<ContentParamTs>(contentParams)...) + {} - template <typename ContentT> struct Prev { - template <typename... ContentParamTs> - explicit Prev(const QJsonObject& unsignedJson, - ContentParamTs&&... contentParams) - : senderId(unsignedJson.value("prev_sender"_ls).toString()), - content(unsignedJson.value(PrevContentKeyL).toObject(), - std::forward<ContentParamTs>(contentParams)...) - { - } + QString senderId; + ContentT content; +}; - QString senderId; - ContentT content; - }; +template <typename ContentT> +class StateEvent : public StateEventBase +{ +public: + using content_type = ContentT; - template <typename ContentT> class StateEvent : public StateEventBase + template <typename... ContentParamTs> + explicit StateEvent(Type type, const QJsonObject& fullJson, + ContentParamTs&&... contentParams) + : StateEventBase(type, fullJson) + , _content(contentJson(), std::forward<ContentParamTs>(contentParams)...) { - public: - using content_type = ContentT; - - template <typename... ContentParamTs> - explicit StateEvent(Type type, const QJsonObject& fullJson, - ContentParamTs&&... contentParams) - : StateEventBase(type, fullJson), - _content(contentJson(), - std::forward<ContentParamTs>(contentParams)...) - { - const auto& unsignedData = unsignedJson(); - if (unsignedData.contains(PrevContentKeyL)) - _prev = std::make_unique<Prev<ContentT>>( - unsignedData, - std::forward<ContentParamTs>(contentParams)...); - } - template <typename... ContentParamTs> - explicit StateEvent(Type type, event_mtype_t matrixType, - ContentParamTs&&... contentParams) - : StateEventBase(type, matrixType), - _content(std::forward<ContentParamTs>(contentParams)...) - { - editJson().insert(ContentKey, _content.toJson()); - } + const auto& unsignedData = unsignedJson(); + if (unsignedData.contains(PrevContentKeyL)) + _prev = std::make_unique<Prev<ContentT>>( + unsignedData, std::forward<ContentParamTs>(contentParams)...); + } + template <typename... ContentParamTs> + explicit StateEvent(Type type, event_mtype_t matrixType, + ContentParamTs&&... contentParams) + : StateEventBase(type, matrixType) + , _content(std::forward<ContentParamTs>(contentParams)...) + { + editJson().insert(ContentKey, _content.toJson()); + } - const ContentT& content() const { return _content; } - template <typename VisitorT> void editContent(VisitorT&& visitor) - { - visitor(_content); - editJson()[ContentKeyL] = _content.toJson(); - } - [[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(); - } + const ContentT& content() const { return _content; } + template <typename VisitorT> + void editContent(VisitorT&& visitor) + { + visitor(_content); + editJson()[ContentKeyL] = _content.toJson(); + } + [[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(); } - private: - ContentT _content; - std::unique_ptr<Prev<ContentT>> _prev; - }; +private: + ContentT _content; + std::unique_ptr<Prev<ContentT>> _prev; +}; } // namespace QMatrixClient -namespace std { - template <> struct hash<QMatrixClient::StateEventKey> { - size_t - operator()(const QMatrixClient::StateEventKey& k) const Q_DECL_NOEXCEPT - { - return qHash(k); - } - }; -} +namespace std +{ +template <> +struct hash<QMatrixClient::StateEventKey> +{ + size_t operator()(const QMatrixClient::StateEventKey& k) const Q_DECL_NOEXCEPT + { + return qHash(k); + } +}; +} // namespace std diff --git a/lib/events/typingevent.cpp b/lib/events/typingevent.cpp index ee3d6b67..128a206a 100644 --- a/lib/events/typingevent.cpp +++ b/lib/events/typingevent.cpp @@ -22,7 +22,8 @@ using namespace QMatrixClient; -TypingEvent::TypingEvent(const QJsonObject& obj) : Event(typeId(), obj) +TypingEvent::TypingEvent(const QJsonObject& obj) + : Event(typeId(), obj) { const auto& array = contentJson()["user_ids"_ls].toArray(); for (const auto& user : array) diff --git a/lib/events/typingevent.h b/lib/events/typingevent.h index f66a1fbc..241359b4 100644 --- a/lib/events/typingevent.h +++ b/lib/events/typingevent.h @@ -20,19 +20,20 @@ #include "event.h" -namespace QMatrixClient { - class TypingEvent : public Event - { - public: - DEFINE_EVENT_TYPEID("m.typing", TypingEvent) +namespace QMatrixClient +{ +class TypingEvent : public Event +{ +public: + DEFINE_EVENT_TYPEID("m.typing", TypingEvent) - TypingEvent(const QJsonObject& obj); + TypingEvent(const QJsonObject& obj); - const QStringList& users() const { return _users; } + const QStringList& users() const { return _users; } - private: - QStringList _users; - }; - REGISTER_EVENT_TYPE(TypingEvent) - DEFINE_EVENTTYPE_ALIAS(Typing, TypingEvent) +private: + QStringList _users; +}; +REGISTER_EVENT_TYPE(TypingEvent) +DEFINE_EVENTTYPE_ALIAS(Typing, TypingEvent) } // namespace QMatrixClient |