summary refs log tree commit diff
path: root/src/timeline/EventStore.h
blob: 7c40410262256546ba254aac7633a5f196a4a0f6 (plain) (blame)
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
// SPDX-FileCopyrightText: 2021 Nheko Contributors
//
// SPDX-License-Identifier: GPL-3.0-or-later

#pragma once

#include <limits>
#include <string>

#include <QCache>
#include <QObject>
#include <QVariant>

#include <mtx/events/collections.hpp>
#include <mtx/responses/messages.hpp>
#include <mtx/responses/sync.hpp>

#include "Reaction.h"

class EventStore : public QObject
{
        Q_OBJECT

public:
        EventStore(std::string room_id, QObject *parent);

        // taken from QtPrivate::QHashCombine
        static uint hashCombine(uint hash, uint seed)
        {
                return seed ^ (hash + 0x9e3779b9 + (seed << 6) + (seed >> 2));
        };
        struct Index
        {
                std::string room;
                uint64_t idx;

                friend uint qHash(const Index &i, uint seed = 0) noexcept
                {
                        seed =
                          hashCombine(qHashBits(i.room.data(), (int)i.room.size(), seed), seed);
                        seed = hashCombine(qHash(i.idx, seed), seed);
                        return seed;
                }

                friend bool operator==(const Index &a, const Index &b) noexcept
                {
                        return a.idx == b.idx && a.room == b.room;
                }
        };
        struct IdIndex
        {
                std::string room, id;

                friend uint qHash(const IdIndex &i, uint seed = 0) noexcept
                {
                        seed =
                          hashCombine(qHashBits(i.room.data(), (int)i.room.size(), seed), seed);
                        seed = hashCombine(qHashBits(i.id.data(), (int)i.id.size(), seed), seed);
                        return seed;
                }

                friend bool operator==(const IdIndex &a, const IdIndex &b) noexcept
                {
                        return a.id == b.id && a.room == b.room;
                }
        };

        void fetchMore();
        void handleSync(const mtx::responses::Timeline &events);

        // optionally returns the event or nullptr and fetches it, after which it emits a
        // relatedFetched event
        mtx::events::collections::TimelineEvents *get(std::string id,
                                                      std::string_view related_to,
                                                      bool decrypt       = true,
                                                      bool resolve_edits = true);
        // always returns a proper event as long as the idx is valid
        mtx::events::collections::TimelineEvents *get(int idx, bool decrypt = true);

        QVariantList reactions(const std::string &event_id);

        int size() const
        {
                return (last != std::numeric_limits<uint64_t>::max() && last >= first)
                         ? static_cast<int>(last - first) + 1
                         : 0;
        }
        int toExternalIdx(uint64_t idx) const { return static_cast<int>(idx - first); }
        uint64_t toInternalIdx(int idx) const { return first + idx; }

        std::optional<int> idToIndex(std::string_view id) const;
        std::optional<std::string> indexToId(int idx) const;

signals:
        void beginInsertRows(int from, int to);
        void endInsertRows();
        void beginResetModel();
        void endResetModel();
        void dataChanged(int from, int to);
        void newEncryptedImage(mtx::crypto::EncryptedFile encryptionInfo);
        void eventFetched(std::string id,
                          std::string relatedTo,
                          mtx::events::collections::TimelineEvents timeline);
        void oldMessagesRetrieved(const mtx::responses::Messages &);
        void fetchedMore();

        void processPending();
        void messageSent(std::string txn_id, std::string event_id);
        void messageFailed(std::string txn_id);
        void startDMVerification(
          const mtx::events::RoomEvent<mtx::events::msg::KeyVerificationRequest> &msg);
        void updateFlowEventId(std::string event_id);

public slots:
        void addPending(mtx::events::collections::TimelineEvents event);
        void receivedSessionKey(const std::string &session_id);
        void clearTimeline();
        void enableKeyRequests(bool suppressKeyRequests_);

private:
        std::vector<mtx::events::collections::TimelineEvents> edits(const std::string &event_id);
        mtx::events::collections::TimelineEvents *decryptEvent(
          const IdIndex &idx,
          const mtx::events::EncryptedEvent<mtx::events::msg::Encrypted> &e);
        void handle_room_verification(mtx::events::collections::TimelineEvents event);

        std::string room_id_;

        uint64_t first = std::numeric_limits<uint64_t>::max(),
                 last  = std::numeric_limits<uint64_t>::max();

        static QCache<IdIndex, mtx::events::collections::TimelineEvents> decryptedEvents_;
        static QCache<Index, mtx::events::collections::TimelineEvents> events_;
        static QCache<IdIndex, mtx::events::collections::TimelineEvents> events_by_id_;

        struct PendingKeyRequests
        {
                std::string request_id;
                std::vector<mtx::events::EncryptedEvent<mtx::events::msg::Encrypted>> events;
        };
        std::map<std::string, PendingKeyRequests> pending_key_requests;

        std::string current_txn;
        int current_txn_error_count = 0;
        bool noMoreMessages         = false;
        bool suppressKeyRequests    = true;
};