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