1 | // SPDX-FileCopyrightText: 2015 Felix Rohrbach <kde@fxrh.de> |
2 | // SPDX-FileCopyrightText: 2016 Kitsune Ral <Kitsune-Ral@users.sf.net> |
3 | // SPDX-FileCopyrightText: 2017 Roman Plášil <me@rplasil.name> |
4 | // SPDX-License-Identifier: LGPL-2.1-or-later |
5 | |
6 | #pragma once |
7 | |
8 | #include "eventcontent.h" |
9 | #include "eventrelation.h" |
10 | #include "roomevent.h" |
11 | |
12 | class QFileInfo; |
13 | |
14 | namespace Quotient { |
15 | namespace MessageEventContent = EventContent; // Back-compatibility |
16 | |
17 | /** |
18 | * The event class corresponding to m.room.message events |
19 | */ |
20 | class QUOTIENT_API RoomMessageEvent : public RoomEvent { |
21 | Q_GADGET |
22 | public: |
23 | QUO_EVENT(RoomMessageEvent, "m.room.message" ) |
24 | |
25 | enum class MsgType { |
26 | Text, |
27 | Emote, |
28 | Notice, |
29 | Image, |
30 | File, |
31 | Location, |
32 | Video, |
33 | Audio, |
34 | Unknown |
35 | }; |
36 | |
37 | RoomMessageEvent(const QString& plainBody, const QString& jsonMsgType, |
38 | EventContent::TypedBase* content = nullptr); |
39 | explicit RoomMessageEvent(const QString& plainBody, |
40 | MsgType msgType = MsgType::Text, |
41 | EventContent::TypedBase* content = nullptr); |
42 | #if QT_VERSION_MAJOR < 6 |
43 | [[deprecated("Create an EventContent object on the client side" |
44 | " and pass it to other constructors" )]] // |
45 | explicit RoomMessageEvent(const QString& plainBody, const QFileInfo& file, |
46 | bool asGenericFile = false); |
47 | #endif |
48 | explicit RoomMessageEvent(const QJsonObject& obj); |
49 | |
50 | MsgType msgtype() const; |
51 | QString rawMsgtype() const; |
52 | QString plainBody() const; |
53 | const EventContent::TypedBase* content() const { return _content.data(); } |
54 | template <typename VisitorT> |
55 | void editContent(VisitorT&& visitor) |
56 | { |
57 | visitor(*_content); |
58 | editJson()[ContentKey] = assembleContentJson(plainBody: plainBody(), jsonMsgType: rawMsgtype(), |
59 | content: _content.data()); |
60 | } |
61 | QMimeType mimeType() const; |
62 | //! \brief Determine whether the message has text content |
63 | //! |
64 | //! \return true, if the message type is one of m.text, m.notice, m.emote, |
65 | //! or the message type is unspecified (in which case plainBody() |
66 | //! can still be examined); false otherwise |
67 | bool hasTextContent() const; |
68 | //! \brief Determine whether the message has a file/attachment |
69 | //! |
70 | //! \return true, if the message has a data structure corresponding to |
71 | //! a file (such as m.file or m.audio); false otherwise |
72 | bool hasFileContent() const; |
73 | //! \brief Determine whether the message has a thumbnail |
74 | //! |
75 | //! \return true, if the message has a data structure corresponding to |
76 | //! a thumbnail (the message type may be one for visual content, |
77 | //! such as m.image, or generic binary content, i.e. m.file); |
78 | //! false otherwise |
79 | bool hasThumbnail() const; |
80 | |
81 | //! \brief Obtain id of an event replaced by the current one |
82 | //! \sa RoomEvent::isReplaced, RoomEvent::replacedBy |
83 | QString replacedEvent() const; |
84 | |
85 | //! \brief Determine whether the event has been replaced |
86 | //! |
87 | //! \return true if this event has been overridden by another event |
88 | //! with `"rel_type": "m.replace"`; false otherwise |
89 | bool isReplaced() const; |
90 | |
91 | QString replacedBy() const; |
92 | |
93 | static QString rawMsgTypeForUrl(const QUrl& url); |
94 | static QString rawMsgTypeForFile(const QFileInfo& fi); |
95 | |
96 | private: |
97 | QScopedPointer<EventContent::TypedBase> _content; |
98 | |
99 | // FIXME: should it really be static? |
100 | static QJsonObject assembleContentJson(const QString& plainBody, |
101 | const QString& jsonMsgType, |
102 | EventContent::TypedBase* content); |
103 | |
104 | Q_ENUM(MsgType) |
105 | }; |
106 | |
107 | using MessageEventType = RoomMessageEvent::MsgType; |
108 | |
109 | namespace EventContent { |
110 | |
111 | struct [[deprecated("Use Quotient::EventRelation instead" )]] RelatesTo |
112 | : EventRelation { |
113 | static constexpr auto ReplyTypeId() { return ReplyType; } |
114 | static constexpr auto ReplacementTypeId() { return ReplacementType; } |
115 | }; |
116 | [[deprecated("Use EventRelation::replyTo() instead" )]] |
117 | inline auto replyTo(QString eventId) |
118 | { |
119 | return EventRelation::replyTo(eventId: std::move(eventId)); |
120 | } |
121 | [[deprecated("Use EventRelation::replace() instead" )]] |
122 | inline auto replacementOf(QString eventId) |
123 | { |
124 | return EventRelation::replace(eventId: std::move(eventId)); |
125 | } |
126 | |
127 | // Additional event content types |
128 | |
129 | /** |
130 | * Rich text content for m.text, m.emote, m.notice |
131 | * |
132 | * Available fields: mimeType, body. The body can be either rich text |
133 | * or plain text, depending on what mimeType specifies. |
134 | */ |
135 | class QUOTIENT_API TextContent : public TypedBase { |
136 | public: |
137 | TextContent(QString text, const QString& contentType, |
138 | Omittable<EventRelation> relatesTo = none); |
139 | explicit TextContent(const QJsonObject& json); |
140 | |
141 | QMimeType type() const override { return mimeType; } |
142 | |
143 | QMimeType mimeType; |
144 | QString body; |
145 | Omittable<EventRelation> relatesTo; |
146 | |
147 | protected: |
148 | void fillJson(QJsonObject& json) const override; |
149 | }; |
150 | |
151 | /** |
152 | * Content class for m.location |
153 | * |
154 | * Available fields: |
155 | * - corresponding to the top-level JSON: |
156 | * - geoUri ("geo_uri" in JSON) |
157 | * - corresponding to the "info" subobject: |
158 | * - thumbnail.url ("thumbnail_url" in JSON) |
159 | * - corresponding to the "info/thumbnail_info" subobject: |
160 | * - thumbnail.payloadSize |
161 | * - thumbnail.mimeType |
162 | * - thumbnail.imageSize |
163 | */ |
164 | class QUOTIENT_API LocationContent : public TypedBase { |
165 | public: |
166 | LocationContent(const QString& geoUri, const Thumbnail& thumbnail = {}); |
167 | explicit LocationContent(const QJsonObject& json); |
168 | |
169 | QMimeType type() const override; |
170 | |
171 | public: |
172 | QString geoUri; |
173 | Thumbnail thumbnail; |
174 | |
175 | protected: |
176 | void fillJson(QJsonObject& o) const override; |
177 | }; |
178 | |
179 | /** |
180 | * A base class for info types that include duration: audio and video |
181 | */ |
182 | template <typename InfoT> |
183 | class PlayableContent : public UrlBasedContent<InfoT> { |
184 | public: |
185 | using UrlBasedContent<InfoT>::UrlBasedContent; |
186 | PlayableContent(const QJsonObject& json) |
187 | : UrlBasedContent<InfoT>(json) |
188 | , duration(FileInfo::originalInfoJson["duration"_ls ].toInt()) |
189 | {} |
190 | |
191 | protected: |
192 | void fillInfoJson(QJsonObject& infoJson) const override |
193 | { |
194 | infoJson.insert(QStringLiteral("duration" ), value: duration); |
195 | } |
196 | |
197 | public: |
198 | int duration; |
199 | }; |
200 | |
201 | /** |
202 | * Content class for m.video |
203 | * |
204 | * Available fields: |
205 | * - corresponding to the top-level JSON: |
206 | * - url |
207 | * - filename (extension to the CS API spec) |
208 | * - corresponding to the "info" subobject: |
209 | * - payloadSize ("size" in JSON) |
210 | * - mimeType ("mimetype" in JSON) |
211 | * - duration |
212 | * - imageSize (QSize for a combination of "h" and "w" in JSON) |
213 | * - thumbnail.url ("thumbnail_url" in JSON) |
214 | * - corresponding to the "info/thumbnail_info" subobject: contents of |
215 | * thumbnail field, in the same vein as for "info": |
216 | * - payloadSize |
217 | * - mimeType |
218 | * - imageSize |
219 | */ |
220 | using VideoContent = PlayableContent<ImageInfo>; |
221 | |
222 | /** |
223 | * Content class for m.audio |
224 | * |
225 | * Available fields: |
226 | * - corresponding to the top-level JSON: |
227 | * - url |
228 | * - filename (extension to the CS API spec) |
229 | * - corresponding to the "info" subobject: |
230 | * - payloadSize ("size" in JSON) |
231 | * - mimeType ("mimetype" in JSON) |
232 | * - duration |
233 | * - thumbnail.url ("thumbnail_url" in JSON - extension to the spec) |
234 | * - corresponding to the "info/thumbnail_info" subobject: contents of |
235 | * thumbnail field (extension to the spec): |
236 | * - payloadSize |
237 | * - mimeType |
238 | * - imageSize |
239 | */ |
240 | using AudioContent = PlayableContent<FileInfo>; |
241 | } // namespace EventContent |
242 | } // namespace Quotient |
243 | |