aboutsummaryrefslogtreecommitdiff
path: root/lib/events/eventcontent.h
diff options
context:
space:
mode:
Diffstat (limited to 'lib/events/eventcontent.h')
-rw-r--r--lib/events/eventcontent.h285
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*)