diff options
Diffstat (limited to 'lib/events')
-rw-r--r-- | lib/events/eventcontent.h | 464 |
1 files changed, 231 insertions, 233 deletions
diff --git a/lib/events/eventcontent.h b/lib/events/eventcontent.h index 7611d077..af26c0a4 100644 --- a/lib/events/eventcontent.h +++ b/lib/events/eventcontent.h @@ -17,240 +17,238 @@ class QFileInfo; -namespace Quotient { -namespace EventContent { - //! \brief Base for all content types that can be stored in RoomMessageEvent +namespace Quotient::EventContent { +//! \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; + + QJsonObject toJson() const; + +public: + QJsonObject originalJson; + + // You can't assign those classes + Base& operator=(const Base&) = delete; + Base& operator=(Base&&) = delete; + +protected: + Base(const Base&) = default; + Base(Base&&) noexcept = default; + + virtual void fillJson(QJsonObject&) const = 0; +}; + +// 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 +// each type for the list of available attributes. + +// A quick classes inheritance structure follows (the definitions are +// spread across eventcontent.h and roommessageevent.h): +// UrlBasedContent<InfoT> : InfoT + thumbnail data +// PlayableContent<InfoT> : + duration attribute +// FileInfo +// FileContent = UrlBasedContent<FileInfo> +// AudioContent = PlayableContent<FileInfo> +// ImageInfo : FileInfo + imageSize attribute +// 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 //! - //! 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; - - QJsonObject toJson() const; - - public: - QJsonObject originalJson; - - // You can't assign those classes - Base& operator=(const Base&) = delete; - Base& operator=(Base&&) = delete; - - protected: - Base(const Base&) = default; - Base(Base&&) noexcept = default; - - virtual void fillJson(QJsonObject&) const = 0; - }; - - // 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 - // each type for the list of available attributes. - - // A quick classes inheritance structure follows (the definitions are - // spread across eventcontent.h and roommessageevent.h): - // UrlBasedContent<InfoT> : InfoT + thumbnail data - // PlayableContent<InfoT> : + duration attribute - // FileInfo - // FileContent = UrlBasedContent<FileInfo> - // AudioContent = PlayableContent<FileInfo> - // ImageInfo : FileInfo + imageSize attribute - // ImageContent = UrlBasedContent<ImageInfo> - // VideoContent = PlayableContent<ImageInfo> - - //! \brief Mix-in class representing `info` subobject in content JSON + //! \param fi a QFileInfo object referring to an existing file + explicit FileInfo(const QFileInfo& fi); + explicit FileInfo(FileSourceInfo sourceInfo, qint64 payloadSize = -1, + const QMimeType& mimeType = {}, + QString originalFilename = {}); + //! \brief Construct from a JSON `info` payload //! - //! 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(FileSourceInfo sourceInfo, qint64 payloadSize = -1, - const QMimeType& mimeType = {}, - 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(FileSourceInfo sourceInfo, const QJsonObject& infoJson, - QString originalFilename = {}); - - bool isValid() const; - QUrl url() 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 - QString mediaId() const { return url().authority() + url().path(); } - - public: - FileSourceInfo source; - QJsonObject originalInfoJson; - QMimeType mimeType; - qint64 payloadSize = 0; - QString originalName; - }; - - QUOTIENT_API QJsonObject toInfoJson(const FileInfo& info); - - //! \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(FileSourceInfo sourceInfo, qint64 fileSize = -1, - const QMimeType& type = {}, QSize imageSize = {}, - const QString& originalFilename = {}); - ImageInfo(FileSourceInfo sourceInfo, const QJsonObject& infoJson, - const QString& originalFilename = {}); - - public: - QSize imageSize; - }; - - 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` - //! (or, in case of an encrypted thumbnail, `info/thumbnail_file`) and - //! `info/thumbnail_info` fields are used. - class QUOTIENT_API Thumbnail : public ImageInfo { - public: - using ImageInfo::ImageInfo; - explicit Thumbnail(const QJsonObject& infoJson, - const Omittable<EncryptedFileMetadata>& efm = none); - - //! \brief Add thumbnail information to the passed `info` JSON object - void dumpTo(QJsonObject& infoJson) const; - }; - - class QUOTIENT_API TypedBase : public Base { - public: - virtual QMimeType type() const = 0; - virtual const FileInfo* fileInfo() const { return nullptr; } - virtual FileInfo* fileInfo() { return nullptr; } - virtual const Thumbnail* thumbnailInfo() const { return nullptr; } - - protected: - explicit TypedBase(QJsonObject o = {}) : Base(std::move(o)) {} - using Base::Base; - }; - - //! \brief A template class for content types with a URL and additional info - //! - //! Types that derive from this class template take `url` (or, if the file - //! is encrypted, `file`) 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 UrlBasedContent : public TypedBase, public InfoT { - public: - using InfoT::InfoT; - explicit UrlBasedContent(const QJsonObject& json) - : TypedBase(json) - , InfoT(QUrl(json["url"].toString()), json["info"].toObject(), - json["filename"].toString()) - , thumbnail(FileInfo::originalInfoJson) - { - if (const auto efmJson = json.value("file"_ls).toObject(); - !efmJson.isEmpty()) - InfoT::source = fromJson<EncryptedFileMetadata>(efmJson); - // 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; } - const Thumbnail* thumbnailInfo() const override { return &thumbnail; } - - public: - Thumbnail thumbnail; - - protected: - virtual void fillInfoJson(QJsonObject& infoJson [[maybe_unused]]) const - {} - - void fillJson(QJsonObject& json) const override - { - Quotient::fillJson(json, { "url"_ls, "file"_ls }, InfoT::source); - 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); - } - }; - - //! \brief Content class for m.image - //! - //! Available fields: - //! - corresponding to the top-level JSON: - //! - source (corresponding to `url` or `file` in JSON) - //! - 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 + //! Make sure to pass the `info` subobject of content JSON, not the + //! whole JSON content. + FileInfo(FileSourceInfo sourceInfo, const QJsonObject& infoJson, + QString originalFilename = {}); + + bool isValid() const; + QUrl url() const; + + //! \brief Extract media id from the URL //! - //! Available fields: - //! - corresponding to the top-level JSON: - //! - source (corresponding to `url` or `file` in JSON) - //! - filename - //! - corresponding to the `info` subobject: - //! - payloadSize (`size` in JSON) - //! - mimeType (`mimetype` in JSON) - //! - thumbnail.source (`thumbnail_url` or `thumbnail_file` 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 + //! 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: + FileSourceInfo source; + QJsonObject originalInfoJson; + QMimeType mimeType; + qint64 payloadSize = 0; + QString originalName; +}; + +QUOTIENT_API QJsonObject toInfoJson(const FileInfo& info); + +//! \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(FileSourceInfo sourceInfo, qint64 fileSize = -1, + const QMimeType& type = {}, QSize imageSize = {}, + const QString& originalFilename = {}); + ImageInfo(FileSourceInfo sourceInfo, const QJsonObject& infoJson, + const QString& originalFilename = {}); + +public: + QSize imageSize; +}; + +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` +//! (or, in case of an encrypted thumbnail, `info/thumbnail_file`) and +//! `info/thumbnail_info` fields are used. +class QUOTIENT_API Thumbnail : public ImageInfo { +public: + using ImageInfo::ImageInfo; + explicit Thumbnail(const QJsonObject& infoJson, + const Omittable<EncryptedFileMetadata>& efm = none); + + //! \brief Add thumbnail information to the passed `info` JSON object + void dumpTo(QJsonObject& infoJson) const; +}; + +class QUOTIENT_API TypedBase : public Base { +public: + virtual QMimeType type() const = 0; + virtual const FileInfo* fileInfo() const { return nullptr; } + virtual FileInfo* fileInfo() { return nullptr; } + virtual const Thumbnail* thumbnailInfo() const { return nullptr; } + +protected: + explicit TypedBase(QJsonObject o = {}) : Base(std::move(o)) {} + using Base::Base; +}; + +//! \brief A template class for content types with a URL and additional info +//! +//! Types that derive from this class template take `url` (or, if the file +//! is encrypted, `file`) 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 UrlBasedContent : public TypedBase, public InfoT { +public: + using InfoT::InfoT; + explicit UrlBasedContent(const QJsonObject& json) + : TypedBase(json) + , InfoT(QUrl(json["url"].toString()), json["info"].toObject(), + json["filename"].toString()) + , thumbnail(FileInfo::originalInfoJson) + { + if (const auto efmJson = json.value("file"_ls).toObject(); + !efmJson.isEmpty()) + InfoT::source = fromJson<EncryptedFileMetadata>(efmJson); + // 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; } + const Thumbnail* thumbnailInfo() const override { return &thumbnail; } + +public: + Thumbnail thumbnail; + +protected: + virtual void fillInfoJson(QJsonObject& infoJson [[maybe_unused]]) const + {} + + void fillJson(QJsonObject& json) const override + { + Quotient::fillJson(json, { "url"_ls, "file"_ls }, InfoT::source); + 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); + } +}; + +//! \brief Content class for m.image +//! +//! Available fields: +//! - corresponding to the top-level JSON: +//! - source (corresponding to `url` or `file` in JSON) +//! - 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: +//! - source (corresponding to `url` or `file` in JSON) +//! - filename +//! - corresponding to the `info` subobject: +//! - payloadSize (`size` in JSON) +//! - mimeType (`mimetype` in JSON) +//! - thumbnail.source (`thumbnail_url` or `thumbnail_file` 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 Quotient::EventContent Q_DECLARE_METATYPE(const Quotient::EventContent::TypedBase*) |