libQuotient
A Qt library for building matrix clients
roomstateview.h
Go to the documentation of this file.
1 // SPDX-FileCopyrightText: 2021 Kitsune Ral <kitsune-ral@users.sf.net>
2 // SPDX-License-Identifier: LGPL-2.1-or-later
3 
4 #pragma once
5 
6 #include "events/stateevent.h"
7 
8 #include <QtCore/QHash>
9 
10 namespace Quotient {
11 
12 class Room;
13 
14 // NB: Both concepts below expect EvT::needsStateKey to exist so you can't
15 // express one via negation of the other (there's still an invalid case of
16 // a non-state event where needsStateKey is not even defined).
17 
18 template <typename FnT, class EvT = std::decay_t<fn_arg_t<FnT>>>
19 concept Keyed_State_Fn = EvT::needsStateKey;
20 
21 template <typename FnT, class EvT = std::decay_t<fn_arg_t<FnT>>>
22 concept Keyless_State_Fn = !EvT::needsStateKey;
23 
24 class QUOTIENT_API RoomStateView
25  : private QHash<StateEventKey, const StateEvent*> {
26  Q_GADGET
27 public:
28  const QHash<StateEventKey, const StateEvent*>& events() const
29  {
30  return *this;
31  }
32 
33  //! \brief Get a state event with the given event type and state key
34  //! \return A state event corresponding to the pair of event type
35  //! \p evtType and state key \p stateKey, or `nullptr` if there's
36  //! no such \p evtType / \p stateKey combination in the current
37  //! state.
38  //! \warning The returned value is not guaranteed to be non-`nullptr`; you
39  //! MUST check it before using or use other methods of this class
40  //! such as query() and content() to access state safely.
41  //! \sa content, contentJson, query
42  const StateEvent* get(const QString& evtType,
43  const QString& stateKey = {}) const;
44 
45  //! \brief Get a state event with the given event type and state key
46  //!
47  //! This is a typesafe overload that accepts a C++ event type instead of
48  //! its Matrix name. It is only defined for events with state key (i.e.,
49  //! derived from KeyedStateEvent).
50  template <Keyed_State_Event EvT>
51  const EvT* get(const QString& stateKey = {}) const
52  {
53  if (const auto* evt = get(EvT::TypeId, stateKey)) {
54  Q_ASSERT(evt->matrixType() == EvT::TypeId
55  && evt->stateKey() == stateKey);
56  return eventCast<const EvT>(evt);
57  }
58  return nullptr;
59  }
60 
61  //! \brief Get a state event with the given event type
62  //!
63  //! This is a typesafe overload that accepts a C++ event type instead of
64  //! its Matrix name. This overload only defined for events that do not use
65  //! state key (i.e., derived from KeylessStateEvent).
66  template <Keyless_State_Event EvT>
67  const EvT* get() const
68  {
69  if (const auto* evt = get(EvT::TypeId)) {
70  Q_ASSERT(evt->matrixType() == EvT::TypeId);
71  return eventCast<const EvT>(evt);
72  }
73  return nullptr;
74  }
75 
76  using QHash::contains;
77 
78  bool contains(const QString& evtType, const QString& stateKey = {}) const;
79 
80  template <Keyed_State_Event EvT>
81  bool contains(const QString& stateKey = {}) const
82  {
83  return contains(EvT::TypeId, stateKey);
84  }
85 
86  template <Keyless_State_Event EvT>
87  bool contains() const
88  {
89  return contains(EvT::TypeId);
90  }
91 
92  template <Keyed_State_Event EvT>
93  auto content(const QString& stateKey,
94  typename EvT::content_type defaultValue = {}) const
95  {
96  // EventBase<>::content is special in that it returns a const-ref,
97  // and lift() inside queryOr() can't wrap that in a temporary optional.
98  if (const auto evt = get<EvT>(stateKey))
99  return evt->content();
100  return std::move(defaultValue);
101  }
102 
103  template <Keyless_State_Event EvT>
104  auto content(typename EvT::content_type defaultValue = {}) const
105  {
106  // Same as above
107  if (const auto evt = get<EvT>())
108  return evt->content();
109  return defaultValue;
110  }
111 
112  //! \brief Get the content of the current state event with the given
113  //! event type and state key
114  //! \return An empty object if there's no event in the current state with
115  //! this event type and state key; the contents of the event
116  //! <tt>'content'</tt> object otherwise
117  Q_INVOKABLE QJsonObject contentJson(const QString& evtType,
118  const QString& stateKey = {}) const;
119 
120  //! \brief Get all state events in the room of a certain type.
121  //!
122  //! This method returns all known state events that have occured in
123  //! the room of the given type.
124  const QVector<const StateEvent*> eventsOfType(const QString& evtType) const;
125 
126  //! \brief Run a function on a state event with the given type and key
127  //!
128  //! Use this overload when there's no predefined event type or the event
129  //! type is unknown at compile time.
130  //! \return an optional with the result of the function call, or std::nullopt if the event
131  //! is not found or \p fn fails
132  template <typename FnT>
133  auto query(const QString& evtType, const QString& stateKey, FnT&& fn) const
134  {
135  return lift(std::forward<FnT>(fn), get(evtType, stateKey));
136  }
137 
138  //! \brief Run a function on a state event with the given type and key
139  //!
140  //! This is an overload for keyed state events (those that have
141  //! `needsStateKey == true`) with type defined at compile time.
142  //! \return an optional with the result of the function call, or std::nullopt if the event
143  //! is not found or \p fn fails
144  template <Keyed_State_Fn FnT>
145  auto query(const QString& stateKey, FnT&& fn) const
146  {
147  using EventT = std::decay_t<fn_arg_t<FnT>>;
148  return lift(std::forward<FnT>(fn), get<EventT>(stateKey));
149  }
150 
151  //! \brief Run a function on a keyless state event with the given type
152  //!
153  //! This is an overload for keyless state events (those having
154  //! `needsStateKey == false`) with type defined at compile time.
155  //! \return an optional with the result of the function call, or std::nullopt if the event
156  //! is not found or \p fn fails
157  template <Keyless_State_Fn FnT>
158  auto query(FnT&& fn) const
159  {
160  using EventT = std::decay_t<fn_arg_t<FnT>>;
161  return lift(std::forward<FnT>(fn), get<EventT>());
162  }
163 
164  //! \brief Same as query() but with a fallback value
165  //!
166  //! This is a shortcut for `query().value_or()`, passing respective
167  //! arguments to the respective functions. This is an overload for the case
168  //! when the event type cannot be fixed at compile time.
169  //! \return the result of \p fn execution, or \p fallback if the requested
170  //! event doesn't exist or the function fails
171  template <typename FnT, typename FallbackT>
172  auto queryOr(const QString& evtType, const QString& stateKey, FnT&& fn,
173  FallbackT&& fallback) const
174  {
175  return query(evtType, stateKey, std::forward<FnT>(fn))
176  .value_or(std::forward<FallbackT>(fallback));
177  }
178 
179  //! \brief Same as query() but with a fallback value
180  //!
181  //! This is a shortcut for `query().value_or()`, passing respective
182  //! arguments to the respective functions. This is an overload for the case
183  //! when the event type cannot be fixed at compile time.
184  //! \return the result of \p fn execution, or \p fallback if the requested
185  //! event doesn't exist or the function fails
186  template <typename FnT, typename FallbackT>
187  auto queryOr(const QString& stateKey, FnT&& fn, FallbackT&& fallback) const
188  {
189  return query(stateKey, std::forward<FnT>(fn))
190  .value_or(std::forward<FallbackT>(fallback));
191  }
192 
193  //! \brief Same as query() but with a fallback value
194  //!
195  //! This is a shortcut for `query().value_or()`, passing respective
196  //! arguments to the respective functions. This is an overload for the case
197  //! when the event type cannot be fixed at compile time.
198  //! \return the result of \p fn execution, or \p fallback if the requested
199  //! event doesn't exist or the function fails
200  template <typename FnT, typename FallbackT>
201  auto queryOr(FnT&& fn, FallbackT&& fallback) const
202  {
203  return query(std::forward<FnT>(fn))
204  .value_or(std::forward<FallbackT>(fallback));
205  }
206 
207 private:
208  friend class Room; // Factory class for RoomStateView
209  using QHash<StateEventKey, const StateEvent*>::QHash;
210 };
211 } // namespace Quotient