libQuotient
A Qt library for building matrix clients
stateevent.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 "roomevent.h"
7 
8 namespace Quotient {
9 
10 constexpr inline auto PrevContentKey = "prev_content"_L1;
11 
12 class QUOTIENT_API StateEvent : public RoomEvent {
13 public:
14  QUO_BASE_EVENT(StateEvent, RoomEvent, "json.contains('state_key')")
15 
16  static bool isValid(const QJsonObject& fullJson)
17  {
18  return fullJson.contains(StateKeyKey);
19  }
20 
21  //! \brief Static setting of whether a given even type uses state keys
22  //!
23  //! Most event types don't use a state key; overriding this to `true`
24  //! for a given type changes the calls across Quotient to include state key
25  //! in their signatures; otherwise, state key is still accessible but
26  //! constructors and calls in, e.g., RoomStateView don't include it.
27  static constexpr auto needsStateKey = false;
28 
29  explicit StateEvent(event_type_t type, const QString& stateKey = {},
30  const QJsonObject& contentJson = {});
31 
32  //! Make a minimal correct Matrix state event JSON
33  static QJsonObject basicJson(const QString& matrixTypeId,
34  const QString& stateKey = {},
35  const QJsonObject& contentJson = {})
36  {
37  return { { TypeKey, matrixTypeId },
38  { StateKeyKey, stateKey },
39  { ContentKey, contentJson } };
40  }
41 
42  QString replacedState() const;
43  virtual bool repeatsState() const;
44 
45 protected:
46  explicit StateEvent(const QJsonObject& json);
47  void dumpTo(QDebug dbg) const override;
48 };
49 using StateEventPtr = event_ptr_tt<StateEvent>;
50 using StateEvents = EventsArray<StateEvent>;
51 
52 /**
53  * A combination of event type and state key uniquely identifies a piece
54  * of state in Matrix.
55  * \sa
56  * https://matrix.org/docs/spec/client_server/unstable.html#types-of-room-events
57  */
58 using StateEventKey = std::pair<QString, QString>;
59 
60 template <typename EventT, typename ContentT>
61 class EventTemplate<EventT, StateEvent, ContentT>
62  : public StateEvent {
63 public:
64  using content_type = ContentT;
65 
66  struct Prev {
67  explicit Prev() = default;
68  explicit Prev(const QJsonObject& unsignedJson)
69  : senderId(fromJson<QString>(unsignedJson["prev_sender"_L1]))
70  , content(fromJson<std::optional<ContentT>>(unsignedJson[PrevContentKey]))
71  {}
72 
73  QString senderId;
74  std::optional<ContentT> content;
75  };
76 
77  explicit EventTemplate(const QJsonObject& fullJson)
78  : StateEvent(fullJson)
79  , _content(fromJson<ContentT>(Event::contentJson()))
80  , _prev(unsignedJson())
81  {}
82  template <typename... ContentParamTs>
83  explicit EventTemplate(const QString& stateKey,
84  ContentParamTs&&... contentParams)
85  : StateEvent(EventT::TypeId, stateKey)
86  , _content { std::forward<ContentParamTs>(contentParams)... }
87  {
88  editJson().insert(ContentKey, toJson(_content));
89  }
90 
91  const ContentT& content() const { return _content; }
92 
93  template <typename VisitorT>
94  void editContent(VisitorT&& visitor)
95  {
96  visitor(_content);
97  editJson()[ContentKey] = toJson(_content);
98  }
99  const std::optional<ContentT>& prevContent() const { return _prev.content; }
100  QString prevSenderId() const { return _prev.senderId; }
101 
102 private:
103  ContentT _content;
104  Prev _prev;
105 };
106 
107 template <typename EventT, typename ContentT>
108 class KeyedStateEventBase
109  : public EventTemplate<EventT, StateEvent, ContentT> {
110 public:
111  static constexpr auto needsStateKey = true;
112 
113  using EventTemplate<EventT, StateEvent, ContentT>::EventTemplate;
114 };
115 
116 template <typename EvT>
117 concept Keyed_State_Event = EvT::needsStateKey;
118 
119 template <typename EventT, typename ContentT>
120 class KeylessStateEventBase
121  : public EventTemplate<EventT, StateEvent, ContentT> {
122 private:
123  using base_type = EventTemplate<EventT, StateEvent, ContentT>;
124 
125 public:
126  template <typename... ContentParamTs>
127  // Ideally, we want to check std::constructible_from<ContentT, ContentParamTs...> -
128  // unfortunately, Xcode 15.4 still thinks that, e.g., AliasEventContent is not constructible
129  // from QString and QStringList, so we have to make the check slightly indirect
130  requires std::constructible_from<base_type, QString, ContentParamTs...>
131  explicit KeylessStateEventBase(ContentParamTs&&... contentParams)
132  : base_type(QString(), std::forward<ContentParamTs>(contentParams)...)
133  {}
134 
135 protected:
136  explicit KeylessStateEventBase(const QJsonObject& fullJson)
137  : base_type(fullJson)
138  {}
139 };
140 
141 template <typename EvT>
142 concept Keyless_State_Event = !EvT::needsStateKey;
143 
144 } // namespace Quotient
145 Q_DECLARE_METATYPE(Quotient::StateEvent*)
146 Q_DECLARE_METATYPE(const Quotient::StateEvent*)
147 
148 // https://stackoverflow.com/questions/68320024/why-did-the-c-standards-committee-not-include-stdhash-for-pair-and-tuple
149 template <>
150 struct std::hash<Quotient::StateEventKey> {
151  size_t operator()(const Quotient::StateEventKey& k) const
152  {
153  return qHash(k, QHashSeed::globalSeed());
154  }
155 };