libQuotient
A Qt library for building matrix clients
Loading...
Searching...
No Matches
eventcontent.h
Go to the documentation of this file.
1// SPDX-FileCopyrightText: 2017 Kitsune Ral <kitsune-ral@users.sf.net>
2// SPDX-License-Identifier: LGPL-2.1-or-later
3
4#pragma once
5
6// This file contains generic event content definitions, applicable to room
7// message events as well as other events (e.g., avatars).
8
10
11#include <QtCore/QJsonObject>
12#include <QtCore/QMetaType>
13#include <QtCore/QMimeType>
14#include <QtCore/QSize>
15#include <QtCore/QUrl>
16
17class QFileInfo;
18
19namespace Quotient {
20constexpr inline auto BodyKey = "body"_L1;
21constexpr inline auto FormatKey = "format"_L1;
22constexpr inline auto FormattedBodyKey = "formatted_body"_L1;
23constexpr inline auto HtmlContentTypeId = "org.matrix.custom.html"_L1;
24constexpr inline auto InfoKey = "info"_L1;
25}
26
28//! \brief Base for all content types that can be stored in RoomMessageEvent
29//!
30//! Each content type class should have a constructor taking
31//! a QJsonObject and override fillJson() with an implementation
32//! that will fill the target QJsonObject with stored values. It is
33//! assumed but not required that a content object can also be created
34//! from plain data.
36public:
37 explicit Base(QJsonObject o = {}) : originalJson(std::move(o)) {}
38 virtual ~Base() = default;
39
41
42 virtual QMimeType type() const = 0;
43
44public:
46
47 // You can't assign those classes
48 Base& operator=(const Base&) = delete;
49 Base& operator=(Base&&) = delete;
50
51protected:
52 Base(const Base&) = default;
53 Base(Base&&) noexcept = default;
54
55 virtual void fillJson(QJsonObject&) const = 0;
56};
57
58// The below structures fairly follow CS spec 11.2.1.6. The overall
59// set of attributes for each content types is a superset of the spec
60// but specific aggregation structure is altered. See doc comments to
61// each type for the list of available attributes.
62
63// A quick class inheritance structure follows:
64// UrlBasedContent<InfoT> : InfoT + thumbnail data
65// PlayableContent<InfoT> : + duration attribute
66// FileInfo
67// FileContent = UrlBasedContent<FileInfo>
68// AudioContent = PlayableContent<FileInfo>
69// ImageInfo : FileInfo + imageSize attribute
70// ImageContent = UrlBasedContent<ImageInfo>
71// VideoContent = PlayableContent<ImageInfo>
72
73//! \brief Mix-in class representing `info` subobject in content JSON
74//!
75//! This is one of base classes for content types that deal with files or URLs. It stores
76//! file metadata attributes, such as size, MIME type etc. found in the `content/info` subobject of
77//! event JSON payloads. Actual content classes derive from this class _and_ Base that provides
78//! a polymorphic interface to access data in the mix-in. FileInfo (as well as ImageInfo, that adds
79//! image size to the metadata) is NOT polymorphic and is used in a non-polymorphic way to store
80//! thumbnail metadata (in a separate instance), next to the metadata on the file itself.
81//!
82//! If you need to make a new _content_ (not info) class based on files/URLs take UrlBasedContent
83//! as the example, i.e.:
84//! 1. Double-inherit from this class (or ImageInfo) and Base.
85//! 2. Provide a constructor from QJsonObject that will pass the `info` subobject (not the whole
86//! content JSON) down to FileInfo/ImageInfo.
87//! 3. Override fillJson() to customise the JSON export logic. Make sure to call toInfoJson()
88//! from it to produce the payload for the `info` subobject in the JSON payload.
89//!
90//! \sa ImageInfo, FileContent, ImageContent, AudioContent, VideoContent, UrlBasedContent
92 FileInfo() = default;
93 //! \brief Construct from a QFileInfo object
94 //!
95 //! \param fi a QFileInfo object referring to an existing file
96 explicit FileInfo(const QFileInfo& fi);
98 const QMimeType& mimeType = {},
100 //! \brief Construct from a JSON `info` payload
101 //!
102 //! Make sure to pass the `info` subobject of content JSON, not the
103 //! whole JSON content.
106
107 bool isValid() const;
108 QUrl url() const;
109
110 //! \brief Extract media id from the URL
111 //!
112 //! This can be used, e.g., to construct a QML-facing image://
113 //! URI as follows:
114 //! \code "image://provider/" + info.mediaId() \endcode
115 QString mediaId() const { return url().authority() + url().path(); }
116
122};
123
125
126//! \brief A content info class for image/video content types and thumbnails
128 ImageInfo() = default;
129 explicit ImageInfo(const QFileInfo& fi, QSize imageSize = {});
131 const QMimeType& type = {}, QSize imageSize = {},
132 const QString& originalFilename = {});
134 const QString& originalFilename = {});
135
137};
138
140
141//! \brief An auxiliary class for an info type that carries a thumbnail
142//!
143//! This class saves/loads a thumbnail to/from `info` subobject of
144//! the JSON representation of event content; namely, `info/thumbnail_url`
145//! (or, in case of an encrypted thumbnail, `info/thumbnail_file`) and
146//! `info/thumbnail_info` fields are used.
148 using ImageInfo::ImageInfo;
150
151 //! \brief Add thumbnail information to the passed `info` JSON object
153};
154
155//! The base for all file-based content classes
157public:
160 {}
161 virtual QUrl url() const = 0;
162 virtual FileInfo commonInfo() const = 0;
163
165
166protected:
167 virtual QJsonObject makeInfoJson() const = 0;
168
170 {
171 const auto fileInfo = commonInfo();
172 Quotient::fillJson(json, { "url"_L1, "file"_L1 }, fileInfo.source);
173 addParam<IfNotEmpty>(json, "filename"_L1, fileInfo.originalName);
174 auto infoJson = makeInfoJson();
175 if (thumbnail.isValid())
178 }
179};
180
181//! \brief Rich text content for m.text, m.emote, m.notice
182//!
183//! Available fields: mimeType, body. The body can be either rich text
184//! or plain text, depending on what mimeType specifies.
186public:
188 explicit TextContent(const QJsonObject& json);
189
190 QMimeType type() const override { return mimeType; }
191
194
195protected:
197};
198
199//! \brief Content class for m.location
200//!
201//! Available fields:
202//! - corresponding to the top-level JSON:
203//! - geoUri ("geo_uri" in JSON)
204//! - corresponding to the "info" subobject:
205//! - thumbnail.url ("thumbnail_url" in JSON)
206//! - corresponding to the "info/thumbnail_info" subobject:
207//! - thumbnail.payloadSize
208//! - thumbnail.mimeType
209//! - thumbnail.imageSize
211public:
214
216
217public:
220
221protected:
223};
224
225//! \brief A template class for content types with a URL and additional info
226//!
227//! Types that derive from this class template take `url` (or, if the file
228//! is encrypted, `file`) and, optionally, `filename` values from
229//! the top-level JSON object and the rest of information from the `info`
230//! subobject, as defined by the parameter type.
231//! \tparam InfoT base info class - FileInfo or ImageInfo
232template <std::derived_from<FileInfo> InfoT>
233class UrlBasedContent : public FileContentBase, public InfoT {
234public:
235 using InfoT::InfoT;
236 explicit UrlBasedContent(const QJsonObject& json)
238 , InfoT(fileSourceInfoFromJson(json, { "url"_L1, "file"_L1 }), json[InfoKey].toObject(),
239 json["filename"_L1].toString())
240 {
241 // Two small hacks on originalJson to expose mediaIds to QML
242 originalJson.insert("mediaId"_L1, InfoT::mediaId());
243 originalJson.insert("thumbnailMediaId"_L1, thumbnail.mediaId());
244 }
245
246 QMimeType type() const override { return InfoT::mimeType; }
247 QUrl url() const override { return InfoT::url(); }
248 FileInfo commonInfo() const override { return SLICE(*this, FileInfo); }
249
250protected:
251 QJsonObject makeInfoJson() const override { return toInfoJson(*this); }
252};
253
254//! \brief Content class for m.image
255//!
256//! Available fields:
257//! - corresponding to the top-level JSON:
258//! - source (corresponding to `url` or `file` in JSON)
259//! - filename (extension to the spec)
260//! - corresponding to the `info` subobject:
261//! - payloadSize (`size` in JSON)
262//! - mimeType (`mimetype` in JSON)
263//! - imageSize (QSize for a combination of `h` and `w` in JSON)
264//! - thumbnail.url (`thumbnail_url` in JSON)
265//! - corresponding to the `info/thumbnail_info` subobject: contents of
266//! thumbnail field, in the same vein as for the main image:
267//! - payloadSize
268//! - mimeType
269//! - imageSize
271
272//! \brief Content class for m.file
273//!
274//! Available fields:
275//! - corresponding to the top-level JSON:
276//! - source (corresponding to `url` or `file` in JSON)
277//! - filename
278//! - corresponding to the `info` subobject:
279//! - payloadSize (`size` in JSON)
280//! - mimeType (`mimetype` in JSON)
281//! - thumbnail.source (`thumbnail_url` or `thumbnail_file` in JSON)
282//! - corresponding to the `info/thumbnail_info` subobject:
283//! - thumbnail.payloadSize
284//! - thumbnail.mimeType
285//! - thumbnail.imageSize (QSize for `h` and `w` in JSON)
287
288//! A base class for info types that include duration: audio and video
289template <typename InfoT>
291public:
293 explicit PlayableContent(const QJsonObject& json)
295 {}
296
297protected:
298 QJsonObject makeInfoJson() const override
299 {
300 auto infoJson = UrlBasedContent<InfoT>::makeInfoJson();
301 infoJson.insert("duration"_L1, duration);
302 return infoJson;
303}
304
305public:
307};
308
309//! \brief Content class for m.video
310//!
311//! Available fields:
312//! - corresponding to the top-level JSON:
313//! - url
314//! - filename (extension to the CS API spec)
315//! - corresponding to the "info" subobject:
316//! - payloadSize ("size" in JSON)
317//! - mimeType ("mimetype" in JSON)
318//! - duration
319//! - imageSize (QSize for a combination of "h" and "w" in JSON)
320//! - thumbnail.url ("thumbnail_url" in JSON)
321//! - corresponding to the "info/thumbnail_info" subobject: contents of
322//! thumbnail field, in the same vein as for "info":
323//! - payloadSize
324//! - mimeType
325//! - imageSize
327
328//! \brief Content class for m.audio
329//!
330//! Available fields:
331//! - corresponding to the top-level JSON:
332//! - url
333//! - filename (extension to the CS API spec)
334//! - corresponding to the "info" subobject:
335//! - payloadSize ("size" in JSON)
336//! - mimeType ("mimetype" in JSON)
337//! - duration
338//! - thumbnail.url ("thumbnail_url" in JSON)
339//! - corresponding to the "info/thumbnail_info" subobject: contents of
340//! thumbnail field, in the same vein as for "info":
341//! - payloadSize
342//! - mimeType
343//! - imageSize
345} // namespace Quotient::EventContent
346Q_DECLARE_METATYPE(const Quotient::EventContent::Base*)
Base for all content types that can be stored in RoomMessageEvent.
The base for all file-based content classes.
Content class for m.location.
A base class for info types that include duration: audio and video.
QJsonObject makeInfoJson() const override
PlayableContent(const QJsonObject &json)
Rich text content for m.text, m.emote, m.notice.
A template class for content types with a URL and additional info.
QJsonObject makeInfoJson() const override
QMimeType type() const override
FileInfo commonInfo() const override
UrlBasedContent(const QJsonObject &json)
constexpr auto FormattedBodyKey
constexpr auto HtmlContentTypeId
constexpr auto FormatKey
constexpr auto BodyKey
constexpr auto InfoKey
#define QUOTIENT_API
Mix-in class representing info subobject in content JSON.
A content info class for image/video content types and thumbnails.
An auxiliary class for an info type that carries a thumbnail.
#define SLICE(Object, ToType)
Copy an object with slicing.
Definition util.h:39