aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKitsuneRal <Kitsune-Ral@users.sf.net>2016-08-29 09:05:37 +0900
committerGitHub <noreply@github.com>2016-08-29 09:05:37 +0900
commitb9423ed655787db3febe5af4f6ff531969e6abb9 (patch)
tree8979cb97430d7e54fc1bd5717c864d29a231e5a8
parent77d45e9afa195f7d715a5074b16f16c6b2d15235 (diff)
parent32ff7cd24bda52c889fd969a43adcf1f845b6e3a (diff)
downloadlibquotient-b9423ed655787db3febe5af4f6ff531969e6abb9.tar.gz
libquotient-b9423ed655787db3febe5af4f6ff531969e6abb9.zip
Merge pull request #20 from Fxrh/kitsune-roommessage-refactoring2
-rw-r--r--events/roommessageevent.cpp229
-rw-r--r--events/roommessageevent.h145
2 files changed, 210 insertions, 164 deletions
diff --git a/events/roommessageevent.cpp b/events/roommessageevent.cpp
index 48f52453..09465238 100644
--- a/events/roommessageevent.cpp
+++ b/events/roommessageevent.cpp
@@ -19,7 +19,7 @@
#include "roommessageevent.h"
#include <QtCore/QJsonObject>
-#include <QtCore/QDateTime>
+#include <QtCore/QMimeDatabase>
#include <QtCore/QDebug>
using namespace QMatrixClient;
@@ -27,20 +27,18 @@ using namespace QMatrixClient;
class RoomMessageEvent::Private
{
public:
- Private() {}
+ Private() : msgtype(MessageEventType::Unknown), content(nullptr) {}
QString userId;
MessageEventType msgtype;
- QDateTime hsob_ts;
- MessageEventContent* content;
+ QString plainBody;
+ MessageEventContent::Base* content;
};
RoomMessageEvent::RoomMessageEvent()
: Event(EventType::RoomMessage)
, d(new Private)
-{
- d->content = nullptr;
-}
+{ }
RoomMessageEvent::~RoomMessageEvent()
{
@@ -57,21 +55,42 @@ MessageEventType RoomMessageEvent::msgtype() const
return d->msgtype;
}
+QString RoomMessageEvent::plainBody() const
+{
+ return d->plainBody;
+}
+
QString RoomMessageEvent::body() const
{
- return d->content->body;
+ return plainBody();
}
-QDateTime RoomMessageEvent::hsob_ts() const
+using namespace MessageEventContent;
+
+Base* RoomMessageEvent::content() const
{
- return d->hsob_ts;
+ return d->content;
}
-MessageEventContent* RoomMessageEvent::content() const
+template <class ContentT>
+Base* make(const QJsonObject& json)
{
- return d->content;
+ return new ContentT(json);
}
+Base* makeVideoContent(const QJsonObject& json)
+{
+ auto c = new VideoContent(json);
+ // Only for m.video, the spec puts a thumbnail inside "info" JSON key. Once
+ // this is fixed, VideoContent creation will switch to make<>().
+ const QJsonObject infoJson = json["info"].toObject();
+ if (infoJson.contains("thumbnail_url"))
+ c->thumbnail = ImageInfo(infoJson["thumbnail_url"].toString(),
+ infoJson["thumbnail_info"].toObject());
+
+ return c;
+};
+
RoomMessageEvent* RoomMessageEvent::fromJson(const QJsonObject& obj)
{
RoomMessageEvent* e = new RoomMessageEvent();
@@ -84,107 +103,103 @@ RoomMessageEvent* RoomMessageEvent::fromJson(const QJsonObject& obj)
}
if( obj.contains("content") )
{
- QJsonObject content = obj.value("content").toObject();
- QString msgtype = content.value("msgtype").toString();
-
- if( msgtype == "m.text" )
- {
- e->d->msgtype = MessageEventType::Text;
- e->d->content = new MessageEventContent();
- }
- else if( msgtype == "m.emote" )
- {
- e->d->msgtype = MessageEventType::Emote;
- e->d->content = new MessageEventContent();
- }
- else if( msgtype == "m.notice" )
- {
- e->d->msgtype = MessageEventType::Notice;
- e->d->content = new MessageEventContent();
- }
- else if( msgtype == "m.image" )
- {
- e->d->msgtype = MessageEventType::Image;
- ImageEventContent* c = new ImageEventContent;
- c->url = QUrl(content.value("url").toString());
- QJsonObject info = content.value("info").toObject();
- c->height = info.value("h").toInt();
- c->width = info.value("w").toInt();
- c->size = info.value("size").toInt();
- c->mimetype = info.value("mimetype").toString();
- e->d->content = c;
- }
- else if( msgtype == "m.file" )
- {
- e->d->msgtype = MessageEventType::File;
- FileEventContent* c = new FileEventContent;
- c->filename = content.value("filename").toString();
- c->url = QUrl(content.value("url").toString());
- QJsonObject info = content.value("info").toObject();
- c->size = info.value("size").toInt();
- c->mimetype = info.value("mimetype").toString();
- e->d->content = c;
- }
- else if( msgtype == "m.location" )
- {
- e->d->msgtype = MessageEventType::Location;
- LocationEventContent* c = new LocationEventContent;
- c->geoUri = content.value("geo_uri").toString();
- c->thumbnailUrl = QUrl(content.value("thumbnail_url").toString());
- QJsonObject info = content.value("thumbnail_info").toObject();
- c->thumbnailHeight = info.value("h").toInt();
- c->thumbnailWidth = info.value("w").toInt();
- c->thumbnailSize = info.value("size").toInt();
- c->thumbnailMimetype = info.value("mimetype").toString();
- e->d->content = c;
- }
- else if( msgtype == "m.video" )
+ const QJsonObject content = obj["content"].toObject();
+ if ( content.contains("msgtype") && content.contains("body") )
{
- e->d->msgtype = MessageEventType::Video;
- VideoEventContent* c = new VideoEventContent;
- c->url = QUrl(content.value("url").toString());
- QJsonObject info = content.value("info").toObject();
- c->height = info.value("h").toInt();
- c->width = info.value("w").toInt();
- c->duration = info.value("duration").toInt();
- c->size = info.value("size").toInt();
- c->thumbnailUrl = QUrl(info.value("thumnail_url").toString());
- QJsonObject thumbnailInfo = content.value("thumbnail_info").toObject();
- c->thumbnailHeight = thumbnailInfo.value("h").toInt();
- c->thumbnailWidth = thumbnailInfo.value("w").toInt();
- c->thumbnailSize = thumbnailInfo.value("size").toInt();
- c->thumbnailMimetype = thumbnailInfo.value("mimetype").toString();
- e->d->content = c;
- }
- else if( msgtype == "m.audio" )
- {
- e->d->msgtype = MessageEventType::Audio;
- AudioEventContent* c = new AudioEventContent;
- c->url = QUrl(content.value("url").toString());
- QJsonObject info = content.value("info").toObject();
- c->duration = info.value("duration").toInt();
- c->mimetype = info.value("mimetype").toString();
- c->size = info.value("size").toInt();
- e->d->content = c;
+ e->d->plainBody = content["body"].toString();
+
+ struct Factory
+ {
+ QString jsonTag;
+ MessageEventType enumTag;
+ Base*(*make)(const QJsonObject& json);
+ };
+
+ const Factory factories[] {
+ { "m.text", MessageEventType::Text, make<TextContent> },
+ { "m.emote", MessageEventType::Emote, make<TextContent> },
+ { "m.notice", MessageEventType::Notice, make<TextContent> },
+ { "m.image", MessageEventType::Image, make<ImageContent> },
+ { "m.file", MessageEventType::File, make<FileContent> },
+ { "m.location", MessageEventType::Location, make<LocationContent> },
+ { "m.video", MessageEventType::Video, makeVideoContent },
+ { "m.audio", MessageEventType::Audio, make<AudioContent> },
+ // Insert new message types before this line
+ };
+
+ QString msgtype = content.value("msgtype").toString();
+ for (auto f: factories)
+ {
+ if (msgtype == f.jsonTag)
+ {
+ e->d->msgtype = f.enumTag;
+ e->d->content = f.make(content);
+ break;
+ }
+ }
+ if (e->d->msgtype == MessageEventType::Unknown)
+ {
+ qDebug() << "RoomMessageEvent: unknown msgtype: " << msgtype;
+ qDebug() << obj;
+ e->d->content = new Base;
+ }
}
else
{
- qDebug() << "RoomMessageEvent: unknown msgtype: " << msgtype;
+ qWarning() << "RoomMessageEvent(" << e->id() << "): no body or msgtype";
qDebug() << obj;
- e->d->msgtype = MessageEventType::Unknown;
- e->d->content = new MessageEventContent;
- }
-
- if( content.contains("body") )
- {
- e->d->content->body = content.value("body").toString();
- } else {
- qDebug() << "RoomMessageEvent: body not found";
}
-// e->d->hsob_ts = QDateTime::fromMSecsSinceEpoch( content.value("hsoc_ts").toInt() );
-// } else {
-// qDebug() << "RoomMessageEvent: hsoc_ts not found";
-// }
}
return e;
}
+
+using namespace MessageEventContent;
+
+TextContent::TextContent(const QJsonObject& json)
+{
+ QMimeDatabase db;
+
+ // Special-casing the custom matrix.org's (actually, Vector's) way
+ // of sending HTML messages.
+ if (json["format"].toString() == "org.matrix.custom.html")
+ {
+ mimeType = db.mimeTypeForName("text/html");
+ body = json["formatted_body"].toString();
+ } else {
+ // Best-guessing from the content
+ body = json["body"].toString();
+ mimeType = db.mimeTypeForData(body.toUtf8());
+ }
+}
+
+FileInfo::FileInfo(QUrl u, const QJsonObject& infoJson, QString originalFilename)
+ : url(u)
+ , fileSize(infoJson["size"].toInt())
+ , mimetype(QMimeDatabase().mimeTypeForName(infoJson["mimetype"].toString()))
+ , originalName(originalFilename)
+{
+ if (!mimetype.isValid())
+ mimetype = QMimeDatabase().mimeTypeForData(QByteArray());
+}
+
+ImageInfo::ImageInfo(QUrl u, const QJsonObject& infoJson)
+ : FileInfo(u, infoJson)
+ , imageSize(infoJson["w"].toInt(), infoJson["h"].toInt())
+{ }
+
+LocationContent::LocationContent(const QJsonObject& json)
+ : geoUri(json["geo_uri"].toString())
+ , thumbnail(json["thumbnail_url"].toString(),
+ json["thumbnail_info"].toObject())
+{ }
+
+VideoInfo::VideoInfo(QUrl u, const QJsonObject& infoJson)
+ : FileInfo(u, infoJson)
+ , duration(infoJson["duration"].toInt())
+ , imageSize(infoJson["w"].toInt(), infoJson["h"].toInt())
+{ }
+
+AudioInfo::AudioInfo(QUrl u, const QJsonObject& infoJson)
+ : FileInfo(u, infoJson)
+ , duration(infoJson["duration"].toInt())
+{ }
diff --git a/events/roommessageevent.h b/events/roommessageevent.h
index b0d5a1cb..591b2df9 100644
--- a/events/roommessageevent.h
+++ b/events/roommessageevent.h
@@ -20,6 +20,8 @@
#define QMATRIXCLIENT_ROOMMESSAGEEVENT_H
#include <QtCore/QUrl>
+#include <QtCore/QMimeType>
+#include <QtCore/QSize>
#include "event.h"
@@ -30,13 +32,10 @@ namespace QMatrixClient
Text, Emote, Notice, Image, File, Location, Video, Audio, Unknown
};
- class MessageEventContent
+ namespace MessageEventContent
{
- public:
- virtual ~MessageEventContent() {}
-
- QString body;
- };
+ class Base { };
+ }
class RoomMessageEvent: public Event
{
@@ -46,10 +45,18 @@ namespace QMatrixClient
QString userId() const;
MessageEventType msgtype() const;
+
+ QString plainBody() const;
+
+ /**
+ * Same as plainBody() for now; might change for "best-looking body"
+ * in the future. For richer contents, use content-specific data.
+ *
+ * @deprecated
+ */
QString body() const;
- QDateTime hsob_ts() const;
- MessageEventContent* content() const;
+ MessageEventContent::Base* content() const;
static RoomMessageEvent* fromJson( const QJsonObject& obj );
@@ -58,61 +65,85 @@ namespace QMatrixClient
Private* d;
};
- class ImageEventContent: public MessageEventContent
+ namespace MessageEventContent
{
- public:
- QUrl url;
- int height;
- int width;
- int size;
- QString mimetype;
- };
+ // 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.
- class FileEventContent: public MessageEventContent
- {
- public:
- QString filename;
- QString mimetype;
- int size;
- QUrl url;
- };
+ class TextContent: public Base
+ {
+ public:
+ TextContent(const QJsonObject& json);
- class LocationEventContent: public MessageEventContent
- {
- public:
- QString geoUri;
- int thumbnailHeight;
- int thumbnailWidth;
- QString thumbnailMimetype;
- int thumbnailSize;
- QUrl thumbnailUrl;
- };
+ QMimeType mimeType;
+ QString body;
+ };
- class VideoEventContent: public MessageEventContent
- {
- public:
- QUrl url;
- int duration;
- int width;
- int height;
- int size;
- QString mimetype;
- int thumbnailWidth;
- int thumbnailHeight;
- int thumbnailSize;
- QString thumbnailMimetype;
- QUrl thumbnailUrl;
- };
+ class FileInfo: public Base
+ {
+ public:
+ FileInfo(QUrl u, const QJsonObject& infoJson,
+ QString originalFilename = QString());
- class AudioEventContent: public MessageEventContent
- {
- public:
- QUrl url;
- int size;
- int duration;
- QString mimetype;
- };
+ QUrl url;
+ int fileSize;
+ QMimeType mimetype;
+ QString originalName;
+ };
+
+ class ImageInfo: public FileInfo
+ {
+ public:
+ ImageInfo(QUrl u, const QJsonObject& infoJson);
+
+ QSize imageSize;
+ };
+
+ template <class ContentInfoT>
+ class ThumbnailedContent: public ContentInfoT
+ {
+ public:
+ ThumbnailedContent(const QJsonObject& json)
+ : ContentInfoT(json["url"].toString(), json["info"].toObject())
+ , thumbnail(json["thumbnail_url"].toString(),
+ json["thumbnail_info"].toObject())
+ { }
+
+ ImageInfo thumbnail;
+ };
+
+ using ImageContent = ThumbnailedContent<ImageInfo>;
+ using FileContent = ThumbnailedContent<FileInfo>;
+
+ class LocationContent: public Base
+ {
+ public:
+ LocationContent(const QJsonObject& json);
+
+ QString geoUri;
+ ImageInfo thumbnail;
+ };
+
+ class VideoInfo: public FileInfo
+ {
+ public:
+ VideoInfo(QUrl u, const QJsonObject& infoJson);
+
+ int duration;
+ QSize imageSize;
+ };
+ using VideoContent = ThumbnailedContent<VideoInfo>;
+
+ class AudioInfo: public FileInfo
+ {
+ public:
+ AudioInfo(QUrl u, const QJsonObject& infoJson);
+ int duration;
+ };
+ using AudioContent = ThumbnailedContent<AudioInfo>;
+ }
}
#endif // QMATRIXCLIENT_ROOMMESSAGEEVENT_H