diff options
Diffstat (limited to 'events/roommessageevent.h')
-rw-r--r-- | events/roommessageevent.h | 330 |
1 files changed, 285 insertions, 45 deletions
diff --git a/events/roommessageevent.h b/events/roommessageevent.h index 299b1b19..308ce742 100644 --- a/events/roommessageevent.h +++ b/events/roommessageevent.h @@ -24,122 +24,362 @@ #include <QtCore/QMimeType> #include <QtCore/QSize> -#include <memory> - namespace QMatrixClient { namespace MessageEventContent { + /** + * A base class for all content types that can be stored + * in a RoomMessageEvent + * + * Each content type class should have a constructor taking + * a QJsonObject and override fillJson() with an implementation + * that will fill the target QJsonObject with stored values. It is + * assumed but not required that a content object can also be created + * from plain data. fillJson() should only fill the main JSON object + * but not the "info" subobject if it exists for a certain content type; + * use \p InfoBase to de/serialize "info" parts with an optional URL + * on the top level. + */ class Base { - Q_GADGET public: - enum class Type + virtual ~Base() = default; + QJsonObject toJson() const { - Text, Emote, Notice, Image, File, Location, Video, Audio, Unknown - }; + QJsonObject o; + fillJson(&o); + return o; + } - virtual ~Base() = default; + protected: + virtual void fillJson(QJsonObject* o) const = 0; + }; - REGISTER_ENUM(Type) + /** + * A base class for content types that have an "info" object in their + * JSON representation + * + * These include most multimedia types currently in the CS API spec. + * Derived classes should override fillInfoJson() to fill the "info" + * subobject, BUT NOT the main JSON object. Most but not all "info" + * classes (specifically, those deriving from UrlInfo) should also + * have a constructor that accepts two parameters, QUrl and QJsonObject, + * in order to load the URL+info part from JSON. + */ + class InfoBase: public Base + { + public: + QJsonObject toInfoJson() const; + + protected: + virtual void fillInfoJson(QJsonObject* infoJson) const { } }; - using CType = Base::Type; } // namespace MessageEventContent - using MessageEventType = MessageEventContent::CType; + /** + * The event class corresponding to m.room.message events + */ class RoomMessageEvent: public RoomEvent { + Q_GADGET public: + enum class MsgType + { + Text, Emote, Notice, Image, File, Location, Video, Audio, Unknown + }; + + RoomMessageEvent(const QString& roomId, const QString& fromUserId, + const QString& plainMessage, + MsgType msgType = MsgType::Text) + : RoomEvent(Type::RoomMessage, roomId, fromUserId) + , _msgtype(msgType), _plainBody(plainMessage), _content(nullptr) + { } + RoomMessageEvent(const QString& roomId, const QString& fromUserId, + const QString& plainBody, + MessageEventContent::Base* content, + MsgType msgType) + : RoomEvent(Type::RoomMessage, roomId, fromUserId) + , _msgtype(msgType), _plainBody(plainBody), _content(content) + { } explicit RoomMessageEvent(const QJsonObject& obj); - ~RoomMessageEvent(); - MessageEventType msgtype() const { return _msgtype; } - const QString& plainBody() const { return _plainBody; } - const MessageEventContent::Base* content() const { return _content; } + MsgType msgtype() const { return _msgtype; } + const QString& plainBody() const { return _plainBody; } + const MessageEventContent::Base* content() const + { return _content.data(); } + + QJsonObject toJson() const; + + static constexpr const char* TypeId = "m.room.message"; private: - MessageEventType _msgtype; + MsgType _msgtype; QString _plainBody; - MessageEventContent::Base* _content; + QScopedPointer<MessageEventContent::Base> _content; + + REGISTER_ENUM(MsgType) }; + using MessageEventType = RoomMessageEvent::MsgType; namespace MessageEventContent { // 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. + // but specific aggregation structure is altered. See doc comments to + // each type for the list of available attributes. + /** + * Rich text content for m.text, m.emote, m.notice + * + * Available fields: mimeType, body. The body can be either rich text + * or plain text, depending on what mimeType specifies. + */ class TextContent: public Base { public: + TextContent(const QString& text, const QString& contentType); explicit TextContent(const QJsonObject& json); + void fillJson(QJsonObject* json) const override; + QMimeType mimeType; QString body; }; - class FileInfo: public Base + /** + * Base class for content types that consist of a URL along with + * additional information + * + * All message types except the (hyper)text mentioned above and + * m.location fall under this category. + */ + class FileInfo: public InfoBase { public: - FileInfo(QUrl u, const QJsonObject& infoJson, - QString originalFilename = QString()); + explicit FileInfo(const QUrl& u, int payloadSize = -1, + const QMimeType& mimeType = {}, + const QString& originalFilename = {}); + FileInfo(const QUrl& u, const QJsonObject& infoJson, + const QString& originalFilename = {}); QUrl url; - int fileSize; + int payloadSize; QMimeType mimetype; QString originalName; + + protected: + void fillJson(QJsonObject* json) const override; + void fillInfoJson(QJsonObject* infoJson) const override; }; - class ImageInfo: public FileInfo + /** + * A base class for image info types: image, thumbnail, video + * + * \tparam InfoT base info class; should derive from \p InfoBase + */ + template <class InfoT = FileInfo> + class ImageInfo : public InfoT { public: - ImageInfo(QUrl u, const QJsonObject& infoJson); + explicit ImageInfo(const QUrl& u, int fileSize = -1, + QMimeType mimeType = {}, + const QSize& imageSize = {}) + : InfoT(u, fileSize, mimeType), imageSize(imageSize) + { } + ImageInfo(const QUrl& u, const QJsonObject& infoJson, + const QString& originalFilename = {}) + : InfoT(u, infoJson, originalFilename) + , imageSize(infoJson["w"].toInt(), infoJson["h"].toInt()) + { } + + void fillInfoJson(QJsonObject* infoJson) const /* override */ + { + InfoT::fillInfoJson(infoJson); + infoJson->insert("w", imageSize.width()); + infoJson->insert("h", imageSize.height()); + } QSize imageSize; }; - template <class ContentInfoT> - class ThumbnailedContent: public ContentInfoT + /** + * A base class for an info type that carries a thumbnail + * + * This class provides a means to save/load a thumbnail to/from "info" + * subobject of the JSON representation of a message; namely, + * "info/thumbnail_url" and "info/thumbnail_info" fields are used. + * + * \tparam InfoT base info class; should derive from \p InfoBase + */ + template <class InfoT = InfoBase> + class Thumbnailed : public InfoT { public: - explicit ThumbnailedContent(const QJsonObject& json) - : ContentInfoT(json["url"].toString(), json["info"].toObject()) - , thumbnail(json["thumbnail_url"].toString(), - json["thumbnail_info"].toObject()) + template <typename... ArgTs> + explicit Thumbnailed(const ImageInfo<>& thumbnail, + ArgTs&&... infoArgs) + : InfoT(std::forward<ArgTs>(infoArgs)...) + , thumbnail(thumbnail) { } - ImageInfo thumbnail; + explicit Thumbnailed(const QJsonObject& infoJson) + : thumbnail(infoJson["thumbnail_url"].toString(), + infoJson["thumbnail_info"].toObject()) + { } + + Thumbnailed(const QUrl& u, const QJsonObject& infoJson, + const QString& originalFilename = {}) + : InfoT(u, infoJson, originalFilename) + , thumbnail(infoJson["thumbnail_url"].toString(), + infoJson["thumbnail_info"].toObject()) + { } + + void fillInfoJson(QJsonObject* infoJson) const /* override */ + { + InfoT::fillInfoJson(infoJson); + infoJson->insert("thumbnail_url", thumbnail.url.toString()); + infoJson->insert("thumbnail_info", thumbnail.toInfoJson()); + } + + ImageInfo<> thumbnail; }; - using ImageContent = ThumbnailedContent<ImageInfo>; - using FileContent = ThumbnailedContent<FileInfo>; + /** + * One more facility base class for content types that have a URL and + * additional info + * + * The assumed layout for types enabled by a combination of UrlInfo and + * UrlWith<> is the following: "url" and, optionally, "filename" in the + * top-level JSON and the rest of information inside the "info" subobject. + * + * \tparam InfoT base info class; should derive from \p UrlInfo or + * provide a constructor with a compatible signature + */ + template <class InfoT> // InfoT : public FileInfo + class UrlWith : public InfoT + { + public: + using InfoT::InfoT; + explicit UrlWith(const QJsonObject& json) + : InfoT(json["url"].toString(), json["info"].toObject(), + json["filename"].toString()) + { } + }; - class LocationContent: public Base + /** + * Content class for m.image + * + * Available fields: + * - corresponding to the top-level JSON: + * - url + * - filename (extension to the spec) + * - corresponding to the "info" subobject: + * - payloadSize ("size" in JSON) + * - mimeType ("mimetype" in JSON) + * - imageSize (QSize for a combination of "h" and "w" in JSON) + * - thumbnail.url ("thumbnail_url" in JSON) + * - corresponding to the "info/thumbnail_info" subobject: contents of + * thumbnail field, in the same vein as for the main image: + * - payloadSize + * - mimeType + * - imageSize + */ + using ImageContent = UrlWith<Thumbnailed<ImageInfo<>>>; + + /** + * Content class for m.file + * + * Available fields: + * - corresponding to the top-level JSON: + * - url + * - filename + * - corresponding to the "info" subobject: + * - payloadSize ("size" in JSON) + * - mimeType ("mimetype" in JSON) + * - thumbnail.url ("thumbnail_url" in JSON) + * - corresponding to the "info/thumbnail_info" subobject: + * - thumbnail.payloadSize + * - thumbnail.mimeType + * - thumbnail.imageSize (QSize for "h" and "w" in JSON) + */ + using FileContent = UrlWith<Thumbnailed<FileInfo>>; + + /** + * Content class for m.location + * + * Available fields: + * - corresponding to the top-level JSON: + * - geoUri ("geo_uri" in JSON) + * - corresponding to the "info" subobject: + * - thumbnail.url ("thumbnail_url" in JSON) + * - corresponding to the "info/thumbnail_info" subobject: + * - thumbnail.payloadSize + * - thumbnail.mimeType + * - thumbnail.imageSize + */ + class LocationContent: public Thumbnailed<> { public: + LocationContent(const QString& geoUri, + const ImageInfo<>& thumbnail); explicit LocationContent(const QJsonObject& json); + void fillJson(QJsonObject* o) const override; + QString geoUri; - ImageInfo thumbnail; }; - class VideoInfo: public FileInfo + /** + * A base class for "playable" info types: audio and video + */ + class PlayableInfo : public FileInfo { public: - VideoInfo(QUrl u, const QJsonObject& infoJson); + explicit PlayableInfo(const QUrl& u, int fileSize, + const QMimeType& mimeType, int duration, + const QString& originalFilename = {}); + PlayableInfo(const QUrl& u, const QJsonObject& infoJson, + const QString& originalFilename = {}); + + void fillInfoJson(QJsonObject* infoJson) const override; int duration; - QSize imageSize; }; - using VideoContent = ThumbnailedContent<VideoInfo>; - class AudioInfo: public FileInfo - { - public: - AudioInfo(QUrl u, const QJsonObject& infoJson); + /** + * Content class for m.video + * + * Available fields: + * - corresponding to the top-level JSON: + * - url + * - filename (extension to the CS API spec) + * - corresponding to the "info" subobject: + * - payloadSize ("size" in JSON) + * - mimeType ("mimetype" in JSON) + * - duration + * - imageSize (QSize for a combination of "h" and "w" in JSON) + * - thumbnail.url ("thumbnail_url" in JSON) + * - corresponding to the "info/thumbnail_info" subobject: contents of + * thumbnail field, in the same vein as for "info": + * - payloadSize + * - mimeType + * - imageSize + */ + using VideoContent = UrlWith<Thumbnailed<ImageInfo<PlayableInfo>>>; - int duration; - }; - using AudioContent = ThumbnailedContent<AudioInfo>; + /** + * Content class for m.audio + * + * Available fields: + * - corresponding to the top-level JSON: + * - url + * - filename (extension to the CS API spec) + * - corresponding to the "info" subobject: + * - payloadSize ("size" in JSON) + * - mimeType ("mimetype" in JSON) + * - duration + */ + using AudioContent = UrlWith<PlayableInfo>; } // namespace MessageEventContent } // namespace QMatrixClient |