1 /****************************************************************************** 2 * Copyright (C) 2015 Felix Rohrbach <kde@fxrh.de> 3 * 4 * This library is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU Lesser General Public 6 * License as published by the Free Software Foundation; either 7 * version 2.1 of the License, or (at your option) any later version. 8 * 9 * This library is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 * Lesser General Public License for more details. 13 * 14 * You should have received a copy of the GNU Lesser General Public 15 * License along with this library; if not, write to the Free Software 16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 17 */ 18 19 #pragma once 20 21 #include "eventcontent.h" 22 #include "roomevent.h" 23 24 class QFileInfo; 25 26 namespace Quotient { 27 namespace MessageEventContent = EventContent; // Back-compatibility 28 29 /** 30 * The event class corresponding to m.room.message events 31 */ 32 class RoomMessageEvent : public RoomEvent { 33 Q_GADGET 34 Q_PROPERTY(QString msgType READ rawMsgtype CONSTANT) 35 Q_PROPERTY(QString plainBody READ plainBody CONSTANT) 36 Q_PROPERTY(QMimeType mimeType READ mimeType STORED false CONSTANT) 37 Q_PROPERTY(const EventContent::TypedBase* content READ content CONSTANT) 38 public: 39 DEFINE_EVENT_TYPEID("m.room.message", RoomMessageEvent) 40 41 enum class MsgType { 42 Text, 43 Emote, 44 Notice, 45 Image, 46 File, 47 Location, 48 Video, 49 Audio, 50 Unknown 51 }; 52 53 RoomMessageEvent(const QString& plainBody, const QString& jsonMsgType, 54 EventContent::TypedBase* content = nullptr); 55 explicit RoomMessageEvent(const QString& plainBody, 56 MsgType msgType = MsgType::Text, 57 EventContent::TypedBase* content = nullptr); 58 explicit RoomMessageEvent(const QString& plainBody, const QFileInfo& file, 59 bool asGenericFile = false); 60 explicit RoomMessageEvent(const QJsonObject& obj); 61 62 MsgType msgtype() const; 63 QString rawMsgtype() const; 64 QString plainBody() const; content()65 const EventContent::TypedBase* content() const { return _content.data(); } 66 template <typename VisitorT> editContent(VisitorT && visitor)67 void editContent(VisitorT&& visitor) 68 { 69 visitor(*_content); 70 editJson()[ContentKeyL] = assembleContentJson(plainBody(), rawMsgtype(), 71 _content.data()); 72 } 73 QMimeType mimeType() const; 74 bool hasTextContent() const; 75 bool hasFileContent() const; 76 bool hasThumbnail() const; 77 QString replacedEvent() const; 78 79 static QString rawMsgTypeForUrl(const QUrl& url); 80 static QString rawMsgTypeForFile(const QFileInfo& fi); 81 82 private: 83 QScopedPointer<EventContent::TypedBase> _content; 84 85 // FIXME: should it really be static? 86 static QJsonObject assembleContentJson(const QString& plainBody, 87 const QString& jsonMsgType, 88 EventContent::TypedBase* content); 89 90 Q_ENUM(MsgType) 91 }; 92 REGISTER_EVENT_TYPE(RoomMessageEvent) 93 using MessageEventType = RoomMessageEvent::MsgType; 94 95 namespace EventContent { 96 // Additional event content types 97 98 struct RelatesTo { ReplyTypeIdRelatesTo99 static constexpr const char* ReplyTypeId() { return "m.in_reply_to"; } ReplacementTypeIdRelatesTo100 static constexpr const char* ReplacementTypeId() { return "m.replace"; } 101 QString type; // The only supported relation so far 102 QString eventId; 103 }; replyTo(QString eventId)104 inline RelatesTo replyTo(QString eventId) 105 { 106 return { RelatesTo::ReplyTypeId(), std::move(eventId) }; 107 } replacementOf(QString eventId)108 inline RelatesTo replacementOf(QString eventId) 109 { 110 return { RelatesTo::ReplacementTypeId(), std::move(eventId) }; 111 } 112 113 /** 114 * Rich text content for m.text, m.emote, m.notice 115 * 116 * Available fields: mimeType, body. The body can be either rich text 117 * or plain text, depending on what mimeType specifies. 118 */ 119 class TextContent : public TypedBase { 120 public: 121 TextContent(QString text, const QString& contentType, 122 Omittable<RelatesTo> relatesTo = none); 123 explicit TextContent(const QJsonObject& json); 124 type()125 QMimeType type() const override { return mimeType; } 126 127 QMimeType mimeType; 128 QString body; 129 Omittable<RelatesTo> relatesTo; 130 131 protected: 132 void fillJson(QJsonObject* json) const override; 133 }; 134 135 /** 136 * Content class for m.location 137 * 138 * Available fields: 139 * - corresponding to the top-level JSON: 140 * - geoUri ("geo_uri" in JSON) 141 * - corresponding to the "info" subobject: 142 * - thumbnail.url ("thumbnail_url" in JSON) 143 * - corresponding to the "info/thumbnail_info" subobject: 144 * - thumbnail.payloadSize 145 * - thumbnail.mimeType 146 * - thumbnail.imageSize 147 */ 148 class LocationContent : public TypedBase { 149 public: 150 LocationContent(const QString& geoUri, const Thumbnail& thumbnail = {}); 151 explicit LocationContent(const QJsonObject& json); 152 153 QMimeType type() const override; 154 155 public: 156 QString geoUri; 157 Thumbnail thumbnail; 158 159 protected: 160 void fillJson(QJsonObject* o) const override; 161 }; 162 163 /** 164 * A base class for info types that include duration: audio and video 165 */ 166 template <typename ContentT> 167 class PlayableContent : public ContentT { 168 public: 169 using ContentT::ContentT; PlayableContent(const QJsonObject & json)170 PlayableContent(const QJsonObject& json) 171 : ContentT(json) 172 , duration(ContentT::originalInfoJson["duration"_ls].toInt()) 173 {} 174 175 protected: fillJson(QJsonObject * json)176 void fillJson(QJsonObject* json) const override 177 { 178 ContentT::fillJson(json); 179 auto infoJson = json->take("info"_ls).toObject(); 180 infoJson.insert(QStringLiteral("duration"), duration); 181 json->insert(QStringLiteral("info"), infoJson); 182 } 183 184 public: 185 int duration; 186 }; 187 188 /** 189 * Content class for m.video 190 * 191 * Available fields: 192 * - corresponding to the top-level JSON: 193 * - url 194 * - filename (extension to the CS API spec) 195 * - corresponding to the "info" subobject: 196 * - payloadSize ("size" in JSON) 197 * - mimeType ("mimetype" in JSON) 198 * - duration 199 * - imageSize (QSize for a combination of "h" and "w" in JSON) 200 * - thumbnail.url ("thumbnail_url" in JSON) 201 * - corresponding to the "info/thumbnail_info" subobject: contents of 202 * thumbnail field, in the same vein as for "info": 203 * - payloadSize 204 * - mimeType 205 * - imageSize 206 */ 207 using VideoContent = PlayableContent<UrlWithThumbnailContent<ImageInfo>>; 208 209 /** 210 * Content class for m.audio 211 * 212 * Available fields: 213 * - corresponding to the top-level JSON: 214 * - url 215 * - filename (extension to the CS API spec) 216 * - corresponding to the "info" subobject: 217 * - payloadSize ("size" in JSON) 218 * - mimeType ("mimetype" in JSON) 219 * - duration 220 */ 221 using AudioContent = PlayableContent<UrlBasedContent<FileInfo>>; 222 } // namespace EventContent 223 } // namespace Quotient 224