diff options
Diffstat (limited to 'lib/events/eventcontent.h')
-rw-r--r-- | lib/events/eventcontent.h | 285 |
1 files changed, 129 insertions, 156 deletions
diff --git a/lib/events/eventcontent.h b/lib/events/eventcontent.h index de9a792b..bbd35618 100644 --- a/lib/events/eventcontent.h +++ b/lib/events/eventcontent.h @@ -19,22 +19,18 @@ class QFileInfo; namespace Quotient { namespace EventContent { - /** - * 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. - */ + //! \brief Base for all content types that can be stored in 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. class QUOTIENT_API Base { public: explicit Base(QJsonObject o = {}) : originalJson(std::move(o)) {} virtual ~Base() = default; - // FIXME: make toJson() from converters.* work on base classes QJsonObject toJson() const; public: @@ -44,7 +40,7 @@ namespace EventContent { Base(const Base&) = default; Base(Base&&) = default; - virtual void fillJson(QJsonObject* o) const = 0; + virtual void fillJson(QJsonObject&) const = 0; }; // The below structures fairly follow CS spec 11.2.1.6. The overall @@ -54,52 +50,64 @@ namespace EventContent { // A quick classes inheritance structure follows (the definitions are // spread across eventcontent.h and roommessageevent.h): + // UrlBasedContent<InfoT> : InfoT + url and thumbnail data + // PlayableContent<InfoT> : + duration attribute // FileInfo - // FileContent : UrlWithThumbnailContent<FileInfo> - // AudioContent : PlayableContent<UrlBasedContent<FileInfo>> + // FileContent = UrlBasedContent<FileInfo> + // AudioContent = PlayableContent<FileInfo> // ImageInfo : FileInfo + imageSize attribute - // ImageContent : UrlWithThumbnailContent<ImageInfo> - // VideoContent : PlayableContent<UrlWithThumbnailContent<ImageInfo>> - - /** - * A base/mixin class for structures representing an "info" object for - * some content types. These include most attachment types currently in - * the CS API spec. - * - * In order to use it in a content class, derive both from TypedBase - * (or Base) and from FileInfo (or its derivative, such as \p ImageInfo) - * and call fillInfoJson() to fill the "info" subobject. Make sure - * to pass an "info" part of JSON to FileInfo constructor, not the whole - * JSON content, as well as contents of "url" (or a similar key) and - * optionally "filename" node from the main JSON content. Assuming you - * don't do unusual things, you should use \p UrlBasedContent<> instead - * of doing multiple inheritance and overriding Base::fillJson() by hand. - * - * This class is not polymorphic. - */ + // ImageContent = UrlBasedContent<ImageInfo> + // VideoContent = PlayableContent<ImageInfo> + + //! \brief Mix-in class representing `info` subobject in content JSON + //! + //! This is one of base classes for content types that deal with files or + //! URLs. It stores the file metadata attributes, such as size, MIME type + //! etc. found in the `content/info` subobject of event JSON payloads. + //! Actual content classes derive from this class _and_ TypedBase that + //! provides a polymorphic interface to access data in the mix-in. FileInfo + //! (as well as ImageInfo, that adds image size to the metadata) is NOT + //! polymorphic and is used in a non-polymorphic way to store thumbnail + //! metadata (in a separate instance), next to the metadata on the file + //! itself. + //! + //! If you need to make a new _content_ (not info) class based on files/URLs + //! take UrlBasedContent as the example, i.e.: + //! 1. Double-inherit from this class (or ImageInfo) and TypedBase. + //! 2. Provide a constructor from QJsonObject that will pass the `info` + //! subobject (not the whole content JSON) down to FileInfo/ImageInfo. + //! 3. Override fillJson() to customise the JSON export logic. Make sure + //! to call toInfoJson() from it to produce the payload for the `info` + //! subobject in the JSON payload. + //! + //! \sa ImageInfo, FileContent, ImageContent, AudioContent, VideoContent, + //! UrlBasedContent class QUOTIENT_API FileInfo { public: FileInfo() = default; + //! \brief Construct from a QFileInfo object + //! + //! \param fi a QFileInfo object referring to an existing file explicit FileInfo(const QFileInfo& fi); explicit FileInfo(QUrl mxcUrl, qint64 payloadSize = -1, const QMimeType& mimeType = {}, - Omittable<EncryptedFile> file = none, + Omittable<EncryptedFile> encryptedFile = none, QString originalFilename = {}); + //! \brief Construct from a JSON `info` payload + //! + //! Make sure to pass the `info` subobject of content JSON, not the + //! whole JSON content. FileInfo(QUrl mxcUrl, const QJsonObject& infoJson, - const Omittable<EncryptedFile> &file, + Omittable<EncryptedFile> encryptedFile, QString originalFilename = {}); bool isValid() const; - void fillInfoJson(QJsonObject* infoJson) const; - - /** - * \brief Extract media id from the URL - * - * This can be used, e.g., to construct a QML-facing image:// - * URI as follows: - * \code "image://provider/" + info.mediaId() \endcode - */ + //! \brief Extract media id from the URL + //! + //! This can be used, e.g., to construct a QML-facing image:// + //! URI as follows: + //! \code "image://provider/" + info.mediaId() \endcode QString mediaId() const { return url.authority() + url.path(); } public: @@ -111,52 +119,40 @@ namespace EventContent { Omittable<EncryptedFile> file = none; }; - template <typename InfoT> - QJsonObject toInfoJson(const InfoT& info) - { - QJsonObject infoJson; - info.fillInfoJson(&infoJson); - return infoJson; - } + QUOTIENT_API QJsonObject toInfoJson(const FileInfo& info); - /** - * A content info class for image content types: image, thumbnail, video - */ + //! \brief A content info class for image/video content types and thumbnails class QUOTIENT_API ImageInfo : public FileInfo { public: ImageInfo() = default; explicit ImageInfo(const QFileInfo& fi, QSize imageSize = {}); explicit ImageInfo(const QUrl& mxcUrl, qint64 fileSize = -1, const QMimeType& type = {}, QSize imageSize = {}, - const Omittable<EncryptedFile> &file = none, + Omittable<EncryptedFile> encryptedFile = none, const QString& originalFilename = {}); ImageInfo(const QUrl& mxcUrl, const QJsonObject& infoJson, - const Omittable<EncryptedFile> &encryptedFile, + Omittable<EncryptedFile> encryptedFile, const QString& originalFilename = {}); - void fillInfoJson(QJsonObject* infoJson) const; - public: QSize imageSize; }; - /** - * An auxiliary class for an info type that carries a thumbnail - * - * This class saves/loads a thumbnail to/from "info" subobject of - * the JSON representation of event content; namely, - * "info/thumbnail_url" and "info/thumbnail_info" fields are used. - */ + QUOTIENT_API QJsonObject toInfoJson(const ImageInfo& info); + + //! \brief An auxiliary class for an info type that carries a thumbnail + //! + //! This class saves/loads a thumbnail to/from `info` subobject of + //! the JSON representation of event content; namely, `info/thumbnail_url` + //! and `info/thumbnail_info` fields are used. class QUOTIENT_API Thumbnail : public ImageInfo { public: using ImageInfo::ImageInfo; - Thumbnail(const QJsonObject& infoJson, const Omittable<EncryptedFile> &file = none); + Thumbnail(const QJsonObject& infoJson, + Omittable<EncryptedFile> encryptedFile = none); - /** - * Writes thumbnail information to "thumbnail_info" subobject - * and thumbnail URL to "thumbnail_url" node inside "info". - */ - void fillInfoJson(QJsonObject* infoJson) const; + //! \brief Add thumbnail information to the passed `info` JSON object + void dumpTo(QJsonObject& infoJson) const; }; class QUOTIENT_API TypedBase : public Base { @@ -171,114 +167,91 @@ namespace EventContent { using Base::Base; }; - /** - * A base class for content types that have a URL and additional info - * - * Types that derive from this class template take "url" and, - * optionally, "filename" values from the top-level JSON object and - * the rest of information from the "info" subobject, as defined by - * the parameter type. - * - * \tparam InfoT base info class - */ + //! \brief A template class for content types with a URL and additional info + //! + //! Types that derive from this class template take `url` and, + //! optionally, `filename` values from the top-level JSON object and + //! the rest of information from the `info` subobject, as defined by + //! the parameter type. + //! \tparam InfoT base info class - FileInfo or ImageInfo template <class InfoT> - class QUOTIENT_API UrlBasedContent : public TypedBase, public InfoT { + class UrlBasedContent : public TypedBase, public InfoT { public: using InfoT::InfoT; explicit UrlBasedContent(const QJsonObject& json) : TypedBase(json) , InfoT(QUrl(json["url"].toString()), json["info"].toObject(), - fromJson<Omittable<EncryptedFile>>(json["file"]), json["filename"].toString()) + fromJson<Omittable<EncryptedFile>>(json["file"]), + json["filename"].toString()) + , thumbnail(FileInfo::originalInfoJson) { - // A small hack to facilitate links creation in QML. + // Two small hacks on originalJson to expose mediaIds to QML originalJson.insert("mediaId", InfoT::mediaId()); + originalJson.insert("thumbnailMediaId", thumbnail.mediaId()); } QMimeType type() const override { return InfoT::mimeType; } const FileInfo* fileInfo() const override { return this; } FileInfo* fileInfo() override { return this; } - - protected: - void fillJson(QJsonObject* json) const override - { - Q_ASSERT(json); - if (!InfoT::file.has_value()) { - json->insert("url", InfoT::url.toString()); - } else { - json->insert("file", Quotient::toJson(*InfoT::file)); - } - if (!InfoT::originalName.isEmpty()) - json->insert("filename", InfoT::originalName); - json->insert("info", toInfoJson<InfoT>(*this)); - } - }; - - template <typename InfoT> - class QUOTIENT_API UrlWithThumbnailContent : public UrlBasedContent<InfoT> { - public: - // NB: when using inherited constructors, thumbnail has to be - // initialised separately - using UrlBasedContent<InfoT>::UrlBasedContent; - explicit UrlWithThumbnailContent(const QJsonObject& json) - : UrlBasedContent<InfoT>(json), thumbnail(InfoT::originalInfoJson) - { - // Another small hack, to simplify making a thumbnail link - UrlBasedContent<InfoT>::originalJson.insert("thumbnailMediaId", - thumbnail.mediaId()); - } - const Thumbnail* thumbnailInfo() const override { return &thumbnail; } public: Thumbnail thumbnail; protected: - void fillJson(QJsonObject* json) const override + virtual void fillInfoJson(QJsonObject& infoJson [[maybe_unused]]) const + {} + + void fillJson(QJsonObject& json) const override { - UrlBasedContent<InfoT>::fillJson(json); - auto infoJson = json->take("info").toObject(); - thumbnail.fillInfoJson(&infoJson); - json->insert("info", infoJson); + if (!InfoT::file.has_value()) { + json.insert("url", InfoT::url.toString()); + } else { + json.insert("file", Quotient::toJson(*InfoT::file)); + } + if (!InfoT::originalName.isEmpty()) + json.insert("filename", InfoT::originalName); + auto infoJson = toInfoJson(*this); + if (thumbnail.isValid()) + thumbnail.dumpTo(infoJson); + fillInfoJson(infoJson); + json.insert("info", infoJson); } }; - /** - * 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 = UrlWithThumbnailContent<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 = UrlWithThumbnailContent<FileInfo>; + //! \brief 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 = UrlBasedContent<ImageInfo>; + + //! \brief 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 = UrlBasedContent<FileInfo>; } // namespace EventContent } // namespace Quotient Q_DECLARE_METATYPE(const Quotient::EventContent::TypedBase*) |