From 514fa7512ea23691f7ae7e23257e752a0b758b50 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Fri, 20 Oct 2017 19:31:41 +0900 Subject: Cleanup --- events/roommessageevent.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'events') diff --git a/events/roommessageevent.h b/events/roommessageevent.h index 74e0defb..ad413943 100644 --- a/events/roommessageevent.h +++ b/events/roommessageevent.h @@ -76,7 +76,7 @@ namespace QMatrixClient protected: using Base::Base; - virtual void fillInfoJson(QJsonObject* infoJson) const { } + virtual void fillInfoJson(QJsonObject* /*infoJson*/) const { } }; } // namespace MessageEventContent -- cgit v1.2.3 From 2fb03272a8bc7da4943347ea7ecca6070f667bd6 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Thu, 26 Oct 2017 19:40:59 +0300 Subject: Move out common message event content classes ImageContent is usable outside of m.room.message (in particular, m.room.avatar uses the same structure for content. And EventContent::Base is very suitable to derive from even for standard event content structures (such as in room name events), let alone non-standard ones. Also, renamed MessageEventContent to EventContent (for obvious reasons). --- CMakeLists.txt | 1 + events/eventcontent.cpp | 71 +++++++++++++ events/eventcontent.h | 245 ++++++++++++++++++++++++++++++++++++++++++++ events/roommessageevent.cpp | 48 +-------- events/roommessageevent.h | 232 ++--------------------------------------- 5 files changed, 327 insertions(+), 270 deletions(-) create mode 100644 events/eventcontent.cpp create mode 100644 events/eventcontent.h (limited to 'events') diff --git a/CMakeLists.txt b/CMakeLists.txt index 2abf7e69..0395774a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -62,6 +62,7 @@ set(libqmatrixclient_SRCS user.cpp settings.cpp events/event.cpp + events/eventcontent.cpp events/roommessageevent.cpp events/roomnameevent.cpp events/roomaliasesevent.cpp diff --git a/events/eventcontent.cpp b/events/eventcontent.cpp new file mode 100644 index 00000000..205d404b --- /dev/null +++ b/events/eventcontent.cpp @@ -0,0 +1,71 @@ +/****************************************************************************** + * Copyright (C) 2017 Kitsune Ral + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "eventcontent.h" + +#include +#include + +using namespace QMatrixClient::EventContent; + +QJsonObject Base::toJson() const +{ + QJsonObject o; + fillJson(&o); + return o; +} + +QJsonObject InfoBase::toInfoJson() const +{ + QJsonObject info; + fillInfoJson(&info); + return info; +} + +FileInfo::FileInfo(const QUrl& u, int payloadSize, const QMimeType& mimeType, + const QString& originalFilename) + : InfoBase(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) +{ + if (!mimeType.isValid()) + mimeType = QMimeDatabase().mimeTypeForData(QByteArray()); +} + +void FileInfo::fillInfoJson(QJsonObject* infoJson) const +{ + Q_ASSERT(infoJson); + infoJson->insert("size", payloadSize); + infoJson->insert("mimetype", mimeType.name()); +} + +void FileInfo::fillJson(QJsonObject* json) const +{ + Q_ASSERT(json); + json->insert("url", url.toString()); + if (!originalName.isEmpty()) + json->insert("filename", originalName); + json->insert("info", toInfoJson()); +} + diff --git a/events/eventcontent.h b/events/eventcontent.h new file mode 100644 index 00000000..2c8d7f3f --- /dev/null +++ b/events/eventcontent.h @@ -0,0 +1,245 @@ +/****************************************************************************** + * Copyright (C) 2017 Kitsune Ral + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#pragma once + +// This file contains generic event content definitions, applicable to room +// message events as well as other events (e.g., avatars). + +#include +#include +#include +#include + +namespace QMatrixClient +{ + 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. 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 + { + public: + virtual ~Base() = default; + + QJsonObject toJson() const; + + QMimeType mimeType; + + protected: + Base() = default; + explicit Base(const QMimeType& type) : mimeType(type) { } + + virtual void fillJson(QJsonObject* o) const = 0; + }; + + /** + * 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 Base + { + public: + QJsonObject toInfoJson() const; + + protected: + using Base::Base; + + virtual void fillInfoJson(QJsonObject* /*infoJson*/) const { } + }; + + // 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. + + /** + * Base class for content types that consist of a URL along with + * additional information. Most of message types except textual fall + * under this category. + */ + class FileInfo: public InfoBase + { + public: + 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 payloadSize; + QString originalName; + + protected: + void fillJson(QJsonObject* json) const override; + 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 + */ + template + class ImageInfo : public InfoT + { + public: + 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; + }; + + /** + * A base 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 + */ + template + class Thumbnailed : public InfoT + { + 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()) + { } + + 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; + }; + + /** + * One more facility 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. + * + * \tparam InfoT base info class; should derive from \p FileInfo or + * provide a constructor with a compatible signature + */ + template // 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()) + { } + }; + + /** + * 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>>; + + /** + * 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>; + } // namespace EventContent +} // namespace QMatrixClient diff --git a/events/roommessageevent.cpp b/events/roommessageevent.cpp index 3fb0226a..82bd07b6 100644 --- a/events/roommessageevent.cpp +++ b/events/roommessageevent.cpp @@ -23,7 +23,7 @@ #include using namespace QMatrixClient; -using namespace MessageEventContent; +using namespace EventContent; using MsgType = RoomMessageEvent::MsgType; @@ -124,20 +124,6 @@ QJsonObject RoomMessageEvent::toJson() const return obj; } -QJsonObject Base::toJson() const -{ - QJsonObject o; - fillJson(&o); - return o; -} - -QJsonObject InfoBase::toInfoJson() const -{ - QJsonObject info; - fillInfoJson(&info); - return info; -} - TextContent::TextContent(const QString& text, const QString& contentType) : Base(QMimeDatabase().mimeTypeForName(contentType)), body(text) { } @@ -167,38 +153,6 @@ void TextContent::fillJson(QJsonObject* json) const json->insert("formatted_body", body); } -FileInfo::FileInfo(const QUrl& u, int payloadSize, const QMimeType& mimeType, - const QString& originalFilename) - : InfoBase(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) -{ - if (!mimeType.isValid()) - mimeType = QMimeDatabase().mimeTypeForData(QByteArray()); -} - -void FileInfo::fillInfoJson(QJsonObject* infoJson) const -{ - Q_ASSERT(infoJson); - infoJson->insert("size", payloadSize); - infoJson->insert("mimetype", mimeType.name()); -} - -void FileInfo::fillJson(QJsonObject* json) const -{ - Q_ASSERT(json); - json->insert("url", url.toString()); - if (!originalName.isEmpty()) - json->insert("filename", originalName); - json->insert("info", toInfoJson()); -} - LocationContent::LocationContent(const QString& geoUri, const ImageInfo<>& thumbnail) : Thumbnailed<>(thumbnail), geoUri(geoUri) diff --git a/events/roommessageevent.h b/events/roommessageevent.h index ad413943..b98f12d6 100644 --- a/events/roommessageevent.h +++ b/events/roommessageevent.h @@ -20,65 +20,11 @@ #include "event.h" -#include -#include -#include +#include "eventcontent.h" 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 - { - public: - virtual ~Base() = default; - - QJsonObject toJson() const; - - QMimeType mimeType; - - protected: - Base() = default; - explicit Base(const QMimeType& type) : mimeType(type) { } - - virtual void fillJson(QJsonObject* o) const = 0; - }; - - /** - * 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: - using Base::Base; - - virtual void fillInfoJson(QJsonObject* /*infoJson*/) const { } - }; - } // namespace MessageEventContent + namespace MessageEventContent = EventContent; // Back-compatibility /** * The event class corresponding to m.room.message events @@ -94,19 +40,19 @@ namespace QMatrixClient RoomMessageEvent(const QString& plainBody, const QString& jsonMsgType, - MessageEventContent::Base* content = nullptr) + EventContent::Base* content = nullptr) : RoomEvent(Type::RoomMessage) , _msgtype(jsonMsgType), _plainBody(plainBody), _content(content) { } explicit RoomMessageEvent(const QString& plainBody, MsgType msgType = MsgType::Text, - MessageEventContent::Base* content = nullptr); + EventContent::Base* content = nullptr); explicit RoomMessageEvent(const QJsonObject& obj); MsgType msgtype() const; QString rawMsgtype() const { return _msgtype; } const QString& plainBody() const { return _plainBody; } - const MessageEventContent::Base* content() const + const EventContent::Base* content() const { return _content.data(); } QMimeType mimeType() const; @@ -117,18 +63,15 @@ namespace QMatrixClient private: QString _msgtype; QString _plainBody; - QScopedPointer _content; + QScopedPointer _content; REGISTER_ENUM(MsgType) }; using MessageEventType = RoomMessageEvent::MsgType; - namespace MessageEventContent + namespace EventContent { - // 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. + // Additional event content types /** * Rich text content for m.text, m.emote, m.notice @@ -147,163 +90,6 @@ namespace QMatrixClient QString body; }; - /** - * 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: - 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 payloadSize; - QString originalName; - - protected: - void fillJson(QJsonObject* json) const override; - 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 - */ - template - class ImageInfo : public InfoT - { - public: - 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; - }; - - /** - * 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 Thumbnailed : public InfoT - { - 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()) - { } - - 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; - }; - - /** - * 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 // 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()) - { } - }; - - /** - * 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>>; - - /** - * 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>; - /** * Content class for m.location * @@ -380,5 +166,5 @@ namespace QMatrixClient * - duration */ using AudioContent = UrlWith; - } // namespace MessageEventContent + } // namespace EventContent } // namespace QMatrixClient -- cgit v1.2.3 From 0fb7adfcaa5dffee4efd0a34a2a4fd655fe5c709 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Fri, 27 Oct 2017 15:19:07 +0300 Subject: Support m.room.avatar events The events are detected in /sync output, and avatars for rooms are loaded from respective URLs. Clients can use Room::avatar() method to request a pixmap of a certain size, and react to avatarChanged() in order to update the UI when new pixmaps/avatars arrive. avatarChanged() signal is overloaded with two tasks - the first firing merely indicates that a new avatar is available (without actual pixmap yet available) while the second firing means that an actual pixmap has arrived (all this is entirely transparent for clients, they just should update their pixmaps from Room::avatar() every time when Room::avatarChanged() is emitted). --- CMakeLists.txt | 1 + events/event.cpp | 2 ++ events/event.h | 3 ++- events/roomavatarevent.cpp | 23 +++++++++++++++++++ events/roomavatarevent.h | 53 ++++++++++++++++++++++++++++++++++++++++++ libqmatrixclient.pri | 2 ++ room.cpp | 57 ++++++++++++++++++++++++++++++++++++++-------- room.h | 10 +++++++- user.cpp | 5 ++++ user.h | 2 ++ 10 files changed, 146 insertions(+), 12 deletions(-) create mode 100644 events/roomavatarevent.cpp create mode 100644 events/roomavatarevent.h (limited to 'events') diff --git a/CMakeLists.txt b/CMakeLists.txt index fc3276c9..26c7bf46 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -70,6 +70,7 @@ set(libqmatrixclient_SRCS events/roomcanonicalaliasevent.cpp events/roommemberevent.cpp events/roomtopicevent.cpp + events/roomavatarevent.cpp events/typingevent.cpp events/receiptevent.cpp events/encryptedevent.cpp diff --git a/events/event.cpp b/events/event.cpp index 304f2af6..9963a0ef 100644 --- a/events/event.cpp +++ b/events/event.cpp @@ -24,6 +24,7 @@ #include "roomcanonicalaliasevent.h" #include "roommemberevent.h" #include "roomtopicevent.h" +#include "roomavatarevent.h" #include "typingevent.h" #include "receiptevent.h" #include "encryptedevent.h" @@ -136,6 +137,7 @@ RoomEvent* RoomEvent::fromJson(const QJsonObject& obj) "m.room.canonical_alias", make, "m.room.member", make, "m.room.topic", make, + "m.room.avatar", make, "m.room.encryption", make, /* Insert new ROOM event types BEFORE this line */ nullptr diff --git a/events/event.h b/events/event.h index ec993522..c151ac0e 100644 --- a/events/event.h +++ b/events/event.h @@ -34,7 +34,8 @@ namespace QMatrixClient enum class Type { RoomMessage, RoomName, RoomAliases, RoomCanonicalAlias, - RoomMember, RoomTopic, RoomEncryption, RoomEncryptedMessage, + RoomMember, RoomTopic, RoomAvatar, + RoomEncryption, RoomEncryptedMessage, Typing, Receipt, Unknown }; diff --git a/events/roomavatarevent.cpp b/events/roomavatarevent.cpp new file mode 100644 index 00000000..7a5f82a1 --- /dev/null +++ b/events/roomavatarevent.cpp @@ -0,0 +1,23 @@ +/****************************************************************************** + * Copyright (C) 2017 Kitsune Ral + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "roomavatarevent.h" + +using namespace QMatrixClient; + + diff --git a/events/roomavatarevent.h b/events/roomavatarevent.h new file mode 100644 index 00000000..411de4e8 --- /dev/null +++ b/events/roomavatarevent.h @@ -0,0 +1,53 @@ +/****************************************************************************** + * Copyright (C) 2017 Kitsune Ral + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#pragma once + +#include "event.h" + +#include + +#include "eventcontent.h" + +namespace QMatrixClient +{ + class RoomAvatarEvent: public RoomEvent + { + public: + explicit RoomAvatarEvent(EventContent::ImageContent avatar) + : RoomEvent(Type::RoomAvatar), _avatar(std::move(avatar)) + { } + explicit RoomAvatarEvent(const QJsonObject& obj) + : RoomEvent(Type::RoomAvatar, obj), _avatar(contentJson()) + { } + + const EventContent::ImageContent& content() const { return _avatar; } + + QJsonObject toJson() const { return _avatar.toJson(); } + + static constexpr const char* TypeId = "m.room.avatar"; + + private: + // It's a bit of an overkill to use a full-fledged ImageContent + // because in reality m.room.avatar usually only has a single URL, + // without a thumbnail. But The Spec says there be thumbnails, and + // we follow The Spec. + EventContent::ImageContent _avatar; + }; + +} // namespace QMatrixClient diff --git a/libqmatrixclient.pri b/libqmatrixclient.pri index 9bcb911f..9eb6bd16 100644 --- a/libqmatrixclient.pri +++ b/libqmatrixclient.pri @@ -18,6 +18,7 @@ HEADERS += \ $$PWD/events/roomcanonicalaliasevent.h \ $$PWD/events/roommemberevent.h \ $$PWD/events/roomtopicevent.h \ + $$PWD/events/roomavatarevent.h \ $$PWD/events/typingevent.h \ $$PWD/events/receiptevent.h \ $$PWD/jobs/basejob.h \ @@ -48,6 +49,7 @@ SOURCES += \ $$PWD/events/roomcanonicalaliasevent.cpp \ $$PWD/events/roommemberevent.cpp \ $$PWD/events/roomtopicevent.cpp \ + $$PWD/events/roomavatarevent.cpp \ $$PWD/events/typingevent.cpp \ $$PWD/events/receiptevent.cpp \ $$PWD/jobs/basejob.cpp \ diff --git a/room.cpp b/room.cpp index 65cf2d2a..53a6b160 100644 --- a/room.cpp +++ b/room.cpp @@ -27,12 +27,14 @@ #include "events/roomaliasesevent.h" #include "events/roomcanonicalaliasevent.h" #include "events/roomtopicevent.h" +#include "events/roomavatarevent.h" #include "events/roommemberevent.h" #include "events/typingevent.h" #include "events/receiptevent.h" #include "jobs/sendeventjob.h" #include "jobs/roommessagesjob.h" #include "jobs/postreceiptjob.h" +#include "avatar.h" #include "connection.h" #include "user.h" @@ -53,7 +55,7 @@ class Room::Private Private(Connection* c, QString id_, JoinState initialJoinState) : q(nullptr), connection(c), id(std::move(id_)) - , joinState(initialJoinState), unreadMessages(false) + , avatar(c), joinState(initialJoinState), unreadMessages(false) , highlightCount(0), notificationCount(0), roomMessagesJob(nullptr) { } @@ -73,6 +75,7 @@ class Room::Private QString name; QString displayname; QString topic; + Avatar avatar; JoinState joinState; bool unreadMessages; int highlightCount; @@ -189,6 +192,23 @@ QString Room::topic() const return d->topic; } +QPixmap Room::avatar(int width, int height) +{ + if (!d->avatar.url().isEmpty()) + return d->avatar.get(width, height, [=] { emit avatarChanged(); }); + + // Use the other side's avatar for 1:1's + if (d->membersMap.size() == 2) + { + auto theOtherOneIt = d->membersMap.begin(); + if (theOtherOneIt.value() == localUser()) + ++theOtherOneIt; + return theOtherOneIt.value()->avatarObject() + .get(width, height, [=] { emit avatarChanged(); }); + } + return {}; +} + JoinState Room::joinState() const { return d->joinState; @@ -778,6 +798,17 @@ void Room::processStateEvents(const RoomEvents& events) emit topicChanged(); break; } + case EventType::RoomAvatar: { + const auto& avatarEventContent = + static_cast(event)->content(); + if (d->avatar.updateUrl(avatarEventContent.url)) + { + qCDebug(MAIN) << "Room avatar URL updated:" + << avatarEventContent.url.toString(); + emit avatarChanged(); + } + break; + } case EventType::RoomMember: { auto memberEvent = static_cast(event); // Can't use d->member() below because the user may be not a member (yet) @@ -939,9 +970,13 @@ void Room::Private::updateDisplayname() emit q->displaynameChanged(q); } -QJsonObject stateEventToJson(const QString& type, const QString& name, - const QJsonValue& content) +template +void appendEventJson(QJsonArray& events, const QString& type, + const QString& name, const T& content) { + if (content.isEmpty()) + return; + QJsonObject contentObj; contentObj.insert(name, content); @@ -949,7 +984,7 @@ QJsonObject stateEventToJson(const QString& type, const QString& name, eventObj.insert("type", type); eventObj.insert("content", contentObj); - return eventObj; + events.append(eventObj); } QJsonObject Room::Private::toJson() const @@ -958,12 +993,14 @@ QJsonObject Room::Private::toJson() const { QJsonArray stateEvents; - stateEvents.append(stateEventToJson("m.room.name", "name", name)); - stateEvents.append(stateEventToJson("m.room.topic", "topic", topic)); - stateEvents.append(stateEventToJson("m.room.aliases", "aliases", - QJsonArray::fromStringList(aliases))); - stateEvents.append(stateEventToJson("m.room.canonical_alias", "alias", - canonicalAlias)); + appendEventJson(stateEvents, "m.room.name", "name", name); + appendEventJson(stateEvents, "m.room.topic", "topic", topic); + appendEventJson(stateEvents, "m.room.avatar", "avatar_url", + avatar.url().toString()); + appendEventJson(stateEvents, "m.room.aliases", "aliases", + QJsonArray::fromStringList(aliases)); + appendEventJson(stateEvents, "m.room.canonical_alias", "alias", + canonicalAlias); for (const auto &i : membersMap) { diff --git a/room.h b/room.h index 2d0453bc..2fa7e7d5 100644 --- a/room.h +++ b/room.h @@ -25,6 +25,7 @@ #include #include #include +#include #include "jobs/syncjob.h" #include "events/roommessageevent.h" @@ -79,7 +80,7 @@ namespace QMatrixClient using rev_iter_t = Timeline::const_reverse_iterator; Room(Connection* connection, QString id, JoinState initialJoinState); - virtual ~Room(); + ~Room() override; Connection* connection() const; User* localUser() const; @@ -97,6 +98,12 @@ namespace QMatrixClient Q_INVOKABLE QStringList memberNames() const; Q_INVOKABLE int memberCount() const; + /** + * Returns a room avatar and requests it from the network if needed + * @return a pixmap with the avatar or a placeholder if there's none + * available yet + */ + Q_INVOKABLE QPixmap avatar(int width, int height); /** * @brief Produces a disambiguated name for a given user in * the context of the room @@ -181,6 +188,7 @@ namespace QMatrixClient /** @brief The room displayname changed */ void displaynameChanged(Room* room); void topicChanged(); + void avatarChanged(); void userAdded(User* user); void userRemoved(User* user); void memberRenamed(User* user); diff --git a/user.cpp b/user.cpp index b2c0dc1e..8c8a7fdb 100644 --- a/user.cpp +++ b/user.cpp @@ -90,6 +90,11 @@ QString User::bridged() const { return d->bridged; } +Avatar& User::avatarObject() +{ + return d->avatar; +} + QPixmap User::avatar(int width, int height) { return d->avatar.get(width, height, [=] { emit avatarChanged(this); }); diff --git a/user.h b/user.h index aee6ec3e..148ed64d 100644 --- a/user.h +++ b/user.h @@ -20,6 +20,7 @@ #include #include +#include "avatar.h" namespace QMatrixClient { @@ -52,6 +53,7 @@ namespace QMatrixClient */ Q_INVOKABLE QString bridged() const; + Avatar& avatarObject(); QPixmap avatar(int requestedWidth, int requestedHeight); QUrl avatarUrl() const; -- cgit v1.2.3 From da8bb556dca628e20be732b4a30895010abcc9bf Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Tue, 31 Oct 2017 13:12:33 +0300 Subject: Streamline EventContent hierarchy Two changes to make EventContent hierarchy easier to understand and use: - InfoBase is unbound from Base, and downstream classes use multiple inheritance to work "info" objects - MIME types are separated from Base into a separate TypedBase class because MIME typing is not common to all content kinds. --- events/eventcontent.cpp | 12 ++---------- events/eventcontent.h | 47 +++++++++++++++++++++++++++++++-------------- events/roommessageevent.cpp | 15 ++++++++++----- events/roommessageevent.h | 28 +++++++++++++++++---------- 4 files changed, 63 insertions(+), 39 deletions(-) (limited to 'events') diff --git a/events/eventcontent.cpp b/events/eventcontent.cpp index 205d404b..dcbccf08 100644 --- a/events/eventcontent.cpp +++ b/events/eventcontent.cpp @@ -37,6 +37,8 @@ QJsonObject InfoBase::toInfoJson() const 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) @@ -59,13 +61,3 @@ void FileInfo::fillInfoJson(QJsonObject* infoJson) const infoJson->insert("size", payloadSize); infoJson->insert("mimetype", mimeType.name()); } - -void FileInfo::fillJson(QJsonObject* json) const -{ - Q_ASSERT(json); - json->insert("url", url.toString()); - if (!originalName.isEmpty()) - json->insert("filename", originalName); - json->insert("info", toInfoJson()); -} - diff --git a/events/eventcontent.h b/events/eventcontent.h index 2c8d7f3f..f9cdaf11 100644 --- a/events/eventcontent.h +++ b/events/eventcontent.h @@ -50,15 +50,16 @@ namespace QMatrixClient QJsonObject toJson() const; - QMimeType mimeType; - protected: - Base() = default; - explicit Base(const QMimeType& type) : mimeType(type) { } - virtual void fillJson(QJsonObject* o) const = 0; }; + class TypedBase: public Base + { + public: + virtual QMimeType type() const = 0; + }; + /** * A base class for content types that have an "info" object in their * JSON representation @@ -70,15 +71,20 @@ namespace QMatrixClient * have a constructor that accepts two parameters, QUrl and QJsonObject, * in order to load the URL+info part from JSON. */ - class InfoBase: public Base + class InfoBase { public: + virtual ~InfoBase() = default; + QJsonObject toInfoJson() const; + QMimeType mimeType; + protected: - using Base::Base; + InfoBase() = default; + explicit InfoBase(const QMimeType& type) : mimeType(type) { } - virtual void fillInfoJson(QJsonObject* /*infoJson*/) const { } + virtual void fillInfoJson(QJsonObject* /*infoJson*/) const = 0; }; // The below structures fairly follow CS spec 11.2.1.6. The overall @@ -105,7 +111,6 @@ namespace QMatrixClient QString originalName; protected: - void fillJson(QJsonObject* json) const override; void fillInfoJson(QJsonObject* infoJson) const override; }; @@ -129,14 +134,15 @@ namespace QMatrixClient , imageSize(infoJson["w"].toInt(), infoJson["h"].toInt()) { } + QSize imageSize; + + protected: void fillInfoJson(QJsonObject* infoJson) const override { InfoT::fillInfoJson(infoJson); infoJson->insert("w", imageSize.width()); infoJson->insert("h", imageSize.height()); } - - QSize imageSize; }; /** @@ -172,14 +178,15 @@ namespace QMatrixClient infoJson["thumbnail_info"].toObject()) { } + 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()); } - - ImageInfo<> thumbnail; }; /** @@ -194,7 +201,7 @@ namespace QMatrixClient * provide a constructor with a compatible signature */ template // InfoT : public FileInfo - class UrlWith : public InfoT + class UrlWith : public TypedBase, public InfoT { public: using InfoT::InfoT; @@ -202,6 +209,18 @@ namespace QMatrixClient : InfoT(json["url"].toString(), json["info"].toObject(), json["filename"].toString()) { } + + QMimeType type() const override { return InfoT::mimeType; } + + protected: + void fillJson(QJsonObject* json) const override + { + Q_ASSERT(json); + json->insert("url", InfoT::url.toString()); + if (!InfoT::originalName.isEmpty()) + json->insert("filename", InfoT::originalName); + json->insert("info", InfoT::toInfoJson()); + } }; /** diff --git a/events/roommessageevent.cpp b/events/roommessageevent.cpp index 82bd07b6..f06474e9 100644 --- a/events/roommessageevent.cpp +++ b/events/roommessageevent.cpp @@ -28,7 +28,7 @@ using namespace EventContent; using MsgType = RoomMessageEvent::MsgType; template -Base* make(const QJsonObject& json) +TypedBase* make(const QJsonObject& json) { return new ContentT(json); } @@ -37,7 +37,7 @@ struct MsgTypeDesc { QString jsonType; MsgType enumType; - Base* (*maker)(const QJsonObject&); + TypedBase* (*maker)(const QJsonObject&); }; const std::vector msgTypes = @@ -74,7 +74,7 @@ MsgType jsonToMsgType(const QString& jsonType) } RoomMessageEvent::RoomMessageEvent(const QString& plainBody, - MsgType msgType, Base* content) + MsgType msgType, TypedBase* content) : RoomMessageEvent(plainBody, msgTypeToJson(msgType), content) { } @@ -112,7 +112,7 @@ RoomMessageEvent::MsgType RoomMessageEvent::msgtype() const QMimeType RoomMessageEvent::mimeType() const { - return _content ? _content->mimeType : + return _content ? _content->type() : QMimeDatabase().mimeTypeForName("text/plain"); } @@ -125,7 +125,7 @@ QJsonObject RoomMessageEvent::toJson() const } TextContent::TextContent(const QString& text, const QString& contentType) - : Base(QMimeDatabase().mimeTypeForName(contentType)), body(text) + : mimeType(QMimeDatabase().mimeTypeForName(contentType)), body(text) { } TextContent::TextContent(const QJsonObject& json) @@ -170,6 +170,11 @@ void LocationContent::fillJson(QJsonObject* o) const 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) diff --git a/events/roommessageevent.h b/events/roommessageevent.h index b98f12d6..eef6b657 100644 --- a/events/roommessageevent.h +++ b/events/roommessageevent.h @@ -40,19 +40,19 @@ namespace QMatrixClient RoomMessageEvent(const QString& plainBody, const QString& jsonMsgType, - EventContent::Base* content = nullptr) + EventContent::TypedBase* content = nullptr) : RoomEvent(Type::RoomMessage) , _msgtype(jsonMsgType), _plainBody(plainBody), _content(content) { } explicit RoomMessageEvent(const QString& plainBody, MsgType msgType = MsgType::Text, - EventContent::Base* content = nullptr); + EventContent::TypedBase* content = nullptr); explicit RoomMessageEvent(const QJsonObject& obj); MsgType msgtype() const; QString rawMsgtype() const { return _msgtype; } const QString& plainBody() const { return _plainBody; } - const EventContent::Base* content() const + const EventContent::TypedBase* content() const { return _content.data(); } QMimeType mimeType() const; @@ -63,7 +63,7 @@ namespace QMatrixClient private: QString _msgtype; QString _plainBody; - QScopedPointer _content; + QScopedPointer _content; REGISTER_ENUM(MsgType) }; @@ -79,15 +79,19 @@ namespace QMatrixClient * Available fields: mimeType, body. The body can be either rich text * or plain text, depending on what mimeType specifies. */ - class TextContent: public Base + class TextContent: public TypedBase { public: TextContent(const QString& text, const QString& contentType); explicit TextContent(const QJsonObject& json); - void fillJson(QJsonObject* json) const override; + QMimeType type() const override { return mimeType; } + QMimeType mimeType; QString body; + + protected: + void fillJson(QJsonObject* json) const override; }; /** @@ -103,16 +107,19 @@ namespace QMatrixClient * - thumbnail.mimeType * - thumbnail.imageSize */ - class LocationContent: public Thumbnailed<> + class LocationContent: public TypedBase, public Thumbnailed<> { public: LocationContent(const QString& geoUri, const ImageInfo<>& thumbnail); explicit LocationContent(const QJsonObject& json); - void fillJson(QJsonObject* o) const override; + QMimeType type() const override; QString geoUri; + + protected: + void fillJson(QJsonObject* o) const override; }; /** @@ -127,9 +134,10 @@ namespace QMatrixClient PlayableInfo(const QUrl& u, const QJsonObject& infoJson, const QString& originalFilename = {}); - void fillInfoJson(QJsonObject* infoJson) const override; - int duration; + + protected: + void fillInfoJson(QJsonObject* infoJson) const override; }; /** -- cgit v1.2.3 From f591e02176f494c06c7288f975e8571dda2bc6ad Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Wed, 1 Nov 2017 11:11:29 +0300 Subject: Drop UnknownEvent files from the source tree We don't use them for several months already. --- events/unknownevent.cpp | 64 ------------------------------------------------- events/unknownevent.h | 40 ------------------------------- 2 files changed, 104 deletions(-) delete mode 100644 events/unknownevent.cpp delete mode 100644 events/unknownevent.h (limited to 'events') diff --git a/events/unknownevent.cpp b/events/unknownevent.cpp deleted file mode 100644 index 1670ff1d..00000000 --- a/events/unknownevent.cpp +++ /dev/null @@ -1,64 +0,0 @@ -/****************************************************************************** - * Copyright (C) 2015 Felix Rohrbach - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "unknownevent.h" - -#include "logging.h" - -#include - -using namespace QMatrixClient; - -class UnknownEvent::Private -{ - public: - QString type; - QString content; -}; - -UnknownEvent::UnknownEvent() - : Event(EventType::Unknown) - , d(new Private) -{ -} - -UnknownEvent::~UnknownEvent() -{ - delete d; -} - -QString UnknownEvent::typeString() const -{ - return d->type; -} - -QString UnknownEvent::content() const -{ - return d->content; -} - -UnknownEvent* UnknownEvent::fromJson(const QJsonObject& obj) -{ - UnknownEvent* e = new UnknownEvent(); - e->parseJson(obj); - e->d->type = obj.value("type").toString(); - e->d->content = QString::fromUtf8(QJsonDocument(obj).toJson()); - qCDebug(EVENTS) << "UnknownEvent, JSON follows:"; - qCDebug(EVENTS) << formatJson << obj; - return e; -} diff --git a/events/unknownevent.h b/events/unknownevent.h deleted file mode 100644 index 51f2c4be..00000000 --- a/events/unknownevent.h +++ /dev/null @@ -1,40 +0,0 @@ -/****************************************************************************** - * Copyright (C) 2015 Felix Rohrbach - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#pragma once - -#include "event.h" - -namespace QMatrixClient -{ - class UnknownEvent: public Event - { - public: - UnknownEvent(); - virtual ~UnknownEvent(); - - QString typeString() const; - QString content() const; - - static UnknownEvent* fromJson(const QJsonObject& obj); - - private: - class Private; - Private* d; - }; -} -- cgit v1.2.3 From 2931b4d13e1883150b92f80f08aa51d86ac2445f Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Wed, 1 Nov 2017 17:40:30 +0300 Subject: ReceiptEvent: Use fromJson<>() from converters.h; add TypeId Event::toTimestamp() duplicates fromJson<>() code, so it should go. --- events/receiptevent.cpp | 6 +++--- events/receiptevent.h | 2 ++ 2 files changed, 5 insertions(+), 3 deletions(-) (limited to 'events') diff --git a/events/receiptevent.cpp b/events/receiptevent.cpp index 646bb989..b36ddb23 100644 --- a/events/receiptevent.cpp +++ b/events/receiptevent.cpp @@ -35,10 +35,9 @@ Example of a Receipt Event: #include "receiptevent.h" +#include "converters.h" #include "logging.h" -#include - using namespace QMatrixClient; ReceiptEvent::ReceiptEvent(const QJsonObject& obj) @@ -62,7 +61,8 @@ ReceiptEvent::ReceiptEvent(const QJsonObject& obj) for( auto userIt = reads.begin(); userIt != reads.end(); ++userIt ) { const QJsonObject user = userIt.value().toObject(); - receipts.push_back({userIt.key(), toTimestamp(user["ts"])}); + receipts.push_back({userIt.key(), + QMatrixClient::fromJson(user["ts"])}); } _eventsWithReceipts.push_back({eventIt.key(), receipts}); } diff --git a/events/receiptevent.h b/events/receiptevent.h index cbe36b10..15fdf946 100644 --- a/events/receiptevent.h +++ b/events/receiptevent.h @@ -43,6 +43,8 @@ namespace QMatrixClient { return _eventsWithReceipts; } bool unreadMessages() const { return _unreadMessages; } + static constexpr const char* const TypeId = "m.receipt"; + private: EventsWithReceipts _eventsWithReceipts; bool _unreadMessages; // Spec extension for caching purposes -- cgit v1.2.3 From 64c0b7e045d9e8d58d91f252219a5579d4ba9441 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Wed, 1 Nov 2017 17:51:47 +0300 Subject: Add TypeId to more events --- events/roommemberevent.h | 2 ++ events/typingevent.h | 2 ++ 2 files changed, 4 insertions(+) (limited to 'events') diff --git a/events/roommemberevent.h b/events/roommemberevent.h index 9ebb75ee..358af257 100644 --- a/events/roommemberevent.h +++ b/events/roommemberevent.h @@ -28,6 +28,8 @@ namespace QMatrixClient { Q_GADGET public: + static constexpr const char* TypeId = "m.room.member"; + enum MembershipType : int {Invite = 0, Join, Knock, Leave, Ban}; explicit RoomMemberEvent(const QJsonObject& obj); diff --git a/events/typingevent.h b/events/typingevent.h index b12d224e..8c9551a4 100644 --- a/events/typingevent.h +++ b/events/typingevent.h @@ -27,6 +27,8 @@ namespace QMatrixClient class TypingEvent: public Event { public: + static constexpr const char* const TypeId = "m.typing"; + TypingEvent(const QJsonObject& obj); QStringList users() const { return _users; } -- cgit v1.2.3 From a275ef911b3f4d0334df0628324aee0081dd3a65 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Wed, 1 Nov 2017 18:19:56 +0300 Subject: StateEvent; EventContent::SimpleContent; event types refactoring * StateEvent<> is a new class template for all state events. It provides a uniform interface to the state content, as well as a means to serialize the content back to JSON. In addition, StateEvent now parses the "prev_content" JSON object, so one can refer to the previous state now (a notable step to proper reflection of state changes in the displayed timeline in clients). * EventContent::SimpleContent, together with StateEvent<>, forms a generalisation for simple state events, such as room name, topic, aliases etc. that boil down to a single key-value pair. DECLARE_SIMPLE_STATE_EVENT is a macro defined to streamline creation of events based on SimpleContent, providing API back-compatibility for events defined so far. As a result, a very concise simplestateevents.h replaces all those room*event.* files. * Event/RoomEvent::fromJson() code is squeezed down to plain type lists passed to makeIfMatches() "chained factory" function template. TypeId is mandatory for an event type to be included into that factory. * Event::toTimestamp() and Event::toStringList are completely superseded by respective fromJson<>() converters. --- CMakeLists.txt | 5 ---- events/encryptedevent.cpp | 5 ---- events/encryptedevent.h | 39 ------------------------ events/event.cpp | 61 ++++++++++++-------------------------- events/event.h | 53 +++++++++++++++++++++++---------- events/eventcontent.h | 31 +++++++++++++++++++ events/roomaliasesevent.cpp | 43 --------------------------- events/roomaliasesevent.h | 37 ----------------------- events/roomavatarevent.h | 22 ++++---------- events/roomcanonicalaliasevent.cpp | 21 ------------- events/roomcanonicalaliasevent.h | 38 ------------------------ events/roomnameevent.cpp | 22 -------------- events/roomnameevent.h | 38 ------------------------ events/roomtopicevent.cpp | 22 -------------- events/roomtopicevent.h | 50 ------------------------------- events/simplestateevents.h | 54 +++++++++++++++++++++++++++++++++ libqmatrixclient.pri | 10 +------ room.cpp | 5 +--- 18 files changed, 150 insertions(+), 406 deletions(-) delete mode 100644 events/encryptedevent.cpp delete mode 100644 events/encryptedevent.h delete mode 100644 events/roomaliasesevent.cpp delete mode 100644 events/roomaliasesevent.h delete mode 100644 events/roomcanonicalaliasevent.cpp delete mode 100644 events/roomcanonicalaliasevent.h delete mode 100644 events/roomnameevent.cpp delete mode 100644 events/roomnameevent.h delete mode 100644 events/roomtopicevent.cpp delete mode 100644 events/roomtopicevent.h create mode 100644 events/simplestateevents.h (limited to 'events') diff --git a/CMakeLists.txt b/CMakeLists.txt index 26c7bf46..6163b7be 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -65,15 +65,10 @@ set(libqmatrixclient_SRCS events/event.cpp events/eventcontent.cpp events/roommessageevent.cpp - events/roomnameevent.cpp - events/roomaliasesevent.cpp - events/roomcanonicalaliasevent.cpp events/roommemberevent.cpp - events/roomtopicevent.cpp events/roomavatarevent.cpp events/typingevent.cpp events/receiptevent.cpp - events/encryptedevent.cpp jobs/basejob.cpp jobs/checkauthmethods.cpp jobs/passwordlogin.cpp diff --git a/events/encryptedevent.cpp b/events/encryptedevent.cpp deleted file mode 100644 index 90e77c36..00000000 --- a/events/encryptedevent.cpp +++ /dev/null @@ -1,5 +0,0 @@ -// -// Created by rusakov on 26/09/2017. -// - -#include "encryptedevent.h" diff --git a/events/encryptedevent.h b/events/encryptedevent.h deleted file mode 100644 index 9db462e1..00000000 --- a/events/encryptedevent.h +++ /dev/null @@ -1,39 +0,0 @@ -/****************************************************************************** - * Copyright (C) 2017 Kitsune Ral - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#pragma once - -#include "event.h" - -namespace QMatrixClient -{ - class EncryptionEvent : public RoomEvent - { - public: - explicit EncryptionEvent(const QJsonObject& obj) - : RoomEvent(Type::RoomEncryption, obj) - , _algorithm(contentJson()["algorithm"].toString()) - { } - - QString algorithm() const { return _algorithm; } - - private: - QString _algorithm; - }; -} // namespace QMatrixClient - diff --git a/events/event.cpp b/events/event.cpp index 9963a0ef..44b742c1 100644 --- a/events/event.cpp +++ b/events/event.cpp @@ -19,15 +19,11 @@ #include "event.h" #include "roommessageevent.h" -#include "roomnameevent.h" -#include "roomaliasesevent.h" -#include "roomcanonicalaliasevent.h" +#include "simplestateevents.h" #include "roommemberevent.h" -#include "roomtopicevent.h" #include "roomavatarevent.h" #include "typingevent.h" #include "receiptevent.h" -#include "encryptedevent.h" #include "logging.h" #include @@ -54,32 +50,24 @@ QJsonObject Event::originalJsonObject() const return _originalJson; } -QDateTime Event::toTimestamp(const QJsonValue& v) +const QJsonObject Event::contentJson() const { - Q_ASSERT(v.isDouble() || v.isNull() || v.isUndefined()); - return QDateTime::fromMSecsSinceEpoch( - static_cast(v.toDouble()), Qt::UTC); + return _originalJson["content"].toObject(); } -QStringList Event::toStringList(const QJsonValue& v) +template +inline BaseEventT* makeIfMatches(const QJsonObject&, const QString&) { - Q_ASSERT(v.isArray() || v.isNull() || v.isUndefined()); - - QStringList l; - for( const QJsonValue& e : v.toArray() ) - l.push_back(e.toString()); - return l; + return nullptr; } -const QJsonObject Event::contentJson() const +template +inline BaseEventT* makeIfMatches(const QJsonObject& o, const QString& selector) { - return _originalJson["content"].toObject(); -} + if (selector == EventT::TypeId) + return new EventT(o); -template -EventT* make(const QJsonObject& o) -{ - return new EventT(o); + return makeIfMatches(o, selector); } Event* Event::fromJson(const QJsonObject& obj) @@ -88,17 +76,14 @@ Event* Event::fromJson(const QJsonObject& obj) if (auto e = RoomEvent::fromJson(obj)) return e; - return dispatch(obj).to(obj["type"].toString(), - "m.typing", make, - "m.receipt", make, - /* Insert new event types (except room events) BEFORE this line */ - nullptr - ); + return makeIfMatches(obj, obj["type"].toString()); } RoomEvent::RoomEvent(Type type, const QJsonObject& rep) : Event(type, rep), _id(rep["event_id"].toString()) - , _serverTimestamp(toTimestamp(rep["origin_server_ts"])) + , _serverTimestamp( + QMatrixClient::fromJson(rep["origin_server_ts"])) , _roomId(rep["room_id"].toString()) , _senderId(rep["sender"].toString()) , _txnId(rep["unsigned"].toObject().value("transactionId").toString()) @@ -130,16 +115,8 @@ void RoomEvent::addId(const QString& id) RoomEvent* RoomEvent::fromJson(const QJsonObject& obj) { - return dispatch(obj).to(obj["type"].toString(), - "m.room.message", make, - "m.room.name", make, - "m.room.aliases", make, - "m.room.canonical_alias", make, - "m.room.member", make, - "m.room.topic", make, - "m.room.avatar", make, - "m.room.encryption", make, - /* Insert new ROOM event types BEFORE this line */ - nullptr - ); + return makeIfMatches(obj, obj["type"].toString()); } diff --git a/events/event.h b/events/event.h index c151ac0e..020ef54b 100644 --- a/events/event.h +++ b/events/event.h @@ -56,9 +56,6 @@ namespace QMatrixClient static Event* fromJson(const QJsonObject& obj); protected: - static QDateTime toTimestamp(const QJsonValue& v); - static QStringList toStringList(const QJsonValue& v); - const QJsonObject contentJson() const; private: @@ -74,25 +71,20 @@ namespace QMatrixClient using EventsBatch = std::vector; using Events = EventsBatch; - template - BaseEventT* makeEvent(const QJsonObject& obj) - { - if (auto e = BaseEventT::fromJson(obj)) - return e; - - return new BaseEventT(EventType::Unknown, obj); - } - template > - BatchT makeEvents(const QJsonArray& objs) + inline BatchT makeEvents(const QJsonArray& objs) { BatchT evs; // The below line accommodates the difference in size types of // STL and Qt containers. evs.reserve(static_cast(objs.size())); - for (auto obj: objs) - evs.push_back(makeEvent(obj.toObject())); + for (auto objValue: objs) + { + const auto o = objValue.toObject(); + auto e = BaseEventT::fromJson(o); + evs.push_back(e ? e : new BaseEventT(EventType::Unknown, o)); + } return evs; } @@ -147,6 +139,37 @@ namespace QMatrixClient QString _txnId; }; using RoomEvents = EventsBatch; + + template + class StateEvent: public RoomEvent + { + public: + using content_type = ContentT; + + template + explicit StateEvent(Type type, const QJsonObject& obj, + ContentParamTs&&... contentParams) + : RoomEvent(type, obj) + , _content(contentJson(), + std::forward(contentParams)...) + , _prev(new ContentT(obj["prev_content"].toObject(), + std::forward(contentParams)...)) + { } + template + explicit StateEvent(Type type, ContentParamTs&&... contentParams) + : RoomEvent(type) + , _content(std::forward(contentParams)...) + { } + + QJsonObject toJson() const { return _content.toJson(); } + + ContentT content() const { return _content; } + ContentT* prev_content() const { return _prev.data(); } + + protected: + ContentT _content; + QScopedPointer _prev; + }; } // namespace QMatrixClient Q_DECLARE_OPAQUE_POINTER(QMatrixClient::Event*) Q_DECLARE_METATYPE(QMatrixClient::Event*) diff --git a/events/eventcontent.h b/events/eventcontent.h index f9cdaf11..60437995 100644 --- a/events/eventcontent.h +++ b/events/eventcontent.h @@ -21,6 +21,8 @@ // This file contains generic event content definitions, applicable to room // message events as well as other events (e.g., avatars). +#include "converters.h" + #include #include #include @@ -60,6 +62,35 @@ namespace QMatrixClient virtual QMimeType type() const = 0; }; + template + class SimpleContent: public Base + { + public: + using value_type = T; + + // The constructor is templated to enable perfect forwarding + template + SimpleContent(QString keyName, TT&& value) + : value(std::forward(value)), key(std::move(keyName)) + { } + SimpleContent(const QJsonObject& json, QString keyName) + : value(QMatrixClient::fromJson(json[keyName])) + , key(std::move(keyName)) + { } + + T value; + + protected: + QString key; + + private: + void fillJson(QJsonObject* json) const override + { + Q_ASSERT(json); + json->insert(key, QMatrixClient::toJson(value)); + } + }; + /** * A base class for content types that have an "info" object in their * JSON representation diff --git a/events/roomaliasesevent.cpp b/events/roomaliasesevent.cpp deleted file mode 100644 index 344b4367..00000000 --- a/events/roomaliasesevent.cpp +++ /dev/null @@ -1,43 +0,0 @@ -/****************************************************************************** - * Copyright (C) 2015 Felix Rohrbach - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -// Example of a RoomAliases Event: -// -// { -// "age":3758857346, -// "content":{ -// "aliases":["#freenode_#testest376:matrix.org"] -// }, -// "event_id":"$1439989428122drFjY:matrix.org", -// "origin_server_ts":1439989428910, -// "replaces_state":"$143613875199223YYPrN:matrix.org", -// "room_id":"!UoqtanuuSGTMvNRfDG:matrix.org", -// "state_key":"matrix.org", -// "type":"m.room.aliases", -// "user_id":"@appservice-irc:matrix.org" -// } - -#include "roomaliasesevent.h" - -using namespace QMatrixClient; - -RoomAliasesEvent::RoomAliasesEvent(const QJsonObject& obj) - : RoomEvent(Type::RoomAliases, obj) - , _aliases(toStringList(contentJson()["aliases"])) -{ } - diff --git a/events/roomaliasesevent.h b/events/roomaliasesevent.h deleted file mode 100644 index efafcb30..00000000 --- a/events/roomaliasesevent.h +++ /dev/null @@ -1,37 +0,0 @@ -/****************************************************************************** - * Copyright (C) 2015 Felix Rohrbach - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#pragma once - -#include "event.h" - -#include - -namespace QMatrixClient -{ - class RoomAliasesEvent: public RoomEvent - { - public: - explicit RoomAliasesEvent(const QJsonObject& obj); - - QStringList aliases() const { return _aliases; } - - private: - QStringList _aliases; - }; -} // namespace QMatrixClient diff --git a/events/roomavatarevent.h b/events/roomavatarevent.h index 411de4e8..ccfe8fbf 100644 --- a/events/roomavatarevent.h +++ b/events/roomavatarevent.h @@ -26,28 +26,18 @@ namespace QMatrixClient { - class RoomAvatarEvent: public RoomEvent + class RoomAvatarEvent: public StateEvent { + // It's a bit of an overkill to use a full-fledged ImageContent + // because in reality m.room.avatar usually only has a single URL, + // without a thumbnail. But The Spec says there be thumbnails, and + // we follow The Spec. public: - explicit RoomAvatarEvent(EventContent::ImageContent avatar) - : RoomEvent(Type::RoomAvatar), _avatar(std::move(avatar)) - { } explicit RoomAvatarEvent(const QJsonObject& obj) - : RoomEvent(Type::RoomAvatar, obj), _avatar(contentJson()) + : StateEvent(Type::RoomAvatar, obj) { } - const EventContent::ImageContent& content() const { return _avatar; } - - QJsonObject toJson() const { return _avatar.toJson(); } - static constexpr const char* TypeId = "m.room.avatar"; - - private: - // It's a bit of an overkill to use a full-fledged ImageContent - // because in reality m.room.avatar usually only has a single URL, - // without a thumbnail. But The Spec says there be thumbnails, and - // we follow The Spec. - EventContent::ImageContent _avatar; }; } // namespace QMatrixClient diff --git a/events/roomcanonicalaliasevent.cpp b/events/roomcanonicalaliasevent.cpp deleted file mode 100644 index 6884bc15..00000000 --- a/events/roomcanonicalaliasevent.cpp +++ /dev/null @@ -1,21 +0,0 @@ -/****************************************************************************** - * Copyright (C) 2016 Felix Rohrbach - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "roomcanonicalaliasevent.h" - -using namespace QMatrixClient; diff --git a/events/roomcanonicalaliasevent.h b/events/roomcanonicalaliasevent.h deleted file mode 100644 index 72620d74..00000000 --- a/events/roomcanonicalaliasevent.h +++ /dev/null @@ -1,38 +0,0 @@ -/****************************************************************************** - * Copyright (C) 2016 Felix Rohrbach - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#pragma once - -#include "event.h" - -namespace QMatrixClient -{ - class RoomCanonicalAliasEvent : public RoomEvent - { - public: - explicit RoomCanonicalAliasEvent(const QJsonObject& obj) - : RoomEvent(Type::RoomCanonicalAlias, obj) - , _canonicalAlias(contentJson()["alias"].toString()) - { } - - QString alias() const { return _canonicalAlias; } - - private: - QString _canonicalAlias; - }; -} // namespace QMatrixClient diff --git a/events/roomnameevent.cpp b/events/roomnameevent.cpp deleted file mode 100644 index c202d17a..00000000 --- a/events/roomnameevent.cpp +++ /dev/null @@ -1,22 +0,0 @@ -/****************************************************************************** - * Copyright (C) 2015 Kitsune Ral - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "roomnameevent.h" - -using namespace QMatrixClient; - diff --git a/events/roomnameevent.h b/events/roomnameevent.h deleted file mode 100644 index bb823933..00000000 --- a/events/roomnameevent.h +++ /dev/null @@ -1,38 +0,0 @@ -/****************************************************************************** - * Copyright (C) 2015 Kitsune Ral - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#pragma once - -#include "event.h" - -namespace QMatrixClient -{ - class RoomNameEvent : public RoomEvent - { - public: - explicit RoomNameEvent(const QJsonObject& obj) - : RoomEvent(Type::RoomName, obj) - , _name(contentJson()["name"].toString()) - { } - - QString name() const { return _name; } - - private: - QString _name{}; - }; -} // namespace QMatrixClient diff --git a/events/roomtopicevent.cpp b/events/roomtopicevent.cpp deleted file mode 100644 index 26677e78..00000000 --- a/events/roomtopicevent.cpp +++ /dev/null @@ -1,22 +0,0 @@ -/****************************************************************************** - * Copyright (C) 2015 Felix Rohrbach - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include "roomtopicevent.h" - -using namespace QMatrixClient; - diff --git a/events/roomtopicevent.h b/events/roomtopicevent.h deleted file mode 100644 index 95ad0e04..00000000 --- a/events/roomtopicevent.h +++ /dev/null @@ -1,50 +0,0 @@ -/****************************************************************************** - * Copyright (C) 2015 Felix Rohrbach - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#pragma once - -#include "event.h" - -namespace QMatrixClient -{ - class RoomTopicEvent: public RoomEvent - { - public: - explicit RoomTopicEvent(const QString& topic) - : RoomEvent(Type::RoomTopic), _topic(topic) - { } - explicit RoomTopicEvent(const QJsonObject& obj) - : RoomEvent(Type::RoomTopic, obj) - , _topic(contentJson()["topic"].toString()) - { } - - QString topic() const { return _topic; } - - QJsonObject toJson() const - { - QJsonObject obj; - obj.insert("topic", _topic); - return obj; - } - - static constexpr const char* TypeId = "m.room.topic"; - - private: - QString _topic; - }; -} // namespace QMatrixClient diff --git a/events/simplestateevents.h b/events/simplestateevents.h new file mode 100644 index 00000000..d5841bdc --- /dev/null +++ b/events/simplestateevents.h @@ -0,0 +1,54 @@ +/****************************************************************************** + * Copyright (C) 2017 Kitsune Ral + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#pragma once + +#include "event.h" + +#include "eventcontent.h" + +namespace QMatrixClient +{ +#define DECLARE_SIMPLE_STATE_EVENT(_Name, _TypeId, _EnumType, _ContentType, _ContentKey) \ + class _Name \ + : public StateEvent> \ + { \ + public: \ + static constexpr const char* TypeId = _TypeId; \ + explicit _Name(const QJsonObject& obj) \ + : StateEvent(_EnumType, obj, #_ContentKey) \ + { } \ + template \ + explicit _Name(T&& value) \ + : StateEvent(_EnumType, #_ContentKey, \ + std::forward(value)) \ + { } \ + _ContentType _ContentKey() const { return content().value; } \ + }; + + DECLARE_SIMPLE_STATE_EVENT(RoomNameEvent, "m.room.name", + Event::Type::RoomName, QString, name) + DECLARE_SIMPLE_STATE_EVENT(RoomAliasesEvent, "m.room.aliases", + Event::Type::RoomAliases, QStringList, aliases) + DECLARE_SIMPLE_STATE_EVENT(RoomCanonicalAliasEvent, "m.room.canonical_alias", + Event::Type::RoomCanonicalAlias, QString, alias) + DECLARE_SIMPLE_STATE_EVENT(RoomTopicEvent, "m.room.topic", + Event::Type::RoomTopic, QString, topic) + DECLARE_SIMPLE_STATE_EVENT(EncryptionEvent, "m.room.encryption", + Event::Type::RoomEncryption, QString, algorithm) +} // namespace QMatrixClient diff --git a/libqmatrixclient.pri b/libqmatrixclient.pri index 9eb6bd16..86648860 100644 --- a/libqmatrixclient.pri +++ b/libqmatrixclient.pri @@ -13,11 +13,8 @@ HEADERS += \ $$PWD/events/event.h \ $$PWD/events/eventcontent.h \ $$PWD/events/roommessageevent.h \ - $$PWD/events/roomnameevent.h \ - $$PWD/events/roomaliasesevent.h \ - $$PWD/events/roomcanonicalaliasevent.h \ + $$PWD/events/simplestateevents.h \ $$PWD/events/roommemberevent.h \ - $$PWD/events/roomtopicevent.h \ $$PWD/events/roomavatarevent.h \ $$PWD/events/typingevent.h \ $$PWD/events/receiptevent.h \ @@ -44,12 +41,7 @@ SOURCES += \ $$PWD/events/event.cpp \ $$PWD/events/eventcontent.cpp \ $$PWD/events/roommessageevent.cpp \ - $$PWD/events/roomnameevent.cpp \ - $$PWD/events/roomaliasesevent.cpp \ - $$PWD/events/roomcanonicalaliasevent.cpp \ $$PWD/events/roommemberevent.cpp \ - $$PWD/events/roomtopicevent.cpp \ - $$PWD/events/roomavatarevent.cpp \ $$PWD/events/typingevent.cpp \ $$PWD/events/receiptevent.cpp \ $$PWD/jobs/basejob.cpp \ diff --git a/room.cpp b/room.cpp index 1b6afac9..c4e4c6cd 100644 --- a/room.cpp +++ b/room.cpp @@ -23,10 +23,7 @@ #include "jobs/generated/banning.h" #include "jobs/generated/leaving.h" #include "jobs/setroomstatejob.h" -#include "events/roomnameevent.h" -#include "events/roomaliasesevent.h" -#include "events/roomcanonicalaliasevent.h" -#include "events/roomtopicevent.h" +#include "events/simplestateevents.h" #include "events/roomavatarevent.h" #include "events/roommemberevent.h" #include "events/typingevent.h" -- cgit v1.2.3 From 2390082f7ca264aabeadc8f62bb71c39ce2b96f0 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Wed, 1 Nov 2017 20:59:52 +0300 Subject: Event::isStateEvent(); fixed StateEvent::_prev always being initialised Event::isStateEvent() is an easier way to make checking an event kind (instead of enumerating through all types corresponding to state changes). StateEvent::_prev (accessible through prev_content) should only be initialised if there's a previous state in the original JSON. --- events/event.h | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) (limited to 'events') diff --git a/events/event.h b/events/event.h index 020ef54b..9c915e35 100644 --- a/events/event.h +++ b/events/event.h @@ -31,12 +31,18 @@ namespace QMatrixClient { Q_GADGET public: - enum class Type + enum class Type : quint16 { - RoomMessage, RoomName, RoomAliases, RoomCanonicalAlias, - RoomMember, RoomTopic, RoomAvatar, - RoomEncryption, RoomEncryptedMessage, - Typing, Receipt, Unknown + Unknown = 0, + Typing, Receipt, + RoomEventBase = 0x1000, + RoomMessage = RoomEventBase + 1, + RoomEncryptedMessage, + RoomStateEventBase = 0x1800, + RoomName = RoomStateEventBase + 1, + RoomAliases, RoomCanonicalAlias, RoomMember, RoomTopic, + RoomAvatar, RoomEncryption, + Reserved = 0x2000 }; explicit Event(Type type) : _type(type) { } @@ -44,6 +50,10 @@ namespace QMatrixClient Event(const Event&) = delete; Type type() const { return _type; } + bool isStateEvent() const + { + return (quint16(_type) & 0x1800) == 0x1800; + } QByteArray originalJson() const; QJsonObject originalJsonObject() const; @@ -152,9 +162,12 @@ namespace QMatrixClient : RoomEvent(type, obj) , _content(contentJson(), std::forward(contentParams)...) - , _prev(new ContentT(obj["prev_content"].toObject(), - std::forward(contentParams)...)) - { } + { + if (obj.contains("prev_content")) + _prev.reset(new ContentT( + obj["prev_content"].toObject(), + std::forward(contentParams)...)); + } template explicit StateEvent(Type type, ContentParamTs&&... contentParams) : RoomEvent(type) -- cgit v1.2.3 From 08fda4edfb1753aff148a664d623aa2497621965 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Wed, 1 Nov 2017 21:01:25 +0300 Subject: Imbue RoomMemberEvent with EventContent It now allows to check what exactly has happened to the member (display name change, joining, avatar update), fixing #105. --- events/roommemberevent.cpp | 43 ++++++++++++++++++++++++++++++------------- events/roommemberevent.h | 41 +++++++++++++++++++++++++++++------------ 2 files changed, 59 insertions(+), 25 deletions(-) (limited to 'events') diff --git a/events/roommemberevent.cpp b/events/roommemberevent.cpp index 19f116d2..40e14c39 100644 --- a/events/roommemberevent.cpp +++ b/events/roommemberevent.cpp @@ -22,21 +22,38 @@ using namespace QMatrixClient; -static const auto membershipStrings = - { "invite", "join", "knock", "leave", "ban" }; +static const std::array membershipStrings = + { { "invite", "join", "knock", "leave", "ban" } }; -RoomMemberEvent::RoomMemberEvent(const QJsonObject& obj) - : RoomEvent(Type::RoomMember, obj), _userId(obj["state_key"].toString()) +namespace QMatrixClient { - const auto contentObj = contentJson(); - _displayName = contentObj["displayname"].toString(); - _avatarUrl = contentObj["avatar_url"].toString(); - QString membershipString = contentObj["membership"].toString(); - for (auto it = membershipStrings.begin(); it != membershipStrings.end(); ++it) - if (membershipString == *it) + template <> + struct FromJson + { + MembershipType operator()(const QJsonValue& jv) const { - _membership = MembershipType(it - membershipStrings.begin()); - return; + const auto membershipString = jv.toString(); + for (auto it = membershipStrings.begin(); + it != membershipStrings.end(); ++it) + if (membershipString == *it) + return MembershipType(it - membershipStrings.begin()); + + qCWarning(EVENTS) << "Unknown MembershipType: " << membershipString; + return MembershipType::Join; } - qCWarning(EVENTS) << "Unknown MembershipType: " << membershipString; + }; +} + +MemberEventContent::MemberEventContent(const QJsonObject& json) + : membership(fromJson(json["membership"])) + , displayName(json["displayname"].toString()) + , avatarUrl(json["avatar_url"].toString()) +{ } + +void MemberEventContent::fillJson(QJsonObject* o) const +{ + Q_ASSERT(o); + o->insert("membership", membershipStrings[membership]); + o->insert("displayname", displayName); + o->insert("avatar_url", avatarUrl.toString()); } diff --git a/events/roommemberevent.h b/events/roommemberevent.h index 358af257..d0c63f15 100644 --- a/events/roommemberevent.h +++ b/events/roommemberevent.h @@ -20,32 +20,49 @@ #include "event.h" +#include "eventcontent.h" + #include namespace QMatrixClient { - class RoomMemberEvent: public RoomEvent + class MemberEventContent: public EventContent::Base + { + public: + enum MembershipType : size_t {Invite = 0, Join, Knock, Leave, Ban}; + + MemberEventContent(const QJsonObject& json); + + MembershipType membership; + QString displayName; + QUrl avatarUrl; + + protected: + void fillJson(QJsonObject* o) const override; + }; + + using MembershipType = MemberEventContent::MembershipType; + + class RoomMemberEvent: public StateEvent { Q_GADGET public: static constexpr const char* TypeId = "m.room.member"; - enum MembershipType : int {Invite = 0, Join, Knock, Leave, Ban}; + using MembershipType = MemberEventContent::MembershipType; - explicit RoomMemberEvent(const QJsonObject& obj); + explicit RoomMemberEvent(const QJsonObject& obj) + : StateEvent(Type::RoomMember, obj) + , _userId(obj["state_key"].toString()) + { } - MembershipType membership() const { return _membership; } - const QString& userId() const { return _userId; } - const QString& displayName() const { return _displayName; } - const QUrl& avatarUrl() const { return _avatarUrl; } + MembershipType membership() const { return content().membership; } + QString userId() const { return _userId; } + QString displayName() const { return content().displayName; } + QUrl avatarUrl() const { return content().avatarUrl; } private: - MembershipType _membership; QString _userId; - QString _displayName; - QUrl _avatarUrl; - REGISTER_ENUM(MembershipType) }; - using MembershipType = RoomMemberEvent::MembershipType; } // namespace QMatrixClient -- cgit v1.2.3 From e11404c364056f1ae23a1820a6f458275463599c Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Thu, 2 Nov 2017 00:05:14 +0300 Subject: Fixed CI --- events/roommemberevent.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) (limited to 'events') diff --git a/events/roommemberevent.cpp b/events/roommemberevent.cpp index 40e14c39..3e1ff8c1 100644 --- a/events/roommemberevent.cpp +++ b/events/roommemberevent.cpp @@ -22,8 +22,11 @@ using namespace QMatrixClient; -static const std::array membershipStrings = - { { "invite", "join", "knock", "leave", "ban" } }; +static const std::array membershipStrings = { { + QStringLiteral("invite"), QStringLiteral("join"), + QStringLiteral("knock"), QStringLiteral("leave"), + QStringLiteral("ban") +} }; namespace QMatrixClient { @@ -32,7 +35,7 @@ namespace QMatrixClient { MembershipType operator()(const QJsonValue& jv) const { - const auto membershipString = jv.toString(); + const auto& membershipString = jv.toString(); for (auto it = membershipStrings.begin(); it != membershipStrings.end(); ++it) if (membershipString == *it) -- cgit v1.2.3 From 366a69f5c1ced61d270832a76233bda1fcdf0b6c Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Thu, 2 Nov 2017 00:40:09 +0300 Subject: Fix AppVeyor CI --- events/roommemberevent.cpp | 2 ++ 1 file changed, 2 insertions(+) (limited to 'events') diff --git a/events/roommemberevent.cpp b/events/roommemberevent.cpp index 3e1ff8c1..76df5f2e 100644 --- a/events/roommemberevent.cpp +++ b/events/roommemberevent.cpp @@ -20,6 +20,8 @@ #include "logging.h" +#include + using namespace QMatrixClient; static const std::array membershipStrings = { { -- cgit v1.2.3 From f12e09ea1b45be1a96533aa83f6227940c70d548 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Thu, 16 Nov 2017 14:03:55 +0900 Subject: Require state_key to be present in all state events This impacts the cache as well, as we don't save state_keys for most state events. --- connection.cpp | 2 +- events/event.h | 3 ++- room.cpp | 19 ++++++++++--------- 3 files changed, 13 insertions(+), 11 deletions(-) (limited to 'events') diff --git a/connection.cpp b/connection.cpp index d15067dd..37d216d7 100644 --- a/connection.cpp +++ b/connection.cpp @@ -402,7 +402,7 @@ QByteArray Connection::generateTxnId() return d->data->generateTxnId(); } -static constexpr int CACHE_VERSION_MAJOR = 0; +static constexpr int CACHE_VERSION_MAJOR = 1; static constexpr int CACHE_VERSION_MINOR = 0; void Connection::saveState(const QUrl &toFile) const diff --git a/events/event.h b/events/event.h index 9c915e35..c0c1b603 100644 --- a/events/event.h +++ b/events/event.h @@ -159,7 +159,8 @@ namespace QMatrixClient template explicit StateEvent(Type type, const QJsonObject& obj, ContentParamTs&&... contentParams) - : RoomEvent(type, obj) + : RoomEvent(obj.contains("state_key") ? type : Type::Unknown, + obj) , _content(contentJson(), std::forward(contentParams)...) { diff --git a/room.cpp b/room.cpp index c4e4c6cd..c5c4e721 100644 --- a/room.cpp +++ b/room.cpp @@ -973,7 +973,7 @@ void Room::Private::updateDisplayname() } template -void appendEventJson(QJsonArray& events, const QString& type, +void appendStateEvent(QJsonArray& events, const QString& type, const QString& name, const T& content) { if (content.isEmpty()) @@ -985,6 +985,7 @@ void appendEventJson(QJsonArray& events, const QString& type, QJsonObject eventObj; eventObj.insert("type", type); eventObj.insert("content", contentObj); + eventObj.insert("state_key", ""); // Mandatory for state events events.append(eventObj); } @@ -995,14 +996,14 @@ QJsonObject Room::Private::toJson() const { QJsonArray stateEvents; - appendEventJson(stateEvents, "m.room.name", "name", name); - appendEventJson(stateEvents, "m.room.topic", "topic", topic); - appendEventJson(stateEvents, "m.room.avatar", "url", - avatar.url().toString()); - appendEventJson(stateEvents, "m.room.aliases", "aliases", - QJsonArray::fromStringList(aliases)); - appendEventJson(stateEvents, "m.room.canonical_alias", "alias", - canonicalAlias); + appendStateEvent(stateEvents, "m.room.name", "name", name); + appendStateEvent(stateEvents, "m.room.topic", "topic", topic); + appendStateEvent(stateEvents, "m.room.avatar", "url", + avatar.url().toString()); + appendStateEvent(stateEvents, "m.room.aliases", "aliases", + QJsonArray::fromStringList(aliases)); + appendStateEvent(stateEvents, "m.room.canonical_alias", "alias", + canonicalAlias); for (const auto &i : membersMap) { -- cgit v1.2.3 From 491e392af73be3ebfd928e80efc0514fd43b8e87 Mon Sep 17 00:00:00 2001 From: Kitsune Ral Date: Thu, 16 Nov 2017 14:10:08 +0900 Subject: Simplify code that loads events from JSON arrays --- events/event.h | 41 +++++++++++++++++++++++------------------ jobs/roommessagesjob.cpp | 4 ++-- jobs/syncjob.h | 5 ++--- 3 files changed, 27 insertions(+), 23 deletions(-) (limited to 'events') diff --git a/events/event.h b/events/event.h index c0c1b603..cc99b57b 100644 --- a/events/event.h +++ b/events/event.h @@ -63,6 +63,10 @@ namespace QMatrixClient // (and in most cases it will be a combination of other fields // instead of "content" field). + /** Create an event with proper type from a JSON object + * Use this factory to detect the type from the JSON object contents + * and create an event object of that type. + */ static Event* fromJson(const QJsonObject& obj); protected: @@ -77,26 +81,27 @@ namespace QMatrixClient Q_PROPERTY(QJsonObject contentJson READ contentJson CONSTANT) }; using EventType = Event::Type; - template - using EventsBatch = std::vector; - using Events = EventsBatch; - template > - inline BatchT makeEvents(const QJsonArray& objs) + template + class EventsBatch : public std::vector { - BatchT evs; - // The below line accommodates the difference in size types of - // STL and Qt containers. - evs.reserve(static_cast(objs.size())); - for (auto objValue: objs) - { - const auto o = objValue.toObject(); - auto e = BaseEventT::fromJson(o); - evs.push_back(e ? e : new BaseEventT(EventType::Unknown, o)); - } - return evs; - } + public: + void fromJson(const QJsonObject& container, const QString& node) + { + const auto objs = container.value(node).toArray(); + using size_type = typename std::vector::size_type; + // The below line accommodates the difference in size types of + // STL and Qt containers. + this->reserve(static_cast(objs.size())); + for (auto objValue: objs) + { + const auto o = objValue.toObject(); + auto e = EventT::fromJson(o); + this->push_back(e ? e : new EventT(EventType::Unknown, o)); + } + } + }; + using Events = EventsBatch; /** This class corresponds to m.room.* events */ class RoomEvent : public Event diff --git a/jobs/roommessagesjob.cpp b/jobs/roommessagesjob.cpp index 078c692a..c527cc71 100644 --- a/jobs/roommessagesjob.cpp +++ b/jobs/roommessagesjob.cpp @@ -58,8 +58,8 @@ QString RoomMessagesJob::end() const BaseJob::Status RoomMessagesJob::parseJson(const QJsonDocument& data) { - QJsonObject obj = data.object(); - d->events.assign(makeEvents(obj.value("chunk").toArray())); + const auto obj = data.object(); + d->events.fromJson(obj, "chunk"); d->end = obj.value("end").toString(); return Success; } diff --git a/jobs/syncjob.h b/jobs/syncjob.h index b1db914d..08bd773e 100644 --- a/jobs/syncjob.h +++ b/jobs/syncjob.h @@ -36,11 +36,10 @@ namespace QMatrixClient explicit Batch(QString k) : jsonKey(std::move(k)) { } void fromJson(const QJsonObject& roomContents) { - this->assign(makeEvents( - roomContents[jsonKey].toObject()["events"].toArray())); + EventsBatch::fromJson( + roomContents[jsonKey].toObject(), "events"); } - private: QString jsonKey; }; -- cgit v1.2.3