From 80b48b270ed66e3557b98d9fc8e82ad868bcde80 Mon Sep 17 00:00:00 2001
From: dec05eba <dec05eba@protonmail.com>
Date: Thu, 20 May 2021 22:33:32 +0200
Subject: Add notifications tab to matrix

---
 plugins/Matrix.hpp | 48 +++++++++++++++++++++++++++++++++++++++++-------
 plugins/Page.hpp   |  2 ++
 2 files changed, 43 insertions(+), 7 deletions(-)

(limited to 'plugins')

diff --git a/plugins/Matrix.hpp b/plugins/Matrix.hpp
index bff89b2..bcb4058 100644
--- a/plugins/Matrix.hpp
+++ b/plugins/Matrix.hpp
@@ -227,6 +227,15 @@ namespace QuickMedia {
         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:
@@ -244,7 +253,7 @@ namespace QuickMedia {
         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(RoomData *room, std::string event_id, std::string sender, std::string body) = 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;
@@ -258,12 +267,13 @@ namespace QuickMedia {
     class MatrixRoomTagsPage;
     class MatrixInvitesPage;
     class MatrixChatPage;
+    class MatrixNotificationsPage;
 
     using UsersByRoom = std::unordered_multimap<RoomData*, MatrixEventUserInfo>;
 
     class MatrixQuickMedia : public MatrixDelegate {
     public:
-        MatrixQuickMedia(Program *program, Matrix *matrix, MatrixRoomsPage *rooms_page, MatrixRoomTagsPage *room_tags_page, MatrixInvitesPage *invites_page);
+        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, LeaveType leave_type, const std::string &reason) override;
@@ -274,13 +284,15 @@ namespace QuickMedia {
         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(RoomData *room, std::string event_id, std::string sender, std::string body) 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 for_each_user_in_room(RoomData *room, std::function<void(const MatrixEventUserInfo&)> callback);
 
+        void set_room_as_read(RoomData *room);
+
         void clear_data() override;
 
         Program *program;
@@ -289,6 +301,7 @@ namespace QuickMedia {
         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, bool sync_is_cache);
     private:
@@ -315,6 +328,8 @@ namespace QuickMedia {
 
         void set_current_chat_page(MatrixChatPage *chat_page);
 
+        void set_room_as_read(RoomData *room);
+
         void clear_data();
 
         MatrixQuickMedia *matrix_delegate = nullptr;
@@ -419,6 +434,8 @@ namespace QuickMedia {
         void set_current_room(RoomData *room, Body *users_body);
         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;
         bool should_clear_data = false;
@@ -458,14 +475,21 @@ namespace QuickMedia {
         int current_page;
     };
 
-    class MatrixNotificationsPage : public Page {
+    class MatrixNotificationsPage : public LazyFetchPage {
     public:
-        MatrixNotificationsPage(Program *program, Body *notifications_body) : Page(program), notifications_body(notifications_body) {}
-        const char* get_title() const override { return "Notifications (0)"; }
+        MatrixNotificationsPage(Program *program, Matrix *matrix, Body *notifications_body);
+        const char* get_title() const override { return "Notifications"; }
         PluginResult submit(const std::string&, const std::string&, std::vector<Tab>&) override {
             return PluginResult::OK;
         }
+        PluginResult get_page(const std::string &str, int page, BodyItems &result_items) override;
+        PluginResult lazy_fetch(BodyItems &result_items) override;
+        bool is_ready() override;
+
+        void add_notification(MatrixNotification notification);
+        void set_room_as_read(RoomData *room);
     private:
+        Matrix *matrix;
         Body *notifications_body;
     };
 
@@ -477,11 +501,14 @@ namespace QuickMedia {
         bool is_initial_sync_finished() const;
         // 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_previous_room_messages(RoomData *room, Messages &messages, bool latest_messages = false);
+        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.
@@ -546,7 +573,7 @@ namespace QuickMedia {
         PluginResult set_qm_last_read_message_timestamp(RoomData *room, int64_t timestamp);
 
         PluginResult parse_sync_response(const rapidjson::Document &root, bool is_additional_messages_sync, bool initial_sync);
-        PluginResult parse_notifications(const rapidjson::Value &notifications_json);
+        PluginResult parse_notifications(const rapidjson::Value &notifications_json, std::function<void(const MatrixNotification&)> callback_func);
         PluginResult parse_sync_account_data(const rapidjson::Value &account_data_json, std::optional<std::set<std::string>> &dm_rooms);
         PluginResult parse_sync_room_data(const rapidjson::Value &rooms_json, bool is_additional_messages_sync, bool initial_sync);
         PluginResult get_previous_room_messages(RoomData *room_data, bool latest_messages, size_t &num_new_messages);
@@ -571,6 +598,8 @@ namespace QuickMedia {
         bool remove_invite(const std::string &room_id);
         void set_next_batch(std::string new_next_batch);
         std::string get_next_batch();
+        void set_next_notifications_token(std::string new_next_token);
+        std::string get_next_notifications_token();
         void clear_sync_cache_for_new_sync();
         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();
@@ -585,11 +614,15 @@ namespace QuickMedia {
         std::string homeserver_domain;
         std::optional<int> upload_limit;
         std::string next_batch;
+        std::string next_notifications_token;
         std::mutex next_batch_mutex;
 
         std::unordered_map<std::string, Invite> invites;
         std::mutex invite_mutex;
 
+        std::vector<MatrixNotification> notifications;
+        std::mutex notifications_mutex;
+
         std::thread sync_thread;
         std::thread sync_additional_messages_thread;
         std::thread notification_thread;
@@ -597,6 +630,7 @@ namespace QuickMedia {
         bool sync_running = false;
         bool sync_failed = false;
         bool sync_is_cache = false;
+        bool finished_fetching_notifications = false;
         std::string sync_fail_reason;
         MatrixDelegate *delegate = nullptr;
         std::optional<std::string> filter_cached;
diff --git a/plugins/Page.hpp b/plugins/Page.hpp
index be6eb76..db27fae 100644
--- a/plugins/Page.hpp
+++ b/plugins/Page.hpp
@@ -51,6 +51,8 @@ namespace QuickMedia {
         virtual bool is_lazy_fetch_page() const { return false; }
         // Note: If submit is done without any selection, then the search term is sent as the |title|, not |url|. Submit will only be sent if the input text is not empty or if an item is selected
         virtual bool allow_submit_no_selection() const { return false; }
+        // This is used to delay loading of the page. For example if the page relies on an external factor to start loading
+        virtual bool is_ready() { return true; }
 
         // This is called both when first navigating to page and when going back to page
         virtual void on_navigate_to_page(Body *body) { (void)body; }
-- 
cgit v1.2.3-70-g09d2