aboutsummaryrefslogtreecommitdiff
path: root/lib/events
diff options
context:
space:
mode:
Diffstat (limited to 'lib/events')
-rw-r--r--lib/events/accountdataevents.h15
-rw-r--r--lib/events/callanswerevent.h4
-rw-r--r--lib/events/callcandidatesevent.cpp27
-rw-r--r--lib/events/callcandidatesevent.h6
-rw-r--r--lib/events/callhangupevent.cpp36
-rw-r--r--lib/events/callhangupevent.h8
-rw-r--r--lib/events/callinviteevent.h4
-rw-r--r--lib/events/encryptedevent.h14
-rw-r--r--lib/events/encryptedfile.h88
-rw-r--r--lib/events/encryptionevent.cpp8
-rw-r--r--lib/events/encryptionevent.h5
-rw-r--r--lib/events/event.cpp3
-rw-r--r--lib/events/event.h298
-rw-r--r--lib/events/eventcontent.cpp63
-rw-r--r--lib/events/eventcontent.h52
-rw-r--r--lib/events/eventloader.h18
-rw-r--r--lib/events/reactionevent.h2
-rw-r--r--lib/events/receiptevent.cpp29
-rw-r--r--lib/events/receiptevent.h5
-rw-r--r--lib/events/redactionevent.h2
-rw-r--r--lib/events/roomavatarevent.h4
-rw-r--r--lib/events/roomcreateevent.cpp27
-rw-r--r--lib/events/roomcreateevent.h2
-rw-r--r--lib/events/roomevent.cpp13
-rw-r--r--lib/events/roomevent.h52
-rw-r--r--lib/events/roomkeyevent.h8
-rw-r--r--lib/events/roommemberevent.cpp77
-rw-r--r--lib/events/roommemberevent.h58
-rw-r--r--lib/events/roommessageevent.cpp18
-rw-r--r--lib/events/roommessageevent.h25
-rw-r--r--lib/events/roomtombstoneevent.cpp4
-rw-r--r--lib/events/simplestateevents.h19
-rw-r--r--lib/events/stateevent.cpp31
-rw-r--r--lib/events/stateevent.h27
-rw-r--r--lib/events/stickerevent.cpp2
-rw-r--r--lib/events/typingevent.cpp2
36 files changed, 596 insertions, 460 deletions
diff --git a/lib/events/accountdataevents.h b/lib/events/accountdataevents.h
index 8cea0ec8..9cf77be3 100644
--- a/lib/events/accountdataevents.h
+++ b/lib/events/accountdataevents.h
@@ -4,7 +4,6 @@
#pragma once
#include "event.h"
-#include "eventcontent.h"
namespace Quotient {
constexpr const char* FavouriteTag = "m.favourite";
@@ -16,12 +15,12 @@ struct TagRecord {
order_type order;
- TagRecord(order_type order = none) : order(std::move(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,
- // against optional<>::operator<() convention.
+ // against std::optional<>::operator<() convention.
return order && (!other.order || *order < *other.order);
}
};
@@ -55,15 +54,15 @@ using TagsMap = QHash<QString, TagRecord>;
public: \
using content_type = _ContentType; \
DEFINE_EVENT_TYPEID(_TypeId, _Name) \
- explicit _Name(QJsonObject obj) : Event(typeId(), std::move(obj)) {} \
- explicit _Name(_ContentType content) \
+ explicit _Name(const QJsonObject& obj) : Event(typeId(), obj) {} \
+ explicit _Name(const content_type& content) \
: Event(typeId(), matrixTypeId(), \
- QJsonObject { { QStringLiteral(#_ContentKey), \
- toJson(std::move(content)) } }) \
+ QJsonObject { \
+ { QStringLiteral(#_ContentKey), toJson(content) } }) \
{} \
auto _ContentKey() const \
{ \
- return content<content_type>(#_ContentKey##_ls); \
+ return contentPart<content_type>(#_ContentKey##_ls); \
} \
}; \
REGISTER_EVENT_TYPE(_Name) \
diff --git a/lib/events/callanswerevent.h b/lib/events/callanswerevent.h
index 6132cb44..4c01c941 100644
--- a/lib/events/callanswerevent.h
+++ b/lib/events/callanswerevent.h
@@ -19,11 +19,11 @@ public:
int lifetime() const
{
- return content<int>("lifetime"_ls);
+ return contentPart<int>("lifetime"_ls);
} // FIXME: Omittable<>?
QString sdp() const
{
- return contentJson()["answer"_ls].toObject().value("sdp"_ls).toString();
+ return contentPart<QJsonObject>("answer"_ls).value("sdp"_ls).toString();
}
};
diff --git a/lib/events/callcandidatesevent.cpp b/lib/events/callcandidatesevent.cpp
deleted file mode 100644
index b87c8e9b..00000000
--- a/lib/events/callcandidatesevent.cpp
+++ /dev/null
@@ -1,27 +0,0 @@
-// SPDX-FileCopyrightText: 2017 Marius Gripsgard <marius@ubports.com>
-// SPDX-FileCopyrightText: 2018 Josip Delic <delijati@googlemail.com>
-// SPDX-License-Identifier: LGPL-2.1-or-later
-
-#include "callcandidatesevent.h"
-
-/*
-m.call.candidates
-{
- "age": 242352,
- "content": {
- "call_id": "12345",
- "candidates": [
- {
- "candidate": "candidate:863018703 1 udp 2122260223 10.9.64.156
-43670 typ host generation 0", "sdpMLineIndex": 0, "sdpMid": "audio"
- }
- ],
- "version": 0
- },
- "event_id": "$WLGTSEFSEF:localhost",
- "origin_server_ts": 1431961217939,
- "room_id": "!Cuyf34gef24t:localhost",
- "sender": "@example:localhost",
- "type": "m.call.candidates"
-}
-*/
diff --git a/lib/events/callcandidatesevent.h b/lib/events/callcandidatesevent.h
index c2ccac3b..74c38f2c 100644
--- a/lib/events/callcandidatesevent.h
+++ b/lib/events/callcandidatesevent.h
@@ -25,17 +25,17 @@ public:
QJsonArray candidates() const
{
- return content<QJsonArray>("candidates"_ls);
+ return contentPart<QJsonArray>("candidates"_ls);
}
QString callId() const
{
- return content<QString>("call_id");
+ return contentPart<QString>("call_id");
}
int version() const
{
- return content<int>("version");
+ return contentPart<int>("version");
}
};
diff --git a/lib/events/callhangupevent.cpp b/lib/events/callhangupevent.cpp
deleted file mode 100644
index 43bc4db0..00000000
--- a/lib/events/callhangupevent.cpp
+++ /dev/null
@@ -1,36 +0,0 @@
-/******************************************************************************
- * SPDX-FileCopyrightText: 2017 Marius Gripsgard <marius@ubports.com>
- * SPDX-FileCopyrightText: 2018 Josip Delic <delijati@googlemail.com>
- *
- * SPDX-License-Identifier: LGPL-2.1-or-later
- */
-
-#include "callhangupevent.h"
-
-/*
-m.call.hangup
-{
- "age": 242352,
- "content": {
- "call_id": "12345",
- "version": 0
- },
- "event_id": "$WLGTSEFSEF:localhost",
- "origin_server_ts": 1431961217939,
- "room_id": "!Cuyf34gef24t:localhost",
- "sender": "@example:localhost",
- "type": "m.call.hangup"
-}
-*/
-
-using namespace Quotient;
-
-CallHangupEvent::CallHangupEvent(const QJsonObject& obj)
- : CallEventBase(typeId(), obj)
-{
- qCDebug(EVENTS) << "Call Hangup event";
-}
-
-CallHangupEvent::CallHangupEvent(const QString& callId)
- : CallEventBase(typeId(), matrixTypeId(), callId, 0)
-{}
diff --git a/lib/events/callhangupevent.h b/lib/events/callhangupevent.h
index 24382ac2..f3f82833 100644
--- a/lib/events/callhangupevent.h
+++ b/lib/events/callhangupevent.h
@@ -11,8 +11,12 @@ 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)
+ : CallEventBase(typeId(), obj)
+ {}
+ explicit CallHangupEvent(const QString& callId)
+ : CallEventBase(typeId(), matrixTypeId(), callId, 0)
+ {}
};
REGISTER_EVENT_TYPE(CallHangupEvent)
diff --git a/lib/events/callinviteevent.h b/lib/events/callinviteevent.h
index d3454c4f..80b7d651 100644
--- a/lib/events/callinviteevent.h
+++ b/lib/events/callinviteevent.h
@@ -18,11 +18,11 @@ public:
int lifetime() const
{
- return content<int>("lifetime"_ls);
+ return contentPart<int>("lifetime"_ls);
} // FIXME: Omittable<>?
QString sdp() const
{
- return contentJson()["offer"_ls].toObject().value("sdp"_ls).toString();
+ return contentPart<QJsonObject>("offer"_ls).value("sdp"_ls).toString();
}
};
diff --git a/lib/events/encryptedevent.h b/lib/events/encryptedevent.h
index eb7123eb..de89a7c6 100644
--- a/lib/events/encryptedevent.h
+++ b/lib/events/encryptedevent.h
@@ -7,7 +7,6 @@
#include "roomevent.h"
namespace Quotient {
-class Room;
/*
* While the specification states:
*
@@ -27,7 +26,6 @@ class Room;
* one and doesn't add new restrictions, just provides additional features.
*/
class EncryptedEvent : public RoomEvent {
- Q_GADGET
public:
DEFINE_EVENT_TYPEID("m.room.encrypted", EncryptedEvent)
@@ -43,7 +41,7 @@ public:
QString algorithm() const
{
- QString algo = content<QString>(AlgorithmKeyL);
+ QString algo = contentPart<QString>(AlgorithmKeyL);
if (!SupportedAlgorithms.contains(algo)) {
qWarning(MAIN) << "The EncryptedEvent's algorithm" << algo
<< "is not supported";
@@ -52,17 +50,17 @@ public:
}
QByteArray ciphertext() const
{
- return content<QString>(CiphertextKeyL).toLatin1();
+ return contentPart<QString>(CiphertextKeyL).toLatin1();
}
QJsonObject ciphertext(const QString& identityKey) const
{
- return content<QJsonObject>(CiphertextKeyL).value(identityKey).toObject();
+ return contentPart<QJsonObject>(CiphertextKeyL).value(identityKey).toObject();
}
- QString senderKey() const { return content<QString>(SenderKeyKeyL); }
+ QString senderKey() const { return contentPart<QString>(SenderKeyKeyL); }
/* device_id and session_id are required with Megolm */
- QString deviceId() const { return content<QString>(DeviceIdKeyL); }
- QString sessionId() const { return content<QString>(SessionIdKeyL); }
+ QString deviceId() const { return contentPart<QString>(DeviceIdKeyL); }
+ QString sessionId() const { return contentPart<QString>(SessionIdKeyL); }
};
REGISTER_EVENT_TYPE(EncryptedEvent)
diff --git a/lib/events/encryptedfile.h b/lib/events/encryptedfile.h
new file mode 100644
index 00000000..24ac9de1
--- /dev/null
+++ b/lib/events/encryptedfile.h
@@ -0,0 +1,88 @@
+// SPDX-FileCopyrightText: 2021 Carl Schwan <carlschwan@kde.org>
+//
+// SPDX-License-Identifier: LGPl-2.1-or-later
+
+#pragma once
+
+#include "converters.h"
+
+namespace Quotient {
+/**
+ * JSON Web Key object as specified in
+ * https://spec.matrix.org/unstable/client-server-api/#extensions-to-mroommessage-msgtypes
+ * The only currently relevant member is `k`, the rest needs to be set to the defaults specified in the spec.
+ */
+struct JWK
+{
+ Q_GADGET
+ Q_PROPERTY(QString kty MEMBER kty CONSTANT)
+ Q_PROPERTY(QStringList keyOps MEMBER keyOps CONSTANT)
+ Q_PROPERTY(QString alg MEMBER alg CONSTANT)
+ Q_PROPERTY(QString k MEMBER k CONSTANT)
+ Q_PROPERTY(bool ext MEMBER ext CONSTANT)
+
+public:
+ QString kty;
+ QStringList keyOps;
+ QString alg;
+ QString k;
+ bool ext;
+};
+
+struct EncryptedFile
+{
+ Q_GADGET
+ Q_PROPERTY(QUrl url MEMBER url CONSTANT)
+ Q_PROPERTY(JWK key MEMBER key CONSTANT)
+ Q_PROPERTY(QString iv MEMBER iv CONSTANT)
+ Q_PROPERTY(QHash<QString, QString> hashes MEMBER hashes CONSTANT)
+ Q_PROPERTY(QString v MEMBER v CONSTANT)
+
+public:
+ QUrl url;
+ JWK key;
+ QString iv;
+ QHash<QString, QString> hashes;
+ QString v;
+};
+
+template <>
+struct JsonObjectConverter<EncryptedFile> {
+ static void dumpTo(QJsonObject& jo, const EncryptedFile& pod)
+ {
+ addParam<>(jo, QStringLiteral("url"), pod.url);
+ addParam<>(jo, QStringLiteral("key"), pod.key);
+ addParam<>(jo, QStringLiteral("iv"), pod.iv);
+ addParam<>(jo, QStringLiteral("hashes"), pod.hashes);
+ addParam<>(jo, QStringLiteral("v"), pod.v);
+ }
+ static void fillFrom(const QJsonObject& jo, EncryptedFile& pod)
+ {
+ fromJson(jo.value("url"_ls), pod.url);
+ fromJson(jo.value("key"_ls), pod.key);
+ fromJson(jo.value("iv"_ls), pod.iv);
+ fromJson(jo.value("hashes"_ls), pod.hashes);
+ fromJson(jo.value("v"_ls), pod.v);
+ }
+};
+
+template <>
+struct JsonObjectConverter<JWK> {
+ static void dumpTo(QJsonObject& jo, const JWK& pod)
+ {
+ addParam<>(jo, QStringLiteral("kty"), pod.kty);
+ addParam<>(jo, QStringLiteral("key_ops"), pod.keyOps);
+ addParam<>(jo, QStringLiteral("alg"), pod.alg);
+ addParam<>(jo, QStringLiteral("k"), pod.k);
+ addParam<>(jo, QStringLiteral("ext"), pod.ext);
+ }
+ static void fillFrom(const QJsonObject& jo, JWK& pod)
+ {
+ fromJson(jo.value("kty"_ls), pod.kty);
+ fromJson(jo.value("key_ops"_ls), pod.keyOps);
+ fromJson(jo.value("alg"_ls), pod.alg);
+ fromJson(jo.value("k"_ls), pod.k);
+ fromJson(jo.value("ext"_ls), pod.ext);
+ }
+};
+} // namespace Quotient
diff --git a/lib/events/encryptionevent.cpp b/lib/events/encryptionevent.cpp
index 490a5e8a..aa05a96e 100644
--- a/lib/events/encryptionevent.cpp
+++ b/lib/events/encryptionevent.cpp
@@ -39,6 +39,14 @@ EncryptionEventContent::EncryptionEventContent(const QJsonObject& json)
, rotationPeriodMsgs(json[RotationPeriodMsgsKeyL].toInt(100))
{}
+EncryptionEventContent::EncryptionEventContent(EncryptionType et)
+ : encryption(et)
+{
+ if(encryption != Undefined) {
+ algorithm = encryptionStrings[encryption];
+ }
+}
+
void EncryptionEventContent::fillJson(QJsonObject* o) const
{
Q_ASSERT(o);
diff --git a/lib/events/encryptionevent.h b/lib/events/encryptionevent.h
index f9bbab12..14439fcc 100644
--- a/lib/events/encryptionevent.h
+++ b/lib/events/encryptionevent.h
@@ -12,9 +12,7 @@ class EncryptionEventContent : public EventContent::Base {
public:
enum EncryptionType : size_t { MegolmV1AesSha2 = 0, Undefined };
- explicit EncryptionEventContent(EncryptionType et = Undefined)
- : encryption(et)
- {}
+ explicit EncryptionEventContent(EncryptionType et = Undefined);
explicit EncryptionEventContent(const QJsonObject& json);
EncryptionType encryption;
@@ -40,6 +38,7 @@ public:
// default value
: StateEvent(typeId(), obj)
{}
+ EncryptionEvent(EncryptionEvent&&) = delete;
template <typename... ArgTs>
EncryptionEvent(ArgTs&&... contentArgs)
: StateEvent(typeId(), matrixTypeId(), QString(),
diff --git a/lib/events/event.cpp b/lib/events/event.cpp
index 3d66ab55..96be717c 100644
--- a/lib/events/event.cpp
+++ b/lib/events/event.cpp
@@ -46,14 +46,11 @@ QString Event::matrixType() const { return fullJson()[TypeKeyL].toString(); }
QByteArray Event::originalJson() const { return QJsonDocument(_json).toJson(); }
-// On const below: this is to catch accidental attempts to change event JSON
-// NOLINTNEXTLINE(readability-const-return-type)
const QJsonObject Event::contentJson() const
{
return fullJson()[ContentKeyL].toObject();
}
-// NOLINTNEXTLINE(readability-const-return-type)
const QJsonObject Event::unsignedJson() const
{
return fullJson()[UnsignedKeyL].toObject();
diff --git a/lib/events/event.h b/lib/events/event.h
index f8f8311d..8f62872d 100644
--- a/lib/events/event.h
+++ b/lib/events/event.h
@@ -5,6 +5,7 @@
#include "converters.h"
#include "logging.h"
+#include "function_traits.h"
namespace Quotient {
// === event_ptr_tt<> and type casting facilities ===
@@ -28,31 +29,30 @@ inline TargetEventT* weakPtrCast(const event_ptr_tt<EventT>& ptr)
// === Standard Matrix key names and basicEventJson() ===
-static const auto TypeKey = QStringLiteral("type");
-static const auto BodyKey = QStringLiteral("body");
-static const auto ContentKey = QStringLiteral("content");
-static const auto EventIdKey = QStringLiteral("event_id");
-static const auto SenderKey = QStringLiteral("sender");
-static const auto RoomIdKey = QStringLiteral("room_id");
-static const auto UnsignedKey = QStringLiteral("unsigned");
-static const auto StateKeyKey = QStringLiteral("state_key");
-static const auto TypeKeyL = "type"_ls;
-static const auto BodyKeyL = "body"_ls;
-static const auto ContentKeyL = "content"_ls;
-static const auto EventIdKeyL = "event_id"_ls;
-static const auto SenderKeyL = "sender"_ls;
-static const auto RoomIdKeyL = "room_id"_ls;
-static const auto UnsignedKeyL = "unsigned"_ls;
-static const auto RedactedCauseKeyL = "redacted_because"_ls;
-static const auto PrevContentKeyL = "prev_content"_ls;
-static const auto StateKeyKeyL = "state_key"_ls;
+constexpr auto TypeKeyL = "type"_ls;
+constexpr auto BodyKeyL = "body"_ls;
+constexpr auto ContentKeyL = "content"_ls;
+constexpr auto EventIdKeyL = "event_id"_ls;
+constexpr auto SenderKeyL = "sender"_ls;
+constexpr auto RoomIdKeyL = "room_id"_ls;
+constexpr auto UnsignedKeyL = "unsigned"_ls;
+constexpr auto RedactedCauseKeyL = "redacted_because"_ls;
+constexpr auto PrevContentKeyL = "prev_content"_ls;
+constexpr auto StateKeyKeyL = "state_key"_ls;
+const QString TypeKey { TypeKeyL };
+const QString BodyKey { BodyKeyL };
+const QString ContentKey { ContentKeyL };
+const QString EventIdKey { EventIdKeyL };
+const QString SenderKey { SenderKeyL };
+const QString RoomIdKey { RoomIdKeyL };
+const QString UnsignedKey { UnsignedKeyL };
+const QString StateKeyKey { StateKeyKeyL };
/// Make a minimal correct Matrix event JSON
-template <typename StrT>
-inline QJsonObject basicEventJson(StrT matrixType, const QJsonObject& content)
+inline QJsonObject basicEventJson(const QString& matrixType,
+ const QJsonObject& content)
{
- return { { TypeKey, std::forward<StrT>(matrixType) },
- { ContentKey, content } };
+ return { { TypeKey, matrixType }, { ContentKey, content } };
}
// === Event types and event types registry ===
@@ -76,8 +76,7 @@ public:
private:
EventTypeRegistry() = default;
- Q_DISABLE_COPY(EventTypeRegistry)
- DISABLE_MOVE(EventTypeRegistry)
+ Q_DISABLE_COPY_MOVE(EventTypeRegistry)
static EventTypeRegistry& get()
{
@@ -111,97 +110,90 @@ inline event_type_t typeId()
inline event_type_t unknownEventTypeId() { return typeId<void>(); }
-// === EventFactory ===
+// === Event creation facilities ===
-/** Create an event of arbitrary type from its arguments */
+//! 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()
+namespace _impl {
+ template <class EventT, class BaseEventT>
+ event_ptr_tt<BaseEventT> makeIfMatches(const QJsonObject& json,
+ const QString& matrixType)
{
- using inner_factory_tt = std::function<event_ptr_tt<BaseEventT>(
- const QJsonObject&, const QString&)>;
- static std::vector<inner_factory_tt> _factories {};
- return _factories;
+ return QLatin1String(EventT::matrixTypeId()) == matrixType
+ ? makeEvent<EventT>(json)
+ : nullptr;
}
-};
-/** 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
-}
+ //! \brief A family of event factories to create events from CS API responses
+ //!
+ //! Each of these factories, as instantiated by event base types (Event,
+ //! RoomEvent etc.) is capable of producing an event object derived from
+ //! \p BaseEventT, using the JSON payload and the event type passed to its
+ //! make() method. Don't use these directly to make events; use loadEvent()
+ //! overloads as the frontend for these. Never instantiate new factories
+ //! outside of base event classes.
+ //! \sa loadEvent, setupFactory, Event::factory, RoomEvent::factory,
+ //! StateEventBase::factory
+ template <typename BaseEventT>
+ class EventFactory
+ : private std::vector<event_ptr_tt<BaseEventT> (*)(const QJsonObject&,
+ const QString&)> {
+ // Actual makeIfMatches specialisations will differ in the first
+ // template parameter but that doesn't affect the function type
+ public:
+ explicit EventFactory(const char* name = "")
+ : name(name)
+ {
+ static auto yetToBeConstructed = true;
+ Q_ASSERT(yetToBeConstructed);
+ if (!yetToBeConstructed) // For Release builds that pass Q_ASSERT
+ qCritical(EVENTS)
+ << "Another EventFactory for the same base type is being "
+ "created - event creation logic will be splintered";
+ yetToBeConstructed = false;
+ }
+ EventFactory(const EventFactory&) = delete;
+
+ //! \brief Add a method to create events of a given type
+ //!
+ //! Adds a standard factory method (makeIfMatches) for \p EventT so that
+ //! event objects of this type can be created dynamically by loadEvent.
+ //! The caller is responsible for ensuring this method is called only
+ //! once per type.
+ //! \sa makeIfMatches, loadEvent, Quotient::loadEvent
+ template <class EventT>
+ bool addMethod()
+ {
+ this->emplace_back(&makeIfMatches<EventT, BaseEventT>);
+ qDebug(EVENTS) << "Added factory method for"
+ << EventT::matrixTypeId() << "events;" << this->size()
+ << "methods in the" << name << "chain by now";
+ return true;
+ }
+
+ auto loadEvent(const QJsonObject& json, const QString& matrixType)
+ {
+ for (const auto& f : *this)
+ if (auto e = f(json, matrixType))
+ return e;
+ return makeEvent<BaseEventT>(unknownEventTypeId(), json);
+ }
+
+ const char* const name;
+ };
+} // namespace _impl
// === 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>;
+ static inline _impl::EventFactory<Event> factory { "Event" };
explicit Event(Type type, const QJsonObject& json);
explicit Event(Type type, event_mtype_t matrixType,
@@ -213,7 +205,10 @@ public:
Type type() const { return _type; }
QString matrixType() const;
+ [[deprecated("Use fullJson() and stringify it with QJsonDocument::toJson() "
+ "or by other means")]]
QByteArray originalJson() const;
+ [[deprecated("Use fullJson() instead")]] //
QJsonObject originalJsonObject() const { return fullJson(); }
const QJsonObject& fullJson() const { return _json; }
@@ -222,19 +217,30 @@ public:
// a "content" object; but since its structure is different for
// different types, we're implementing it per-event type.
+ // NB: const return types below are meant to catch accidental attempts
+ // to change event JSON (e.g., consider contentJson()["inexistentKey"]).
+
const QJsonObject contentJson() const;
- const QJsonObject unsignedJson() const;
+
+ template <typename T = QJsonValue, typename KeyT>
+ const T contentPart(KeyT&& key) const
+ {
+ return fromJson<T>(contentJson()[std::forward<KeyT>(key)]);
+ }
template <typename T>
+ [[deprecated("Use contentPart() to get a part of the event content")]] //
T content(const QString& key) const
{
- return fromJson<T>(contentJson()[key]);
+ return contentPart<T>(key);
}
- template <typename T>
- T content(QLatin1String key) const
+ const QJsonObject unsignedJson() const;
+
+ template <typename T = QJsonValue, typename KeyT>
+ const T unsignedPart(KeyT&& key) const
{
- return fromJson<T>(contentJson()[key]);
+ return fromJson<T>(unsignedJson()[std::forward<KeyT>(key)]);
}
friend QDebug operator<<(QDebug dbg, const Event& e)
@@ -262,7 +268,7 @@ template <typename EventT>
using EventsArray = std::vector<event_ptr_tt<EventT>>;
using Events = EventsArray<Event>;
-// === Macros used with event class definitions ===
+// === Facilities for event class definitions ===
// This macro should be used in a public section of an event class to
// provide matrixTypeId() and typeId().
@@ -274,14 +280,28 @@ using Events = EventsArray<Event>;
// 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 { \
- [[maybe_unused]] static const auto _factoryAdded##_Type = \
- registerEventType<_Type>(); \
- } \
+#define REGISTER_EVENT_TYPE(_Type) \
+ [[maybe_unused]] inline const auto _factoryAdded##_Type = \
+ _Type::factory.addMethod<_Type>(); \
// End of macro
-// === is<>(), eventCast<>() and visit<>() ===
+// === Event loading ===
+// (see also event_loader.h)
+
+//! \brief Point of customisation to dynamically load events
+//!
+//! The default specialisation of this calls BaseEventT::factory and if that
+//! fails (i.e. returns nullptr) creates an unknown event of BaseEventT.
+//! Other specialisations may reuse other factories, add validations common to
+//! BaseEventT, and so on
+template <class BaseEventT>
+event_ptr_tt<BaseEventT> doLoadEvent(const QJsonObject& json,
+ const QString& matrixType)
+{
+ return BaseEventT::factory.loadEvent(json, matrixType);
+}
+
+// === is<>(), eventCast<>() and switchOnType<>() ===
template <class EventT>
inline bool is(const Event& e)
@@ -303,12 +323,12 @@ inline auto eventCast(const BasePtrT& eptr)
: nullptr;
}
-// A single generic catch-all visitor
+// A trivial generic catch-all "switch"
template <class BaseEventT, typename FnT>
-inline auto visit(const BaseEventT& event, FnT&& visitor)
- -> decltype(visitor(event))
+inline auto switchOnType(const BaseEventT& event, FnT&& fn)
+ -> decltype(fn(event))
{
- return visitor(event);
+ return fn(event);
}
namespace _impl {
@@ -319,52 +339,60 @@ namespace _impl {
&& !std::is_same_v<BaseT, std::decay_t<fn_arg_t<FnT>>>;
}
-// A single type-specific void visitor
+// A trivial type-specific "switch" for a void function
template <class BaseT, typename FnT>
-inline auto visit(const BaseT& event, FnT&& visitor)
+inline auto switchOnType(const BaseT& event, FnT&& fn)
-> std::enable_if_t<_impl::needs_downcast<BaseT, FnT>
&& std::is_void_v<fn_return_t<FnT>>>
{
using event_type = fn_arg_t<FnT>;
if (is<std::decay_t<event_type>>(event))
- visitor(static_cast<event_type>(event));
+ fn(static_cast<event_type>(event));
}
-// A single type-specific non-void visitor with an optional default value
-// non-voidness is guarded by defaultValue type
+// A trivial type-specific "switch" for non-void functions with an optional
+// default value; non-voidness is guarded by defaultValue type
template <class BaseT, typename FnT>
-inline auto visit(const BaseT& event, FnT&& visitor,
- fn_return_t<FnT>&& defaultValue = {})
+inline auto switchOnType(const BaseT& event, FnT&& fn,
+ fn_return_t<FnT>&& defaultValue = {})
-> std::enable_if_t<_impl::needs_downcast<BaseT, FnT>, fn_return_t<FnT>>
{
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);
+ return fn(static_cast<event_type>(event));
+ return std::move(defaultValue);
}
-// A chain of 2 or more visitors
+// A switch for a chain of 2 or more functions
template <class BaseT, typename FnT1, typename FnT2, typename... FnTs>
-inline std::common_type_t<fn_return_t<FnT1>, fn_return_t<FnT2>> visit(
- const BaseT& event, FnT1&& visitor1, FnT2&& visitor2,
- FnTs&&... visitors)
+inline std::common_type_t<fn_return_t<FnT1>, fn_return_t<FnT2>>
+switchOnType(const BaseT& event, FnT1&& fn1, FnT2&& fn2, FnTs&&... fns)
{
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)...);
+ return fn1(static_cast<event_type1&>(event));
+ return switchOnType(event, std::forward<FnT2>(fn2),
+ std::forward<FnTs>(fns)...);
+}
+
+template <class BaseT, typename... FnTs>
+[[deprecated("The new name for visit() is switchOnType()")]] //
+inline std::common_type_t<fn_return_t<FnTs>...>
+visit(const BaseT& event, FnTs&&... fns)
+{
+ return switchOnType(event, std::forward<FnTs>(fns)...);
}
-// A facility overload that calls void-returning visit() on each event
+ // A facility overload that calls void-returning switchOnType() on each event
// over a range of event pointers
+// TODO: replace with ranges::for_each once all standard libraries have it
template <typename RangeT, typename... FnTs>
-inline auto visitEach(RangeT&& events, FnTs&&... visitors)
+inline auto visitEach(RangeT&& events, FnTs&&... fns)
-> std::enable_if_t<std::is_void_v<
- decltype(visit(**begin(events), std::forward<FnTs>(visitors)...))>>
+ decltype(switchOnType(**begin(events), std::forward<FnTs>(fns)...))>>
{
for (auto&& evtPtr: events)
- visit(*evtPtr, std::forward<FnTs>(visitors)...);
+ switchOnType(*evtPtr, std::forward<FnTs>(fns)...);
}
} // namespace Quotient
Q_DECLARE_METATYPE(Quotient::Event*)
diff --git a/lib/events/eventcontent.cpp b/lib/events/eventcontent.cpp
index b249b160..22878d4c 100644
--- a/lib/events/eventcontent.cpp
+++ b/lib/events/eventcontent.cpp
@@ -5,10 +5,13 @@
#include "converters.h"
#include "util.h"
+#include "logging.h"
#include <QtCore/QMimeDatabase>
+#include <QtCore/QFileInfo>
using namespace Quotient::EventContent;
+using std::move;
QJsonObject Base::toJson() const
{
@@ -17,23 +20,44 @@ QJsonObject Base::toJson() const
return o;
}
-FileInfo::FileInfo(const QUrl& u, qint64 payloadSize, const QMimeType& mimeType,
- const QString& originalFilename)
+FileInfo::FileInfo(const QFileInfo &fi)
+ : mimeType(QMimeDatabase().mimeTypeForFile(fi))
+ , url(QUrl::fromLocalFile(fi.filePath()))
+ , payloadSize(fi.size())
+ , originalName(fi.fileName())
+{
+ Q_ASSERT(fi.isFile());
+}
+
+FileInfo::FileInfo(QUrl u, qint64 payloadSize, const QMimeType& mimeType,
+ Omittable<EncryptedFile> file, QString originalFilename)
: mimeType(mimeType)
- , url(u)
+ , url(move(u))
, payloadSize(payloadSize)
- , originalName(originalFilename)
-{}
+ , originalName(move(originalFilename))
+ , file(file)
+{
+ if (!isValid())
+ qCWarning(MESSAGES)
+ << "To client developers: using FileInfo(QUrl, qint64, ...) "
+ "constructor for non-mxc resources is deprecated since Quotient "
+ "0.7; for local resources, use FileInfo(QFileInfo) instead";
+}
-FileInfo::FileInfo(const QUrl& u, const QJsonObject& infoJson,
- const QString& originalFilename)
+FileInfo::FileInfo(QUrl mxcUrl, const QJsonObject& infoJson,
+ const Omittable<EncryptedFile> &file,
+ QString originalFilename)
: originalInfoJson(infoJson)
, mimeType(
QMimeDatabase().mimeTypeForName(infoJson["mimetype"_ls].toString()))
- , url(u)
+ , url(move(mxcUrl))
, payloadSize(fromJson<qint64>(infoJson["size"_ls]))
- , originalName(originalFilename)
+ , originalName(move(originalFilename))
+ , file(file)
{
+ if(url.isEmpty() && file.has_value()) {
+ url = file->url;
+ }
if (!mimeType.isValid())
mimeType = QMimeDatabase().mimeTypeForData(QByteArray());
}
@@ -53,14 +77,20 @@ void FileInfo::fillInfoJson(QJsonObject* infoJson) const
infoJson->insert(QStringLiteral("mimetype"), mimeType.name());
}
-ImageInfo::ImageInfo(const QUrl& u, qint64 fileSize, QMimeType mimeType,
- const QSize& imageSize, const QString& originalFilename)
- : FileInfo(u, fileSize, mimeType, originalFilename), imageSize(imageSize)
+ImageInfo::ImageInfo(const QFileInfo& fi, QSize imageSize)
+ : FileInfo(fi), imageSize(imageSize)
+{}
+
+ImageInfo::ImageInfo(const QUrl& mxcUrl, qint64 fileSize, const QMimeType& type,
+ QSize imageSize, const Omittable<EncryptedFile> &file, const QString& originalFilename)
+ : FileInfo(mxcUrl, fileSize, type, file, originalFilename)
+ , imageSize(imageSize)
{}
-ImageInfo::ImageInfo(const QUrl& u, const QJsonObject& infoJson,
+ImageInfo::ImageInfo(const QUrl& mxcUrl, const QJsonObject& infoJson,
+ const Omittable<EncryptedFile> &file,
const QString& originalFilename)
- : FileInfo(u, infoJson, originalFilename)
+ : FileInfo(mxcUrl, infoJson, file, originalFilename)
, imageSize(infoJson["w"_ls].toInt(), infoJson["h"_ls].toInt())
{}
@@ -73,9 +103,10 @@ void ImageInfo::fillInfoJson(QJsonObject* infoJson) const
infoJson->insert(QStringLiteral("h"), imageSize.height());
}
-Thumbnail::Thumbnail(const QJsonObject& infoJson)
+Thumbnail::Thumbnail(const QJsonObject& infoJson, const Omittable<EncryptedFile> &file)
: ImageInfo(QUrl(infoJson["thumbnail_url"_ls].toString()),
- infoJson["thumbnail_info"_ls].toObject())
+ infoJson["thumbnail_info"_ls].toObject(),
+ file)
{}
void Thumbnail::fillInfoJson(QJsonObject* infoJson) const
diff --git a/lib/events/eventcontent.h b/lib/events/eventcontent.h
index 60d1f7b7..f609a603 100644
--- a/lib/events/eventcontent.h
+++ b/lib/events/eventcontent.h
@@ -12,6 +12,10 @@
#include <QtCore/QUrl>
#include <QtCore/QMetaType>
+#include "encryptedfile.h"
+
+class QFileInfo;
+
namespace Quotient {
namespace EventContent {
/**
@@ -47,13 +51,14 @@ namespace EventContent {
// but specific aggregation structure is altered. See doc comments to
// each type for the list of available attributes.
- // A quick classes inheritance structure follows:
+ // A quick classes inheritance structure follows (the definitions are
+ // spread across eventcontent.h and roommessageevent.h):
// FileInfo
- // FileContent : UrlBasedContent<FileInfo, Thumbnail>
- // AudioContent : UrlBasedContent<FileInfo, Duration>
+ // FileContent : UrlWithThumbnailContent<FileInfo>
+ // AudioContent : PlayableContent<UrlBasedContent<FileInfo>>
// ImageInfo : FileInfo + imageSize attribute
- // ImageContent : UrlBasedContent<ImageInfo, Thumbnail>
- // VideoContent : UrlBasedContent<ImageInfo, Thumbnail, Duration>
+ // ImageContent : UrlWithThumbnailContent<ImageInfo>
+ // VideoContent : PlayableContent<UrlWithThumbnailContent<ImageInfo>>
/**
* A base/mixin class for structures representing an "info" object for
@@ -73,11 +78,15 @@ namespace EventContent {
*/
class FileInfo {
public:
- explicit FileInfo(const QUrl& u, qint64 payloadSize = -1,
+ FileInfo() = default;
+ explicit FileInfo(const QFileInfo& fi);
+ explicit FileInfo(QUrl mxcUrl, qint64 payloadSize = -1,
const QMimeType& mimeType = {},
- const QString& originalFilename = {});
- FileInfo(const QUrl& u, const QJsonObject& infoJson,
- const QString& originalFilename = {});
+ Omittable<EncryptedFile> file = none,
+ QString originalFilename = {});
+ FileInfo(QUrl mxcUrl, const QJsonObject& infoJson,
+ const Omittable<EncryptedFile> &file,
+ QString originalFilename = {});
bool isValid() const;
@@ -98,6 +107,7 @@ namespace EventContent {
QUrl url;
qint64 payloadSize;
QString originalName;
+ Omittable<EncryptedFile> file = none;
};
template <typename InfoT>
@@ -113,10 +123,14 @@ namespace EventContent {
*/
class ImageInfo : public FileInfo {
public:
- explicit ImageInfo(const QUrl& u, qint64 fileSize = -1,
- QMimeType mimeType = {}, const QSize& imageSize = {},
+ ImageInfo() = default;
+ explicit ImageInfo(const QFileInfo& fi, QSize imageSize = {});
+ explicit ImageInfo(const QUrl& mxcUrl, qint64 fileSize = -1,
+ const QMimeType& type = {}, QSize imageSize = {},
+ const Omittable<EncryptedFile> &file = none,
const QString& originalFilename = {});
- ImageInfo(const QUrl& u, const QJsonObject& infoJson,
+ ImageInfo(const QUrl& mxcUrl, const QJsonObject& infoJson,
+ const Omittable<EncryptedFile> &encryptedFile,
const QString& originalFilename = {});
void fillInfoJson(QJsonObject* infoJson) const;
@@ -134,8 +148,8 @@ namespace EventContent {
*/
class Thumbnail : public ImageInfo {
public:
- Thumbnail() : ImageInfo(QUrl()) {} // To allow empty thumbnails
- Thumbnail(const QJsonObject& infoJson);
+ Thumbnail() = default; // Allow empty thumbnails
+ Thumbnail(const QJsonObject& infoJson, const Omittable<EncryptedFile> &file = none);
Thumbnail(const ImageInfo& info) : ImageInfo(info) {}
using ImageInfo::ImageInfo;
@@ -148,13 +162,13 @@ namespace EventContent {
class TypedBase : public Base {
public:
- explicit TypedBase(QJsonObject o = {}) : Base(std::move(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; }
protected:
+ explicit TypedBase(QJsonObject o = {}) : Base(std::move(o)) {}
using Base::Base;
};
@@ -175,7 +189,7 @@ namespace EventContent {
explicit UrlBasedContent(const QJsonObject& json)
: TypedBase(json)
, InfoT(QUrl(json["url"].toString()), json["info"].toObject(),
- json["filename"].toString())
+ fromJson<Omittable<EncryptedFile>>(json["file"]), json["filename"].toString())
{
// A small hack to facilitate links creation in QML.
originalJson.insert("mediaId", InfoT::mediaId());
@@ -189,7 +203,11 @@ namespace EventContent {
void fillJson(QJsonObject* json) const override
{
Q_ASSERT(json);
- json->insert("url", InfoT::url.toString());
+ if (!InfoT::file.has_value()) {
+ json->insert("url", InfoT::url.toString());
+ } else {
+ json->insert("file", Quotient::toJson(*InfoT::file));
+ }
if (!InfoT::originalName.isEmpty())
json->insert("filename", InfoT::originalName);
json->insert("info", toInfoJson<InfoT>(*this));
diff --git a/lib/events/eventloader.h b/lib/events/eventloader.h
index 978668f2..fe624d70 100644
--- a/lib/events/eventloader.h
+++ b/lib/events/eventloader.h
@@ -6,16 +6,6 @@
#include "stateevent.h"
namespace Quotient {
-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);
- }
-} // namespace _impl
/*! Create an event with proper type from a JSON object
*
@@ -26,7 +16,7 @@ namespace _impl {
template <typename BaseEventT>
inline event_ptr_tt<BaseEventT> loadEvent(const QJsonObject& fullJson)
{
- return _impl::loadEvent<BaseEventT>(fullJson, fullJson[TypeKeyL].toString());
+ return doLoadEvent<BaseEventT>(fullJson, fullJson[TypeKeyL].toString());
}
/*! Create an event from a type string and content JSON
@@ -39,8 +29,8 @@ template <typename BaseEventT>
inline event_ptr_tt<BaseEventT> loadEvent(const QString& matrixType,
const QJsonObject& content)
{
- return _impl::loadEvent<BaseEventT>(basicEventJson(matrixType, content),
- matrixType);
+ return doLoadEvent<BaseEventT>(basicEventJson(matrixType, content),
+ matrixType);
}
/*! Create a state event from a type string, content JSON and state key
@@ -53,7 +43,7 @@ inline StateEventPtr loadStateEvent(const QString& matrixType,
const QJsonObject& content,
const QString& stateKey = {})
{
- return _impl::loadEvent<StateEventBase>(
+ return doLoadEvent<StateEventBase>(
basicStateEventJson(matrixType, content, stateKey), matrixType);
}
diff --git a/lib/events/reactionevent.h b/lib/events/reactionevent.h
index 777905f2..5a2b98c4 100644
--- a/lib/events/reactionevent.h
+++ b/lib/events/reactionevent.h
@@ -47,7 +47,7 @@ public:
explicit ReactionEvent(const QJsonObject& obj) : RoomEvent(typeId(), obj) {}
EventRelation relation() const
{
- return content<EventRelation>(QStringLiteral("m.relates_to"));
+ return contentPart<EventRelation>("m.relates_to"_ls);
}
};
REGISTER_EVENT_TYPE(ReactionEvent)
diff --git a/lib/events/receiptevent.cpp b/lib/events/receiptevent.cpp
index 4185d92d..72dbf2e3 100644
--- a/lib/events/receiptevent.cpp
+++ b/lib/events/receiptevent.cpp
@@ -25,6 +25,27 @@ Example of a Receipt Event:
using namespace Quotient;
+// The library loads the event-ids-to-receipts JSON map into a vector because
+// map lookups are not used and vectors are massively faster. Same goes for
+// de-/serialization of ReceiptsForEvent::receipts.
+// (XXX: would this be generally preferred across CS API JSON maps?..)
+QJsonObject toJson(const EventsWithReceipts& ewrs)
+{
+ QJsonObject json;
+ for (const auto& e : ewrs) {
+ QJsonObject receiptsJson;
+ for (const auto& r : e.receipts)
+ receiptsJson.insert(r.userId,
+ QJsonObject { { "ts"_ls, toJson(r.timestamp) } });
+ json.insert(e.evtId, QJsonObject { { "m.read"_ls, receiptsJson } });
+ }
+ return json;
+}
+
+ReceiptEvent::ReceiptEvent(const EventsWithReceipts &ewrs)
+ : Event(typeId(), matrixTypeId(), toJson(ewrs))
+{}
+
EventsWithReceipts ReceiptEvent::eventsWithReceipts() const
{
EventsWithReceipts result;
@@ -39,14 +60,14 @@ EventsWithReceipts ReceiptEvent::eventsWithReceipts() const
}
const auto reads =
eventIt.value().toObject().value("m.read"_ls).toObject();
- QVector<Receipt> receipts;
- receipts.reserve(reads.size());
+ QVector<UserTimestamp> usersAtEvent;
+ usersAtEvent.reserve(reads.size());
for (auto userIt = reads.begin(); userIt != reads.end(); ++userIt) {
const auto user = userIt.value().toObject();
- receipts.push_back(
+ usersAtEvent.push_back(
{ userIt.key(), fromJson<QDateTime>(user["ts"_ls]) });
}
- result.push_back({ eventIt.key(), std::move(receipts) });
+ result.push_back({ eventIt.key(), std::move(usersAtEvent) });
}
return result;
}
diff --git a/lib/events/receiptevent.h b/lib/events/receiptevent.h
index 4feec9ea..9683deef 100644
--- a/lib/events/receiptevent.h
+++ b/lib/events/receiptevent.h
@@ -9,19 +9,20 @@
#include <QtCore/QVector>
namespace Quotient {
-struct Receipt {
+struct UserTimestamp {
QString userId;
QDateTime timestamp;
};
struct ReceiptsForEvent {
QString evtId;
- QVector<Receipt> receipts;
+ QVector<UserTimestamp> receipts;
};
using EventsWithReceipts = QVector<ReceiptsForEvent>;
class ReceiptEvent : public Event {
public:
DEFINE_EVENT_TYPEID("m.receipt", ReceiptEvent)
+ explicit ReceiptEvent(const EventsWithReceipts& ewrs);
explicit ReceiptEvent(const QJsonObject& obj) : Event(typeId(), obj) {}
EventsWithReceipts eventsWithReceipts() const;
diff --git a/lib/events/redactionevent.h b/lib/events/redactionevent.h
index ed560331..be20bf52 100644
--- a/lib/events/redactionevent.h
+++ b/lib/events/redactionevent.h
@@ -17,7 +17,7 @@ public:
{
return fullJson()["redacts"_ls].toString();
}
- QString reason() const { return contentJson()["reason"_ls].toString(); }
+ QString reason() const { return contentPart<QString>("reason"_ls); }
};
REGISTER_EVENT_TYPE(RedactionEvent)
} // namespace Quotient
diff --git a/lib/events/roomavatarevent.h b/lib/events/roomavatarevent.h
index a4257895..8618ba31 100644
--- a/lib/events/roomavatarevent.h
+++ b/lib/events/roomavatarevent.h
@@ -20,12 +20,12 @@ public:
: StateEvent(typeId(), matrixTypeId(), QString(), avatar)
{}
// A replica of EventContent::ImageInfo constructor
- explicit RoomAvatarEvent(const QUrl& u, qint64 fileSize = -1,
+ explicit RoomAvatarEvent(const QUrl& mxcUrl, qint64 fileSize = -1,
QMimeType mimeType = {},
const QSize& imageSize = {},
const QString& originalFilename = {})
: RoomAvatarEvent(EventContent::ImageContent {
- u, fileSize, mimeType, imageSize, originalFilename })
+ mxcUrl, fileSize, mimeType, imageSize, none, originalFilename })
{}
QUrl url() const { return content().url; }
diff --git a/lib/events/roomcreateevent.cpp b/lib/events/roomcreateevent.cpp
index 6558bade..bb6de648 100644
--- a/lib/events/roomcreateevent.cpp
+++ b/lib/events/roomcreateevent.cpp
@@ -5,19 +5,35 @@
using namespace Quotient;
+template <>
+struct Quotient::JsonConverter<RoomType> {
+ static RoomType load(const QJsonValue& jv)
+ {
+ const auto& roomTypeString = jv.toString();
+ for (auto it = RoomTypeStrings.begin(); it != RoomTypeStrings.end();
+ ++it)
+ if (roomTypeString == *it)
+ return RoomType(it - RoomTypeStrings.begin());
+
+ if (!roomTypeString.isEmpty())
+ qCWarning(EVENTS) << "Unknown Room Type: " << roomTypeString;
+ return RoomType::Undefined;
+ }
+};
+
bool RoomCreateEvent::isFederated() const
{
- return fromJson<bool>(contentJson()["m.federate"_ls]);
+ return contentPart<bool>("m.federate"_ls);
}
QString RoomCreateEvent::version() const
{
- return fromJson<QString>(contentJson()["room_version"_ls]);
+ return contentPart<QString>("room_version"_ls);
}
RoomCreateEvent::Predecessor RoomCreateEvent::predecessor() const
{
- const auto predJson = contentJson()["predecessor"_ls].toObject();
+ const auto predJson = contentPart<QJsonObject>("predecessor"_ls);
return { fromJson<QString>(predJson[RoomIdKeyL]),
fromJson<QString>(predJson[EventIdKeyL]) };
}
@@ -26,3 +42,8 @@ bool RoomCreateEvent::isUpgrade() const
{
return contentJson().contains("predecessor"_ls);
}
+
+RoomType RoomCreateEvent::roomType() const
+{
+ return contentPart<RoomType>("type"_ls);
+}
diff --git a/lib/events/roomcreateevent.h b/lib/events/roomcreateevent.h
index 05e623ed..b3ad287c 100644
--- a/lib/events/roomcreateevent.h
+++ b/lib/events/roomcreateevent.h
@@ -4,6 +4,7 @@
#pragma once
#include "stateevent.h"
+#include "quotient_common.h"
namespace Quotient {
class RoomCreateEvent : public StateEventBase {
@@ -24,6 +25,7 @@ public:
QString version() const;
Predecessor predecessor() const;
bool isUpgrade() const;
+ RoomType roomType() const;
};
REGISTER_EVENT_TYPE(RoomCreateEvent)
} // namespace Quotient
diff --git a/lib/events/roomevent.cpp b/lib/events/roomevent.cpp
index 4fec9d2b..b728e0bf 100644
--- a/lib/events/roomevent.cpp
+++ b/lib/events/roomevent.cpp
@@ -9,9 +9,6 @@
using namespace Quotient;
-[[maybe_unused]] static auto roomEventTypeInitialised =
- Event::factory_t::chainFactory<RoomEvent>();
-
RoomEvent::RoomEvent(Type type, event_mtype_t matrixType,
const QJsonObject& contentJson)
: Event(type, matrixType, contentJson)
@@ -19,7 +16,7 @@ RoomEvent::RoomEvent(Type type, event_mtype_t matrixType,
RoomEvent::RoomEvent(Type type, const QJsonObject& json) : Event(type, json)
{
- if (const auto redaction = unsignedJson()[RedactedCauseKeyL];
+ if (const auto redaction = unsignedPart(RedactedCauseKeyL);
redaction.isObject())
_redactedBecause = makeEvent<RedactionEvent>(redaction.toObject());
}
@@ -45,14 +42,14 @@ QString RoomEvent::senderId() const
bool RoomEvent::isReplaced() const
{
- return unsignedJson()["m.relations"_ls].toObject().contains("m.replace");
+ return unsignedPart<QJsonObject>("m.relations"_ls).contains("m.replace");
}
QString RoomEvent::replacedBy() const
{
// clang-format off
- return unsignedJson()["m.relations"_ls].toObject()
- .value("m.replace").toObject()
+ return unsignedPart<QJsonObject>("m.relations"_ls)
+ .value("m.replace"_ls).toObject()
.value(EventIdKeyL).toString();
// clang-format on
}
@@ -64,7 +61,7 @@ QString RoomEvent::redactionReason() const
QString RoomEvent::transactionId() const
{
- return unsignedJson()["transaction_id"_ls].toString();
+ return unsignedPart<QString>("transaction_id"_ls);
}
QString RoomEvent::stateKey() const
diff --git a/lib/events/roomevent.h b/lib/events/roomevent.h
index fea509c0..8be58481 100644
--- a/lib/events/roomevent.h
+++ b/lib/events/roomevent.h
@@ -12,16 +12,8 @@ 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>;
+ static inline _impl::EventFactory<RoomEvent> factory { "RoomEvent" };
// RedactionEvent is an incomplete type here so we cannot inline
// constructors and destructors and we cannot use 'using'.
@@ -32,11 +24,12 @@ public:
QString id() const;
QDateTime originTimestamp() const;
- [[deprecated("Use originTimestamp()")]] QDateTime timestamp() const {
- return originTimestamp();
- }
QString roomId() const;
QString senderId() const;
+ //! \brief Determine whether the event has been replaced
+ //!
+ //! \return true if this event has been overridden by another event
+ //! with `"rel_type": "m.replace"`; false otherwise
bool isReplaced() const;
QString replacedBy() const;
bool isRedacted() const { return bool(_redactedBecause); }
@@ -48,28 +41,23 @@ public:
QString transactionId() const;
QString stateKey() const;
+ //! \brief Fill the pending event object with the room id
void setRoomId(const QString& roomId);
+ //! \brief Fill the pending event object with the sender id
void setSender(const QString& senderId);
-
- /**
- * 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()
- */
+ //! \brief Fill the pending event object with the transaction id
+ //! \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
- */
+ //! \brief Add an event id to locally created events after they are sent
+ //!
+ //! When a new event is created locally, it has no id; the homeserver
+ //! assigns it once the event is sent. 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 about the change; there's no signal or
+ //! callback for that in RoomEvent.
void addId(const QString& newId);
protected:
@@ -90,8 +78,8 @@ public:
~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 contentPart<QString>("call_id"_ls); }
+ int version() const { return contentPart<int>("version"_ls); }
};
} // namespace Quotient
Q_DECLARE_METATYPE(Quotient::RoomEvent*)
diff --git a/lib/events/roomkeyevent.h b/lib/events/roomkeyevent.h
index 14e80324..d021fbec 100644
--- a/lib/events/roomkeyevent.h
+++ b/lib/events/roomkeyevent.h
@@ -13,10 +13,10 @@ public:
explicit RoomKeyEvent(const QJsonObject& obj);
- QString algorithm() const { return content<QString>("algorithm"_ls); }
- QString roomId() const { return content<QString>(RoomIdKeyL); }
- QString sessionId() const { return content<QString>("session_id"_ls); }
- QString sessionKey() const { return content<QString>("session_key"_ls); }
+ QString algorithm() const { return contentPart<QString>("algorithm"_ls); }
+ QString roomId() const { return contentPart<QString>(RoomIdKeyL); }
+ QString sessionId() const { return contentPart<QString>("session_id"_ls); }
+ QString sessionKey() const { return contentPart<QString>("session_key"_ls); }
};
REGISTER_EVENT_TYPE(RoomKeyEvent)
} // namespace Quotient
diff --git a/lib/events/roommemberevent.cpp b/lib/events/roommemberevent.cpp
index 9634ca3a..3141f6b5 100644
--- a/lib/events/roommemberevent.cpp
+++ b/lib/events/roommemberevent.cpp
@@ -7,27 +7,26 @@
#include "converters.h"
#include "logging.h"
-#include <array>
-
-static const std::array<QString, 5> membershipStrings = {
- { QStringLiteral("invite"), QStringLiteral("join"), QStringLiteral("knock"),
- QStringLiteral("leave"), QStringLiteral("ban") }
-};
+#include <QtCore/QtAlgorithms>
namespace Quotient {
template <>
-struct JsonConverter<MembershipType> {
- static MembershipType load(const QJsonValue& jv)
+struct JsonConverter<Membership> {
+ static Membership 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());
-
- if (!membershipString.isEmpty())
- qCWarning(EVENTS) << "Unknown MembershipType: " << membershipString;
- return MembershipType::Undefined;
+ const auto& ms = jv.toString();
+ if (ms.isEmpty())
+ {
+ qCWarning(EVENTS) << "Empty membership state";
+ return Membership::Invalid;
+ }
+ const auto it =
+ std::find(MembershipStrings.begin(), MembershipStrings.end(), ms);
+ if (it != MembershipStrings.end())
+ return Membership(1U << (it - MembershipStrings.begin()));
+
+ qCWarning(EVENTS) << "Unknown Membership value: " << ms;
+ return Membership::Invalid;
}
};
} // namespace Quotient
@@ -35,7 +34,7 @@ struct JsonConverter<MembershipType> {
using namespace Quotient;
MemberEventContent::MemberEventContent(const QJsonObject& json)
- : membership(fromJson<MembershipType>(json["membership"_ls]))
+ : membership(fromJson<Membership>(json["membership"_ls]))
, isDirect(json["is_direct"_ls].toBool())
, displayName(fromJson<Omittable<QString>>(json["displayname"_ls]))
, avatarUrl(fromJson<Omittable<QString>>(json["avatar_url"_ls]))
@@ -48,10 +47,10 @@ MemberEventContent::MemberEventContent(const QJsonObject& json)
void MemberEventContent::fillJson(QJsonObject* o) const
{
Q_ASSERT(o);
- Q_ASSERT_X(membership != MembershipType::Undefined, __FUNCTION__,
- "The key 'membership' must be explicit in MemberEventContent");
- if (membership != MembershipType::Undefined)
- o->insert(QStringLiteral("membership"), membershipStrings[membership]);
+ if (membership != Membership::Invalid)
+ o->insert(QStringLiteral("membership"),
+ MembershipStrings[qCountTrailingZeroBits(
+ std::underlying_type_t<Membership>(membership))]);
if (displayName)
o->insert(QStringLiteral("displayname"), *displayName);
if (avatarUrl && avatarUrl->isValid())
@@ -67,51 +66,49 @@ bool RoomMemberEvent::changesMembership() const
bool RoomMemberEvent::isInvite() const
{
- return membership() == MembershipType::Invite && changesMembership();
+ return membership() == Membership::Invite && changesMembership();
}
bool RoomMemberEvent::isRejectedInvite() const
{
- return membership() == MembershipType::Leave && prevContent()
- && prevContent()->membership == MembershipType::Invite;
+ return membership() == Membership::Leave && prevContent()
+ && prevContent()->membership == Membership::Invite;
}
bool RoomMemberEvent::isJoin() const
{
- return membership() == MembershipType::Join && changesMembership();
+ return membership() == Membership::Join && changesMembership();
}
bool RoomMemberEvent::isLeave() const
{
- return membership() == MembershipType::Leave && prevContent()
+ return membership() == Membership::Leave && prevContent()
&& prevContent()->membership != membership()
- && prevContent()->membership != MembershipType::Ban
- && prevContent()->membership != MembershipType::Invite;
+ && prevContent()->membership != Membership::Ban
+ && prevContent()->membership != Membership::Invite;
}
bool RoomMemberEvent::isBan() const
{
- return membership() == MembershipType::Ban && changesMembership();
+ return membership() == Membership::Ban && changesMembership();
}
bool RoomMemberEvent::isUnban() const
{
- return membership() == MembershipType::Leave && prevContent()
- && prevContent()->membership == MembershipType::Ban;
+ return membership() == Membership::Leave && prevContent()
+ && prevContent()->membership == Membership::Ban;
}
bool RoomMemberEvent::isRename() const
{
- auto prevName = prevContent() && prevContent()->displayName
- ? *prevContent()->displayName
- : QString();
- return newDisplayName() != prevName;
+ return prevContent() && prevContent()->displayName
+ ? newDisplayName() != *prevContent()->displayName
+ : newDisplayName().has_value();
}
bool RoomMemberEvent::isAvatarUpdate() const
{
- auto prevAvatarUrl = prevContent() && prevContent()->avatarUrl
- ? *prevContent()->avatarUrl
- : QUrl();
- return newAvatarUrl() != prevAvatarUrl;
+ return prevContent() && prevContent()->avatarUrl
+ ? newAvatarUrl() != *prevContent()->avatarUrl
+ : newAvatarUrl().has_value();
}
diff --git a/lib/events/roommemberevent.h b/lib/events/roommemberevent.h
index f2fbe689..0fb464d4 100644
--- a/lib/events/roommemberevent.h
+++ b/lib/events/roommemberevent.h
@@ -7,23 +7,21 @@
#include "eventcontent.h"
#include "stateevent.h"
+#include "quotient_common.h"
namespace Quotient {
class MemberEventContent : public EventContent::Base {
public:
- enum MembershipType : unsigned char {
- Invite = 0,
- Join,
- Knock,
- Leave,
- Ban,
- Undefined
- };
+ using MembershipType
+ [[deprecated("Use Quotient::Membership instead")]] = Membership;
- explicit MemberEventContent(MembershipType mt = Join) : membership(mt) {}
+ explicit MemberEventContent(Membership ms = Membership::Join)
+ : membership(ms)
+ {}
explicit MemberEventContent(const QJsonObject& json);
- MembershipType membership;
+ Membership membership;
+ /// (Only for invites) Whether the invite is to a direct chat
bool isDirect = false;
Omittable<QString> displayName;
Omittable<QUrl> avatarUrl;
@@ -33,15 +31,15 @@ protected:
void fillJson(QJsonObject* o) const override;
};
-using MembershipType = MemberEventContent::MembershipType;
+using MembershipType [[deprecated("Use Membership instead")]] = Membership;
class RoomMemberEvent : public StateEvent<MemberEventContent> {
Q_GADGET
public:
DEFINE_EVENT_TYPEID("m.room.member", RoomMemberEvent)
- using MembershipType = MemberEventContent::MembershipType;
- Q_ENUM(MembershipType)
+ using MembershipType
+ [[deprecated("Use Quotient::Membership instead")]] = Membership;
explicit RoomMemberEvent(const QJsonObject& obj) : StateEvent(typeId(), obj)
{}
@@ -51,21 +49,20 @@ public:
std::forward<ArgTs>(contentArgs)...)
{}
- /// 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
- */
+ //! \brief 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 have a specialisation of
+ //! EventFactory\<> and be constructible with unknownTypeId() instead of
+ //! its genuine id. Don't use directly.
+ //! \sa EventFactory, loadEvent, GetMembersByRoomJob
RoomMemberEvent(Type type, const QJsonObject& fullJson)
: StateEvent(type, fullJson)
{}
- MembershipType membership() const { return content().membership; }
+ Membership membership() const { return content().membership; }
QString userId() const { return stateKey(); }
bool isDirect() const { return content().isDirect; }
Omittable<QString> newDisplayName() const { return content().displayName; }
@@ -91,14 +88,13 @@ public:
};
template <>
-class EventFactory<RoomMemberEvent> {
-public:
- static event_ptr_tt<RoomMemberEvent> make(const QJsonObject& json,
- const QString&)
- {
+inline event_ptr_tt<RoomMemberEvent>
+doLoadEvent<RoomMemberEvent>(const QJsonObject& json, const QString& matrixType)
+{
+ if (matrixType == QLatin1String(RoomMemberEvent::matrixTypeId()))
return makeEvent<RoomMemberEvent>(json);
- }
-};
+ return makeEvent<RoomMemberEvent>(unknownEventTypeId(), json);
+}
REGISTER_EVENT_TYPE(RoomMemberEvent)
} // namespace Quotient
diff --git a/lib/events/roommessageevent.cpp b/lib/events/roommessageevent.cpp
index 31c0fd9e..2b7b4166 100644
--- a/lib/events/roommessageevent.cpp
+++ b/lib/events/roommessageevent.cpp
@@ -10,7 +10,9 @@
#include <QtCore/QFileInfo>
#include <QtCore/QMimeDatabase>
#include <QtGui/QImageReader>
-#include <QtMultimedia/QMediaResource>
+#if QT_VERSION_MAJOR < 6
+# include <QtMultimedia/QMediaResource>
+#endif
using namespace Quotient;
using namespace EventContent;
@@ -133,6 +135,7 @@ RoomMessageEvent::RoomMessageEvent(const QString& plainBody, MsgType msgType,
: RoomMessageEvent(plainBody, msgTypeToJson(msgType), content)
{}
+#if QT_VERSION_MAJOR < 6
TypedBase* contentFromFile(const QFileInfo& file, bool asGenericFile)
{
auto filePath = file.absoluteFilePath();
@@ -142,21 +145,21 @@ TypedBase* contentFromFile(const QFileInfo& file, bool asGenericFile)
auto mimeTypeName = mimeType.name();
if (mimeTypeName.startsWith("image/"))
return new ImageContent(localUrl, file.size(), mimeType,
- QImageReader(filePath).size(),
+ QImageReader(filePath).size(), none,
file.fileName());
// duration can only be obtained asynchronously and can only be reliably
// done by starting to play the file. Left for a future implementation.
if (mimeTypeName.startsWith("video/"))
return new VideoContent(localUrl, file.size(), mimeType,
- QMediaResource(localUrl).resolution(),
+ QMediaResource(localUrl).resolution(), none,
file.fileName());
if (mimeTypeName.startsWith("audio/"))
- return new AudioContent(localUrl, file.size(), mimeType,
+ return new AudioContent(localUrl, file.size(), mimeType, none,
file.fileName());
}
- return new FileContent(localUrl, file.size(), mimeType, file.fileName());
+ return new FileContent(localUrl, file.size(), mimeType, none, file.fileName());
}
RoomMessageEvent::RoomMessageEvent(const QString& plainBody,
@@ -166,6 +169,7 @@ RoomMessageEvent::RoomMessageEvent(const QString& plainBody,
: rawMsgTypeForFile(file),
contentFromFile(file, asGenericFile))
{}
+#endif
RoomMessageEvent::RoomMessageEvent(const QJsonObject& obj)
: RoomEvent(typeId(), obj), _content(nullptr)
@@ -200,12 +204,12 @@ RoomMessageEvent::MsgType RoomMessageEvent::msgtype() const
QString RoomMessageEvent::rawMsgtype() const
{
- return contentJson()[MsgTypeKeyL].toString();
+ return contentPart<QString>(MsgTypeKeyL);
}
QString RoomMessageEvent::plainBody() const
{
- return contentJson()[BodyKeyL].toString();
+ return contentPart<QString>(BodyKeyL);
}
QMimeType RoomMessageEvent::mimeType() const
diff --git a/lib/events/roommessageevent.h b/lib/events/roommessageevent.h
index 8303ce4e..56597ddc 100644
--- a/lib/events/roommessageevent.h
+++ b/lib/events/roommessageevent.h
@@ -18,10 +18,6 @@ namespace MessageEventContent = EventContent; // Back-compatibility
*/
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(const EventContent::TypedBase* content READ content CONSTANT)
public:
DEFINE_EVENT_TYPEID("m.room.message", RoomMessageEvent)
@@ -42,8 +38,12 @@ public:
explicit RoomMessageEvent(const QString& plainBody,
MsgType msgType = MsgType::Text,
EventContent::TypedBase* content = nullptr);
+#if QT_VERSION_MAJOR < 6
+ [[deprecated("Create an EventContent object on the client side"
+ " and pass it to other constructors")]] //
explicit RoomMessageEvent(const QString& plainBody, const QFileInfo& file,
bool asGenericFile = false);
+#endif
explicit RoomMessageEvent(const QJsonObject& obj);
MsgType msgtype() const;
@@ -58,9 +58,26 @@ public:
_content.data());
}
QMimeType mimeType() const;
+ //! \brief Determine whether the message has text content
+ //!
+ //! \return true, if the message type is one of m.text, m.notice, m.emote,
+ //! or the message type is unspecified (in which case plainBody()
+ //! can still be examined); false otherwise
bool hasTextContent() const;
+ //! \brief Determine whether the message has a file/attachment
+ //!
+ //! \return true, if the message has a data structure corresponding to
+ //! a file (such as m.file or m.audio); false otherwise
bool hasFileContent() const;
+ //! \brief Determine whether the message has a thumbnail
+ //!
+ //! \return true, if the message has a data structure corresponding to
+ //! a thumbnail (the message type may be one for visual content,
+ //! such as m.image, or generic binary content, i.e. m.file);
+ //! false otherwise
bool hasThumbnail() const;
+ //! \brief Obtain id of an event replaced by the current one
+ //! \sa RoomEvent::isReplaced, RoomEvent::replacedBy
QString replacedEvent() const;
static QString rawMsgTypeForUrl(const QUrl& url);
diff --git a/lib/events/roomtombstoneevent.cpp b/lib/events/roomtombstoneevent.cpp
index 080d269c..2c3492d6 100644
--- a/lib/events/roomtombstoneevent.cpp
+++ b/lib/events/roomtombstoneevent.cpp
@@ -7,10 +7,10 @@ using namespace Quotient;
QString RoomTombstoneEvent::serverMessage() const
{
- return fromJson<QString>(contentJson()["body"_ls]);
+ return contentPart<QString>("body"_ls);
}
QString RoomTombstoneEvent::successorRoomId() const
{
- return fromJson<QString>(contentJson()["replacement_room"_ls]);
+ return contentPart<QString>("replacement_room"_ls);
}
diff --git a/lib/events/simplestateevents.h b/lib/events/simplestateevents.h
index c977cb6e..13597979 100644
--- a/lib/events/simplestateevents.h
+++ b/lib/events/simplestateevents.h
@@ -8,8 +8,7 @@
namespace Quotient {
namespace EventContent {
template <typename T>
- class SimpleContent {
- public:
+ struct SimpleContent {
using value_type = T;
// The constructor is templated to enable perfect forwarding
@@ -25,11 +24,8 @@ namespace EventContent {
return { { key, Quotient::toJson(value) } };
}
- public:
T value;
-
- protected:
- QString key;
+ const QString key;
};
} // namespace EventContent
@@ -57,19 +53,16 @@ DEFINE_SIMPLE_STATE_EVENT(RoomNameEvent, "m.room.name", QString, name)
DEFINE_SIMPLE_STATE_EVENT(RoomTopicEvent, "m.room.topic", QString, topic)
DEFINE_SIMPLE_STATE_EVENT(RoomPinnedEvent, "m.room.pinned_messages", QStringList, pinnedEvents)
-class RoomAliasesEvent
- : public StateEvent<EventContent::SimpleContent<QStringList>> {
+class [[deprecated(
+ "m.room.aliases events are deprecated by the Matrix spec; use"
+ " RoomCanonicalAliasEvent::altAliases() to get non-authoritative aliases")]] //
+RoomAliasesEvent : public StateEvent<EventContent::SimpleContent<QStringList>> {
public:
DEFINE_EVENT_TYPEID("m.room.aliases", RoomAliasesEvent)
explicit RoomAliasesEvent(const QJsonObject& obj)
: StateEvent(typeId(), obj, QStringLiteral("aliases"))
{}
- RoomAliasesEvent(const QString& server, const QStringList& aliases)
- : StateEvent(typeId(), matrixTypeId(), server,
- QStringLiteral("aliases"), aliases)
- {}
QString server() const { return stateKey(); }
QStringList aliases() const { return content().value; }
};
-REGISTER_EVENT_TYPE(RoomAliasesEvent)
} // namespace Quotient
diff --git a/lib/events/stateevent.cpp b/lib/events/stateevent.cpp
index 42fc9054..e53d47d4 100644
--- a/lib/events/stateevent.cpp
+++ b/lib/events/stateevent.cpp
@@ -5,20 +5,13 @@
using namespace Quotient;
-// Aside from the normal factory to instantiate StateEventBase inheritors
-// StateEventBase itself can be instantiated if there's a state_key JSON key
-// but the event type is unknown.
-[[maybe_unused]] static auto stateEventTypeInitialised =
- RoomEvent::factory_t::addMethod(
- [](const QJsonObject& json, const QString& matrixType) -> StateEventPtr {
- if (!json.contains(StateKeyKeyL))
- return nullptr;
-
- if (auto e = StateEventBase::factory_t::make(json, matrixType))
- return e;
-
- return makeEvent<StateEventBase>(unknownEventTypeId(), json);
- });
+StateEventBase::StateEventBase(Type type, const QJsonObject& json)
+ : RoomEvent(json.contains(StateKeyKeyL) ? type : unknownEventTypeId(), json)
+{
+ if (Event::type() == unknownEventTypeId() && !json.contains(StateKeyKeyL))
+ qWarning(EVENTS) << "Attempt to create a state event with no stateKey -"
+ "forcing the event type to unknown to avoid damage";
+}
StateEventBase::StateEventBase(Event::Type type, event_mtype_t matrixType,
const QString& stateKey,
@@ -28,22 +21,22 @@ StateEventBase::StateEventBase(Event::Type type, event_mtype_t matrixType,
bool StateEventBase::repeatsState() const
{
- const auto prevContentJson = unsignedJson().value(PrevContentKeyL);
+ const auto prevContentJson = unsignedPart(PrevContentKeyL);
return fullJson().value(ContentKeyL) == prevContentJson;
}
QString StateEventBase::replacedState() const
{
- return unsignedJson().value("replaces_state"_ls).toString();
+ return unsignedPart<QString>("replaces_state"_ls);
}
void StateEventBase::dumpTo(QDebug dbg) const
{
if (!stateKey().isEmpty())
dbg << '<' << stateKey() << "> ";
- if (unsignedJson().contains(PrevContentKeyL))
- dbg << QJsonDocument(unsignedJson()[PrevContentKeyL].toObject())
- .toJson(QJsonDocument::Compact)
+ if (const auto prevContentJson = unsignedPart<QJsonObject>(PrevContentKeyL);
+ !prevContentJson.isEmpty())
+ dbg << QJsonDocument(prevContentJson).toJson(QJsonDocument::Compact)
<< " -> ";
RoomEvent::dumpTo(dbg);
}
diff --git a/lib/events/stateevent.h b/lib/events/stateevent.h
index 1415f709..c37965aa 100644
--- a/lib/events/stateevent.h
+++ b/lib/events/stateevent.h
@@ -18,13 +18,10 @@ inline QJsonObject basicStateEventJson(const QString& matrixTypeId,
}
class StateEventBase : public RoomEvent {
- Q_GADGET
- Q_PROPERTY(QString stateKey READ stateKey CONSTANT)
public:
- using factory_t = EventFactory<StateEventBase>;
+ static inline _impl::EventFactory<StateEventBase> factory { "StateEvent" };
- StateEventBase(Type type, const QJsonObject& json) : RoomEvent(type, json)
- {}
+ StateEventBase(Type type, const QJsonObject& json);
StateEventBase(Type type, event_mtype_t matrixType,
const QString& stateKey = {},
const QJsonObject& contentJson = {});
@@ -39,6 +36,22 @@ public:
using StateEventPtr = event_ptr_tt<StateEventBase>;
using StateEvents = EventsArray<StateEventBase>;
+//! \brief Override RoomEvent factory with that from StateEventBase if JSON has
+//! stateKey
+//!
+//! This means in particular that an event with a type known to RoomEvent but
+//! having stateKey set (even to an empty value) will be treated as a state
+//! event and most likely end up as unknown (consider, e.g., m.room.message
+//! that has stateKey set).
+template <>
+inline RoomEventPtr doLoadEvent(const QJsonObject& json,
+ const QString& matrixType)
+{
+ if (json.contains(StateKeyKeyL))
+ return StateEventBase::factory.loadEvent(json, matrixType);
+ return RoomEvent::factory.loadEvent(json, matrixType);
+}
+
template <>
inline bool is<StateEventBase>(const Event& e)
{
@@ -100,10 +113,6 @@ public:
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;
diff --git a/lib/events/stickerevent.cpp b/lib/events/stickerevent.cpp
index ea4dff3f..628fd154 100644
--- a/lib/events/stickerevent.cpp
+++ b/lib/events/stickerevent.cpp
@@ -12,7 +12,7 @@ StickerEvent::StickerEvent(const QJsonObject &obj)
QString StickerEvent::body() const
{
- return content<QString>("body"_ls);
+ return contentPart<QString>("body"_ls);
}
const EventContent::ImageContent &StickerEvent::image() const
diff --git a/lib/events/typingevent.cpp b/lib/events/typingevent.cpp
index e97e978f..7e5d7ee6 100644
--- a/lib/events/typingevent.cpp
+++ b/lib/events/typingevent.cpp
@@ -7,5 +7,5 @@ using namespace Quotient;
QStringList TypingEvent::users() const
{
- return fromJson<QStringList>(contentJson()["user_ids"_ls]);
+ return contentPart<QStringList>("user_ids"_ls);
}