libQuotient
A Qt library for building matrix clients
eventitem.h
Go to the documentation of this file.
1 // SPDX-FileCopyrightText: 2018 Kitsune Ral <kitsune-ral@users.sf.net>
2 // SPDX-License-Identifier: LGPL-2.1-or-later
3 
4 #pragma once
5 
6 #include "events/roomevent.h"
7 #include "events/filesourceinfo.h"
8 
9 #include <QtCore/QPromise>
10 #include <QtCore/QFuture>
11 
12 #include <any>
13 #include <utility>
14 
15 namespace Quotient {
16 
17 namespace EventStatus {
18  Q_NAMESPACE_EXPORT(QUOTIENT_API)
19 
20  /** Special marks an event can assume
21  *
22  * This is used to hint at a special status of some events in UI.
23  * All values except Redacted and Hidden are mutually exclusive.
24  */
25  enum Code : uint16_t {
26  Normal = 0x0, ///< No special designation
27  Submitted = 0x01, ///< The event has just been submitted for sending
28  FileUploaded = 0x02, ///< The file attached to the event has been
29  /// uploaded to the server
30  Departed = 0x03, ///< The event has left the client
31  ReachedServer = 0x04, ///< The server has received the event
32  SendingFailed = 0x05, ///< The server could not receive the event
33  Redacted = 0x08, ///< The event has been redacted
34  Replaced = 0x10, ///< The event has been replaced
35  Hidden = 0x100, ///< The event should not be shown in the timeline
36  };
37  Q_ENUM_NS(Code)
38 } // namespace EventStatus
39 
40 class QUOTIENT_API EventItemBase {
41 public:
42  using value_type = RoomEvent;
43 
44  explicit EventItemBase(RoomEventPtr&& e) : evt(std::move(e))
45  {
46  Q_ASSERT(evt);
47  }
48 
49  const RoomEvent* event() const { return std::to_address(evt); }
50  const RoomEvent* get() const { return event(); }
51  template <EventClass<RoomEvent> EventT>
52  const EventT* viewAs() const
53  {
54  return eventCast<const EventT>(evt);
55  }
56  const RoomEventPtr& operator->() const { return evt; }
57  const RoomEvent& operator*() const { return *evt; }
58 
59  // Used for event redaction
60  RoomEventPtr replaceEvent(RoomEventPtr&& other)
61  {
62  return std::exchange(evt, std::move(other));
63  }
64 
65  /// Store arbitrary data with the event item
66  void setUserData(std::any userData) { data = std::move(userData); }
67  /// Obtain custom data previously stored with the event item
68  const std::any& userdata() const { return data; }
69  std::any& userData() { return data; }
70 
71 protected:
72  template <EventClass<RoomEvent> EventT>
73  EventT* getAs()
74  {
75  return eventCast<EventT>(evt);
76  }
77 
78 private:
79  RoomEventPtr evt;
80  std::any data;
81 };
82 
83 class QUOTIENT_API TimelineItem : public EventItemBase {
84 public:
85  // For compatibility with Qt containers, even though we use
86  // a std:: container now for the room timeline
87  using index_t = int;
88 
89  TimelineItem(RoomEventPtr&& e, index_t number)
90  : EventItemBase(std::move(e)), idx(number)
91  {}
92 
93  index_t index() const { return idx; }
94 
95 private:
96  index_t idx;
97 };
98 
99 class QUOTIENT_API PendingEventItem : public EventItemBase {
100 public:
101  using future_type = QFuture<std::reference_wrapper<const RoomEvent>>;
102 
103  explicit PendingEventItem(RoomEventPtr&& e) : EventItemBase(std::move(e))
104  {
105  _promise.setProgressRange(0, 5);
106  }
107 
108  EventStatus::Code deliveryStatus() const { return _status; }
109  QDateTime lastUpdated() const { return _lastUpdated; }
110  QString annotation() const { return _annotation; }
111 
112  void setDeparted() { setStatus(EventStatus::Departed); }
113  void setFileUploaded(const FileSourceInfo &uploadedFileData);
114  void setReachedServer(const QString& eventId)
115  {
116  setStatus(EventStatus::ReachedServer);
117  (*this)->addId(eventId);
118  }
119  void setMerged(const RoomEvent& intoEvent)
120  {
121  _promise.addResult(intoEvent);
122  _promise.finish();
123  }
124  void setSendingFailed(QString errorText)
125  {
126  setStatus(EventStatus::SendingFailed);
127  _annotation = std::move(errorText);
128  }
129  void resetStatus() { setStatus(EventStatus::Submitted); }
130 
131  //! \brief Get a future for the moment when the item gets merged in the timeline
132  //!
133  //! The future will get finished just before this pending item is merged into its remote
134  //! counterpart that comes with /sync. The pending item will always be in ReachedServer state.
135  //! The future result has type implicitly convertible to `const RoomEvent&`.
136  future_type whenMerged() const { return _promise.future(); }
137 
138 private:
139  // Unlike TimelineItems, it's reasonable to assume PendingEventItems are not many; so we can
140  // add extra fields without breaking the memory bill
141  EventStatus::Code _status = EventStatus::Submitted;
142  QDateTime _lastUpdated = QDateTime::currentDateTimeUtc();
143  QString _annotation;
144  QPromise<std::reference_wrapper<const RoomEvent>> _promise;
145 
146  void setStatus(EventStatus::Code status)
147  {
148  _status = status;
149  _lastUpdated = QDateTime::currentDateTimeUtc();
150  _annotation.clear();
151  _promise.start();
152  _promise.setProgressValue(_status);
153  }
154 };
155 
156 inline QDebug& operator<<(QDebug& d, const TimelineItem& ti)
157 {
158  QDebugStateSaver dss(d);
159  d.nospace() << "(" << ti.index() << "|" << ti->id() << ")";
160  return d;
161 }
162 } // namespace Quotient