From b9f4b655273481e64d7d7ead6a30dbf85a901063 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Sat, 13 Jan 2018 19:42:37 +0900 Subject: Refactor EventContent; allow to easily check files out of message events The whole inheritance/templating structure has been considerably simplified by using a trick with mixin classes; thanks to that, *Info classes are no more templated, they are just mixed together by the almighty UrlBasedContent<> template (but the same can easily be done outside of it, as LocationContent implementation shows). RoomMessageEvent has gained hasFileContent(); it's also possible to easily get a FileInfo core object just by calling msgEvent->content()->fileInfo(). --- events/eventcontent.cpp | 51 ++++++++--- events/eventcontent.h | 212 ++++++++++++++++++++------------------------ events/roommessageevent.cpp | 43 +++++---- events/roommessageevent.h | 34 +++---- 4 files changed, 175 insertions(+), 165 deletions(-) (limited to 'events') diff --git a/events/eventcontent.cpp b/events/eventcontent.cpp index dcbccf08..271669e2 100644 --- a/events/eventcontent.cpp +++ b/events/eventcontent.cpp @@ -30,27 +30,21 @@ QJsonObject Base::toJson() const return o; } -QJsonObject InfoBase::toInfoJson() const -{ - QJsonObject info; - fillInfoJson(&info); - return info; -} - -void InfoBase::fillInfoJson(QJsonObject*) const { } - FileInfo::FileInfo(const QUrl& u, int payloadSize, const QMimeType& mimeType, const QString& originalFilename) - : InfoBase(mimeType), url(u), payloadSize(payloadSize) + : mimeType(mimeType), url(u), payloadSize(payloadSize) , originalName(originalFilename) { } FileInfo::FileInfo(const QUrl& u, const QJsonObject& infoJson, const QString& originalFilename) - : FileInfo(u, infoJson["size"].toInt(), - QMimeDatabase().mimeTypeForName(infoJson["mimetype"].toString()), - originalFilename) + : originalInfoJson(infoJson) + , mimeType(QMimeDatabase().mimeTypeForName(infoJson["mimetype"].toString())) + , url(u) + , payloadSize(infoJson["size"].toInt()) + , originalName(originalFilename) { + originalInfoJson.insert("mediaId", url.authority() + url.path()); if (!mimeType.isValid()) mimeType = QMimeDatabase().mimeTypeForData(QByteArray()); } @@ -61,3 +55,34 @@ void FileInfo::fillInfoJson(QJsonObject* infoJson) const infoJson->insert("size", payloadSize); infoJson->insert("mimetype", mimeType.name()); } + +ImageInfo::ImageInfo(const QUrl& u, int fileSize, QMimeType mimeType, + const QSize& imageSize) + : FileInfo(u, fileSize, mimeType), imageSize(imageSize) +{ } + +ImageInfo::ImageInfo(const QUrl& u, const QJsonObject& infoJson, + const QString& originalFilename) + : FileInfo(u, infoJson, originalFilename) + , imageSize(infoJson["w"].toInt(), infoJson["h"].toInt()) +{ } + +void ImageInfo::fillInfoJson(QJsonObject* infoJson) const +{ + FileInfo::fillInfoJson(infoJson); + infoJson->insert("w", imageSize.width()); + infoJson->insert("h", imageSize.height()); +} + +WithThumbnail::WithThumbnail(const QJsonObject& infoJson) + : thumbnail(infoJson["thumbnail_url"].toString(), + infoJson["thumbnail_info"].toObject()) +{ } + +void WithThumbnail::fillInfoJson(QJsonObject* infoJson) const +{ + infoJson->insert("thumbnail_url", thumbnail.url.toString()); + QJsonObject thumbnailInfoJson; + thumbnail.fillInfoJson(&thumbnailInfoJson); + infoJson->insert("thumbnail_info", thumbnailInfoJson); +} diff --git a/events/eventcontent.h b/events/eventcontent.h index 60437995..b37dc923 100644 --- a/events/eventcontent.h +++ b/events/eventcontent.h @@ -23,7 +23,6 @@ #include "converters.h" -#include #include #include #include @@ -40,28 +39,23 @@ namespace QMatrixClient * 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. + * from plain data. */ class Base { public: + explicit Base (const QJsonObject& o = {}) : originalJson(o) { } virtual ~Base() = default; QJsonObject toJson() const; + public: + QJsonObject originalJson; + protected: virtual void fillJson(QJsonObject* o) const = 0; }; - class TypedBase: public Base - { - public: - virtual QMimeType type() const = 0; - }; - template class SimpleContent: public Base { @@ -74,10 +68,12 @@ namespace QMatrixClient : value(std::forward(value)), key(std::move(keyName)) { } SimpleContent(const QJsonObject& json, QString keyName) - : value(QMatrixClient::fromJson(json[keyName])) + : Base(json) + , value(QMatrixClient::fromJson(json[keyName])) , key(std::move(keyName)) { } + public: T value; protected: @@ -91,44 +87,36 @@ namespace QMatrixClient } }; - /** - * 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 FileInfo) 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: - virtual ~InfoBase() = default; - - QJsonObject toInfoJson() const; - - QMimeType mimeType; - - protected: - InfoBase() = default; - explicit InfoBase(const QMimeType& type) : mimeType(type) { } - - virtual void fillInfoJson(QJsonObject* /*infoJson*/) 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: + // FileInfo + // FileContent : UrlBasedContent + // AudioContent : UrlBasedContent + // ImageInfo : FileInfo + imageSize attribute + // ImageContent : UrlBasedContent + // VideoContent : UrlBasedContent + /** - * Base class for content types that consist of a URL along with - * additional information. Most of message types except textual fall - * under this category. + * 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. */ - class FileInfo: public InfoBase + class FileInfo { public: explicit FileInfo(const QUrl& u, int payloadSize = -1, @@ -137,111 +125,101 @@ namespace QMatrixClient FileInfo(const QUrl& u, const QJsonObject& infoJson, const QString& originalFilename = {}); + 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 + */ + QString mediaId() const { return url.authority() + url.path(); } + + public: + QJsonObject originalInfoJson; + QMimeType mimeType; QUrl url; int payloadSize; QString originalName; - - protected: - void fillInfoJson(QJsonObject* infoJson) const override; }; /** - * A base class for image info types: image, thumbnail, video - * - * \tparam InfoT base info class; should derive from \p InfoBase + * A content info class for image content types: image, thumbnail, video */ - template - class ImageInfo : public InfoT + class ImageInfo : public FileInfo { public: explicit ImageInfo(const QUrl& u, int fileSize = -1, QMimeType mimeType = {}, - const QSize& imageSize = {}) - : InfoT(u, fileSize, mimeType), imageSize(imageSize) - { } + const QSize& imageSize = {}); ImageInfo(const QUrl& u, const QJsonObject& infoJson, - const QString& originalFilename = {}) - : InfoT(u, infoJson, originalFilename) - , imageSize(infoJson["w"].toInt(), infoJson["h"].toInt()) - { } + const QString& originalFilename = {}); - QSize imageSize; + void fillInfoJson(QJsonObject* infoJson) const; - protected: - void fillInfoJson(QJsonObject* infoJson) const override - { - InfoT::fillInfoJson(infoJson); - infoJson->insert("w", imageSize.width()); - infoJson->insert("h", imageSize.height()); - } + public: + QSize imageSize; }; /** - * A base class for an info type that carries a thumbnail + * A mixin class for an info type that carries a thumbnail * - * This class decorates the underlying type, adding ability to save/load - * a thumbnail to/from "info" subobject of the JSON representation of - * event content; namely, "info/thumbnail_url" and "info/thumbnail_info" - * fields are used. - * - * \tparam InfoT base info class; should derive from \p InfoBase + * 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. */ - template - class Thumbnailed : public InfoT + class WithThumbnail { public: - template - explicit Thumbnailed(const ImageInfo<>& thumbnail, - ArgTs&&... infoArgs) - : InfoT(std::forward(infoArgs)...) - , thumbnail(thumbnail) - { } - - explicit Thumbnailed(const QJsonObject& infoJson) - : thumbnail(infoJson["thumbnail_url"].toString(), - infoJson["thumbnail_info"].toObject()) + WithThumbnail(const QJsonObject& infoJson); + WithThumbnail(const ImageInfo& info) + : thumbnail(info) { } - Thumbnailed(const QUrl& u, const QJsonObject& infoJson, - const QString& originalFilename = {}) - : InfoT(u, infoJson, originalFilename) - , thumbnail(infoJson["thumbnail_url"].toString(), - infoJson["thumbnail_info"].toObject()) - { } + /** + * Writes thumbnail information to "thumbnail_info" subobject + * and thumbnail URL to "thumbnail_url" node inside "info". + */ + void fillInfoJson(QJsonObject* infoJson) const; - ImageInfo<> thumbnail; + public: + ImageInfo thumbnail; + }; - protected: - void fillInfoJson(QJsonObject* infoJson) const override - { - InfoT::fillInfoJson(infoJson); - infoJson->insert("thumbnail_url", thumbnail.url.toString()); - infoJson->insert("thumbnail_info", thumbnail.toInfoJson()); - } + class TypedBase: public Base + { + public: + explicit TypedBase(const QJsonObject& o = {}) : Base(o) { } + virtual QMimeType type() const = 0; + virtual const FileInfo* fileInfo() const { return nullptr; } }; /** - * One more facility base class for content types that have a URL and - * additional info + * A base class for content types that have a URL and additional info * - * Types that derive from UrlWith take "url" and, optionally, - * "filename" values from the top-level JSON object and the rest of - * information from the "info" subobject. + * 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; should derive from \p FileInfo or - * provide a constructor with a compatible signature + * \tparam InfoT base info class + * \tparam InfoMixinTs... additional info mixin classes (e.g. WithThumbnail) */ - template // InfoT : public FileInfo - class UrlWith : public TypedBase, public InfoT + template + class UrlBasedContent : + public TypedBase, public InfoT, public InfoMixinTs... { public: - using InfoT::InfoT; - explicit UrlWith(const QJsonObject& json) - : InfoT(json["url"].toString(), json["info"].toObject(), + explicit UrlBasedContent(const QJsonObject& json) + : TypedBase(json) + , InfoT(json["url"].toString(), json["info"].toObject(), json["filename"].toString()) + , InfoMixinTs(InfoT::originalInfoJson)... { } QMimeType type() const override { return InfoT::mimeType; } + const FileInfo* fileInfo() const override { return this; } protected: void fillJson(QJsonObject* json) const override @@ -250,7 +228,13 @@ namespace QMatrixClient json->insert("url", InfoT::url.toString()); if (!InfoT::originalName.isEmpty()) json->insert("filename", InfoT::originalName); - json->insert("info", InfoT::toInfoJson()); + QJsonObject infoJson; + InfoT::fillInfoJson(&infoJson); + // http://en.cppreference.com/w/cpp/language/parameter_pack#Brace-enclosed_initializers + // Looking forward to C++17 and its folding awesomeness. + int d[] = { (InfoMixinTs::fillInfoJson(&infoJson), 0)... }; + Q_UNUSED(d); + json->insert("info", infoJson); } }; @@ -272,7 +256,7 @@ namespace QMatrixClient * - mimeType * - imageSize */ - using ImageContent = UrlWith>>; + using ImageContent = UrlBasedContent; /** * Content class for m.file @@ -290,6 +274,6 @@ namespace QMatrixClient * - thumbnail.mimeType * - thumbnail.imageSize (QSize for "h" and "w" in JSON) */ - using FileContent = UrlWith>; + using FileContent = UrlBasedContent; } // namespace EventContent } // namespace QMatrixClient diff --git a/events/roommessageevent.cpp b/events/roommessageevent.cpp index bc41abf6..20e81564 100644 --- a/events/roommessageevent.cpp +++ b/events/roommessageevent.cpp @@ -116,6 +116,11 @@ QMimeType RoomMessageEvent::mimeType() const QMimeDatabase().mimeTypeForName("text/plain"); } +bool RoomMessageEvent::hasFileContent() const +{ + return content() && content()->fileInfo(); +} + QJsonObject RoomMessageEvent::toJson() const { QJsonObject obj = _content ? _content->toJson() : QJsonObject(); @@ -153,43 +158,35 @@ void TextContent::fillJson(QJsonObject* json) const json->insert("formatted_body", body); } -LocationContent::LocationContent(const QString& geoUri, - const ImageInfo<>& thumbnail) - : Thumbnailed<>(thumbnail), geoUri(geoUri) +LocationContent::LocationContent(const QString& geoUri, const ImageInfo& thumbnail) + : WithThumbnail(thumbnail), geoUri(geoUri) { } LocationContent::LocationContent(const QJsonObject& json) - : Thumbnailed<>(json["info"].toObject()) + : TypedBase(json) + , WithThumbnail(json["info"].toObject()) , geoUri(json["geo_uri"].toString()) { } -void LocationContent::fillJson(QJsonObject* o) const -{ - Q_ASSERT(o); - o->insert("geo_uri", geoUri); - o->insert("info", Thumbnailed::toInfoJson()); -} - QMimeType LocationContent::type() const { return QMimeDatabase().mimeTypeForData(geoUri.toLatin1()); } -PlayableInfo::PlayableInfo(const QUrl& u, int fileSize, - const QMimeType& mimeType, int duration, - const QString& originalFilename) - : FileInfo(u, fileSize, mimeType, originalFilename) - , duration(duration) -{ } +void LocationContent::fillJson(QJsonObject* o) const +{ + Q_ASSERT(o); + o->insert("geo_uri", geoUri); + QJsonObject infoJson; + WithThumbnail::fillInfoJson(&infoJson); + o->insert("info", infoJson); +} -PlayableInfo::PlayableInfo(const QUrl& u, const QJsonObject& infoJson, - const QString& originalFilename) - : FileInfo(u, infoJson, originalFilename) - , duration(infoJson["duration"].toInt()) +WithDuration::WithDuration(const QJsonObject& infoJson) + : duration(infoJson["duration"].toInt()) { } -void PlayableInfo::fillInfoJson(QJsonObject* infoJson) const +void WithDuration::fillInfoJson(QJsonObject* infoJson) const { - FileInfo::fillInfoJson(infoJson); infoJson->insert("duration", duration); } diff --git a/events/roommessageevent.h b/events/roommessageevent.h index eef6b657..6b551b76 100644 --- a/events/roommessageevent.h +++ b/events/roommessageevent.h @@ -32,6 +32,10 @@ namespace QMatrixClient class RoomMessageEvent: public RoomEvent { Q_GADGET + Q_PROPERTY(QString msgType READ rawMsgtype CONSTANT) + Q_PROPERTY(QString plainBody READ plainBody CONSTANT) + Q_PROPERTY(QMimeType mimeType READ mimeType STORED false CONSTANT) + Q_PROPERTY(EventContent::TypedBase* content READ content CONSTANT) public: enum class MsgType { @@ -52,9 +56,10 @@ namespace QMatrixClient MsgType msgtype() const; QString rawMsgtype() const { return _msgtype; } const QString& plainBody() const { return _plainBody; } - const EventContent::TypedBase* content() const + EventContent::TypedBase* content() const { return _content.data(); } QMimeType mimeType() const; + bool hasFileContent() const; QJsonObject toJson() const; @@ -107,15 +112,16 @@ namespace QMatrixClient * - thumbnail.mimeType * - thumbnail.imageSize */ - class LocationContent: public TypedBase, public Thumbnailed<> + class LocationContent: public TypedBase, public WithThumbnail { public: LocationContent(const QString& geoUri, - const ImageInfo<>& thumbnail); + const ImageInfo& thumbnail); explicit LocationContent(const QJsonObject& json); QMimeType type() const override; + public: QString geoUri; protected: @@ -123,21 +129,18 @@ namespace QMatrixClient }; /** - * A base class for "playable" info types: audio and video + * A mixin class for info types that include duration: audio and video */ - class PlayableInfo : public FileInfo + class WithDuration { public: - 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 = {}); + explicit WithDuration(int duration) : duration(duration) { } + WithDuration(const QJsonObject& infoJson); - int duration; + void fillInfoJson(QJsonObject* infoJson) const; - protected: - void fillInfoJson(QJsonObject* infoJson) const override; + public: + int duration; }; /** @@ -159,7 +162,8 @@ namespace QMatrixClient * - mimeType * - imageSize */ - using VideoContent = UrlWith>>; + using VideoContent = + UrlBasedContent; /** * Content class for m.audio @@ -173,6 +177,6 @@ namespace QMatrixClient * - mimeType ("mimetype" in JSON) * - duration */ - using AudioContent = UrlWith; + using AudioContent = UrlBasedContent; } // namespace EventContent } // namespace QMatrixClient -- cgit v1.2.3 From 525be6dce815f88ed9cc97480bff461741665e8f Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Sun, 14 Jan 2018 18:32:26 +0900 Subject: EventContent: rewrite without mixins MSVC is not good at dealing with type parameter packs of member functions, which is what the whole mixin magic in UrlBasedContent<> relied on. So it's one more level of inheritance instead of mixins now. --- events/eventcontent.cpp | 14 +++++----- events/eventcontent.h | 66 ++++++++++++++++++++++++++++++--------------- events/roommessageevent.cpp | 17 +++--------- events/roommessageevent.h | 28 ++++++++++++------- 4 files changed, 73 insertions(+), 52 deletions(-) (limited to 'events') diff --git a/events/eventcontent.cpp b/events/eventcontent.cpp index 271669e2..c96da9b3 100644 --- a/events/eventcontent.cpp +++ b/events/eventcontent.cpp @@ -74,15 +74,13 @@ void ImageInfo::fillInfoJson(QJsonObject* infoJson) const infoJson->insert("h", imageSize.height()); } -WithThumbnail::WithThumbnail(const QJsonObject& infoJson) - : thumbnail(infoJson["thumbnail_url"].toString(), - infoJson["thumbnail_info"].toObject()) +Thumbnail::Thumbnail(const QJsonObject& infoJson) + : ImageInfo(infoJson["thumbnail_url"].toString(), + infoJson["thumbnail_info"].toObject()) { } -void WithThumbnail::fillInfoJson(QJsonObject* infoJson) const +void Thumbnail::fillInfoJson(QJsonObject* infoJson) const { - infoJson->insert("thumbnail_url", thumbnail.url.toString()); - QJsonObject thumbnailInfoJson; - thumbnail.fillInfoJson(&thumbnailInfoJson); - infoJson->insert("thumbnail_info", thumbnailInfoJson); + infoJson->insert("thumbnail_url", url.toString()); + infoJson->insert("thumbnail_info", toInfoJson(*this)); } diff --git a/events/eventcontent.h b/events/eventcontent.h index b37dc923..91e4ca94 100644 --- a/events/eventcontent.h +++ b/events/eventcontent.h @@ -27,6 +27,8 @@ #include #include +#include + namespace QMatrixClient { namespace EventContent @@ -144,6 +146,14 @@ namespace QMatrixClient QString originalName; }; + template + QJsonObject toInfoJson(const InfoT& info) + { + QJsonObject infoJson; + info.fillInfoJson(&infoJson); + return infoJson; + } + /** * A content info class for image content types: image, thumbnail, video */ @@ -163,18 +173,18 @@ namespace QMatrixClient }; /** - * A mixin class for an info type that carries a thumbnail + * 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 WithThumbnail + class Thumbnail : public ImageInfo { public: - WithThumbnail(const QJsonObject& infoJson); - WithThumbnail(const ImageInfo& info) - : thumbnail(info) + Thumbnail(const QJsonObject& infoJson); + Thumbnail(const ImageInfo& info) + : ImageInfo(info) { } /** @@ -182,9 +192,6 @@ namespace QMatrixClient * and thumbnail URL to "thumbnail_url" node inside "info". */ void fillInfoJson(QJsonObject* infoJson) const; - - public: - ImageInfo thumbnail; }; class TypedBase: public Base @@ -204,18 +211,18 @@ namespace QMatrixClient * the parameter type. * * \tparam InfoT base info class - * \tparam InfoMixinTs... additional info mixin classes (e.g. WithThumbnail) */ - template - class UrlBasedContent : - public TypedBase, public InfoT, public InfoMixinTs... + template + class UrlBasedContent : public TypedBase, public InfoT { public: + UrlBasedContent(QUrl url, InfoT&& info, QString filename = {}) + : InfoT(url, std::forward(info), filename) + { } explicit UrlBasedContent(const QJsonObject& json) : TypedBase(json) , InfoT(json["url"].toString(), json["info"].toObject(), json["filename"].toString()) - , InfoMixinTs(InfoT::originalInfoJson)... { } QMimeType type() const override { return InfoT::mimeType; } @@ -228,12 +235,29 @@ namespace QMatrixClient json->insert("url", InfoT::url.toString()); if (!InfoT::originalName.isEmpty()) json->insert("filename", InfoT::originalName); - QJsonObject infoJson; - InfoT::fillInfoJson(&infoJson); - // http://en.cppreference.com/w/cpp/language/parameter_pack#Brace-enclosed_initializers - // Looking forward to C++17 and its folding awesomeness. - int d[] = { (InfoMixinTs::fillInfoJson(&infoJson), 0)... }; - Q_UNUSED(d); + json->insert("info", toInfoJson(*this)); + } + }; + + template + class UrlWithThumbnailContent : public UrlBasedContent + { + public: + // TODO: POD constructor + UrlWithThumbnailContent(const QJsonObject& json) + : UrlBasedContent(json) + , thumbnail(InfoT::originalInfoJson) + { } + + public: + Thumbnail thumbnail; + + protected: + void fillJson(QJsonObject* json) const override + { + UrlBasedContent::fillJson(json); + auto infoJson = json->take("info").toObject(); + thumbnail.fillInfoJson(&infoJson); json->insert("info", infoJson); } }; @@ -256,7 +280,7 @@ namespace QMatrixClient * - mimeType * - imageSize */ - using ImageContent = UrlBasedContent; + using ImageContent = UrlWithThumbnailContent; /** * Content class for m.file @@ -274,6 +298,6 @@ namespace QMatrixClient * - thumbnail.mimeType * - thumbnail.imageSize (QSize for "h" and "w" in JSON) */ - using FileContent = UrlBasedContent; + using FileContent = UrlWithThumbnailContent; } // namespace EventContent } // namespace QMatrixClient diff --git a/events/roommessageevent.cpp b/events/roommessageevent.cpp index 20e81564..3c5d10ad 100644 --- a/events/roommessageevent.cpp +++ b/events/roommessageevent.cpp @@ -159,13 +159,13 @@ void TextContent::fillJson(QJsonObject* json) const } LocationContent::LocationContent(const QString& geoUri, const ImageInfo& thumbnail) - : WithThumbnail(thumbnail), geoUri(geoUri) + : geoUri(geoUri), thumbnail(thumbnail) { } LocationContent::LocationContent(const QJsonObject& json) : TypedBase(json) - , WithThumbnail(json["info"].toObject()) , geoUri(json["geo_uri"].toString()) + , thumbnail(json["info"].toObject()) { } QMimeType LocationContent::type() const @@ -177,16 +177,5 @@ void LocationContent::fillJson(QJsonObject* o) const { Q_ASSERT(o); o->insert("geo_uri", geoUri); - QJsonObject infoJson; - WithThumbnail::fillInfoJson(&infoJson); - o->insert("info", infoJson); -} - -WithDuration::WithDuration(const QJsonObject& infoJson) - : duration(infoJson["duration"].toInt()) -{ } - -void WithDuration::fillInfoJson(QJsonObject* infoJson) const -{ - infoJson->insert("duration", duration); + o->insert("info", toInfoJson(thumbnail)); } diff --git a/events/roommessageevent.h b/events/roommessageevent.h index 6b551b76..867d8880 100644 --- a/events/roommessageevent.h +++ b/events/roommessageevent.h @@ -112,7 +112,7 @@ namespace QMatrixClient * - thumbnail.mimeType * - thumbnail.imageSize */ - class LocationContent: public TypedBase, public WithThumbnail + class LocationContent: public TypedBase { public: LocationContent(const QString& geoUri, @@ -123,21 +123,32 @@ namespace QMatrixClient public: QString geoUri; + Thumbnail thumbnail; protected: void fillJson(QJsonObject* o) const override; }; /** - * A mixin class for info types that include duration: audio and video + * A base class for info types that include duration: audio and video */ - class WithDuration + template + class PlayableContent : public ContentT { public: - explicit WithDuration(int duration) : duration(duration) { } - WithDuration(const QJsonObject& infoJson); + PlayableContent(const QJsonObject& json) + : ContentT(json) + , duration(ContentT::originalInfoJson["duration"].toInt()) + { } - void fillInfoJson(QJsonObject* infoJson) const; + protected: + void fillJson(QJsonObject* json) const override + { + ContentT::fillJson(json); + auto infoJson = json->take("info").toObject(); + infoJson.insert("duration", duration); + json->insert("info", infoJson); + } public: int duration; @@ -162,8 +173,7 @@ namespace QMatrixClient * - mimeType * - imageSize */ - using VideoContent = - UrlBasedContent; + using VideoContent = PlayableContent>; /** * Content class for m.audio @@ -177,6 +187,6 @@ namespace QMatrixClient * - mimeType ("mimetype" in JSON) * - duration */ - using AudioContent = UrlBasedContent; + using AudioContent = PlayableContent>; } // namespace EventContent } // namespace QMatrixClient -- cgit v1.2.3 From 5d6360b641084cf95a7f30bb760abdb7a73ebd27 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Mon, 15 Jan 2018 11:20:48 +0900 Subject: EventContent: provide mediaId in the top-level JSON, not in "info" subobject Given that it's a sidedoor anyway, it should at least be straightforward to use. --- events/eventcontent.cpp | 1 - events/eventcontent.h | 5 ++++- 2 files changed, 4 insertions(+), 2 deletions(-) (limited to 'events') diff --git a/events/eventcontent.cpp b/events/eventcontent.cpp index c96da9b3..f5974b46 100644 --- a/events/eventcontent.cpp +++ b/events/eventcontent.cpp @@ -44,7 +44,6 @@ FileInfo::FileInfo(const QUrl& u, const QJsonObject& infoJson, , payloadSize(infoJson["size"].toInt()) , originalName(originalFilename) { - originalInfoJson.insert("mediaId", url.authority() + url.path()); if (!mimeType.isValid()) mimeType = QMimeDatabase().mimeTypeForData(QByteArray()); } diff --git a/events/eventcontent.h b/events/eventcontent.h index 91e4ca94..0733b347 100644 --- a/events/eventcontent.h +++ b/events/eventcontent.h @@ -223,7 +223,10 @@ namespace QMatrixClient : TypedBase(json) , InfoT(json["url"].toString(), json["info"].toObject(), json["filename"].toString()) - { } + { + // A small hack to facilitate links creation in QML. + originalJson.insert("mediaId", InfoT::mediaId()); + } QMimeType type() const override { return InfoT::mimeType; } const FileInfo* fileInfo() const override { return this; } -- cgit v1.2.3 From 038659c6997e5acad6ecf7171bbd3c4ec14b5d3d Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Tue, 16 Jan 2018 17:00:16 +0900 Subject: EventContent: inject mediaThumbnailId into content original JSON For QML to easily make URLs to thumbnails. --- events/eventcontent.h | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'events') diff --git a/events/eventcontent.h b/events/eventcontent.h index 0733b347..4afbaff3 100644 --- a/events/eventcontent.h +++ b/events/eventcontent.h @@ -247,10 +247,14 @@ namespace QMatrixClient { public: // TODO: POD constructor - UrlWithThumbnailContent(const QJsonObject& json) + explicit UrlWithThumbnailContent(const QJsonObject& json) : UrlBasedContent(json) , thumbnail(InfoT::originalInfoJson) - { } + { + // Another small hack, to simplify making a thumbnail link + UrlBasedContent::originalJson.insert( + "thumbnailMediaId", thumbnail.mediaId()); + } public: Thumbnail thumbnail; -- cgit v1.2.3 From 72ff8cf1e77e859de08e343df964f496dc2f9dd8 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Sat, 20 Jan 2018 14:50:33 +0900 Subject: RoomMessageEvent::hasTextContent() Similar to hasFileContent(), allows to ascertain that an event has TextContent without checking against all possible msgtypes. --- events/roommessageevent.cpp | 7 +++++++ events/roommessageevent.h | 1 + 2 files changed, 8 insertions(+) (limited to 'events') diff --git a/events/roommessageevent.cpp b/events/roommessageevent.cpp index 3c5d10ad..8c088f21 100644 --- a/events/roommessageevent.cpp +++ b/events/roommessageevent.cpp @@ -116,6 +116,13 @@ QMimeType RoomMessageEvent::mimeType() const QMimeDatabase().mimeTypeForName("text/plain"); } +bool RoomMessageEvent::hasTextContent() const +{ + return content() && + (msgtype() == MsgType::Text || msgtype() == MsgType::Emote || + msgtype() == MsgType::Notice); // FIXME: Unbind from specific msgtypes +} + bool RoomMessageEvent::hasFileContent() const { return content() && content()->fileInfo(); diff --git a/events/roommessageevent.h b/events/roommessageevent.h index 867d8880..2a5eeb7e 100644 --- a/events/roommessageevent.h +++ b/events/roommessageevent.h @@ -59,6 +59,7 @@ namespace QMatrixClient EventContent::TypedBase* content() const { return _content.data(); } QMimeType mimeType() const; + bool hasTextContent() const; bool hasFileContent() const; QJsonObject toJson() const; -- cgit v1.2.3 From 8590054a675bc5d2b07fff1acbb084d67c068113 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Thu, 25 Jan 2018 19:42:13 +0900 Subject: StateEvent<>: introduce Prev structure and prevSenderId() accessor Also switch prev_content() from accidental snake case to camel case (old name still provided for compatibility). --- events/event.h | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) (limited to 'events') diff --git a/events/event.h b/events/event.h index 6ed5ba49..b5a4d94e 100644 --- a/events/event.h +++ b/events/event.h @@ -255,6 +255,21 @@ namespace QMatrixClient virtual bool repeatsState() const; }; + template + struct Prev + { + template + explicit Prev(const QJsonObject& unsignedJson, + ContentParamTs&&... contentParams) + : senderId(unsignedJson.value("prev_sender").toString()) + , content(unsignedJson.value("prev_content").toObject(), + std::forward(contentParams)...) + { } + + QString senderId; + ContentT content; + }; + template class StateEvent: public StateEventBase { @@ -270,9 +285,8 @@ namespace QMatrixClient { auto unsignedData = obj.value("unsigned").toObject(); if (unsignedData.contains("prev_content")) - _prev.reset(new ContentT( - unsignedData.value("prev_content").toObject(), - std::forward(contentParams)...)); + _prev = std::make_unique>(unsignedData, + std::forward(contentParams)...); } template explicit StateEvent(Type type, ContentParamTs&&... contentParams) @@ -283,11 +297,15 @@ namespace QMatrixClient QJsonObject toJson() const { return _content.toJson(); } ContentT content() const { return _content; } - ContentT* prev_content() const { return _prev.data(); } + /** @deprecated Use prevContent instead */ + ContentT* prev_content() const { return prevContent(); } + ContentT* prevContent() const + { return _prev ? &_prev->content : nullptr; } + QString prevSenderId() const { return _prev ? _prev->senderId : ""; } protected: ContentT _content; - QScopedPointer _prev; + std::unique_ptr> _prev; }; } // namespace QMatrixClient Q_DECLARE_METATYPE(QMatrixClient::Event*) -- cgit v1.2.3