aboutsummaryrefslogtreecommitdiff
path: root/lib/events/event.h
diff options
context:
space:
mode:
Diffstat (limited to 'lib/events/event.h')
-rw-r--r--lib/events/event.h314
1 files changed, 314 insertions, 0 deletions
diff --git a/lib/events/event.h b/lib/events/event.h
new file mode 100644
index 00000000..d614115a
--- /dev/null
+++ b/lib/events/event.h
@@ -0,0 +1,314 @@
+/******************************************************************************
+ * Copyright (C) 2015 Felix Rohrbach <kde@fxrh.de>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#pragma once
+
+#include <QtCore/QString>
+#include <QtCore/QDateTime>
+#include <QtCore/QJsonObject>
+#include <QtCore/QJsonArray>
+
+#include "util.h"
+
+#include <memory>
+
+namespace QMatrixClient
+{
+ template <typename EventT>
+ using event_ptr_tt = std::unique_ptr<EventT>;
+
+ namespace _impl
+ {
+ template <typename EventT>
+ event_ptr_tt<EventT> doMakeEvent(const QJsonObject& obj);
+ }
+
+ 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);
+ Event(const Event&) = delete;
+ virtual ~Event();
+
+ Type type() const { return _type; }
+ QString jsonType() const;
+ bool isStateEvent() const
+ {
+ return (quint16(_type) & 0x1800) == 0x1800;
+ }
+ QByteArray originalJson() const;
+ QJsonObject originalJsonObject() const;
+
+ // 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).
+
+ const QJsonObject contentJson() const;
+
+ private:
+ Type _type;
+ QJsonObject _originalJson;
+
+ 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>
+ event_ptr_tt<EventT> makeEvent(const QJsonObject& obj)
+ {
+ auto e = _impl::doMakeEvent<EventT>(obj);
+ if (!e)
+ e = std::make_unique<EventT>(EventType::Unknown, obj);
+ return e;
+ }
+
+ namespace _impl
+ {
+ template <>
+ EventPtr doMakeEvent<Event>(const QJsonObject& obj);
+ }
+
+ /**
+ * \brief A vector of pointers to events with deserialisation capabilities
+ *
+ * This is a simple wrapper over a generic vector type that adds
+ * a convenience method to deserialise events from QJsonArray.
+ * \tparam EventT base type of all events in the vector
+ */
+ template <typename EventT>
+ class EventsBatch : public std::vector<event_ptr_tt<EventT>>
+ {
+ public:
+ /**
+ * \brief Deserialise events from an array
+ *
+ * Given the following JSON construct, creates events from
+ * the array stored at key "node":
+ * \code
+ * "container": {
+ * "node": [ { "event_id": "!evt1:srv.org", ... }, ... ]
+ * }
+ * \endcode
+ * \param container - the wrapping JSON object
+ * \param node - the key in container that holds the array of events
+ */
+ void fromJson(const QJsonObject& container, const QString& node)
+ {
+ const auto objs = container.value(node).toArray();
+ using size_type = typename std::vector<event_ptr_tt<EventT>>::size_type;
+ // The below line accommodates the difference in size types of
+ // STL and Qt containers.
+ this->reserve(static_cast<size_type>(objs.size()));
+ for (const auto& objValue: objs)
+ this->emplace_back(makeEvent<EventT>(objValue.toObject()));
+ }
+ };
+ using Events = EventsBatch<Event>;
+
+ class RedactionEvent;
+
+ /** This class corresponds to m.room.* events */
+ class RoomEvent : public Event
+ {
+ Q_GADGET
+ Q_PROPERTY(QString id READ id)
+ Q_PROPERTY(QDateTime timestamp READ timestamp CONSTANT)
+ Q_PROPERTY(QString roomId READ roomId CONSTANT)
+ Q_PROPERTY(QString senderId READ senderId CONSTANT)
+ Q_PROPERTY(QString redactionReason READ redactionReason)
+ Q_PROPERTY(bool isRedacted READ isRedacted)
+ Q_PROPERTY(QString transactionId READ transactionId)
+ public:
+ // RedactionEvent is an incomplete type here so we cannot inline
+ // constructors and destructors
+ explicit RoomEvent(Type type);
+ RoomEvent(Type type, const QJsonObject& rep);
+ ~RoomEvent();
+
+ QString id() const { return _id; }
+ QDateTime timestamp() const;
+ QString roomId() const;
+ QString senderId() const;
+ bool isRedacted() const { return bool(_redactedBecause); }
+ const RedactionEvent* redactedBecause() const
+ {
+ return _redactedBecause.get();
+ }
+ QString redactionReason() const;
+ const QString& transactionId() const { return _txnId; }
+
+ /**
+ * Sets the transaction id for locally created events. This should be
+ * done before the event is exposed to any code using the respective
+ * Q_PROPERTY.
+ *
+ * \param txnId - transaction id, normally obtained from
+ * Connection::generateTxnId()
+ */
+ void setTransactionId(const QString& txnId) { _txnId = txnId; }
+
+ /**
+ * Sets event id for locally created events
+ *
+ * When a new event is created locally, it has no server id yet.
+ * This function allows to add the id once the confirmation from
+ * the server is received. There should be no id set previously
+ * in the event. It's the responsibility of the code calling addId()
+ * to notify clients that use Q_PROPERTY(id) about its change
+ */
+ void addId(const QString& id);
+
+ private:
+ QString _id;
+// QString _roomId;
+// QString _senderId;
+// QDateTime _serverTimestamp;
+ event_ptr_tt<RedactionEvent> _redactedBecause;
+ QString _txnId;
+ };
+ using RoomEvents = EventsBatch<RoomEvent>;
+ using RoomEventPtr = event_ptr_tt<RoomEvent>;
+
+ namespace _impl
+ {
+ template <>
+ RoomEventPtr doMakeEvent<RoomEvent>(const QJsonObject& obj);
+ }
+
+ /**
+ * Conceptually similar to QStringView (but much more primitive), it's a
+ * simple abstraction over a pair of RoomEvents::const_iterator values
+ * referring to the beginning and the end of a range in a RoomEvents
+ * container.
+ */
+ struct RoomEventsRange
+ {
+ RoomEvents::iterator from;
+ RoomEvents::iterator to;
+
+ RoomEvents::size_type size() const
+ {
+ Q_ASSERT(std::distance(from, to) >= 0);
+ return RoomEvents::size_type(std::distance(from, to));
+ }
+ bool empty() const { return from == to; }
+ RoomEvents::const_iterator begin() const { return from; }
+ RoomEvents::const_iterator end() const { return to; }
+ RoomEvents::iterator begin() { return from; }
+ RoomEvents::iterator end() { return to; }
+ };
+
+ 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)
+ { }
+ ~StateEventBase() override = 0;
+
+ virtual bool repeatsState() const;
+ };
+
+ 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(),
+ std::forward<ContentParamTs>(contentParams)...)
+ { }
+
+ QString senderId;
+ ContentT content;
+ };
+
+ template <typename ContentT>
+ class StateEvent: public StateEventBase
+ {
+ public:
+ using content_type = ContentT;
+
+ template <typename... ContentParamTs>
+ explicit StateEvent(Type type, const QJsonObject& obj,
+ ContentParamTs&&... contentParams)
+ : StateEventBase(type, obj)
+ , _content(contentJson(),
+ std::forward<ContentParamTs>(contentParams)...)
+ {
+ auto unsignedData = obj.value("unsigned").toObject();
+ if (unsignedData.contains("prev_content"))
+ _prev = std::make_unique<Prev<ContentT>>(unsignedData,
+ std::forward<ContentParamTs>(contentParams)...);
+ }
+ template <typename... ContentParamTs>
+ explicit StateEvent(Type type, ContentParamTs&&... contentParams)
+ : StateEventBase(type)
+ , _content(std::forward<ContentParamTs>(contentParams)...)
+ { }
+
+ QJsonObject toJson() const { return _content.toJson(); }
+
+ const ContentT& content() const { return _content; }
+ /** @deprecated Use prevContent instead */
+ const ContentT* prev_content() const { return prevContent(); }
+ const ContentT* prevContent() const
+ { return _prev ? &_prev->content : nullptr; }
+ QString prevSenderId() const { return _prev ? _prev->senderId : ""; }
+
+ protected:
+ ContentT _content;
+ std::unique_ptr<Prev<ContentT>> _prev;
+ };
+} // namespace QMatrixClient
+Q_DECLARE_METATYPE(QMatrixClient::Event*)
+Q_DECLARE_METATYPE(QMatrixClient::RoomEvent*)
+Q_DECLARE_METATYPE(const QMatrixClient::Event*)
+Q_DECLARE_METATYPE(const QMatrixClient::RoomEvent*)