aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/connection.cpp7
-rw-r--r--lib/converters.h6
-rw-r--r--lib/events/accountdataevents.h39
-rw-r--r--lib/events/directchatevent.cpp8
-rw-r--r--lib/events/directchatevent.h9
-rw-r--r--lib/events/event.cpp140
-rw-r--r--lib/events/event.h349
-rw-r--r--lib/events/eventcontent.cpp25
-rw-r--r--lib/events/eventcontent.h36
-rw-r--r--lib/events/receiptevent.cpp12
-rw-r--r--lib/events/receiptevent.h6
-rw-r--r--lib/events/redactionevent.h17
-rw-r--r--lib/events/roomavatarevent.h10
-rw-r--r--lib/events/roommemberevent.cpp14
-rw-r--r--lib/events/roommemberevent.h23
-rw-r--r--lib/events/roommessageevent.cpp88
-rw-r--r--lib/events/roommessageevent.h26
-rw-r--r--lib/events/simplestateevents.h68
-rw-r--r--lib/events/typingevent.cpp8
-rw-r--r--lib/events/typingevent.h7
-rw-r--r--lib/jobs/sendeventjob.h4
-rw-r--r--lib/room.cpp247
-rw-r--r--lib/room.h6
-rw-r--r--lib/user.h8
-rw-r--r--lib/util.h41
25 files changed, 676 insertions, 528 deletions
diff --git a/lib/connection.cpp b/lib/connection.cpp
index 8fd960b6..339be5b9 100644
--- a/lib/connection.cpp
+++ b/lib/connection.cpp
@@ -355,9 +355,9 @@ void Connection::onSyncSuccess(SyncData &&data) {
continue;
}
- d->accountData[accountEvent->jsonType()] =
+ d->accountData[accountEvent->matrixType()] =
fromJson<AccountDataMap>(accountEvent->contentJson());
- emit accountDataChanged(accountEvent->jsonType());
+ emit accountDataChanged(accountEvent->matrixType());
}
}
@@ -603,7 +603,7 @@ SendToDeviceJob* Connection::sendToDevices(const QString& eventType,
std::for_each(devicesToEvents.begin(), devicesToEvents.end(),
[&jsonUser] (const auto& deviceToEvents) {
jsonUser.insert(deviceToEvents.first,
- deviceToEvents.second.toJson());
+ deviceToEvents.second.contentJson());
});
});
return callApi<SendToDeviceJob>(BackgroundRequest,
@@ -1048,4 +1048,3 @@ void Connection::setCacheState(bool newValue)
emit cacheStateChanged();
}
}
-
diff --git a/lib/converters.h b/lib/converters.h
index a59809e7..c01d7c8e 100644
--- a/lib/converters.h
+++ b/lib/converters.h
@@ -18,10 +18,14 @@
#pragma once
+#include "util.h"
+
#include <QtCore/QJsonObject>
#include <QtCore/QJsonArray> // Includes <QtCore/QJsonValue>
#include <QtCore/QDate>
#include <QtCore/QUrlQuery>
+#include <QtCore/QSet>
+#include <QtCore/QVector>
#include <unordered_map>
#include <vector>
@@ -351,7 +355,7 @@ namespace QMatrixClient
template <typename ValT>
inline void addTo(QUrlQuery& q, const QString& k, ValT&& v)
- { q.addQueryItem(k, QString("%1").arg(v)); }
+ { q.addQueryItem(k, QStringLiteral("%1").arg(v)); }
// OpenAPI is entirely JSON-based, which means representing bools as
// textual true/false, rather than 1/0.
diff --git a/lib/events/accountdataevents.h b/lib/events/accountdataevents.h
index 11667172..6d53d2aa 100644
--- a/lib/events/accountdataevents.h
+++ b/lib/events/accountdataevents.h
@@ -32,7 +32,7 @@ namespace QMatrixClient
{
TagRecord (QString order = {}) : order(std::move(order)) { }
explicit TagRecord(const QJsonValue& jv)
- : order(jv.toObject().value("order").toString())
+ : order(jv.toObject().value("order"_ls).toString())
{ }
QString order;
@@ -50,28 +50,29 @@ namespace QMatrixClient
using TagsMap = QHash<QString, TagRecord>;
-#define DEFINE_SIMPLE_EVENT(_Name, _TypeId, _EnumType, _ContentType, _ContentKey) \
+#define DEFINE_SIMPLE_EVENT(_Name, _TypeId, _ContentType, _ContentKey) \
class _Name : public Event \
{ \
public: \
- static constexpr const char* typeId() { return _TypeId; } \
- explicit _Name(const QJsonObject& obj) \
- : Event((_EnumType), obj) \
- , _content(contentJson(), QStringLiteral(#_ContentKey)) \
+ using content_type = _ContentType; \
+ DEFINE_EVENT_TYPEID(_TypeId, _Name) \
+ explicit _Name(QJsonObject obj) \
+ : Event(typeId(), std::move(obj)) \
{ } \
- template <typename... Ts> \
- explicit _Name(Ts&&... contentArgs) \
- : Event(_EnumType) \
- , _content(QStringLiteral(#_ContentKey), \
- std::forward<Ts>(contentArgs)...) \
+ explicit _Name(_ContentType content) \
+ : Event(typeId(), matrixTypeId(), \
+ QJsonObject { { QStringLiteral(#_ContentKey), \
+ toJson(std::move(content)) } }) \
{ } \
- const _ContentType& _ContentKey() const { return _content.value; } \
- QJsonObject toJson() const { return _content.toJson(); } \
- protected: \
- EventContent::SimpleContent<_ContentType> _content; \
- };
+ auto _ContentKey() const \
+ { return fromJson<content_type>(contentJson()[#_ContentKey]); } \
+ }; // End of macro
+
+ DEFINE_SIMPLE_EVENT(TagEvent, "m.tag", TagsMap, tags)
+ DEFINE_SIMPLE_EVENT(ReadMarkerEvent, "m.fully_read", QString, event_id)
+ DEFINE_SIMPLE_EVENT(IgnoredUsersEvent, "m.ignored_user_list",
+ QSet<QString>, ignored_users)
- DEFINE_SIMPLE_EVENT(TagEvent, "m.tag", EventType::Tag, TagsMap, tags)
- DEFINE_SIMPLE_EVENT(ReadMarkerEvent, "m.fully_read", EventType::ReadMarker,
- QString, event_id)
+ DEFINE_EVENTTYPE_ALIAS(Tag, TagEvent)
+ DEFINE_EVENTTYPE_ALIAS(ReadMarker, ReadMarkerEvent)
}
diff --git a/lib/events/directchatevent.cpp b/lib/events/directchatevent.cpp
index 63d638a3..6f5d34f2 100644
--- a/lib/events/directchatevent.cpp
+++ b/lib/events/directchatevent.cpp
@@ -18,18 +18,12 @@
#include "directchatevent.h"
-#include "converters.h"
-
using namespace QMatrixClient;
-DirectChatEvent::DirectChatEvent(const QJsonObject& obj)
- : Event(Type::DirectChat, obj)
-{ }
-
QMultiHash<QString, QString> DirectChatEvent::usersToDirectChats() const
{
QMultiHash<QString, QString> result;
- const auto json = contentJson();
+ const auto& json = contentJson();
for (auto it = json.begin(); it != json.end(); ++it)
{
// Beware of range-for's over temporary returned from temporary
diff --git a/lib/events/directchatevent.h b/lib/events/directchatevent.h
index bd8f2d35..1d366721 100644
--- a/lib/events/directchatevent.h
+++ b/lib/events/directchatevent.h
@@ -25,10 +25,13 @@ namespace QMatrixClient
class DirectChatEvent : public Event
{
public:
- explicit DirectChatEvent(const QJsonObject& obj);
+ DEFINE_EVENT_TYPEID("m.direct", DirectChatEvent)
- QMultiHash<QString, QString> usersToDirectChats() const;
+ explicit DirectChatEvent(const QJsonObject& obj)
+ : Event(typeId(), obj)
+ { }
- static constexpr const char* typeId() { return "m.direct"; }
+ QMultiHash<QString, QString> usersToDirectChats() const;
};
+ DEFINE_EVENTTYPE_ALIAS(DirectChat, DirectChatEvent)
}
diff --git a/lib/events/event.cpp b/lib/events/event.cpp
index 576e9426..3f507347 100644
--- a/lib/events/event.cpp
+++ b/lib/events/event.cpp
@@ -33,117 +33,93 @@
using namespace QMatrixClient;
-Event::Event(Type type, const QJsonObject& rep)
- : _type(type), _originalJson(rep)
+event_type_t EventTypeRegistry::nextTypeId()
{
- if (!rep.contains("content") &&
- !rep.value("unsigned").toObject().contains("redacted_because"))
+ static event_type_t typeIndex = unknownTypeId();
+ return ++typeIndex;
+}
+
+Event::Event(Type type, const QJsonObject& json)
+ : _type(type), _json(json)
+{
+ if (!json.contains(ContentKeyL) &&
+ !json.value(UnsignedKeyL).toObject().contains(RedactedCauseKeyL))
{
qCWarning(EVENTS) << "Event without 'content' node";
- qCWarning(EVENTS) << formatJson << rep;
+ qCWarning(EVENTS) << formatJson << json;
}
}
+Event::Event(Type type, event_mtype_t matrixType, const QJsonObject& contentJson)
+ : Event(type, basicEventJson(matrixType, contentJson))
+{ }
+
Event::~Event() = default;
-QString Event::jsonType() const
+QString Event::matrixType() const
{
- return originalJsonObject().value("type").toString();
+ return fullJson()[TypeKeyL].toString();
}
QByteArray Event::originalJson() const
{
- return QJsonDocument(_originalJson).toJson();
-}
-
-QJsonObject Event::originalJsonObject() const
-{
- return _originalJson;
+ return QJsonDocument(_json).toJson();
}
const QJsonObject Event::contentJson() const
{
- return _originalJson["content"].toObject();
-}
-
-template <typename BaseEventT>
-inline event_ptr_tt<BaseEventT> makeIfMatches(const QJsonObject&, const QString&)
-{
- return nullptr;
+ return fullJson()[ContentKeyL].toObject();
}
-template <typename BaseEventT, typename EventT, typename... EventTs>
-inline event_ptr_tt<BaseEventT> makeIfMatches(const QJsonObject& o,
- const QString& selector)
+const QJsonObject Event::unsignedJson() const
{
- if (selector == EventT::typeId())
- return _impl::create<EventT>(o);
-
- return makeIfMatches<BaseEventT, EventTs...>(o, selector);
+ return fullJson()[UnsignedKeyL].toObject();
}
-template <>
-EventPtr _impl::doMakeEvent<Event>(const QJsonObject& obj)
-{
- // Check more specific event types first
- if (auto e = doMakeEvent<RoomEvent>(obj))
- return ptrCast<Event>(move(e));
-
- return makeIfMatches<Event,
- TypingEvent, ReceiptEvent, TagEvent, ReadMarkerEvent, DirectChatEvent>(
- obj, obj["type"].toString());
-}
+[[gnu::unused]] static auto roomEventTypeInitialised =
+ EventTypeRegistry::chainFactories<Event, RoomEvent>();
-RoomEvent::RoomEvent(Event::Type type) : Event(type) { }
+RoomEvent::RoomEvent(Type type, event_mtype_t matrixType,
+ const QJsonObject& contentJson)
+ : Event(type, matrixType, contentJson)
+{ }
-RoomEvent::RoomEvent(Type type, const QJsonObject& rep)
- : Event(type, rep)
- , _id(rep["event_id"].toString())
+RoomEvent::RoomEvent(Type type, const QJsonObject& json)
+ : Event(type, json)
{
-// if (_id.isEmpty())
-// {
-// qCWarning(EVENTS) << "Can't find event_id in a room event";
-// qCWarning(EVENTS) << formatJson << rep;
-// }
-// if (!rep.contains("origin_server_ts"))
-// {
-// qCWarning(EVENTS) << "Can't find server timestamp in a room event";
-// qCWarning(EVENTS) << formatJson << rep;
-// }
-// if (_senderId.isEmpty())
-// {
-// qCWarning(EVENTS) << "Can't find sender in a room event";
-// qCWarning(EVENTS) << formatJson << rep;
-// }
- auto unsignedData = rep["unsigned"].toObject();
- auto redaction = unsignedData.value("redacted_because");
+ const auto unsignedData = json[UnsignedKeyL].toObject();
+ const auto redaction = unsignedData[RedactedCauseKeyL];
if (redaction.isObject())
{
- _redactedBecause = _impl::create<RedactionEvent>(redaction.toObject());
+ _redactedBecause = makeEvent<RedactionEvent>(redaction.toObject());
return;
}
- _txnId = unsignedData.value("transactionId").toString();
+ _txnId = unsignedData.value("transactionId"_ls).toString();
if (!_txnId.isEmpty())
qCDebug(EVENTS) << "Event transactionId:" << _txnId;
}
RoomEvent::~RoomEvent() = default; // Let the smart pointer do its job
+QString RoomEvent::id() const
+{
+ return fullJson()[EventIdKeyL].toString();
+}
+
QDateTime RoomEvent::timestamp() const
{
- return QMatrixClient::fromJson<QDateTime>(
- originalJsonObject().value("origin_server_ts"));
+ return QMatrixClient::fromJson<QDateTime>(fullJson()["origin_server_ts"_ls]);
}
QString RoomEvent::roomId() const
{
- return originalJsonObject().value("room_id").toString();
+ return fullJson()["room_id"_ls].toString();
}
QString RoomEvent::senderId() const
{
- return originalJsonObject().value("sender").toString();
+ return fullJson()["sender"_ls].toString();
}
QString RoomEvent::redactionReason() const
@@ -151,37 +127,17 @@ QString RoomEvent::redactionReason() const
return isRedacted() ? _redactedBecause->reason() : QString{};
}
-void RoomEvent::addId(const QString& id)
+void RoomEvent::addId(const QString& newId)
{
- Q_ASSERT(_id.isEmpty()); Q_ASSERT(!id.isEmpty());
- _id = id;
+ Q_ASSERT(id().isEmpty()); Q_ASSERT(!newId.isEmpty());
+ editJson().insert(EventIdKey, newId);
}
-template <>
-RoomEventPtr _impl::doMakeEvent(const QJsonObject& obj)
-{
- // Check more specific event types first
- if (auto e = doMakeEvent<StateEventBase>(obj))
- return ptrCast<RoomEvent>(move(e));
-
- return makeIfMatches<RoomEvent,
- RoomMessageEvent, RedactionEvent>(obj, obj["type"].toString());
-}
+[[gnu::unused]] static auto stateEventTypeInitialised =
+ EventTypeRegistry::chainFactories<RoomEvent, StateEventBase>();
bool StateEventBase::repeatsState() const
{
- auto contentJson = originalJsonObject().value("content");
- auto prevContentJson = originalJsonObject().value("unsigned")
- .toObject().value("prev_content");
- return contentJson == prevContentJson;
-}
-
-template<>
-StateEventPtr _impl::doMakeEvent<StateEventBase>(const QJsonObject& obj)
-{
- return makeIfMatches<StateEventBase,
- RoomNameEvent, RoomAliasesEvent,
- RoomCanonicalAliasEvent, RoomMemberEvent, RoomTopicEvent,
- RoomAvatarEvent, EncryptionEvent>(obj, obj["type"].toString());
-
+ const auto prevContentJson = unsignedJson().value(PrevContentKeyL);
+ return fullJson().value(ContentKeyL) == prevContentJson;
}
diff --git a/lib/events/event.h b/lib/events/event.h
index cbfa06ac..f8264baf 100644
--- a/lib/events/event.h
+++ b/lib/events/event.h
@@ -23,6 +23,8 @@
namespace QMatrixClient
{
+ // === event_ptr_tt<> and type casting facilities ===
+
template <typename EventT>
using event_ptr_tt = std::unique_ptr<EventT>;
@@ -44,106 +46,262 @@ namespace QMatrixClient
return unique_ptr_cast<TargetT>(ptr);
}
- namespace _impl
+ // === Predefined types and JSON key names
+
+ using event_type_t = uint;
+ using event_mtype_t = const char*;
+
+ static const auto TypeKey = QStringLiteral("type");
+ static const auto ContentKey = QStringLiteral("content");
+ static const auto EventIdKey = QStringLiteral("event_id");
+ static const auto TypeKeyL = "type"_ls;
+ static const auto ContentKeyL = "content"_ls;
+ static const auto EventIdKeyL = "event_id"_ls;
+ static const auto UnsignedKeyL = "unsigned"_ls;
+ static const auto RedactedCauseKeyL = "redacted_because"_ls;
+ static const auto PrevContentKeyL = "prev_content"_ls;
+
+ // === Event factory ===
+
+ template <typename EventT, typename... ArgTs>
+ inline event_ptr_tt<EventT> makeEvent(ArgTs&&... args)
{
- template <typename EventT, typename... ArgTs>
- inline event_ptr_tt<EventT> create(ArgTs&&... args)
- {
- return std::make_unique<EventT>(std::forward<ArgTs>(args)...);
- }
+ return std::make_unique<EventT>(std::forward<ArgTs>(args)...);
+ }
+
+ class EventTypeRegistry
+ {
+ public:
+ static constexpr event_type_t unknownTypeId() { return 0; }
+ static event_type_t nextTypeId();
+
+ /** Chain two type factories
+ * Adds the factory class of EventT2 (EventT2::factory_t) to
+ * the list in factory class of EventT1 (EventT1::factory_t) so
+ * that when EventT1::factory_t::make() is invoked, types of
+ * EventT2 factory are looked through as well. This is used
+ * to include RoomEvent types into the more general Event factory,
+ * and state event types into the RoomEvent factory.
+ */
+ template <typename EventT1, typename EventT2>
+ static auto chainFactories()
+ {
+ EventT1::factory_t::addFactory(&EventT2::factory_t::make);
+ return 0;
+ }
+
+ /** Add a type to its default factory
+ * Adds a standard factory method (via makeEvent<>) for a given
+ * type to EventT::factory_t factory class so that it can be
+ * created dynamically from loadEvent<>().
+ *
+ * \tparam EventT the type to enable dynamic creation of
+ * \return the registered type id
+ * \sa loadEvent, Event::type
+ */
+ template <typename EventT>
+ static auto addType()
+ {
+ EventT::factory_t::addFactory(
+ [] (const QJsonObject& json, const QString& jsonMatrixType)
+ {
+ return EventT::matrixTypeId() == jsonMatrixType
+ ? makeEvent<EventT>(json) : nullptr;
+ });
+ return nextTypeId();
+ }
+
+ template <typename EventT>
+ static auto typeId() { return _typeId<std::decay_t<EventT>>; }
+
+ private:
+ template <typename EventT>
+ static const event_type_t _typeId;
+ };
+
+ template <typename EventT>
+ const event_type_t EventTypeRegistry::_typeId = addType<EventT>();
+
+ template <typename BaseEventT>
+ class EventFactory
+ {
+ public:
+ template <typename FnT>
+ static void addFactory(FnT&& factory)
+ {
+ factories().emplace_back(std::forward<FnT>(factory));
+ }
+
+ 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 makeEvent<BaseEventT>(EventTypeRegistry::unknownTypeId(),
+ json);
+ }
+
+ 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;
+ }
+ };
+
+ template <typename StrT>
+ inline QJsonObject basicEventJson(StrT matrixType,
+ const QJsonObject& content)
+ {
+ return { { TypeKey, std::forward<StrT>(matrixType) },
+ { ContentKey, content } };
+ }
+
+ /** Create an event with proper type from a JSON object
+ * Use this factory template to detect the type from the JSON object
+ * contents (the detected event type should derive from the template
+ * parameter type) and create an event object of that type.
+ */
+ template <typename BaseEventT>
+ inline event_ptr_tt<BaseEventT> loadEvent(const QJsonObject& fullJson)
+ {
+ return EventFactory<BaseEventT>
+ ::make(fullJson, fullJson[TypeKeyL].toString());
+ }
- template <typename EventT>
- inline event_ptr_tt<EventT> doMakeEvent(const QJsonObject& obj)
+ /** Create an event from a type string and content JSON
+ * Use this factory template to resolve the C++ type from the Matrix
+ * type string in \p matrixType and create an event of that type that has
+ * its content part set to \p content.
+ */
+ template <typename BaseEventT>
+ inline event_ptr_tt<BaseEventT> loadEvent(const QString& matrixType,
+ const QJsonObject& content)
+ {
+ return EventFactory<BaseEventT>
+ ::make(basicEventJson(matrixType, content), matrixType);
+ }
+
+ template <typename EventT> struct FromJson<event_ptr_tt<EventT>>
+ {
+ auto operator()(const QJsonValue& jv) const
{
- return create<EventT>(obj);
+ return loadEvent<EventT>(jv.toObject());
}
- }
+ };
+
+ // === Event ===
class Event
{
Q_GADGET
public:
- enum class Type : quint16
- {
- Unknown = 0,
- Typing, Receipt, Tag, DirectChat, ReadMarker,
- RoomEventBase = 0x1000,
- RoomMessage = RoomEventBase + 1,
- RoomEncryptedMessage, Redaction,
- RoomStateEventBase = 0x1800,
- RoomName = RoomStateEventBase + 1,
- RoomAliases, RoomCanonicalAlias, RoomMember, RoomTopic,
- RoomAvatar, RoomEncryption, RoomCreate, RoomJoinRules,
- RoomPowerLevels,
- Reserved = 0x2000
- };
-
- explicit Event(Type type) : _type(type) { }
- Event(Type type, const QJsonObject& rep);
+ using Type = event_type_t;
+ using factory_t = EventFactory<Event>;
+
+ explicit Event(Type type, const QJsonObject& json);
+ explicit Event(Type type, event_mtype_t matrixType,
+ const QJsonObject& contentJson = {});
Event(const Event&) = delete;
+ Event(Event&&) = default;
+ Event& operator=(const Event&) = delete;
+ Event& operator=(Event&&) = delete;
virtual ~Event();
Type type() const { return _type; }
- QString jsonType() const;
- bool isStateEvent() const
- {
- return (quint16(_type) & 0x1800) == 0x1800;
- }
+ QString matrixType() const;
QByteArray originalJson() const;
- QJsonObject originalJsonObject() const;
+ QJsonObject originalJsonObject() const { return fullJson(); }
+
+ const QJsonObject& fullJson() const { return _json; }
// According to the CS API spec, every event also has
// a "content" object; but since its structure is different for
- // different types, we're implementing it per-event type
- // (and in most cases it will be a combination of other fields
- // instead of "content" field).
+ // different types, we're implementing it per-event type.
const QJsonObject contentJson() const;
+ const QJsonObject unsignedJson() const;
+
+ virtual bool isStateEvent() const { return false; }
+
+ template <typename EventT>
+ bool is() const
+ {
+ const auto eventTypeId = EventTypeRegistry::typeId<EventT>();
+ return _type == eventTypeId;
+ }
- virtual QJsonObject toJson() const { Q_ASSERT(false); return {}; }
+ protected:
+ QJsonObject& editJson() { return _json; }
private:
Type _type;
- QJsonObject _originalJson;
+ QJsonObject _json;
- REGISTER_ENUM(Type)
Q_PROPERTY(Type type READ type CONSTANT)
Q_PROPERTY(QJsonObject contentJson READ contentJson CONSTANT)
};
- using EventType = Event::Type;
using EventPtr = event_ptr_tt<Event>;
- /** Create an event with proper type from a JSON object
- * Use this factory template to detect the type from the JSON object
- * contents (the detected event type should derive from the template
- * parameter type) and create an event object of that type.
- */
template <typename EventT>
- inline event_ptr_tt<EventT> makeEvent(const QJsonObject& obj)
+ using EventsArray = std::vector<event_ptr_tt<EventT>>;
+ using Events = EventsArray<Event>;
+
+ // This macro should be used in a public section of an event class to
+ // provide matrixTypeId() and typeId().
+#define DEFINE_EVENT_TYPEID(_Id, _Type) \
+ static constexpr event_mtype_t matrixTypeId() { return _Id; } \
+ static event_type_t typeId() { return EventTypeRegistry::typeId<_Type>(); }
+
+ // This macro should be put after an event class definition to define an
+ // additional constant that can be used for an event type id. The constant
+ // will be inside EventType namespace. This is for back-compatibility,
+ // to support clients checking for EventType::ShortName (previously
+ // EventType was a typedef for an enumeration). New code should use
+ // either typeId() for a specific event type, or (better) casting methods
+ // defined in the very beginning of this file.
+#define DEFINE_EVENTTYPE_ALIAS(_Id, _Type) \
+ namespace EventType \
+ { \
+ [[deprecated("Use "#_Type"::typeId(), Event::is<>() or visit<>()")]] \
+ static const auto _Id { _Type::typeId() }; \
+ } // End of macro
+
+ // === visit<>() ===
+
+ template <typename FnT>
+ inline fn_return_t<FnT> visit(const Event& event, FnT visitor)
{
- auto e = _impl::doMakeEvent<EventT>(obj);
- if (!e)
- e = _impl::create<EventT>(EventType::Unknown, obj);
- return e;
+ using event_type = fn_arg_t<FnT>;
+ if (event.is<event_type>())
+ return visitor(static_cast<event_type>(event));
+ return fn_return_t<FnT>();
}
- namespace _impl
+ template <typename FnT, typename... FnTs>
+ inline auto visit(const Event& event, FnT visitor1, FnTs&&... visitors)
{
- template <>
- EventPtr doMakeEvent<Event>(const QJsonObject& obj);
+ using event_type1 = fn_arg_t<FnT>;
+ if (event.is<event_type1>())
+ return visitor1(static_cast<event_type1&>(event));
+
+ return visit(event, std::forward<FnTs>(visitors)...);
}
- template <typename EventT> struct FromJson<event_ptr_tt<EventT>>
+ template <typename BaseEventT, typename... FnTs>
+ inline auto visit(const event_ptr_tt<BaseEventT>& eptr, FnTs&&... visitors)
{
- auto operator()(const QJsonValue& jv) const
- {
- return makeEvent<EventT>(jv.toObject());
- }
- };
+ using return_type = decltype(visit(*eptr, visitors...));
+ if (eptr)
+ return visit(*eptr, visitors...);
+ return return_type();
+ }
- template <typename EventT>
- using EventsArray = std::vector<event_ptr_tt<EventT>>;
- using Events = EventsArray<Event>;
+ // === RoomEvent ===
class RedactionEvent;
@@ -159,13 +317,16 @@ namespace QMatrixClient
Q_PROPERTY(bool isRedacted READ isRedacted)
Q_PROPERTY(QString transactionId READ transactionId)
public:
+ using factory_t = EventFactory<RoomEvent>;
+
// RedactionEvent is an incomplete type here so we cannot inline
- // constructors and destructors
- explicit RoomEvent(Type type);
- RoomEvent(Type type, const QJsonObject& rep);
+ // constructors and destructors and we cannot use 'using'.
+ RoomEvent(Type type, event_mtype_t matrixType,
+ const QJsonObject& contentJson = {});
+ RoomEvent(Type type, const QJsonObject& json);
~RoomEvent() override;
- QString id() const { return _id; }
+ QString id() const;
QDateTime timestamp() const;
QString roomId() const;
QString senderId() const;
@@ -196,10 +357,9 @@ namespace QMatrixClient
* in the event. It's the responsibility of the code calling addId()
* to notify clients that use Q_PROPERTY(id) about its change
*/
- void addId(const QString& id);
+ void addId(const QString& newId);
private:
- QString _id;
event_ptr_tt<RedactionEvent> _redactedBecause;
QString _txnId;
};
@@ -207,43 +367,30 @@ namespace QMatrixClient
using RoomEvents = EventsArray<RoomEvent>;
using RoomEventsRange = Range<RoomEvents>;
- namespace _impl
- {
- template <>
- RoomEventPtr doMakeEvent<RoomEvent>(const QJsonObject& obj);
- }
+ // === State events ===
class StateEventBase: public RoomEvent
{
public:
- explicit StateEventBase(Type type, const QJsonObject& obj)
- : RoomEvent(obj.contains("state_key") ? type : Type::Unknown,
- obj)
- { }
- explicit StateEventBase(Type type)
- : RoomEvent(type)
- { }
+ using factory_t = EventFactory<StateEventBase>;
+
+ using RoomEvent::RoomEvent;
~StateEventBase() override = default;
+ bool isStateEvent() const override { return true; }
virtual bool repeatsState() const;
};
using StateEventPtr = event_ptr_tt<StateEventBase>;
using StateEvents = EventsArray<StateEventBase>;
- namespace _impl
- {
- template <>
- StateEventPtr doMakeEvent<StateEventBase>(const QJsonObject& obj);
- }
-
template <typename ContentT>
struct Prev
{
template <typename... ContentParamTs>
explicit Prev(const QJsonObject& unsignedJson,
ContentParamTs&&... contentParams)
- : senderId(unsignedJson.value("prev_sender").toString())
- , content(unsignedJson.value("prev_content").toObject(),
+ : senderId(unsignedJson.value("prev_sender"_ls).toString())
+ , content(unsignedJson.value(PrevContentKeyL).toObject(),
std::forward<ContentParamTs>(contentParams)...)
{ }
@@ -258,31 +405,33 @@ namespace QMatrixClient
using content_type = ContentT;
template <typename... ContentParamTs>
- explicit StateEvent(Type type, const QJsonObject& obj,
+ explicit StateEvent(Type type, const QJsonObject& fullJson,
ContentParamTs&&... contentParams)
- : StateEventBase(type, obj)
+ : StateEventBase(type, fullJson)
, _content(contentJson(),
std::forward<ContentParamTs>(contentParams)...)
{
- auto unsignedData = obj.value("unsigned").toObject();
- if (unsignedData.contains("prev_content"))
+ const auto& unsignedData = unsignedJson();
+ if (unsignedData.contains(PrevContentKeyL))
_prev = std::make_unique<Prev<ContentT>>(unsignedData,
- std::forward<ContentParamTs>(contentParams)...);
+ std::forward<ContentParamTs>(contentParams)...);
}
template <typename... ContentParamTs>
- explicit StateEvent(Type type, ContentParamTs&&... contentParams)
- : StateEventBase(type)
+ explicit StateEvent(Type type, event_mtype_t matrixType,
+ ContentParamTs&&... contentParams)
+ : StateEventBase(type, matrixType)
, _content(std::forward<ContentParamTs>(contentParams)...)
- { }
-
- QJsonObject toJson() const override { return _content.toJson(); }
+ {
+ editJson().insert(ContentKey, _content.toJson());
+ }
const ContentT& content() const { return _content; }
- /** @deprecated Use prevContent instead */
+ [[deprecated("Use prevContent instead")]]
const ContentT* prev_content() const { return prevContent(); }
const ContentT* prevContent() const
{ return _prev ? &_prev->content : nullptr; }
- QString prevSenderId() const { return _prev ? _prev->senderId : ""; }
+ QString prevSenderId() const
+ { return _prev ? _prev->senderId : QString(); }
protected:
ContentT _content;
diff --git a/lib/events/eventcontent.cpp b/lib/events/eventcontent.cpp
index f5974b46..a6b1c763 100644
--- a/lib/events/eventcontent.cpp
+++ b/lib/events/eventcontent.cpp
@@ -17,8 +17,8 @@
*/
#include "eventcontent.h"
+#include "util.h"
-#include <QtCore/QUrl>
#include <QtCore/QMimeDatabase>
using namespace QMatrixClient::EventContent;
@@ -39,9 +39,9 @@ FileInfo::FileInfo(const QUrl& u, int payloadSize, const QMimeType& mimeType,
FileInfo::FileInfo(const QUrl& u, const QJsonObject& infoJson,
const QString& originalFilename)
: originalInfoJson(infoJson)
- , mimeType(QMimeDatabase().mimeTypeForName(infoJson["mimetype"].toString()))
+ , mimeType(QMimeDatabase().mimeTypeForName(infoJson["mimetype"_ls].toString()))
, url(u)
- , payloadSize(infoJson["size"].toInt())
+ , payloadSize(infoJson["size"_ls].toInt())
, originalName(originalFilename)
{
if (!mimeType.isValid())
@@ -51,8 +51,8 @@ FileInfo::FileInfo(const QUrl& u, const QJsonObject& infoJson,
void FileInfo::fillInfoJson(QJsonObject* infoJson) const
{
Q_ASSERT(infoJson);
- infoJson->insert("size", payloadSize);
- infoJson->insert("mimetype", mimeType.name());
+ infoJson->insert(QStringLiteral("size"), payloadSize);
+ infoJson->insert(QStringLiteral("mimetype"), mimeType.name());
}
ImageInfo::ImageInfo(const QUrl& u, int fileSize, QMimeType mimeType,
@@ -63,23 +63,24 @@ ImageInfo::ImageInfo(const QUrl& u, int fileSize, QMimeType mimeType,
ImageInfo::ImageInfo(const QUrl& u, const QJsonObject& infoJson,
const QString& originalFilename)
: FileInfo(u, infoJson, originalFilename)
- , imageSize(infoJson["w"].toInt(), infoJson["h"].toInt())
+ , imageSize(infoJson["w"_ls].toInt(), infoJson["h"_ls].toInt())
{ }
void ImageInfo::fillInfoJson(QJsonObject* infoJson) const
{
FileInfo::fillInfoJson(infoJson);
- infoJson->insert("w", imageSize.width());
- infoJson->insert("h", imageSize.height());
+ infoJson->insert(QStringLiteral("w"), imageSize.width());
+ infoJson->insert(QStringLiteral("h"), imageSize.height());
}
Thumbnail::Thumbnail(const QJsonObject& infoJson)
- : ImageInfo(infoJson["thumbnail_url"].toString(),
- infoJson["thumbnail_info"].toObject())
+ : ImageInfo(infoJson["thumbnail_url"_ls].toString(),
+ infoJson["thumbnail_info"_ls].toObject())
{ }
void Thumbnail::fillInfoJson(QJsonObject* infoJson) const
{
- infoJson->insert("thumbnail_url", url.toString());
- infoJson->insert("thumbnail_info", toInfoJson<ImageInfo>(*this));
+ infoJson->insert(QStringLiteral("thumbnail_url"), url.toString());
+ infoJson->insert(QStringLiteral("thumbnail_info"),
+ toInfoJson<ImageInfo>(*this));
}
diff --git a/lib/events/eventcontent.h b/lib/events/eventcontent.h
index 9d44aec0..91d7a8c8 100644
--- a/lib/events/eventcontent.h
+++ b/lib/events/eventcontent.h
@@ -21,14 +21,11 @@
// This file contains generic event content definitions, applicable to room
// message events as well as other events (e.g., avatars).
-#include "converters.h"
-
+#include <QtCore/QJsonObject>
#include <QtCore/QMimeType>
#include <QtCore/QUrl>
#include <QtCore/QSize>
-#include <functional>
-
namespace QMatrixClient
{
namespace EventContent
@@ -58,37 +55,6 @@ namespace QMatrixClient
virtual void fillJson(QJsonObject* o) const = 0;
};
- template <typename T = QString>
- class SimpleContent: public Base
- {
- 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)
- : Base(json)
- , value(QMatrixClient::fromJson<T>(json[keyName]))
- , key(std::move(keyName))
- { }
-
- public:
- T value;
-
- protected:
- QString key;
-
- private:
- void fillJson(QJsonObject* json) const override
- {
- Q_ASSERT(json);
- json->insert(key, QMatrixClient::toJson(value));
- }
- };
-
// The below structures fairly follow CS spec 11.2.1.6. The overall
// set of attributes for each content types is a superset of the spec
// but specific aggregation structure is altered. See doc comments to
diff --git a/lib/events/receiptevent.cpp b/lib/events/receiptevent.cpp
index a12f4c05..3451a40e 100644
--- a/lib/events/receiptevent.cpp
+++ b/lib/events/receiptevent.cpp
@@ -35,17 +35,14 @@ Example of a Receipt Event:
#include "receiptevent.h"
-#include "converters.h"
#include "logging.h"
using namespace QMatrixClient;
ReceiptEvent::ReceiptEvent(const QJsonObject& obj)
- : Event(Type::Receipt, obj)
+ : Event(typeId(), obj)
{
- Q_ASSERT(obj["type"].toString() == typeId());
-
- const QJsonObject contents = contentJson();
+ const auto& contents = contentJson();
_eventsWithReceipts.reserve(contents.size());
for( auto eventIt = contents.begin(); eventIt != contents.end(); ++eventIt )
{
@@ -55,14 +52,15 @@ ReceiptEvent::ReceiptEvent(const QJsonObject& obj)
qCDebug(EPHEMERAL) << "ReceiptEvent content follows:\n" << contents;
continue;
}
- const QJsonObject reads = eventIt.value().toObject().value("m.read").toObject();
+ const QJsonObject reads = eventIt.value().toObject()
+ .value("m.read"_ls).toObject();
QVector<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(),
- QMatrixClient::fromJson<QDateTime>(user["ts"])});
+ 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 e8d3a65f..10b3f631 100644
--- a/lib/events/receiptevent.h
+++ b/lib/events/receiptevent.h
@@ -39,14 +39,14 @@ namespace QMatrixClient
class ReceiptEvent: public Event
{
public:
+ DEFINE_EVENT_TYPEID("m.receipt", ReceiptEvent)
explicit ReceiptEvent(const QJsonObject& obj);
- EventsWithReceipts eventsWithReceipts() const
+ const EventsWithReceipts& eventsWithReceipts() const
{ return _eventsWithReceipts; }
- static constexpr const char* typeId() { return "m.receipt"; }
-
private:
EventsWithReceipts _eventsWithReceipts;
};
+ DEFINE_EVENTTYPE_ALIAS(Receipt, ReceiptEvent)
} // namespace QMatrixClient
diff --git a/lib/events/redactionevent.h b/lib/events/redactionevent.h
index dad54788..8c6cbeff 100644
--- a/lib/events/redactionevent.h
+++ b/lib/events/redactionevent.h
@@ -25,19 +25,16 @@ namespace QMatrixClient
class RedactionEvent : public RoomEvent
{
public:
- static constexpr const char* typeId() { return "m.room.redaction"; }
+ DEFINE_EVENT_TYPEID("m.room.redaction", RedactionEvent)
explicit RedactionEvent(const QJsonObject& obj)
- : RoomEvent(Type::Redaction, obj)
- , _redactedEvent(obj.value("redacts").toString())
- , _reason(contentJson().value("reason").toString())
+ : RoomEvent(typeId(), obj)
{ }
- const QString& redactedEvent() const { return _redactedEvent; }
- const QString& reason() const { return _reason; }
-
- private:
- QString _redactedEvent;
- QString _reason;
+ QString redactedEvent() const
+ { return fullJson()["redacts"_ls].toString(); }
+ QString reason() const
+ { return contentJson()["reason"_ls].toString(); }
};
+ DEFINE_EVENTTYPE_ALIAS(Redaction, RedactionEvent)
} // namespace QMatrixClient
diff --git a/lib/events/roomavatarevent.h b/lib/events/roomavatarevent.h
index 0e44ad7c..fe11807c 100644
--- a/lib/events/roomavatarevent.h
+++ b/lib/events/roomavatarevent.h
@@ -20,8 +20,6 @@
#include "event.h"
-#include <utility>
-
#include "eventcontent.h"
namespace QMatrixClient
@@ -33,11 +31,11 @@ namespace QMatrixClient
// without a thumbnail. But The Spec says there be thumbnails, and
// we follow The Spec.
public:
+ DEFINE_EVENT_TYPEID("m.room.avatar", RoomAvatarEvent)
explicit RoomAvatarEvent(const QJsonObject& obj)
- : StateEvent(Type::RoomAvatar, obj)
+ : StateEvent(typeId(), obj)
{ }
-
- static constexpr const char* typeId() { return "m.room.avatar"; }
+ QUrl url() const { return content().url; }
};
-
+ DEFINE_EVENTTYPE_ALIAS(RoomAvatar, RoomAvatarEvent)
} // namespace QMatrixClient
diff --git a/lib/events/roommemberevent.cpp b/lib/events/roommemberevent.cpp
index 76b003c2..3acce7cb 100644
--- a/lib/events/roommemberevent.cpp
+++ b/lib/events/roommemberevent.cpp
@@ -50,10 +50,10 @@ namespace QMatrixClient
}
MemberEventContent::MemberEventContent(const QJsonObject& json)
- : membership(fromJson<MembershipType>(json["membership"]))
- , isDirect(json["is_direct"].toBool())
- , displayName(json["displayname"].toString())
- , avatarUrl(json["avatar_url"].toString())
+ : membership(fromJson<MembershipType>(json["membership"_ls]))
+ , isDirect(json["is_direct"_ls].toBool())
+ , displayName(json["displayname"_ls].toString())
+ , avatarUrl(json["avatar_url"_ls].toString())
{ }
void MemberEventContent::fillJson(QJsonObject* o) const
@@ -62,8 +62,8 @@ void MemberEventContent::fillJson(QJsonObject* o) const
Q_ASSERT_X(membership != MembershipType::Undefined, __FUNCTION__,
"The key 'membership' must be explicit in MemberEventContent");
if (membership != MembershipType::Undefined)
- o->insert("membership", membershipStrings[membership]);
- o->insert("displayname", displayName);
+ o->insert(QStringLiteral("membership"), membershipStrings[membership]);
+ o->insert(QStringLiteral("displayname"), displayName);
if (avatarUrl.isValid())
- o->insert("avatar_url", avatarUrl.toString());
+ o->insert(QStringLiteral("avatar_url"), avatarUrl.toString());
}
diff --git a/lib/events/roommemberevent.h b/lib/events/roommemberevent.h
index 8e0cc0a4..943d5ac6 100644
--- a/lib/events/roommemberevent.h
+++ b/lib/events/roommemberevent.h
@@ -22,8 +22,6 @@
#include "eventcontent.h"
-#include <QtCore/QUrl>
-
namespace QMatrixClient
{
class MemberEventContent: public EventContent::Base
@@ -36,6 +34,9 @@ namespace QMatrixClient
: membership(mt)
{ }
explicit MemberEventContent(const QJsonObject& json);
+ explicit MemberEventContent(const QJsonValue& jv)
+ : MemberEventContent(jv.toObject())
+ { }
MembershipType membership;
bool isDirect = false;
@@ -52,23 +53,26 @@ namespace QMatrixClient
{
Q_GADGET
public:
- static constexpr const char* typeId() { return "m.room.member"; }
+ DEFINE_EVENT_TYPEID("m.room.member", RoomMemberEvent)
using MembershipType = MemberEventContent::MembershipType;
- explicit RoomMemberEvent(Type type, const QJsonObject& obj)
- : StateEvent(type, obj)
+ explicit RoomMemberEvent(const QJsonObject& obj)
+ : StateEvent(typeId(), obj)
{ }
RoomMemberEvent(MemberEventContent&& c)
- : StateEvent(Type::RoomMember, c)
+ : StateEvent(typeId(), matrixTypeId(), c.toJson())
{ }
- explicit RoomMemberEvent(const QJsonObject& obj)
- : RoomMemberEvent(Type::RoomMember, obj)
+
+ // This is a special constructor enabling RoomMemberEvent to be
+ // a base class for more specific member events.
+ RoomMemberEvent(Type type, const QJsonObject& fullJson)
+ : StateEvent(type, fullJson)
{ }
MembershipType membership() const { return content().membership; }
QString userId() const
- { return originalJsonObject().value("state_key").toString(); }
+ { return fullJson()["state_key"_ls].toString(); }
bool isDirect() const { return content().isDirect; }
QString displayName() const { return content().displayName; }
QUrl avatarUrl() const { return content().avatarUrl; }
@@ -76,4 +80,5 @@ namespace QMatrixClient
private:
REGISTER_ENUM(MembershipType)
};
+ DEFINE_EVENTTYPE_ALIAS(RoomMember, RoomMemberEvent)
} // namespace QMatrixClient
diff --git a/lib/events/roommessageevent.cpp b/lib/events/roommessageevent.cpp
index 1a4e74bf..e07054a4 100644
--- a/lib/events/roommessageevent.cpp
+++ b/lib/events/roommessageevent.cpp
@@ -35,7 +35,7 @@ TypedBase* make(const QJsonObject& json)
struct MsgTypeDesc
{
- QString jsonType;
+ QString matrixType;
MsgType enumType;
TypedBase* (*maker)(const QJsonObject&);
};
@@ -56,39 +56,56 @@ QString msgTypeToJson(MsgType enumType)
auto it = std::find_if(msgTypes.begin(), msgTypes.end(),
[=](const MsgTypeDesc& mtd) { return mtd.enumType == enumType; });
if (it != msgTypes.end())
- return it->jsonType;
+ return it->matrixType;
return {};
}
-MsgType jsonToMsgType(const QString& jsonType)
+MsgType jsonToMsgType(const QString& matrixType)
{
auto it = std::find_if(msgTypes.begin(), msgTypes.end(),
- [=](const MsgTypeDesc& mtd) { return mtd.jsonType == jsonType; });
+ [=](const MsgTypeDesc& mtd) { return mtd.matrixType == matrixType; });
if (it != msgTypes.end())
return it->enumType;
return MsgType::Unknown;
}
+inline QJsonObject toMsgJson(const QString& plainBody, const QString& jsonMsgType,
+ TypedBase* content)
+{
+ auto json = content ? content->toJson() : QJsonObject();
+ json.insert(QStringLiteral("msgtype"), jsonMsgType);
+ json.insert(QStringLiteral("body"), plainBody);
+ return json;
+}
+
+static const auto MsgTypeKey = "msgtype"_ls;
+static const auto BodyKey = "body"_ls;
+
+RoomMessageEvent::RoomMessageEvent(const QString& plainBody,
+ const QString& jsonMsgType, TypedBase* content)
+ : RoomEvent(typeId(), matrixTypeId(),
+ toMsgJson(plainBody, jsonMsgType, content))
+ , _content(content)
+{ }
+
RoomMessageEvent::RoomMessageEvent(const QString& plainBody,
MsgType msgType, TypedBase* content)
: RoomMessageEvent(plainBody, msgTypeToJson(msgType), content)
{ }
RoomMessageEvent::RoomMessageEvent(const QJsonObject& obj)
- : RoomEvent(Type::RoomMessage, obj), _content(nullptr)
+ : RoomEvent(typeId(), obj), _content(nullptr)
{
if (isRedacted())
return;
const QJsonObject content = contentJson();
- if ( content.contains("msgtype") && content.contains("body") )
+ if ( content.contains(MsgTypeKey) && content.contains(BodyKey) )
{
- _plainBody = content["body"].toString();
-
- _msgtype = content["msgtype"].toString();
+ auto msgtype = content[MsgTypeKey].toString();
for (const auto& mt: msgTypes)
- if (mt.jsonType == _msgtype)
+ if (mt.matrixType == msgtype)
_content.reset(mt.maker(content));
if (!_content)
@@ -107,13 +124,25 @@ RoomMessageEvent::RoomMessageEvent(const QJsonObject& obj)
RoomMessageEvent::MsgType RoomMessageEvent::msgtype() const
{
- return jsonToMsgType(_msgtype);
+ return jsonToMsgType(rawMsgtype());
+}
+
+QString RoomMessageEvent::rawMsgtype() const
+{
+ return contentJson()[MsgTypeKey].toString();
+}
+
+QString RoomMessageEvent::plainBody() const
+{
+ return contentJson()[BodyKey].toString();
}
QMimeType RoomMessageEvent::mimeType() const
{
- return _content ? _content->type() :
- QMimeDatabase().mimeTypeForName("text/plain");
+ static const auto PlainTextMimeType =
+ QMimeDatabase().mimeTypeForName("text/plain");
+ return _content ? _content->type() : PlainTextMimeType;
+ ;
}
bool RoomMessageEvent::hasTextContent() const
@@ -133,14 +162,6 @@ bool RoomMessageEvent::hasThumbnail() const
return content() && content()->thumbnailInfo();
}
-QJsonObject RoomMessageEvent::toJson() const
-{
- QJsonObject obj = _content ? _content->toJson() : QJsonObject();
- obj.insert("msgtype", msgTypeToJson(msgtype()));
- obj.insert("body", plainBody());
- return obj;
-}
-
TextContent::TextContent(const QString& text, const QString& contentType)
: mimeType(QMimeDatabase().mimeTypeForName(contentType)), body(text)
{ }
@@ -148,26 +169,29 @@ TextContent::TextContent(const QString& text, const QString& contentType)
TextContent::TextContent(const QJsonObject& json)
{
QMimeDatabase db;
+ static const auto PlainTextMimeType = db.mimeTypeForName("text/plain");
+ static const auto HtmlMimeType = db.mimeTypeForName("text/html");
// Special-casing the custom matrix.org's (actually, Riot's) way
// of sending HTML messages.
- if (json["format"].toString() == "org.matrix.custom.html")
+ if (json["format"_ls].toString() == "org.matrix.custom.html")
{
- mimeType = db.mimeTypeForName("text/html");
- body = json["formatted_body"].toString();
+ mimeType = HtmlMimeType;
+ body = json["formatted_body"_ls].toString();
} else {
// Falling back to plain text, as there's no standard way to describe
// rich text in messages.
- mimeType = db.mimeTypeForName("text/plain");
- body = json["body"].toString();
+ mimeType = PlainTextMimeType;
+ body = json[BodyKey].toString();
}
}
void TextContent::fillJson(QJsonObject* json) const
{
Q_ASSERT(json);
- json->insert("format", QStringLiteral("org.matrix.custom.html"));
- json->insert("formatted_body", body);
+ json->insert(QStringLiteral("format"),
+ QStringLiteral("org.matrix.custom.html"));
+ json->insert(QStringLiteral("formatted_body"), body);
}
LocationContent::LocationContent(const QString& geoUri, const ImageInfo& thumbnail)
@@ -176,8 +200,8 @@ LocationContent::LocationContent(const QString& geoUri, const ImageInfo& thumbna
LocationContent::LocationContent(const QJsonObject& json)
: TypedBase(json)
- , geoUri(json["geo_uri"].toString())
- , thumbnail(json["info"].toObject())
+ , geoUri(json["geo_uri"_ls].toString())
+ , thumbnail(json["info"_ls].toObject())
{ }
QMimeType LocationContent::type() const
@@ -188,6 +212,6 @@ QMimeType LocationContent::type() const
void LocationContent::fillJson(QJsonObject* o) const
{
Q_ASSERT(o);
- o->insert("geo_uri", geoUri);
- o->insert("info", toInfoJson(thumbnail));
+ o->insert(QStringLiteral("geo_uri"), geoUri);
+ o->insert(QStringLiteral("info"), toInfoJson(thumbnail));
}
diff --git a/lib/events/roommessageevent.h b/lib/events/roommessageevent.h
index 075d7188..3f3832d4 100644
--- a/lib/events/roommessageevent.h
+++ b/lib/events/roommessageevent.h
@@ -37,6 +37,8 @@ namespace QMatrixClient
Q_PROPERTY(QMimeType mimeType READ mimeType STORED false CONSTANT)
Q_PROPERTY(EventContent::TypedBase* content READ content CONSTANT)
public:
+ DEFINE_EVENT_TYPEID("m.room.message", RoomMessageEvent)
+
enum class MsgType
{
Text, Emote, Notice, Image, File, Location, Video, Audio, Unknown
@@ -44,18 +46,15 @@ namespace QMatrixClient
RoomMessageEvent(const QString& plainBody,
const QString& jsonMsgType,
- EventContent::TypedBase* content = nullptr)
- : RoomEvent(Type::RoomMessage)
- , _msgtype(jsonMsgType), _plainBody(plainBody), _content(content)
- { }
+ EventContent::TypedBase* content = nullptr);
explicit RoomMessageEvent(const QString& plainBody,
MsgType msgType = MsgType::Text,
EventContent::TypedBase* content = nullptr);
explicit RoomMessageEvent(const QJsonObject& obj);
MsgType msgtype() const;
- QString rawMsgtype() const { return _msgtype; }
- const QString& plainBody() const { return _plainBody; }
+ QString rawMsgtype() const;
+ QString plainBody() const;
EventContent::TypedBase* content() const
{ return _content.data(); }
QMimeType mimeType() const;
@@ -63,17 +62,12 @@ namespace QMatrixClient
bool hasFileContent() const;
bool hasThumbnail() const;
- QJsonObject toJson() const override;
-
- static constexpr const char* typeId() { return "m.room.message"; }
-
private:
- QString _msgtype;
- QString _plainBody;
QScopedPointer<EventContent::TypedBase> _content;
REGISTER_ENUM(MsgType)
};
+ DEFINE_EVENTTYPE_ALIAS(RoomMessage, RoomMessageEvent)
using MessageEventType = RoomMessageEvent::MsgType;
namespace EventContent
@@ -140,16 +134,16 @@ namespace QMatrixClient
public:
PlayableContent(const QJsonObject& json)
: ContentT(json)
- , duration(ContentT::originalInfoJson["duration"].toInt())
+ , duration(ContentT::originalInfoJson["duration"_ls].toInt())
{ }
protected:
void fillJson(QJsonObject* json) const override
{
ContentT::fillJson(json);
- auto infoJson = json->take("info").toObject();
- infoJson.insert("duration", duration);
- json->insert("info", infoJson);
+ auto infoJson = json->take("info"_ls).toObject();
+ infoJson.insert(QStringLiteral("duration"), duration);
+ json->insert(QStringLiteral("info"), infoJson);
}
public:
diff --git a/lib/events/simplestateevents.h b/lib/events/simplestateevents.h
index d9f403e8..a117efb1 100644
--- a/lib/events/simplestateevents.h
+++ b/lib/events/simplestateevents.h
@@ -23,31 +23,69 @@
namespace QMatrixClient
{
-#define DEFINE_SIMPLE_STATE_EVENT(_Name, _TypeId, _EnumType, _ContentType, _ContentKey) \
- class _Name \
- : public StateEvent<EventContent::SimpleContent<_ContentType>> \
+ namespace EventContent
+ {
+ template <typename T>
+ class SimpleContent: public Base
+ {
+ 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)
+ : Base(json)
+ , value(QMatrixClient::fromJson<T>(json[keyName]))
+ , key(std::move(keyName))
+ { }
+
+ public:
+ T value;
+
+ protected:
+ QString key;
+
+ private:
+ void fillJson(QJsonObject* json) const override
+ {
+ Q_ASSERT(json);
+ json->insert(key, QMatrixClient::toJson(value));
+ }
+ };
+ } // namespace EventContent
+
+#define DEFINE_SIMPLE_STATE_EVENT(_Name, _TypeId, _ContentType, _ContentKey) \
+ class _Name : public StateEvent<EventContent::SimpleContent<_ContentType>> \
{ \
public: \
- static constexpr const char* typeId() { return _TypeId; } \
+ using content_type = _ContentType; \
+ DEFINE_EVENT_TYPEID(_TypeId, _Name) \
explicit _Name(const QJsonObject& obj) \
- : StateEvent(_EnumType, obj, QStringLiteral(#_ContentKey)) \
+ : StateEvent(typeId(), obj, QStringLiteral(#_ContentKey)) \
{ } \
template <typename T> \
explicit _Name(T&& value) \
- : StateEvent(_EnumType, QStringLiteral(#_ContentKey), \
+ : StateEvent(typeId(), matrixTypeId(), \
+ QStringLiteral(#_ContentKey), \
std::forward<T>(value)) \
{ } \
- const _ContentType& _ContentKey() const { return content().value; } \
- };
+ auto _ContentKey() const { return content().value; } \
+ }; // End of macro
- DEFINE_SIMPLE_STATE_EVENT(RoomNameEvent, "m.room.name",
- Event::Type::RoomName, QString, name)
+ DEFINE_SIMPLE_STATE_EVENT(RoomNameEvent, "m.room.name", QString, name)
+ DEFINE_EVENTTYPE_ALIAS(RoomName, RoomNameEvent)
DEFINE_SIMPLE_STATE_EVENT(RoomAliasesEvent, "m.room.aliases",
- Event::Type::RoomAliases, QStringList, aliases)
+ QStringList, aliases)
+ DEFINE_EVENTTYPE_ALIAS(RoomAliases, RoomAliasesEvent)
DEFINE_SIMPLE_STATE_EVENT(RoomCanonicalAliasEvent, "m.room.canonical_alias",
- Event::Type::RoomCanonicalAlias, QString, alias)
- DEFINE_SIMPLE_STATE_EVENT(RoomTopicEvent, "m.room.topic",
- Event::Type::RoomTopic, QString, topic)
+ QString, alias)
+ DEFINE_EVENTTYPE_ALIAS(RoomCanonicalAlias, RoomCanonicalAliasEvent)
+ DEFINE_SIMPLE_STATE_EVENT(RoomTopicEvent, "m.room.topic", QString, topic)
+ DEFINE_EVENTTYPE_ALIAS(RoomTopic, RoomTopicEvent)
DEFINE_SIMPLE_STATE_EVENT(EncryptionEvent, "m.room.encryption",
- Event::Type::RoomEncryption, QString, algorithm)
+ QString, algorithm)
+ DEFINE_EVENTTYPE_ALIAS(RoomEncryption, EncryptionEvent)
} // namespace QMatrixClient
diff --git a/lib/events/typingevent.cpp b/lib/events/typingevent.cpp
index a4d3bae4..c7b69e33 100644
--- a/lib/events/typingevent.cpp
+++ b/lib/events/typingevent.cpp
@@ -21,12 +21,10 @@
using namespace QMatrixClient;
TypingEvent::TypingEvent(const QJsonObject& obj)
- : Event(Type::Typing, obj)
+ : Event(typeId(), obj)
{
- QJsonValue result;
- result= contentJson()["user_ids"];
- QJsonArray array = result.toArray();
- for( const QJsonValue& user: array )
+ const auto& array = contentJson()["user_ids"_ls].toArray();
+ for(const auto& user: array )
_users.push_back(user.toString());
}
diff --git a/lib/events/typingevent.h b/lib/events/typingevent.h
index 6ccbc1c8..0c4b350d 100644
--- a/lib/events/typingevent.h
+++ b/lib/events/typingevent.h
@@ -20,20 +20,19 @@
#include "event.h"
-#include <QtCore/QStringList>
-
namespace QMatrixClient
{
class TypingEvent: public Event
{
public:
- static constexpr const char* typeId() { return "m.typing"; }
+ DEFINE_EVENT_TYPEID("m.typing", TypingEvent)
TypingEvent(const QJsonObject& obj);
- QStringList users() const { return _users; }
+ const QStringList& users() const { return _users; }
private:
QStringList _users;
};
+ DEFINE_EVENTTYPE_ALIAS(Typing, TypingEvent)
} // namespace QMatrixClient
diff --git a/lib/jobs/sendeventjob.h b/lib/jobs/sendeventjob.h
index a3e9a291..af81ae26 100644
--- a/lib/jobs/sendeventjob.h
+++ b/lib/jobs/sendeventjob.h
@@ -32,9 +32,9 @@ namespace QMatrixClient
SendEventJob(const QString& roomId, const EvT& event)
: BaseJob(HttpVerb::Put, QStringLiteral("SendEventJob"),
QStringLiteral("_matrix/client/r0/rooms/%1/send/%2/")
- .arg(roomId, EvT::typeId()), // See also beforeStart()
+ .arg(roomId, EvT::matrixTypeId()), // See also beforeStart()
Query(),
- Data(event.toJson()))
+ Data(event.contentJson()))
{ }
/**
diff --git a/lib/room.cpp b/lib/room.cpp
index 8e1eb8e8..9f4b5a0e 100644
--- a/lib/room.cpp
+++ b/lib/room.cpp
@@ -110,7 +110,7 @@ class Room::Private
QHash<const User*, QString> lastReadEventIds;
QString serverReadMarker;
TagsMap tags;
- QHash<QString, AccountDataMap> accountData;
+ std::unordered_map<QString, EventPtr> accountData;
QString prevBatch;
QPointer<GetRoomEventsJob> eventsHistoryJob;
@@ -168,7 +168,7 @@ class Room::Private
{
return !ti->isRedacted() &&
ti->senderId() != connection->userId() &&
- ti->type() == EventType::RoomMessage;
+ ti->is<RoomMessageEvent>();
}
void addNewMessageEvents(RoomEvents&& events);
@@ -203,14 +203,14 @@ class Room::Private
auto requestSetState(const QString& stateKey, const EvT& event)
{
return connection->callApi<SetRoomStateWithKeyJob>(
- id, EvT::typeId(), stateKey, event.toJson());
+ id, EvT::matrixTypeId(), stateKey, event.contentJson());
}
template <typename EvT>
auto requestSetState(const EvT& event)
{
return connection->callApi<SetRoomStateJob>(
- id, EvT::typeId(), event.toJson());
+ id, EvT::matrixTypeId(), event.contentJson());
}
/**
@@ -224,8 +224,8 @@ class Room::Private
void broadcastTagUpdates()
{
connection->callApi<SetAccountDataPerRoomJob>(
- connection->userId(), id, TagEvent::typeId(),
- TagEvent(tags).toJson());
+ connection->userId(), id, TagEvent::matrixTypeId(),
+ TagEvent(tags).fullJson());
emit q->tagsChanged();
}
@@ -650,12 +650,14 @@ void Room::resetHighlightCount()
bool Room::hasAccountData(const QString& type) const
{
- return d->accountData.contains(type);
+ return d->accountData.find(type) != d->accountData.end();
}
-Room::AccountDataMap Room::accountData(const QString& type) const
+const EventPtr& Room::accountData(const QString& type) const
{
- return d->accountData.value(type);
+ static EventPtr NoEventPtr {};
+ const auto it = d->accountData.find(type);
+ return it != d->accountData.end() ? it->second : NoEventPtr;
}
QStringList Room::tagNames() const
@@ -723,8 +725,7 @@ const RoomMessageEvent*
Room::Private::getEventWithFile(const QString& eventId) const
{
auto evtIt = q->findInTimeline(eventId);
- if (evtIt != timeline.rend() &&
- evtIt->event()->type() == EventType::RoomMessage)
+ if (evtIt != timeline.rend() && evtIt->event()->is<RoomMessageEvent>())
{
auto* event = evtIt->viewAs<RoomMessageEvent>();
if (event->hasFileContent())
@@ -1274,7 +1275,7 @@ void Room::Private::dropDuplicateEvents(RoomEvents& events) const
inline bool isRedaction(const RoomEventPtr& e)
{
- return e->type() == EventType::Redaction;
+ return e && e->is<RedactionEvent>();
}
void Room::Private::processRedaction(event_ptr_tt<RedactionEvent>&& redaction)
@@ -1292,26 +1293,30 @@ void Room::Private::processRedaction(event_ptr_tt<RedactionEvent>&& redaction)
// Apply the redaction procedure from chapter 6.5 of The Spec
auto originalJson = ti->originalJsonObject();
- if (originalJson.value("unsigned").toObject()
- .value("redacted_because").toObject()
- .value("event_id") == redaction->id())
+ if (originalJson.value(UnsignedKeyL).toObject()
+ .value(RedactedCauseKeyL).toObject()
+ .value(EventIdKeyL) == redaction->id())
{
qCDebug(MAIN) << "Redaction" << redaction->id()
<< "of event" << ti.event()->id() << "already done, skipping";
return;
}
static const QStringList keepKeys =
- { "event_id", "type", "room_id", "sender", "state_key",
- "prev_content", "content", "origin_server_ts" };
- static const
- std::vector<std::pair<EventType, QStringList>> keepContentKeysMap
- { { Event::Type::RoomMember, { "membership" } }
- , { Event::Type::RoomCreate, { "creator" } }
- , { Event::Type::RoomJoinRules, { "join_rule" } }
- , { Event::Type::RoomPowerLevels,
- { "ban", "events", "events_default", "kick", "redact",
- "state_default", "users", "users_default" } }
- , { Event::Type::RoomAliases, { "alias" } }
+ { EventIdKey, TypeKey, QStringLiteral("room_id"),
+ QStringLiteral("sender"), QStringLiteral("state_key"),
+ QStringLiteral("prev_content"), ContentKey,
+ QStringLiteral("origin_server_ts") };
+
+ std::vector<std::pair<Event::Type, QStringList>> keepContentKeysMap
+ { { RoomMemberEvent::typeId(), { QStringLiteral("membership") } }
+// , { RoomCreateEvent::typeId(), { QStringLiteral("creator") } }
+// , { RoomJoinRules::typeId(), { QStringLiteral("join_rule") } }
+// , { RoomPowerLevels::typeId(),
+// { QStringLiteral("ban"), QStringLiteral("events"),
+// QStringLiteral("events_default"), QStringLiteral("kick"),
+// QStringLiteral("redact"), QStringLiteral("state_default"),
+// QStringLiteral("users"), QStringLiteral("users_default") } }
+ , { RoomAliasesEvent::typeId(), { QStringLiteral("alias") } }
};
for (auto it = originalJson.begin(); it != originalJson.end();)
{
@@ -1325,10 +1330,10 @@ void Room::Private::processRedaction(event_ptr_tt<RedactionEvent>&& redaction)
[&ti](const auto& t) { return ti->type() == t.first; } );
if (keepContentKeys == keepContentKeysMap.end())
{
- originalJson.remove("content");
- originalJson.remove("prev_content");
+ originalJson.remove(ContentKeyL);
+ originalJson.remove(PrevContentKeyL);
} else {
- auto content = originalJson.take("content").toObject();
+ auto content = originalJson.take(ContentKeyL).toObject();
for (auto it = content.begin(); it != content.end(); )
{
if (!keepContentKeys->second.contains(it.key()))
@@ -1336,16 +1341,15 @@ void Room::Private::processRedaction(event_ptr_tt<RedactionEvent>&& redaction)
else
++it;
}
- originalJson.insert("content", content);
+ originalJson.insert(ContentKey, content);
}
- auto unsignedData = originalJson.take("unsigned").toObject();
- unsignedData["redacted_because"] = redaction->originalJsonObject();
- originalJson.insert("unsigned", unsignedData);
+ auto unsignedData = originalJson.take(UnsignedKeyL).toObject();
+ unsignedData[RedactedCauseKeyL] = redaction->originalJsonObject();
+ originalJson.insert(QStringLiteral("unsigned"), unsignedData);
// Make a new event from the redacted JSON, exchange events,
// notify everyone and delete the old event
- RoomEventPtr oldEvent
- { ti.replaceEvent(makeEvent<RoomEvent>(originalJson)) };
+ auto oldEvent = ti.replaceEvent(loadEvent<RoomEvent>(originalJson));
q->onRedaction(*oldEvent, *ti.event());
qCDebug(MAIN) << "Redacted" << oldEvent->id() << "with" << redaction->id();
emit q->replacedEvent(ti.event(), rawPtr(oldEvent));
@@ -1442,52 +1446,48 @@ void Room::Private::addHistoricalMessageEvents(RoomEvents&& events)
bool Room::processStateEvent(const RoomEvent& e)
{
- switch (e.type())
- {
- case EventType::RoomName: {
- d->name = static_cast<const RoomNameEvent&>(e).name();
+ return visit(e
+ , [this] (const RoomNameEvent& evt) {
+ d->name = evt.name();
qCDebug(MAIN) << "Room name updated:" << d->name;
return true;
}
- case EventType::RoomAliases: {
- d->aliases = static_cast<const RoomAliasesEvent&>(e).aliases();
+ , [this] (const RoomAliasesEvent& evt) {
+ d->aliases = evt.aliases();
qCDebug(MAIN) << "Room aliases updated:" << d->aliases;
return true;
}
- case EventType::RoomCanonicalAlias: {
- d->canonicalAlias =
- static_cast<const RoomCanonicalAliasEvent&>(e).alias();
+ , [this] (const RoomCanonicalAliasEvent& evt) {
+ d->canonicalAlias = evt.alias();
setObjectName(d->canonicalAlias);
- qCDebug(MAIN) << "Room canonical alias updated:" << d->canonicalAlias;
+ qCDebug(MAIN) << "Room canonical alias updated:"
+ << d->canonicalAlias;
return true;
}
- case EventType::RoomTopic: {
- d->topic = static_cast<const RoomTopicEvent&>(e).topic();
+ , [this] (const RoomTopicEvent& evt) {
+ d->topic = evt.topic();
qCDebug(MAIN) << "Room topic updated:" << d->topic;
emit topicChanged();
return false;
}
- case EventType::RoomAvatar: {
- const auto& avatarEventContent =
- static_cast<const RoomAvatarEvent&>(e).content();
- if (d->avatar.updateUrl(avatarEventContent.url))
+ , [this] (const RoomAvatarEvent& evt) {
+ if (d->avatar.updateUrl(evt.url()))
{
qCDebug(MAIN) << "Room avatar URL updated:"
- << avatarEventContent.url.toString();
+ << evt.url().toString();
emit avatarChanged();
}
return false;
}
- case EventType::RoomMember: {
- const auto& memberEvent = static_cast<const RoomMemberEvent&>(e);
- auto* u = user(memberEvent.userId());
- u->processEvent(memberEvent, this);
+ , [this] (const RoomMemberEvent& evt) {
+ auto* u = user(evt.userId());
+ u->processEvent(evt, this);
if (u == localUser() && memberJoinState(u) == JoinState::Invite
- && memberEvent.isDirect())
+ && evt.isDirect())
connection()->addToDirectChats(this,
- user(memberEvent.senderId()));
+ user(evt.senderId()));
- if( memberEvent.membership() == MembershipType::Join )
+ if( evt.membership() == MembershipType::Join )
{
if (memberJoinState(u) != JoinState::Join)
{
@@ -1505,7 +1505,7 @@ bool Room::processStateEvent(const RoomEvent& e)
emit userAdded(u);
}
}
- else if( memberEvent.membership() == MembershipType::Leave )
+ else if( evt.membership() == MembershipType::Leave )
{
if (memberJoinState(u) == JoinState::Join)
{
@@ -1517,52 +1517,43 @@ bool Room::processStateEvent(const RoomEvent& e)
}
return false;
}
- case EventType::RoomEncryption:
- {
- d->encryptionAlgorithm =
- static_cast<const EncryptionEvent&>(e).algorithm();
- qCDebug(MAIN) << "Encryption switched on in" << displayName();
+ , [this] (const EncryptionEvent& evt) {
+ d->encryptionAlgorithm = evt.algorithm();
+ qCDebug(MAIN) << "Encryption switched on in room" << id()
+ << "with algorithm" << d->encryptionAlgorithm;
emit encryption();
return false;
}
- default:
- /* Ignore events of other types */
- return false;
- }
+ );
}
void Room::processEphemeralEvent(EventPtr&& event)
{
QElapsedTimer et; et.start();
- switch (event->type())
- {
- case EventType::Typing: {
- auto typingEvent = ptrCast<TypingEvent>(move(event));
+ visit(event
+ , [this,et] (const TypingEvent& evt) {
d->usersTyping.clear();
- for( const QString& userId: typingEvent->users() )
+ for( const QString& userId: qAsConst(evt.users()) )
{
auto u = user(userId);
if (memberJoinState(u) == JoinState::Join)
d->usersTyping.append(u);
}
- if (!typingEvent->users().isEmpty())
+ if (!evt.users().isEmpty())
qCDebug(PROFILER) << "*** Room::processEphemeralEvent(typing):"
- << typingEvent->users().size() << "users," << et;
+ << evt.users().size() << "users," << et;
emit typingChanged();
- break;
}
- case EventType::Receipt: {
- auto receiptEvent = ptrCast<ReceiptEvent>(move(event));
- for( const auto &p: receiptEvent->eventsWithReceipts() )
+ , [this,et] (const ReceiptEvent& evt) {
+ for( const auto &p: qAsConst(evt.eventsWithReceipts()) )
{
{
if (p.receipts.size() == 1)
qCDebug(EPHEMERAL) << "Marking" << p.evtId
- << "as read for" << p.receipts[0].userId;
+ << "as read for" << p.receipts[0].userId;
else
qCDebug(EPHEMERAL) << "Marking" << p.evtId
- << "as read for"
- << p.receipts.size() << "users";
+ << "as read for" << p.receipts.size() << "users";
}
const auto newMarker = findInTimeline(p.evtId);
if (newMarker != timelineEdge())
@@ -1578,7 +1569,7 @@ void Room::processEphemeralEvent(EventPtr&& event)
} else
{
qCDebug(EPHEMERAL) << "Event" << p.evtId
- << "not found; saving read receipts anyway";
+ << "not found; saving read receipts anyway";
// If the event is not found (most likely, because it's too old
// and hasn't been fetched from the server yet), but there is
// a previous marker for a user, keep the previous marker.
@@ -1594,37 +1585,28 @@ void Room::processEphemeralEvent(EventPtr&& event)
}
}
}
- if (!receiptEvent->eventsWithReceipts().isEmpty())
+ if (!evt.eventsWithReceipts().isEmpty())
qCDebug(PROFILER) << "*** Room::processEphemeralEvent(receipts):"
- << receiptEvent->eventsWithReceipts().size()
+ << evt.eventsWithReceipts().size()
<< "events with receipts," << et;
- break;
}
- default:
- qCWarning(EPHEMERAL) << "Unexpected event type in 'ephemeral' batch:"
- << event->jsonType();
- }
+ );
}
void Room::processAccountDataEvent(EventPtr&& event)
{
- switch (event->type())
- {
- case EventType::Tag:
- {
- auto newTags = ptrCast<const TagEvent>(move(event))->tags();
+ visit(event
+ , [this] (const TagEvent& evt) {
+ auto newTags = evt.tags();
if (newTags == d->tags)
- break;
+ return;
d->tags = newTags;
qCDebug(MAIN) << "Room" << id() << "is tagged with:"
<< tagNames().join(", ");
emit tagsChanged();
- break;
}
- case EventType::ReadMarker:
- {
- auto readEventId =
- ptrCast<const ReadMarkerEvent>(move(event))->event_id();
+ , [this] (const ReadMarkerEvent& evt) {
+ auto readEventId = evt.event_id();
qCDebug(MAIN) << "Server-side read marker at" << readEventId;
d->serverReadMarker = readEventId;
const auto newMarker = findInTimeline(readEventId);
@@ -1632,12 +1614,18 @@ void Room::processAccountDataEvent(EventPtr&& event)
d->markMessagesAsRead(newMarker);
else
d->setLastReadEvent(localUser(), readEventId);
- break;
}
- default:
- d->accountData[event->jsonType()] =
- fromJson<AccountDataMap>(event->contentJson());
- emit accountDataChanged(event->jsonType());
+ );
+ // For all account data events
+ auto& currentData = d->accountData[event->matrixType()];
+ // A polymorphic event-specific comparison might be a bit more
+ // efficient; maaybe do it another day
+ if (!currentData || currentData->contentJson() != event->contentJson())
+ {
+ currentData = std::move(event);
+ qCDebug(MAIN) << "Updated account data of type"
+ << currentData->matrixType();
+ emit accountDataChanged(currentData->matrixType());
}
}
@@ -1674,8 +1662,8 @@ QString Room::Private::roomNameFromMemberNames(const QList<User *> &userlist) co
// ii. Two users besides the current one.
if (userlist.size() == 3)
return tr("%1 and %2")
- .arg(q->roomMembername(first_two[0]))
- .arg(q->roomMembername(first_two[1]));
+ .arg(q->roomMembername(first_two[0]),
+ q->roomMembername(first_two[1]));
// iii. More users.
if (userlist.size() > 3)
@@ -1731,11 +1719,11 @@ void appendStateEvent(QJsonArray& events, const QString& type,
const QJsonObject& content, const QString& stateKey = {})
{
if (!content.isEmpty() || !stateKey.isEmpty())
- events.append(QJsonObject
- { { QStringLiteral("type"), type }
- , { QStringLiteral("content"), content }
- , { QStringLiteral("state_key"), stateKey }
- });
+ {
+ auto json = basicEventJson(type, content);
+ json.insert(QStringLiteral("state_key"), stateKey);
+ events.append(json);
+ }
}
#define ADD_STATE_EVENT(events, type, name, content) \
@@ -1746,16 +1734,13 @@ void appendEvent(QJsonArray& events, const QString& type,
const QJsonObject& content)
{
if (!content.isEmpty())
- events.append(QJsonObject
- { { QStringLiteral("type"), type }
- , { QStringLiteral("content"), content }
- });
+ events.append(basicEventJson(type, content));
}
template <typename EvtT>
void appendEvent(QJsonArray& events, const EvtT& event)
{
- appendEvent(events, EvtT::typeId(), event.toJson());
+ appendEvent(events, EvtT::matrixTypeId(), event.toJson());
}
QJsonObject Room::Private::toJson() const
@@ -1790,29 +1775,23 @@ QJsonObject Room::Private::toJson() const
}
QJsonArray accountDataEvents;
- if (!tags.empty())
- appendEvent(accountDataEvents, TagEvent(tags));
-
- if (!serverReadMarker.isEmpty())
- appendEvent(accountDataEvents, ReadMarkerEvent(serverReadMarker));
-
if (!accountData.empty())
{
- for (auto it = accountData.begin(); it != accountData.end(); ++it)
- appendEvent(accountDataEvents, it.key(),
- QMatrixClient::toJson(it.value()));
+ for (const auto& e: accountData)
+ appendEvent(accountDataEvents, e.first, e.second->contentJson());
}
- result.insert("account_data", QJsonObject {{ "events", accountDataEvents }});
+ result.insert(QStringLiteral("account_data"),
+ QJsonObject {{ QStringLiteral("events"), accountDataEvents }});
- QJsonObject unreadNotificationsObj;
+ QJsonObject unreadNotifObj
+ { { SyncRoomData::UnreadCountKey, unreadMessages } };
- unreadNotificationsObj.insert(SyncRoomData::UnreadCountKey, unreadMessages);
if (highlightCount > 0)
- unreadNotificationsObj.insert("highlight_count", highlightCount);
+ unreadNotifObj.insert(QStringLiteral("highlight_count"), highlightCount);
if (notificationCount > 0)
- unreadNotificationsObj.insert("notification_count", notificationCount);
+ unreadNotifObj.insert(QStringLiteral("notification_count"), notificationCount);
- result.insert("unread_notifications", unreadNotificationsObj);
+ result.insert(QStringLiteral("unread_notifications"), unreadNotifObj);
if (et.elapsed() > 50)
qCDebug(PROFILER) << "Room::toJson() for" << displayname << "took" << et;
diff --git a/lib/room.h b/lib/room.h
index f2a372e9..21016c38 100644
--- a/lib/room.h
+++ b/lib/room.h
@@ -129,10 +129,6 @@ namespace QMatrixClient
using rev_iter_t = Timeline::const_reverse_iterator;
using timeline_iter_t = Timeline::const_iterator;
- using AccountDataMap = std::conditional_t<
- QT_VERSION >= QT_VERSION_CHECK(5, 5, 0),
- QVariantHash, QVariantMap>;
-
Room(Connection* connection, QString id, JoinState initialJoinState);
~Room() override;
@@ -279,7 +275,7 @@ namespace QMatrixClient
* stored on the server. Tags and read markers cannot be retrieved
* using this method _yet_.
*/
- AccountDataMap accountData(const QString& type) const;
+ const EventPtr& accountData(const QString& type) const;
QStringList tagNames() const;
TagsMap tags() const;
diff --git a/lib/user.h b/lib/user.h
index 76aa672f..1cf72155 100644
--- a/lib/user.h
+++ b/lib/user.h
@@ -106,10 +106,18 @@ namespace QMatrixClient
void processEvent(const RoomMemberEvent& event, const Room* r);
public slots:
+ /** Set a new name in the global user profile */
void rename(const QString& newName);
+ /** Set a new name for the user in one room */
void rename(const QString& newName, const Room* r);
+ /** Upload the file and use it as an avatar */
bool setAvatar(const QString& fileName);
+ /** Upload contents of the QIODevice and set that as an avatar */
bool setAvatar(QIODevice* source);
+ /** Create or find a direct chat with this user
+ * The resulting chat is returned asynchronously via
+ * Connection::directChatAvailable()
+ */
void requestDirectChat();
signals:
diff --git a/lib/util.h b/lib/util.h
index d6e1cef6..28315429 100644
--- a/lib/util.h
+++ b/lib/util.h
@@ -59,6 +59,42 @@ namespace QMatrixClient
return std::unique_ptr<T1>(static_cast<T1*>(p.release()));
}
+ /** Determine traits of an arbitrary function/lambda/functor
+ * This only works with arity of 1 (1-argument) for now but is extendable
+ * to other cases. Also, doesn't work with generic lambdas and function
+ * objects that have operator() overloaded
+ * \sa https://stackoverflow.com/questions/7943525/is-it-possible-to-figure-out-the-parameter-type-and-return-type-of-a-lambda#7943765
+ */
+ template <typename T>
+ struct function_traits : public function_traits<decltype(&T::operator())>
+ { }; // A generic function object that has (non-overloaded) operator()
+
+ // Specialisation for a function
+ template <typename ReturnT, typename ArgT>
+ struct function_traits<ReturnT(ArgT)>
+ {
+ using return_type = ReturnT;
+ using arg_type = ArgT;
+ };
+
+ // Specialisation for a member function
+ template <typename ReturnT, typename ClassT, typename ArgT>
+ struct function_traits<ReturnT(ClassT::*)(ArgT)>
+ : function_traits<ReturnT(ArgT)>
+ { };
+
+ // Specialisation for a const member function
+ template <typename ReturnT, typename ClassT, typename ArgT>
+ struct function_traits<ReturnT(ClassT::*)(ArgT) const>
+ : function_traits<ReturnT(ArgT)>
+ { };
+
+ template <typename FnT>
+ using fn_return_t = typename function_traits<FnT>::return_type;
+
+ template <typename FnT>
+ using fn_arg_t = typename function_traits<FnT>::arg_type;
+
#if QT_VERSION < QT_VERSION_CHECK(5, 7, 0)
// Copy-pasted from Qt 5.10
template <typename T>
@@ -68,6 +104,11 @@ namespace QMatrixClient
static void qAsConst(const T &&) Q_DECL_EQ_DELETE;
#endif
+ inline auto operator"" _ls(const char* s, std::size_t size)
+ {
+ return QLatin1String(s, int(size));
+ }
+
/** An abstraction over a pair of iterators
* This is a very basic range type over a container with iterators that
* are at least ForwardIterators. Inspired by Ranges TS.