123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867 |
- #pragma once
- #include "../include/FileAnalyzer.hpp"
- #include "../include/MessageQueue.hpp"
- #include "Plugin.hpp"
- #include "Page.hpp"
- #include <mglpp/graphics/Color.hpp>
- #include <unordered_map>
- #include <unordered_set>
- #include <map>
- #include <set>
- #include <mutex>
- #include <atomic>
- #include <thread>
- #include <rapidjson/fwd.h>
- namespace QuickMedia {
- struct RoomData;
- struct Message;
- static const int AUTHOR_MAX_LENGTH = 192;
- class Matrix;
- std::string extract_first_line_remove_newline_elipses(const std::string &str, size_t max_length);
- mgl::Color user_id_to_color(const std::string &user_id);
- // |image_max_size| 0, 0 means no max size
- std::string formatted_text_to_qm_text(Matrix *matrix, const char *str, size_t size, bool allow_formatted_text, mgl::vec2i image_max_size = mgl::vec2i(0, 0));
- // |image_max_size| 0, 0 means no max size
- std::string message_to_qm_text(Matrix *matrix, const Message *message, bool allow_formatted_text = true, mgl::vec2i image_max_size = mgl::vec2i(0, 0));
- std::string pantalaimon_url_to_homeserver_url(Matrix *matrix, const std::string &url);
- Message* get_latest_message_in_edit_chain(Message *message);
- bool matrix_gpg_encrypt_for_each_user_in_room(Matrix *matrix, RoomData *room, const std::string &my_gpg_user_id, const std::string &str, std::string &encrypted_str);
- // Returns empty string on error
- std::string extract_user_name_from_user_id(const std::string &user_id);
- // Returns empty string on error
- std::string extract_user_name_from_email(const std::string &email);
- std::vector<std::string> matrix_extract_room_ids(const std::string &str);
- std::string remove_reply_formatting(Matrix *matrix, const Message *message, bool keep_formatted = false);
- struct MatrixChatBodyDecryptJob {
- enum class DecryptState {
- NOT_DECRYPTED,
- DECRYPTING,
- DECRYPTED,
- FAILED_TO_DECRYPT
- };
- std::string text;
- DecryptState decrypt_state = DecryptState::NOT_DECRYPTED;
- bool cancel = false;
- };
- class MatrixChatBodyItemData : public BodyItemExtra {
- public:
- enum class DecryptState {
- NOT_DECRYPTED,
- DECRYPTING,
- DECRYPTED
- };
- MatrixChatBodyItemData(Matrix *matrix, std::string text_to_decrypt, RoomData *room = nullptr, uint64_t gpg_decrypt_message_id = 0) : matrix(matrix), text_to_decrypt(std::move(text_to_decrypt)), room(room), gpg_decrypt_message_id(gpg_decrypt_message_id) {}
- ~MatrixChatBodyItemData();
- void draw_overlay(mgl::Window&, const Widgets &widgets) override;
- DecryptState decrypt_state = DecryptState::NOT_DECRYPTED;
- std::shared_ptr<MatrixChatBodyDecryptJob> decrypt_job;
- Matrix *matrix = nullptr;
- std::string text_to_decrypt;
- RoomData *room = nullptr;
- uint64_t gpg_decrypt_message_id = 0;
- };
- // TODO: Remove. Not needed anymore.
- struct TimestampedDisplayData {
- std::string data;
- time_t timestamp = 0; // In milliseconds
- // Force update by settings |new_timestamp| to 0
- bool set_data_if_newer(std::string new_data, time_t new_timestamp);
- };
- struct UserInfo {
- friend struct RoomData;
- UserInfo(RoomData *room, std::string user_id);
- UserInfo(RoomData *room, std::string user_id, std::string display_name, std::string avatar_url, time_t update_timestamp_ms);
- RoomData *room;
- const mgl::Color display_name_color;
- const std::string user_id;
- int power_level = 0;
- private:
- TimestampedDisplayData display_name;
- TimestampedDisplayData avatar_url;
- std::string read_marker_event_id;
- };
- enum class MessageType {
- TEXT,
- IMAGE,
- VIDEO,
- AUDIO,
- FILE,
- REDACTION,
- REACTION,
- MEMBERSHIP,
- SYSTEM,
- UNIMPLEMENTED
- };
- bool is_visual_media_message_type(MessageType message_type);
- bool is_system_message_type(MessageType message_type);
- enum class RelatedEventType {
- NONE,
- REPLY,
- EDIT,
- REDACTION,
- REACTION
- };
- struct MatrixEventUserInfo {
- RoomData *room;
- std::string user_id;
- std::optional<std::string> display_name;
- std::optional<std::string> avatar_url;
- };
- struct MatrixEventRoomInfo {
- RoomData *room;
- std::optional<std::string> name;
- std::optional<std::string> topic;
- std::optional<std::string> avatar_url;
- };
- enum class MatrixEventType {
- ADD_USER,
- REMOVE_USER,
- USER_INFO,
- ROOM_INFO
- };
- struct Message {
- std::shared_ptr<UserInfo> user;
- std::string event_id;
- std::string body;
- std::string url;
- std::string thumbnail_url;
- std::string related_event_id;
- mgl::vec2i thumbnail_size; // Set to {0, 0} if not specified
- RelatedEventType related_event_type = RelatedEventType::NONE;
- bool notification_mentions_me = false;
- bool body_is_formatted = false;
- std::string transaction_id;
- time_t timestamp = 0; // In milliseconds
- MessageType type;
- Message *replaces = nullptr;
- std::shared_ptr<Message> replaced_by = nullptr;
- // TODO: Store body item ref here
- };
- using Messages = std::vector<std::shared_ptr<Message>>;
- 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(const std::shared_ptr<UserInfo> &user);
- std::string get_user_display_name(const std::shared_ptr<UserInfo> &user);
- std::string get_user_avatar_url(const std::shared_ptr<UserInfo> &user);
- // Set to empty to remove (in which case the display name will be the user id)
- bool set_user_display_name(std::shared_ptr<UserInfo> &user, std::string display_name, time_t update_timestamp_ms);
- bool set_user_avatar_url(std::shared_ptr<UserInfo> &user, std::string avatar_url, time_t update_timestamp_ms);
- // Ignores duplicates, returns the number of added messages
- size_t prepend_messages_reverse(const Messages &new_messages);
- // Ignores duplicates, returns the number of added messages
- size_t append_messages(const Messages &new_messages);
- std::shared_ptr<Message> get_message_by_id(const std::string &id);
- std::vector<std::shared_ptr<UserInfo>> get_users();
- 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 Messages& get_messages_thread_unsafe() const;
- const std::vector<std::string>& get_pinned_events_thread_unsafe() const;
- void clear_messages();
- bool has_prev_batch();
- void set_prev_batch(const std::string &new_prev_batch);
- std::string get_prev_batch();
- bool has_name();
- bool set_name(const std::string &new_name, time_t update_timestamp_ms);
- // TODO: Remove this
- std::string get_name();
- bool set_topic(const std::string &new_topic, time_t update_timestamp_ms);
- std::string get_topic();
- bool has_avatar_url();
- bool set_avatar_url(const std::string &new_avatar_url, time_t update_timestamp_ms);
- std::string get_avatar_url();
- void set_pinned_events(std::vector<std::string> new_pinned_events);
- std::set<std::string>& get_tags_thread_unsafe();
- void clear_data();
- std::string id;
- // These 5 variables are set by QuickMedia, not the matrix plugin
- bool initial_prev_messages_fetch = true;
- bool last_message_read = true;
- bool users_fetched = false;
- time_t last_read_message_timestamp = 0;
- std::shared_ptr<BodyItem> body_item;
- int offset_to_latest_message_text = 0;
- // These are messages fetched with |Matrix::get_message_by_id|. Needed to show replies, when replying to old message not part of /sync.
- // The value is nullptr if the message is fetched and cached but the event if referenced an invalid message.
- // TODO: Verify if replied to messages are also part of /sync; then this is not needed.
- std::unordered_map<std::string, std::shared_ptr<Message>> fetched_messages_by_event_id;
- size_t messages_read_index = 0;
- bool pinned_events_updated = false;
- bool name_is_fallback = false;
- bool avatar_is_fallback = false;
- std::atomic_int64_t last_message_timestamp = 0;
- std::atomic_int64_t read_marker_event_timestamp = 0;
- int notification_power_level = 50;
- size_t index = 0;
- std::atomic<uint64_t> gpg_decrypt_message_id = 0;
- private:
- std::mutex user_mutex;
- std::recursive_mutex room_mutex;
- TimestampedDisplayData name;
- TimestampedDisplayData topic;
- TimestampedDisplayData avatar_url;
- std::string prev_batch;
- // 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;
- Messages messages;
- std::unordered_map<std::string, std::shared_ptr<Message>> message_by_event_id;
- std::vector<std::string> pinned_events;
- std::set<std::string> tags;
- };
- struct RoomExtraData {
- std::string chat_message;
- std::string editing_message_id;
- };
- struct Invite {
- std::string room_name;
- std::string room_avatar_url;
- std::shared_ptr<UserInfo> invited_by;
- time_t timestamp = 0; // In milliseconds
- };
- enum class MessageDirection {
- BEFORE,
- AFTER
- };
- struct UploadInfo {
- ContentType content_type;
- int64_t file_size = 0;
- std::optional<Dimensions> dimensions;
- std::optional<double> duration_seconds;
- std::string content_uri;
- };
- struct SyncData {
- Messages messages;
- std::optional<std::vector<std::string>> pinned_events;
- };
- using Rooms = std::vector<RoomData*>;
- bool message_contains_user_mention(Matrix *matrix, const Message *message, const std::string &username, const std::string &user_id);
- bool message_contains_user_mention(const BodyItem *body_item, const std::string &username, const std::string &user_id);
- bool message_is_timeline(Message *message);
- void body_set_selected_item_by_url(Body *body, const std::string &url);
- std::string create_transaction_id();
- enum class MatrixPageType {
- ROOM_LIST,
- CHAT
- };
- enum class LeaveType {
- LEAVE,
- KICKED,
- BANNED
- };
- struct MatrixNotification {
- RoomData *room;
- std::string event_id;
- std::string sender_user_id;
- std::string body; // Without reply formatting
- time_t timestamp; // The timestamp in milliseconds or 0
- bool read;
- };
- // All of methods in this class are called in the main (ui) thread
- class MatrixDelegate {
- public:
- virtual ~MatrixDelegate() = default;
- virtual void join_room(RoomData *room) = 0;
- virtual void leave_room(RoomData *room, const std::string &event_id, LeaveType leave_type, const std::string &reason) = 0;
- // Note: calling |room| methods inside this function is not allowed
- virtual void room_add_tag(RoomData *room, const std::string &tag) = 0;
- // Note: calling |room| methods inside this function is not allowed
- virtual void room_remove_tag(RoomData *room, const std::string &tag) = 0;
- virtual void room_add_new_messages(RoomData *room, const Messages &messages, bool is_initial_sync, MessageDirection message_dir) = 0;
- virtual void room_clear_messages(RoomData *room) = 0;
- virtual void add_invite(const std::string &room_id, const Invite &invite) = 0;
- virtual void remove_invite(const std::string &room_id) = 0;
- virtual void add_unread_notification(MatrixNotification notification) = 0;
- virtual void add_user(MatrixEventUserInfo user_info) = 0;
- virtual void remove_user(MatrixEventUserInfo user_info) = 0;
- virtual void set_user_info(MatrixEventUserInfo user_info) = 0;
- virtual void set_room_info(MatrixEventRoomInfo room_info) = 0;
- virtual void set_room_as_read(RoomData *room) = 0;
- };
- class Matrix;
- class MatrixRoomsPage;
- class MatrixRoomTagsPage;
- class MatrixInvitesPage;
- class MatrixChatPage;
- class MatrixNotificationsPage;
- using UsersByRoom = std::unordered_map<RoomData*, std::unordered_map<std::string, MatrixEventUserInfo>>;
- class MatrixQuickMedia : public MatrixDelegate {
- public:
- MatrixQuickMedia(Program *program, Matrix *matrix, MatrixRoomsPage *rooms_page, MatrixRoomTagsPage *room_tags_page, MatrixInvitesPage *invites_page, MatrixNotificationsPage *notifications_page);
- void join_room(RoomData *room) override;
- void leave_room(RoomData *room, const std::string &event_id, LeaveType leave_type, const std::string &reason) override;
- void room_add_tag(RoomData *room, const std::string &tag) override;
- void room_remove_tag(RoomData *room, const std::string &tag) override;
- void room_add_new_messages(RoomData *room, const Messages &messages, bool is_initial_sync, MessageDirection message_dir) override;
- void room_clear_messages(RoomData *room) override;
- void add_invite(const std::string &room_id, const Invite &invite) override;
- void remove_invite(const std::string &room_id) override;
- void add_unread_notification(MatrixNotification notification) override;
- void add_user(MatrixEventUserInfo user_info) override;
- void remove_user(MatrixEventUserInfo user_info) override;
- void set_user_info(MatrixEventUserInfo user_info) override;
- void set_room_info(MatrixEventRoomInfo room_info) override;
- void for_each_user_in_room(RoomData *room, std::function<void(const MatrixEventUserInfo&)> callback);
- void set_room_as_read(RoomData *room) override;
- Program *program;
- Matrix *matrix;
- MatrixChatPage *chat_page;
- MatrixRoomsPage *rooms_page;
- MatrixRoomTagsPage *room_tags_page;
- MatrixInvitesPage *invites_page;
- MatrixNotificationsPage *notifications_page;
- private:
- void update_room_description(RoomData *room, const Messages &new_messages, bool is_initial_sync);
- private:
- std::map<RoomData*, std::shared_ptr<BodyItem>> room_body_item_by_room;
- std::map<RoomData*, std::shared_ptr<Message>> last_message_by_room;
- std::map<RoomData*, int> unread_mention_count_by_room;
- std::unordered_set<std::string> notifications_shown;
- UsersByRoom users_by_room;
- };
- class MatrixRoomsPage : public Page {
- public:
- MatrixRoomsPage(Program *program, Body *body, std::string title, MatrixRoomTagsPage *room_tags_page, SearchBar *search_bar);
- ~MatrixRoomsPage() override;
- const char* get_title() const override { return title.c_str(); }
- PluginResult submit(const SubmitArgs &args, std::vector<Tab> &result_tabs) override;
- bool submit_is_async() const override { return false; }
- bool clear_search_after_submit() override { return true; }
- void add_body_item(std::shared_ptr<BodyItem> body_item);
- void move_room_to_top(RoomData *room);
- void remove_body_item_by_room_id(const std::string &room_id);
- void set_current_chat_page(MatrixChatPage *chat_page);
- void set_room_as_read(RoomData *room);
- void clear_search();
- MatrixQuickMedia *matrix_delegate = nullptr;
- Body *body = nullptr;
- private:
- std::string title;
- MatrixRoomTagsPage *room_tags_page = nullptr;
- MatrixChatPage *current_chat_page = nullptr;
- SearchBar *search_bar;
- };
- class MatrixRoomTagsPage : public Page {
- public:
- MatrixRoomTagsPage(Program *program, Body *body) : Page(program), body(body) {}
- const char* get_title() const override { return "Tags"; }
- PluginResult submit(const SubmitArgs &args, std::vector<Tab> &result_tabs) override;
- bool submit_is_async() const override { return false; }
- bool clear_search_after_submit() override { return true; }
- void add_room_body_item_to_tag(std::shared_ptr<BodyItem> body_item, const std::string &tag);
- void remove_room_body_item_from_tag(std::shared_ptr<BodyItem> body_item, const std::string &tag);
- void move_room_to_top(RoomData *room);
- void remove_body_item_by_room_id(const std::string &room_id);
- void set_current_rooms_page(MatrixRoomsPage *rooms_page);
- MatrixQuickMedia *matrix_delegate = nullptr;
- private:
- struct TagData {
- std::shared_ptr<BodyItem> tag_item;
- std::vector<std::shared_ptr<BodyItem>> room_body_items;
- };
- Body *body;
- std::map<std::string, TagData> tag_body_items_by_name;
- MatrixRoomsPage *current_rooms_page = nullptr;
- };
- class MatrixInvitesPage : public Page {
- public:
- MatrixInvitesPage(Program *program, Matrix *matrix, Body *body);
- const char* get_title() const override { return title.c_str(); }
- PluginResult submit(const SubmitArgs &args, std::vector<Tab> &result_tabs) override;
- bool submit_is_async() const override { return false; }
- bool clear_search_after_submit() override { return true; }
- void add_body_item(std::shared_ptr<BodyItem> body_item);
- void remove_body_item_by_room_id(const std::string &room_id);
- private:
- Matrix *matrix;
- Body *body;
- std::string title = "Invites (0)";
- size_t prev_invite_count = 0;
- };
- class MatrixInviteDetailsPage : public Page {
- public:
- MatrixInviteDetailsPage(Program *program, Matrix *matrix, MatrixInvitesPage *invites_page, std::string room_id, std::string title) :
- Page(program), matrix(matrix), invites_page(invites_page), room_id(std::move(room_id)), title(std::move(title)) {}
- const char* get_title() const override { return title.c_str(); }
- PluginResult submit(const SubmitArgs &args, std::vector<Tab> &result_tabs) override;
- Matrix *matrix;
- MatrixInvitesPage *invites_page;
- const std::string room_id;
- const std::string title;
- };
- class MatrixSettingsPage : public Page {
- public:
- MatrixSettingsPage(Program *program, Matrix *matrix) : Page(program), matrix(matrix) {}
- const char* get_title() const override { return "Settings"; }
- PluginResult submit(const SubmitArgs &args, std::vector<Tab> &result_tabs) override;
- private:
- Matrix *matrix;
- };
- class MatrixRoomInputPage : public Page {
- public:
- MatrixRoomInputPage(Program *program, Matrix *matrix) : Page(program), matrix(matrix) {}
- const char* get_title() const override { return "Enter the id of a room to join"; }
- PluginResult submit(const SubmitArgs &args, std::vector<Tab> &result_tabs) override;
- bool allow_submit_no_selection() const override { return true; }
- private:
- Matrix *matrix;
- };
- class MatrixCustomEmojiPage : public LazyFetchPage {
- public:
- MatrixCustomEmojiPage(Program *program, Matrix *matrix) : LazyFetchPage(program), matrix(matrix) {}
- const char* get_title() const override { return "Custom emoji"; }
- PluginResult submit(const SubmitArgs &args, std::vector<Tab> &result_tabs) override;
- PluginResult lazy_fetch(BodyItems &result_items) override;
- bool is_ready() override;
- private:
- Matrix *matrix;
- };
- class MatrixCustomEmojiRenameSelectPage : public LazyFetchPage {
- public:
- MatrixCustomEmojiRenameSelectPage(Program *program, Matrix *matrix) : LazyFetchPage(program), matrix(matrix) {}
- const char* get_title() const override { return "Select emoji to rename"; }
- PluginResult submit(const SubmitArgs &args, std::vector<Tab> &result_tabs) override;
- PluginResult lazy_fetch(BodyItems &result_items) override;
- bool submit_is_async() const override { return false; }
- bool reload_on_page_change() override { return true; }
- private:
- Matrix *matrix;
- };
- class MatrixCustomEmojiRenamePage : public Page {
- public:
- MatrixCustomEmojiRenamePage(Program *program, Matrix *matrix, std::string emoji_key) : Page(program), matrix(matrix), emoji_key(std::move(emoji_key)) {}
- const char* get_title() const override { return "Enter a new name for the emoji"; }
- PluginResult submit(const SubmitArgs &args, std::vector<Tab> &result_tabs) override;
- bool allow_submit_no_selection() const override { return true; }
- private:
- Matrix *matrix;
- std::string emoji_key;
- };
- class MatrixCustomEmojiDeletePage : public Page {
- public:
- MatrixCustomEmojiDeletePage(Program *program, Matrix *matrix, Body *body) : Page(program), matrix(matrix), body(body) {}
- const char* get_title() const override { return "Select emoji to delete"; }
- PluginResult submit(const SubmitArgs &args, std::vector<Tab> &result_tabs) override;
- private:
- Matrix *matrix;
- Body *body;
- };
- // Only play one video. TODO: Play all videos in room, as related videos?
- class MatrixVideoPage : public VideoPage {
- public:
- MatrixVideoPage(Program *program, std::string filename) : VideoPage(program, ""), filename(std::move(filename)) {}
- const char* get_title() const override;
- std::string get_filename() override;
- std::string get_download_url_for_clipboard(int max_height) override;
- private:
- std::string filename;
- };
- using MatrixRoomInfoUpdateCallback = std::function<void(const MatrixEventRoomInfo &room_info)>;
- class MatrixChatPage : public Page {
- public:
- MatrixChatPage(Program *program, std::string room_id, MatrixRoomsPage *rooms_page, std::string jump_to_event_id = "");
- ~MatrixChatPage();
- const char* get_title() const override { return ""; }
- PageTypez get_type() const override { return PageTypez::CHAT; }
- void add_user(MatrixEventUserInfo user_info);
- void remove_user(MatrixEventUserInfo user_info);
- void set_user_info(MatrixEventUserInfo user_info);
- void set_room_info(MatrixEventRoomInfo room_info);
- void set_current_room(RoomData *room, Body *users_body, MatrixRoomInfoUpdateCallback room_info_update_callback);
- size_t get_num_users_in_current_room() const;
- void set_room_as_read(RoomData *room);
- const std::string room_id;
- MatrixRoomsPage *rooms_page = nullptr;
- Body *chat_body = nullptr;
- bool messages_tab_visible = false;
- bool is_regular_navigation = true;
- const std::string jump_to_event_id;
- private:
- void add_user_to_body_by_user_info(const MatrixEventUserInfo &user_info);
- private:
- RoomData *current_room = nullptr;
- Body *users_body = nullptr;
- MatrixRoomInfoUpdateCallback room_info_update_callback;
- std::unordered_map<std::string, BodyItem*> user_body_item_by_user_id;
- };
- class MatrixRoomDirectoryPage : public Page {
- public:
- MatrixRoomDirectoryPage(Program *program, Matrix *matrix) : Page(program), matrix(matrix) {}
- const char* get_title() const override { return "Room directory"; }
- bool allow_submit_no_selection() const override { return true; }
- bool clear_search_after_submit() override { return true; }
- PluginResult submit(const SubmitArgs &args, std::vector<Tab> &result_tabs) override;
- private:
- Matrix *matrix;
- };
- class MatrixServerRoomListPage : public LazyFetchPage {
- public:
- MatrixServerRoomListPage(Program *program, Matrix *matrix, const std::string &server_name) : LazyFetchPage(program), matrix(matrix), server_name(server_name), current_page(0) {}
- const char* get_title() const override { return "Select a room to join"; }
- bool search_is_filter() override { return false; }
- PluginResult lazy_fetch(BodyItems &result_items) override;
- PluginResult get_page(const std::string &str, int page, BodyItems &result_items) override;
- SearchResult search(const std::string &str, BodyItems &result_items) override;
- PluginResult submit(const SubmitArgs &args, std::vector<Tab> &result_tabs) override;
- private:
- Matrix *matrix;
- const std::string server_name;
- std::string next_batch;
- std::string search_term;
- int current_page;
- };
- class MatrixNotificationsPage : public LazyFetchPage {
- public:
- MatrixNotificationsPage(Program *program, Matrix *matrix, Body *notifications_body, MatrixRoomsPage *all_rooms_page);
- const char* get_title() const override { return "Notifications"; }
- PluginResult submit(const SubmitArgs &args, std::vector<Tab>&) override;
- PluginResult get_page(const std::string &str, int page, BodyItems &result_items) override;
- PluginResult lazy_fetch(BodyItems &result_items) override;
- bool is_ready() override;
- bool submit_is_async() const override { return false; }
- bool clear_search_after_submit() override { return true; }
- void add_notification(MatrixNotification notification);
- void set_room_as_read(RoomData *room);
- private:
- bool has_fetched = false;
- Matrix *matrix;
- Body *notifications_body;
- MatrixRoomsPage *all_rooms_page;
- // room id[event_id[]]
- std::unordered_map<std::string, std::unordered_map<std::string, std::shared_ptr<BodyItem>>> room_notifications;
- // Notifications are here until the notifications has been fetched, so that page handler doesn't the notifications
- std::unordered_map<std::string, std::unordered_map<std::string, MatrixNotification>> pending_room_notifications;
- };
- class MatrixInviteUserPage : public Page {
- public:
- MatrixInviteUserPage(Program *program, Matrix *matrix, std::string room_id) : Page(program), matrix(matrix), room_id(std::move(room_id)) {}
- const char* get_title() const override { return "Invite user"; }
- bool search_is_filter() override { return false; }
- SearchResult search(const std::string &str, BodyItems &result_items) override;
- PluginResult submit(const SubmitArgs &args, std::vector<Tab> &result_tabs) override;
- bool allow_submit_no_selection() const override { return true; }
- private:
- Matrix *matrix;
- std::string room_id;
- };
- struct CustomEmoji {
- std::string url;
- mgl::vec2i size;
- };
- class Matrix {
- public:
- Matrix(bool matrix_instance_already_running);
- // TODO: Make this return the Matrix object instead, to force users to call start_sync
- bool start_sync(MatrixDelegate *delegate, bool &cached);
- void stop_sync();
- bool is_initial_sync_finished();
- // Returns true if initial sync failed, and |err_msg| is set to the error reason in that case
- bool did_initial_sync_fail(std::string &err_msg);
- bool has_finished_fetching_notifications() const;
- void get_room_sync_data(RoomData *room, SyncData &sync_data);
- void get_all_synced_room_messages(RoomData *room, Messages &messages);
- void get_all_pinned_events(RoomData *room, std::vector<std::string> &events);
- PluginResult get_messages_in_direction(RoomData *room, const std::string &token, MessageDirection message_dir, Messages &messages, std::string &new_token);
- PluginResult get_previous_room_messages(RoomData *room, Messages &messages, bool latest_messages = false, bool *reached_end = nullptr);
- PluginResult get_previous_notifications(std::function<void(const MatrixNotification&)> callback_func);
- void get_cached_notifications(std::function<void(const MatrixNotification&)> callback_func);
- // |url| should only be set when uploading media.
- // TODO: Make api better.
- PluginResult post_message(RoomData *room, const std::string &body, std::string &event_id_response, const std::optional<UploadInfo> &file_info, const std::optional<UploadInfo> &thumbnail_info, const std::string &msgtype = "", const std::string &custom_transaction_id = "");
- // |relates_to| is from |BodyItem.userdata| and is of type |Message*|
- // If |custom_transaction_id| is empty, then a new transaction id is generated
- PluginResult post_reply(RoomData *room, const std::string &body, void *relates_to, std::string &event_id_response, const std::string &custom_transaction_id = "", const std::optional<UploadInfo> &file_info = std::nullopt, const std::optional<UploadInfo> &thumbnail_info = std::nullopt);
- // |relates_to| is from |BodyItem.userdata| and is of type |Message*|
- PluginResult post_edit(RoomData *room, const std::string &body, void *relates_to, std::string &event_id_response, const std::string &custom_transaction_id = "");
- // |relates_to| is from |BodyItem.userdata| and is of type |Message*|
- PluginResult post_reaction(RoomData *room, const std::string &body, void *relates_to, std::string &event_id_response, const std::string &custom_transaction_id = "");
-
- // If filename is empty then the filename is extracted from filepath
- PluginResult post_file(RoomData *room, const std::string &filepath, std::string filename, std::string &event_id_response, std::string &err_msg, void *relates_to = nullptr);
- PluginResult upload_custom_emoji(const std::string &filepath, const std::string &key, std::string &mxc_url, std::string &err_msg);
- bool delete_custom_emoji(const std::string &key);
- bool rename_custom_emoji(const std::string &key, const std::string &new_key);
- bool does_custom_emoji_with_name_exist(const std::string &name);
- std::unordered_map<std::string, CustomEmoji> get_custom_emojis();
- 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(RoomData *room, void *message, std::string &err_msg);
- PluginResult pin_message(RoomData *room, const std::string &event_id);
- PluginResult unpin_message(RoomData *room, const std::string &event_id);
- PluginResult load_cached_session();
- PluginResult on_start_typing(RoomData *room);
- PluginResult on_stop_typing(RoomData *room);
- PluginResult set_read_marker(RoomData *room, const std::string &event_id, int64_t event_timestamp);
- PluginResult join_room(const std::string &room_id_or_name);
- PluginResult leave_room(const std::string &room_id);
- bool is_invite_silenced(const std::string &room_id, int64_t timestamp);
- void silence_invite(const std::string &room_id, int64_t timestamp);
- // If |since| is empty, then the first page is fetched
- PluginResult get_public_rooms(const std::string &server, const std::string &search_term, const std::string &since, BodyItems &rooms, std::string &next_batch);
- PluginResult search_user(const std::string &search_term, unsigned int limit, BodyItems &result_items);
- PluginResult invite_user(const std::string &room_id, const std::string &user_id);
- // |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(int64_t *upload_size);
- std::shared_ptr<UserInfo> get_me(RoomData *room);
- const std::string& get_homeserver_domain() const;
- std::string get_remote_homeserver_url() const;
- // Returns nullptr if message cant be found. Note: cached
- std::shared_ptr<Message> get_message_by_id(RoomData *room, const std::string &event_id);
- PluginResult get_message_context(RoomData *room, const std::string &event_id, std::shared_ptr<Message> &message, Messages &before_messages, Messages &after_messages, std::string &before_token, std::string &after_token);
- void clear_previous_messages_token(RoomData *room);
- RoomData* get_room_by_id(const std::string &id);
- void update_room_users(RoomData *room);
- std::string get_media_url(const std::string &mxc_id);
- RoomExtraData& get_room_extra_data(RoomData *room);
- void append_system_message(RoomData *room_data, std::shared_ptr<Message> message);
- std::string body_to_formatted_body(RoomData *room, const std::string &body);
- void on_exit_room(RoomData *room);
- void async_decrypt_message(std::shared_ptr<MatrixChatBodyDecryptJob> decrypt_job);
- MatrixDelegate* get_delegate();
- bool is_another_instance_running() const { return matrix_instance_already_running; }
- void mark_other_notification_as_read(const std::string &event_id);
- bool is_other_notification_read(const std::string &event_id) const;
- // Calls the |MatrixDelegate| pending events.
- // Should be called from the main (ui) thread
- void update();
- private:
- void trigger_event(RoomData *room, MatrixEventType type, MatrixEventUserInfo user_info);
- void trigger_event(RoomData *room, MatrixEventType type, MatrixEventRoomInfo room_info);
- void formatted_body_add_line(RoomData *room, std::string &formatted_body, const std::string &line_str, const std::unordered_map<std::string, CustomEmoji> &custom_emojis);
- void replace_mentions(RoomData *room, std::string &text);
- std::string create_formatted_body_for_message_reply(RoomData *room, const Message *message, const std::string &body);
- std::string create_formatted_body_for_message_edit(RoomData *room, const Message *replied_to_message, const std::string &body);
- PluginResult set_pinned_events(RoomData *room, const std::vector<std::string> &pinned_events, bool is_add);
- PluginResult set_qm_last_read_message_timestamp(RoomData *room, int64_t timestamp);
- bool load_qm_read_markers_from_account_data();
- PluginResult parse_sync_response(const rapidjson::Document &root, bool initial_sync);
- PluginResult parse_notifications(const rapidjson::Value ¬ifications_json, std::function<void(const MatrixNotification&)> callback_func);
- PluginResult parse_sync_account_data(const rapidjson::Value &account_data_json);
- PluginResult parse_sync_room_data(const rapidjson::Value &rooms_json, bool initial_sync);
- void add_new_invites();
- void parse_custom_emoji(const rapidjson::Value &custom_emoji_json);
- void load_custom_emoji_from_cache();
- void load_other_notifications();
- PluginResult get_previous_room_messages(RoomData *room_data, bool latest_messages, size_t &num_new_messages, bool *reached_end = nullptr);
- void events_add_user_info(const rapidjson::Value &events_json, RoomData *room_data, int64_t timestamp);
- std::shared_ptr<UserInfo> parse_user_info(const rapidjson::Value &json, const std::string &user_id, RoomData *room_data, int64_t timestamp);
- void events_set_user_read_marker(const rapidjson::Value &events_json, RoomData *room_data, std::shared_ptr<UserInfo> &me);
- // Returns the number of messages added
- size_t events_add_messages(const rapidjson::Value &events_json, RoomData *room_data, MessageDirection message_dir, bool has_unread_notifications);
- void events_set_room_info(const rapidjson::Value &events_json, RoomData *room_data, int64_t timestamp);
- void set_room_info_to_users_if_empty(RoomData *room, const std::string &room_creator_user_id);
- void events_add_pinned_events(const rapidjson::Value &events_json, RoomData *room_data);
- void events_add_room_to_tags(const rapidjson::Value &events_json, RoomData *room_data);
- void add_invites(const rapidjson::Value &invite_json);
- void remove_rooms(const rapidjson::Value &leave_json);
- PluginResult get_pinned_events(RoomData *room, std::vector<std::string> &pinned_events);
- std::shared_ptr<Message> parse_message_event(const rapidjson::Value &event_item_json, RoomData *room_data);
- PluginResult upload_file(const std::string &filepath, std::string filename, UploadInfo &file_info, UploadInfo &thumbnail_info, std::string &err_msg, bool upload_thumbnail = true, bool bypass_proxy = false);
- void add_room(std::unique_ptr<RoomData> room);
- void remove_room(const std::string &room_id);
- // Returns false if an invite to the room already exists
- bool set_invite(const std::string &room_id, Invite invite);
- // Returns true if an invite for |room_id| exists
- bool remove_invite(const std::string &room_id);
- void set_next_batch(std::string new_next_batch, bool set_initial_sync);
- std::string get_next_batch();
- void set_next_notifications_token(std::string new_next_token);
- std::string get_next_notifications_token();
- std::shared_ptr<UserInfo> get_user_by_id(RoomData *room, const std::string &user_id, bool *is_new_user = nullptr, bool create_if_not_found = true);
- std::string get_filter_cached();
- void load_silenced_invites();
- private:
- MessageQueue<std::function<void()>> ui_thread_tasks;
- std::vector<std::unique_ptr<RoomData>> rooms;
- std::unordered_map<std::string, size_t> room_data_by_id; // value is an index into |rooms|
- std::recursive_mutex room_data_mutex;
- std::string my_user_id;
- std::string access_token;
- std::string homeserver;
- std::string homeserver_domain;
- std::string well_known_base_url;
- std::optional<int64_t> upload_limit;
- std::string next_batch;
- std::string next_notifications_token;
- std::mutex next_batch_mutex;
- bool initial_sync_finished = false;
- bool matrix_instance_already_running = false;
- std::unordered_map<std::string, Invite> invites;
- std::mutex invite_mutex;
- std::vector<MatrixNotification> notifications;
- std::unordered_set<std::string> notifications_by_event_id;
- std::mutex notifications_mutex;
- std::thread sync_thread;
- std::thread notification_thread;
- bool sync_running = false;
- bool sync_failed = false;
- bool finished_fetching_notifications = false;
- std::string sync_fail_reason;
- MatrixDelegate *delegate = nullptr;
- std::optional<std::string> filter_cached;
- std::vector<std::unique_ptr<RoomData>> invite_rooms;
- std::unordered_map<std::string, CustomEmoji> custom_emoji_by_key;
- std::unordered_set<std::string> silenced_invites;
- std::unordered_map<std::string, int64_t> qm_read_markers_by_room_cache;
- std::unordered_set<std::string> other_notifications_read;
- MessageQueue<std::shared_ptr<MatrixChatBodyDecryptJob>> decrypt_task;
- std::thread decrypt_thread;
- std::unordered_map<std::string, RoomExtraData> room_extra_data_by_id;
- };
- }
|