libQuotient
A Qt library for building matrix clients
roommessageevent.h
Go to the documentation of this file.
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 
16 /**
17  * The event class corresponding to m.room.message events
18  */
19 class QUOTIENT_API RoomMessageEvent : public RoomEvent {
20  Q_GADGET
21 public:
22  QUO_EVENT(RoomMessageEvent, "m.room.message")
23 
24  enum class MsgType {
25  Text,
26  Emote,
27  Notice,
28  Image,
29  File,
30  Location,
31  Video,
32  Audio,
33  Unknown
34  };
35 
36  RoomMessageEvent(const QString& plainBody, const QString& jsonMsgType,
37  std::unique_ptr<EventContent::Base> content = nullptr,
38  const std::optional<EventRelation>& relatesTo = std::nullopt);
39  explicit RoomMessageEvent(const QString& plainBody, MsgType msgType = MsgType::Text,
40  std::unique_ptr<EventContent::Base> content = nullptr,
41  const std::optional<EventRelation>& relatesTo = std::nullopt);
42 
43  explicit RoomMessageEvent(const QJsonObject& obj);
44 
45  MsgType msgtype() const;
46  QString rawMsgtype() const;
47  QString plainBody() const;
48 
49  //! \brief Load event content from the event JSON
50  //!
51  //! \warning The result must be checked for nullptr as an event with just a plainBody
52  //! will not have a content object.
53  //! \warning Since libQuotient 0.9, the returned value has changed from a C pointer (TypedBase*)
54  //! to `std::unique_ptr<>` because the deserialised content object is no more stored
55  //! inside the event. The calling code must either store the entire returned value
56  //! in a variable or copy/move away the needed field from the returned value;
57  //! a reference or a pointer to a field will become dangling at the statement end.
58  //!
59  //! \return an event content object if the event has content, nullptr otherwise.
60  std::unique_ptr<EventContent::Base> content() const;
61 
62  //! Update the message JSON with the given content
63  void setContent(std::unique_ptr<EventContent::Base> content);
64 
65  //! \brief Determine whether the message has content/attachment of a specified type
66  //!
67  //! \return true, if the message has type and content corresponding to \p ContentT (e.g.
68  //! `m.file` or `m.audio` for FileContent); false otherwise
69  template <std::derived_from<EventContent::Base> ContentT>
70  bool has() const { return false; }
71 
72  //! \brief Get the message content and try to cast it to the specified type
73  //!
74  //! \return A pointer to the object of the requested type if the event has content of this type;
75  //! nullptr, if the event has no content or the content cannot be cast to this type
76  template <std::derived_from<EventContent::Base> ContentT>
77  std::unique_ptr<ContentT> get() const
78  {
79  return has<ContentT>()
80  ? std::unique_ptr<ContentT>(static_cast<ContentT*>(content().release()))
81  : nullptr;
82  }
83 
84  QMimeType mimeType() const;
85 
86  //! \brief Determine whether the message has a thumbnail
87  //!
88  //! \return true, if the message has a data structure corresponding to
89  //! a thumbnail (the message type may be one for visual content,
90  //! such as m.image, or non-visual, i.e. m.file or m.location);
91  //! false otherwise
92  bool hasThumbnail() const;
93 
94  //! Retrieve a thumbnail from the message event
95  EventContent::Thumbnail getThumbnail() const;
96 
97  //! \brief The EventRelation for this event.
98  //!
99  //! \return an EventRelation object which can be checked for type if it exists,
100  //! std::nullopt otherwise.
101  std::optional<EventRelation> relatesTo() const;
102 
103  //! \brief The upstream event ID for the relation.
104  //!
105  //! \warning If your client is not thread aware use replyEventId() as this will
106  //! return the fallback reply ID so you can treat a threaded reply like a normal one.
107  //!
108  //! \warning If your client is thread aware use threadRootEventId() to get the
109  //! thread root ID as this will return an empty string on the root event.
110  //! threadRootEventId() will return the root messages ID on itself.
111  QString upstreamEventId() const;
112 
113  //! \brief Obtain id of an event replaced by the current one
114  //! \sa RoomEvent::isReplaced, RoomEvent::replacedBy
115  QString replacedEvent() const;
116 
117  //! \brief Determine whether the event has been replaced
118  //!
119  //! \return true if this event has been overridden by another event
120  //! with `"rel_type": "m.replace"`; false otherwise
121  bool isReplaced() const;
122 
123  QString replacedBy() const;
124 
125  //! \brief Determine whether the event is a reply to another message.
126  //!
127  //! \param includeFallbacks include thread fallback replies for non-threaded clients.
128  //!
129  //! \return true if this event is a reply, i.e. it has `"m.in_reply_to"`
130  //! event ID and is not a thread fallback (except where \p includeFallbacks is true);
131  //! false otherwise.
132  //!
133  //! \note It's possible to reply to another message in a thread so this function
134  //! will return true for a `"rel_type"` of `"m.thread"` if `"is_falling_back"`
135  //! is false.
136  bool isReply(bool includeFallbacks = false) const;
137 
138  //! \brief The ID for the event being replied to.
139  //!
140  //! \param includeFallbacks include thread fallback replies for non-threaded clients.
141  //!
142  //!
143  //! \return The event ID for a reply, this includes threaded replies where `"rel_type"`
144  //! is `"m.thread"` and `"is_falling_back"` is false (except where \p includeFallbacks is true).
145  QString replyEventId(bool includeFallbacks = false)const;
146 
147  //! \brief Determine whether the event is part of a thread.
148  //!
149  //! \return true if this event is part of a thread, i.e. it has
150  //! `"rel_type": "m.thread"` or `"m.relations": { "m.thread": {}}`;
151  //! false otherwise.
152  bool isThreaded() const;
153 
154  //! \brief The event ID for the thread root event.
155  //!
156  //! \note This will return the ID of the event if it is the thread root.
157  //!
158  //! \return The event ID of the thread root if threaded, an empty string otherwise.
159  QString threadRootEventId()const;
160 
161  QString fileNameToDownload() const;
162 
163  void updateFileSourceInfo(const FileSourceInfo& fsi);
164 
165  static QString rawMsgTypeForUrl(const QUrl& url);
166  static QString rawMsgTypeForFile(const QFileInfo& fi);
167 
168 private:
169  // FIXME: should it really be static?
170  static QJsonObject assembleContentJson(const QString& plainBody, const QString& jsonMsgType,
171  std::unique_ptr<EventContent::Base> content,
172  const std::optional<EventRelation>& relatesTo);
173 
174  Q_ENUM(MsgType)
175 };
176 
177 template <> QUOTIENT_API bool RoomMessageEvent::has<EventContent::TextContent>() const;
178 template <> QUOTIENT_API bool RoomMessageEvent::has<EventContent::LocationContent>() const;
179 template <> QUOTIENT_API bool RoomMessageEvent::has<EventContent::FileContentBase>() const;
180 template <> QUOTIENT_API bool RoomMessageEvent::has<EventContent::FileContent>() const;
181 template <> QUOTIENT_API bool RoomMessageEvent::has<EventContent::ImageContent>() const;
182 template <> QUOTIENT_API bool RoomMessageEvent::has<EventContent::AudioContent>() const;
183 template <> QUOTIENT_API bool RoomMessageEvent::has<EventContent::VideoContent>() const;
184 
185 using MessageEventType = RoomMessageEvent::MsgType;
186 } // namespace Quotient