diff options
Diffstat (limited to 'lib/events/event.h')
-rw-r--r-- | lib/events/event.h | 314 |
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*) |