aboutsummaryrefslogtreecommitdiff
path: root/plugins/Matrix.hpp
blob: de7d9b8dc6303311e62eb6b3b4efac1eb50038c9 (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
150
151
152
153
154
155
156
157
158
159
160
161
162
163
#pragma once

#include "../include/FileAnalyzer.hpp"
#include "Plugin.hpp"
#include <unordered_map>
#include <json/value.h>

namespace QuickMedia {
    struct RoomData;

    struct UserInfo {
        friend struct RoomData;

        std::string user_id;
        std::string display_name;
        std::string avatar_url;
        sf::Color display_name_color;
    private:
        std::string read_marker_event_id;
    };

    enum class MessageType {
        TEXT,
        IMAGE,
        VIDEO,
        AUDIO,
        FILE
    };

    struct Message {
        std::shared_ptr<UserInfo> user;
        std::string event_id;
        std::string body;
        std::string url;
        std::string thumbnail_url;
        std::string replaces_event_id;
        bool mentions_me = false;
        time_t timestamp = 0;
        MessageType type;
    };

    struct RoomData {
        std::shared_ptr<UserInfo> get_user_by_id(const std::string &user_id);
        void add_user(std::shared_ptr<UserInfo> user);

        void set_user_read_marker(std::shared_ptr<UserInfo> &user, const std::string &event_id);
        std::string get_user_read_marker(std::shared_ptr<UserInfo> &user);

        void prepend_messages_reverse(std::vector<std::shared_ptr<Message>> new_messages);
        void append_messages(std::vector<std::shared_ptr<Message>> new_messages);

        std::shared_ptr<Message> get_message_by_id(const std::string &id);

        std::vector<std::shared_ptr<UserInfo>> get_users_excluding_me(const std::string &my_user_id);

        void acquire_room_lock();
        void release_room_lock();

        const std::vector<std::shared_ptr<Message>>& get_messages_thread_unsafe() const;

        std::string id;
        std::string name;
        std::string avatar_url;
        std::string prev_batch;
        bool initial_fetch_finished = false;
        size_t last_read_index = 0;
    private:
        std::mutex user_mutex;
        std::mutex room_mutex;
        // Each room has its own list of user data, even if multiple rooms has the same user
        // because users can have different display names and avatars in different rooms.
        std::unordered_map<std::string, std::shared_ptr<UserInfo>> user_info_by_user_id;
        std::vector<std::shared_ptr<Message>> messages;
        std::unordered_map<std::string, std::shared_ptr<Message>> message_by_event_id;
    };

    enum class MessageDirection {
        BEFORE,
        AFTER
    };

    struct UploadInfo {
        ContentType content_type;
        size_t file_size;
        std::optional<Dimensions> dimensions;
        std::optional<double> duration_seconds;
        std::string content_uri;
    };

    using RoomSyncMessages = std::unordered_map<std::shared_ptr<RoomData>, std::vector<std::shared_ptr<Message>>>;

    class Matrix : public Plugin {
    public:
        Matrix();
        virtual ~Matrix() = default;
        bool search_is_filter() override { return true; }
        bool search_suggestions_has_thumbnails() const override { return true; }
        bool search_results_has_thumbnails() const override { return false; }
        int get_search_delay() const override { return 0; }
        bool search_suggestion_is_search() const override { return true; }
        Page get_page_after_search() const override { return Page::EXIT; }
        PluginResult sync(RoomSyncMessages &room_messages);
        PluginResult get_joined_rooms(BodyItems &result_items);
        PluginResult get_all_synced_room_messages(const std::string &room_id, BodyItems &result_items);
        PluginResult get_new_room_messages(const std::string &room_id, BodyItems &result_items);
        PluginResult get_previous_room_messages(const std::string &room_id, BodyItems &result_items);
        SearchResult search(const std::string &text, BodyItems &result_items) override;

        // |url| should only be set when uploading media.
        // TODO: Make api better.
        PluginResult post_message(const std::string &room_id, const std::string &body, const std::optional<UploadInfo> &file_info, const std::optional<UploadInfo> &thumbnail_info);
        // |relates_to| is from |BodyItem.userdata| and is of type |Message*|
        PluginResult post_reply(const std::string &room_id, const std::string &body, void *relates_to);
        // |relates_to| is from |BodyItem.userdata| and is of type |Message*|
        PluginResult post_edit(const std::string &room_id, const std::string &body, void *relates_to);
        
        PluginResult post_file(const std::string &room_id, const std::string &filepath, std::string &err_msg);
        PluginResult login(const std::string &username, const std::string &password, const std::string &homeserver, std::string &err_msg);
        PluginResult logout();

        // |message| is from |BodyItem.userdata| and is of type |Message*|
        PluginResult delete_message(const std::string &room_id, void *message, std::string &err_msg);

        PluginResult load_and_verify_cached_session();

        PluginResult on_start_typing(const std::string &room_id);
        PluginResult on_stop_typing(const std::string &room_id);

        PluginResult set_read_marker(const std::string &room_id, const Message *message);

        // |message| is from |BodyItem.userdata| and is of type |Message*|
        bool was_message_posted_by_me(void *message);

        std::string message_get_author_displayname(Message *message) const;

        // Cached
        PluginResult get_config(int *upload_size);

        std::shared_ptr<UserInfo> get_me(const std::string &room_id);
    private:
        PluginResult sync_response_to_body_items(const Json::Value &root, RoomSyncMessages &room_messages);
        PluginResult get_previous_room_messages(std::shared_ptr<RoomData> &room_data);
        void events_add_user_info(const Json::Value &events_json, RoomData *room_data);
        void events_add_user_read_markers(const Json::Value &events_json, RoomData *room_data);
        void events_add_messages(const Json::Value &events_json, std::shared_ptr<RoomData> &room_data, MessageDirection message_dir, RoomSyncMessages *room_messages, bool has_unread_notifications);
        void events_set_room_name(const Json::Value &events_json, RoomData *room_data);
        PluginResult upload_file(const std::string &room_id, const std::string &filepath, UploadInfo &file_info, UploadInfo &thumbnail_info, std::string &err_msg);

        std::shared_ptr<Message> get_edited_message_original_message(RoomData *room_data, std::shared_ptr<Message> message);

        std::shared_ptr<RoomData> get_room_by_id(const std::string &id);
        void add_room(std::shared_ptr<RoomData> room);
    private:
        std::unordered_map<std::string, std::shared_ptr<RoomData>> room_data_by_id;
        std::mutex room_data_mutex;
        std::string user_id;
        std::string username;
        std::string access_token;
        std::string homeserver;
        std::optional<int> upload_limit;
        std::string next_batch;
    };
}