aboutsummaryrefslogtreecommitdiff
path: root/lib/events/stateevent.h
blob: ffbce76e6e6f3b722071cfa101c1a7a55a2e7bb8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
// SPDX-FileCopyrightText: 2018 Kitsune Ral <kitsune-ral@users.sf.net>
// SPDX-License-Identifier: LGPL-2.1-or-later

#pragma once

#include "roomevent.h"

namespace Quotient {

class QUOTIENT_API StateEventBase : public RoomEvent {
public:
    QUO_BASE_EVENT(StateEventBase, "json.contains('state_key')"_ls,
                    RoomEvent::BaseMetaType)
    static bool isValid(const QJsonObject& fullJson)
    {
        return fullJson.contains(StateKeyKeyL);
    }

    //! \brief Static setting of whether a given even type uses state keys
    //!
    //! Most event types don't use a state key; overriding this to `true`
    //! for a given type changes the calls across Quotient to include state key
    //! in their signatures; otherwise, state key is still accessible but
    //! constructors and calls in, e.g., RoomStateView don't include it.
    static constexpr auto needsStateKey = false;

    explicit StateEventBase(Type type, const QString& stateKey = {},
                            const QJsonObject& contentJson = {});

    //! Make a minimal correct Matrix state event JSON
    static QJsonObject basicJson(const QString& matrixTypeId,
                                 const QString& stateKey = {},
                                 const QJsonObject& contentJson = {})
    {
        return { { TypeKey, matrixTypeId },
                 { StateKeyKey, stateKey },
                 { ContentKey, contentJson } };
    }

    QString replacedState() const;
    virtual bool repeatsState() const;

protected:
    explicit StateEventBase(const QJsonObject& json);
    void dumpTo(QDebug dbg) const override;
};
using StateEventPtr = event_ptr_tt<StateEventBase>;
using StateEvents = EventsArray<StateEventBase>;

[[deprecated("Use StateEventBase::basicJson() instead")]]
inline QJsonObject basicStateEventJson(const QString& matrixTypeId,
                                       const QJsonObject& content,
                                       const QString& stateKey = {})
{
    return StateEventBase::basicJson(matrixTypeId, stateKey, content);
}

/**
 * A combination of event type and state key uniquely identifies a piece
 * of state in Matrix.
 * \sa
 * https://matrix.org/docs/spec/client_server/unstable.html#types-of-room-events
 */
using StateEventKey = std::pair<QString, QString>;

template <typename EventT, typename ContentT>
class EventTemplate<EventT, StateEventBase, ContentT>
    : public StateEventBase {
public:
    using content_type = ContentT;

    struct Prev {
        explicit Prev() = default;
        explicit Prev(const QJsonObject& unsignedJson)
            : senderId(fromJson<QString>(unsignedJson["prev_sender"_ls]))
            , content(
                  fromJson<Omittable<ContentT>>(unsignedJson[PrevContentKeyL]))
        {}

        QString senderId;
        Omittable<ContentT> content;
    };

    explicit EventTemplate(const QJsonObject& fullJson)
        : StateEventBase(fullJson)
        , _content(fromJson<ContentT>(Event::contentJson()))
        , _prev(unsignedJson())
    {}
    template <typename... ContentParamTs>
    explicit EventTemplate(const QString& stateKey,
                           ContentParamTs&&... contentParams)
        : StateEventBase(EventT::TypeId, stateKey)
        , _content { std::forward<ContentParamTs>(contentParams)... }
    {
        editJson().insert(ContentKey, toJson(_content));
    }

    const ContentT& content() const { return _content; }

    template <typename VisitorT>
    void editContent(VisitorT&& visitor)
    {
        visitor(_content);
        editJson()[ContentKeyL] = toJson(_content);
    }
    const Omittable<ContentT>& prevContent() const { return _prev.content; }
    QString prevSenderId() const { return _prev.senderId; }

private:
    ContentT _content;
    Prev _prev;
};

template <typename EventT, typename ContentT>
class KeyedStateEventBase
    : public EventTemplate<EventT, StateEventBase, ContentT> {
public:
    static constexpr auto needsStateKey = true;

    using EventTemplate<EventT, StateEventBase, ContentT>::EventTemplate;
};

template <typename EvT>
concept Keyed_State_Event = EvT::needsStateKey;

template <typename EventT, typename ContentT>
class KeylessStateEventBase
    : public EventTemplate<EventT, StateEventBase, ContentT> {
private:
    using base_type = EventTemplate<EventT, StateEventBase, ContentT>;

public:
    template <typename... ContentParamTs>
    explicit KeylessStateEventBase(ContentParamTs&&... contentParams)
        : base_type(QString(), std::forward<ContentParamTs>(contentParams)...)
    {}

protected:
    explicit KeylessStateEventBase(const QJsonObject& fullJson)
        : base_type(fullJson)
    {}
};

template <typename EvT>
concept Keyless_State_Event = !EvT::needsStateKey;

} // namespace Quotient
Q_DECLARE_METATYPE(Quotient::StateEventBase*)
Q_DECLARE_METATYPE(const Quotient::StateEventBase*)