From 59c4a651ab9d795c0d1788a8ddf29949a56b1ab9 Mon Sep 17 00:00:00 2001 From: dec05eba Date: Tue, 15 Nov 2022 19:36:10 +0100 Subject: Matrix: use a local cache of latest read marker timestamps to try and avoid many unread message notices --- TODO | 4 ++- plugins/Matrix.hpp | 3 ++ src/plugins/Matrix.cpp | 81 +++++++++++++++++++++++++++++++++++++++++--------- 3 files changed, 73 insertions(+), 15 deletions(-) diff --git a/TODO b/TODO index d708f6d..beea673 100644 --- a/TODO +++ b/TODO @@ -242,4 +242,6 @@ Text editing should take into consideration FORMATTED_TEXT_START/FORMATTED_TEXT_ Matrix autocomplete for emoji (and custom emoji) and option show different sizes (for custom emoji). Nicer custom emoji upload options - Selecting name, size, cropping. Add option to copy somebody else custom emoji in matrix by pressing enter to bring up message option and in that a list of every emoji in the message should be added with "Add (emoji)..." text to add the emoji. -Detect invidious urls too, even the ones that dont have watch?v=.. this could be done by downloading the webpage (maybe only HEAD?) to check if it's invidious. \ No newline at end of file +Detect invidious urls too, even the ones that dont have watch?v=.. this could be done by downloading the webpage (maybe only HEAD?) to check if it's invidious. +Atomic file operations should use a random generated name instead of .tmp, because multiple instances of quickmedia may be running and they may try to write to the same file at the same time. In such cases they can also write to the same temporary file at the same time. +TODO: https://github.com/matrix-org/synapse/issues/14444. \ No newline at end of file diff --git a/plugins/Matrix.hpp b/plugins/Matrix.hpp index 77ef252..671f718 100644 --- a/plugins/Matrix.hpp +++ b/plugins/Matrix.hpp @@ -722,6 +722,8 @@ namespace QuickMedia { PluginResult set_pinned_events(RoomData *room, const std::vector &pinned_events, bool is_add); PluginResult set_qm_last_read_message_timestamp(RoomData *room, int64_t timestamp); + void load_qm_read_markers_from_cache(); + void update_room_qm_read_markers_in_cache(const std::string &room_id, 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 ¬ifications_json, std::function callback_func); @@ -797,5 +799,6 @@ namespace QuickMedia { std::unordered_map custom_emoji_by_key; std::unordered_set silenced_invites; + std::unordered_map qm_read_markers_by_room_cache; }; } \ No newline at end of file diff --git a/src/plugins/Matrix.cpp b/src/plugins/Matrix.cpp index ba4111d..d3c6f81 100644 --- a/src/plugins/Matrix.cpp +++ b/src/plugins/Matrix.cpp @@ -730,7 +730,7 @@ namespace QuickMedia { if(!room->body_item) return; - if(!sync_is_cache && (last_unread_message || room->unread_notification_count > 0)) { + if(!sync_is_cache && last_unread_message) { bool is_window_focused = program->is_window_focused(); RoomData *current_room = program->get_current_chat_room(); Body *chat_body = chat_page ? chat_page->chat_body : nullptr; @@ -748,20 +748,13 @@ namespace QuickMedia { room->body_item->set_description_color(get_theme().faded_text_color); } - if(last_unread_message) { - if(!room_desc.empty()) - room_desc += '\n'; - } + if(!room_desc.empty()) + room_desc += '\n'; if(!unread_mentions && set_room_as_unread) room_desc += "Unread: "; - if(last_unread_message) { - room->latest_message = extract_first_line_remove_newline_elipses(matrix->message_get_author_displayname(last_unread_message), AUTHOR_MAX_LENGTH) + ": " + message_to_room_description_text(matrix, last_unread_message, custom_emoji_max_size); - } else { - room->latest_message.clear(); - } - + room->latest_message = extract_first_line_remove_newline_elipses(matrix->message_get_author_displayname(last_unread_message), AUTHOR_MAX_LENGTH) + ": " + message_to_room_description_text(matrix, last_unread_message, custom_emoji_max_size); room_desc += room->latest_message; room->body_item->set_description(std::move(room_desc)); @@ -1557,6 +1550,7 @@ namespace QuickMedia { load_silenced_invites(); load_custom_emoji_from_cache(); + load_qm_read_markers_from_cache(); sync_thread = std::thread([this, matrix_cache_dir]() { sync_is_cache = true; @@ -1764,6 +1758,8 @@ namespace QuickMedia { filter_cached.reset(); finished_fetching_notifications = false; custom_emoji_by_key.clear(); + silenced_invites.clear(); + qm_read_markers_by_room_cache.clear(); } bool Matrix::is_initial_sync_finished() { @@ -4815,6 +4811,9 @@ namespace QuickMedia { Path custom_emoji_path = get_cache_dir().join("matrix").join("custom_emoji.json"); remove(custom_emoji_path.data.c_str()); + + Path read_markers_path = get_cache_dir().join("matrix").join("read_markers.json"); + remove(read_markers_path.data.c_str()); //Path filter_cache_path = get_storage_dir().join("matrix").join("filter"); //remove(filter_cache_path.data.c_str()); for_files_in_dir(get_cache_dir().join("matrix").join("events"), [](const Path &filepath, FileType) { @@ -5203,11 +5202,62 @@ namespace QuickMedia { { "--data-binary", buffer.GetString() } }; - room->read_marker_event_timestamp = timestamp; - std::string server_response; DownloadResult download_result = download_to_string(homeserver + "/_matrix/client/r0/user/" + my_user_id + "/rooms/" + room->id + "/account_data/qm.last_read_message_timestamp", server_response, std::move(additional_args), true); - return download_result_to_plugin_result(download_result); + if(download_result != DownloadResult::OK) + return download_result_to_plugin_result(download_result); + + room->read_marker_event_timestamp = timestamp; + update_room_qm_read_markers_in_cache(room->id, timestamp); + return PluginResult::OK; + } + + // TODO: Separate file for each room? + void Matrix::load_qm_read_markers_from_cache() { + std::string file_content; + if(file_get_content(get_cache_dir().join("matrix").join("read_markers.json"), file_content) != 0) + return; + + rapidjson::Document json_root; + rapidjson::ParseResult parse_result = json_root.Parse(file_content.c_str(), file_content.size()); + if(parse_result.IsError()) { + fprintf(stderr, "Warning: failed to parse read_markers.json, error: %d\n", parse_result.Code()); + return; + } + + if(!json_root.IsObject()) { + fprintf(stderr, "Warning: failed to parse read_markers.json\n"); + return; + } + + qm_read_markers_by_room_cache.clear(); + std::lock_guard lock(room_data_mutex); + for(auto const &obj : json_root.GetObject()) { + if(!obj.name.IsString() || !obj.value.IsInt64()) + continue; + + std::string room_id = std::string(obj.name.GetString(), obj.name.GetStringLength()); + auto room_it = room_data_by_id.find(room_id); + if(room_it != room_data_by_id.end()) + rooms[room_it->second]->read_marker_event_timestamp = obj.value.GetInt64(); + qm_read_markers_by_room_cache[std::move(room_id)] = obj.value.GetInt64(); + } + } + + void Matrix::update_room_qm_read_markers_in_cache(const std::string &room_id, int64_t timestamp) { + load_qm_read_markers_from_cache(); // TODO: Remove this? + qm_read_markers_by_room_cache[room_id] = timestamp; + + rapidjson::Document request_data(rapidjson::kObjectType); + for(const auto &[key, val] : qm_read_markers_by_room_cache) { + request_data.AddMember(rapidjson::Value(key.c_str(), request_data.GetAllocator()).Move(), val, request_data.GetAllocator()); + } + + rapidjson::StringBuffer buffer; + rapidjson::Writer writer(buffer); + request_data.Accept(writer); + + file_overwrite_atomic(get_cache_dir().join("matrix").join("read_markers.json"), std::string(buffer.GetString(), buffer.GetSize())); } PluginResult Matrix::join_room(const std::string &room_id_or_name) { @@ -5587,6 +5637,9 @@ namespace QuickMedia { std::lock_guard lock(room_data_mutex); room->index = rooms.size(); room_data_by_id.insert(std::make_pair(room->id, room->index)); + auto read_marker_it = qm_read_markers_by_room_cache.find(room->id); + if(read_marker_it != qm_read_markers_by_room_cache.end()) + room->read_marker_event_timestamp = read_marker_it->second; rooms.push_back(std::move(room)); } -- cgit v1.2.3