From 01f23292bf2451a0c7b9ada88c6314dcb09509b1 Mon Sep 17 00:00:00 2001 From: dec05eba Date: Sat, 12 Nov 2022 19:05:13 +0100 Subject: Matrix: add option to silence invites without declining them or hiding them (ignore) --- include/Path.hpp | 5 +++++ include/StringUtils.hpp | 4 ++-- plugins/Matrix.hpp | 5 +++++ src/StringUtils.cpp | 16 +++++++------- src/plugins/Matrix.cpp | 56 +++++++++++++++++++++++++++++++++++++++++++++---- 5 files changed, 73 insertions(+), 13 deletions(-) diff --git a/include/Path.hpp b/include/Path.hpp index 3d849e2..c9047be 100644 --- a/include/Path.hpp +++ b/include/Path.hpp @@ -22,6 +22,11 @@ namespace QuickMedia { return *this; } + Path& append(const char *str) { + data += str; + return *this; + } + // Includes extension const char* filename() const { size_t index = data.rfind('/'); diff --git a/include/StringUtils.hpp b/include/StringUtils.hpp index fb16d7a..2a01f54 100644 --- a/include/StringUtils.hpp +++ b/include/StringUtils.hpp @@ -7,8 +7,8 @@ namespace QuickMedia { // Return false to stop iterating using StringSplitCallback = std::function; - void string_split(const std::string &str, const std::string &delimiter, StringSplitCallback callback_func); - void string_split(const std::string &str, char delimiter, StringSplitCallback callback_func); + void string_split(const std::string &str, const std::string &delimiter, StringSplitCallback callback_func, bool include_empty = true); + void string_split(const std::string &str, char delimiter, StringSplitCallback callback_func, bool include_empty = true); // Returns the number of replaced substrings size_t string_replace_all(std::string &str, char old_char, char new_char); // Returns the number of replaced substrings diff --git a/plugins/Matrix.hpp b/plugins/Matrix.hpp index 61239fe..788050b 100644 --- a/plugins/Matrix.hpp +++ b/plugins/Matrix.hpp @@ -671,6 +671,9 @@ namespace QuickMedia { 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); @@ -754,6 +757,7 @@ namespace QuickMedia { void clear_sync_cache_for_new_sync(); std::shared_ptr 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> ui_thread_tasks; std::vector> rooms; @@ -793,5 +797,6 @@ namespace QuickMedia { std::unordered_set my_events_transaction_ids; std::unordered_map custom_emoji_by_key; + std::unordered_set silenced_invites; }; } \ No newline at end of file diff --git a/src/StringUtils.cpp b/src/StringUtils.cpp index 927c6e1..bc59730 100644 --- a/src/StringUtils.cpp +++ b/src/StringUtils.cpp @@ -3,15 +3,17 @@ namespace QuickMedia { template - static void string_split_t(const std::string &str, const T &delimiter, StringSplitCallback callback_func) { + static void string_split_t(const std::string &str, const T &delimiter, StringSplitCallback callback_func, bool include_empty) { size_t index = 0; while(index < str.size()) { size_t new_index = str.find(delimiter, index); if(new_index == std::string::npos) new_index = str.size(); - if(!callback_func(str.data() + index, new_index - index)) - break; + if(include_empty || new_index - index > 0) { + if(!callback_func(str.data() + index, new_index - index)) + break; + } if constexpr(std::is_same::value) index = new_index + 1; @@ -20,12 +22,12 @@ namespace QuickMedia { } } - void string_split(const std::string &str, const std::string &delimiter, StringSplitCallback callback_func) { - string_split_t(str, delimiter, callback_func); + void string_split(const std::string &str, const std::string &delimiter, StringSplitCallback callback_func, bool include_empty) { + string_split_t(str, delimiter, callback_func, include_empty); } - void string_split(const std::string &str, char delimiter, StringSplitCallback callback_func) { - string_split_t(str, delimiter, callback_func); + void string_split(const std::string &str, char delimiter, StringSplitCallback callback_func, bool include_empty) { + string_split_t(str, delimiter, callback_func, include_empty); } size_t string_replace_all(std::string &str, char old_char, char new_char) { diff --git a/src/plugins/Matrix.cpp b/src/plugins/Matrix.cpp index a821158..133193b 100644 --- a/src/plugins/Matrix.cpp +++ b/src/plugins/Matrix.cpp @@ -510,8 +510,12 @@ namespace QuickMedia { body_item->thumbnail_mask_type = ThumbnailMaskType::CIRCLE; body_item->thumbnail_size = mgl::vec2i(32, 32); body_item->set_timestamp(invite.timestamp); + body_item->userdata = body_item.get(); + const bool silenced_invite = matrix->is_invite_silenced(room_id, invite.timestamp); + if(silenced_invite) + body_item->add_reaction("Silenced", nullptr); invites_page->add_body_item(std::move(body_item)); - if(invite.new_invite) { + if(invite.new_invite && !silenced_invite) { show_notification("QuickMedia matrix - " + invite.room_name, "You were invited to " + invite.room_name + " by " + invited_by_display_name + " (" + invite.invited_by->user_id + ")"); } } @@ -960,6 +964,10 @@ namespace QuickMedia { auto body = create_body(); body->append_item(BodyItem::create("Accept")); body->append_item(BodyItem::create("Decline")); + body->append_item(BodyItem::create("Silence")); + body->for_each_item([&](auto &body_item) { + body_item->userdata = args.userdata; + }); result_tabs.push_back(Tab{std::move(body), std::make_unique(program, matrix, this, args.url, "Invite to " + title), nullptr}); return PluginResult::OK; } @@ -981,6 +989,9 @@ namespace QuickMedia { } else { show_notification("QuickMedia", "Failed to decline the room invite", Urgency::CRITICAL); } + } else if(args.title == "Silence") { + BodyItem *body_item = (BodyItem*)args.userdata; + matrix->silence_invite(room_id, body_item->get_timestamp()); } program->set_go_to_previous_page(); @@ -1544,6 +1555,7 @@ namespace QuickMedia { sync_is_cache = false; sync_running = true; + load_silenced_invites(); load_custom_emoji_from_cache(); sync_thread = std::thread([this, matrix_cache_dir]() { @@ -2065,13 +2077,13 @@ namespace QuickMedia { RoomData *room = get_room_by_id(std::string(room_id_json.GetString(), room_id_json.GetStringLength())); if(!room) { - fprintf(stderr, "Warning: got m.direct for room %s that we haven't created yet\n", room_id_json.GetString()); + //fprintf(stderr, "Warning: got m.direct for room %s that we haven't created yet\n", room_id_json.GetString()); continue; } auto user = get_user_by_id(room, std::string(it.name.GetString(), it.name.GetStringLength()), nullptr, false); if(!user) { - fprintf(stderr, "Warning: got m.direct for user %s that doesn't exist in the room %s yet\n", it.name.GetString(), room_id_json.GetString()); + //fprintf(stderr, "Warning: got m.direct for user %s that doesn't exist in the room %s yet\n", it.name.GetString(), room_id_json.GetString()); continue; } @@ -2090,7 +2102,9 @@ namespace QuickMedia { rapidjson::StringBuffer buffer; rapidjson::Writer writer(buffer); content_json.Accept(writer); - file_overwrite_atomic(get_cache_dir().join("matrix").join("custom_emoji.json"), std::string(buffer.GetString(), buffer.GetSize())); + Path matrix_dir = get_cache_dir().join("matrix"); + create_directory_recursive(matrix_dir); + file_overwrite_atomic(matrix_dir.join("custom_emoji.json"), std::string(buffer.GetString(), buffer.GetSize())); } } @@ -5242,6 +5256,28 @@ namespace QuickMedia { return download_result_to_plugin_result(download_result); } + bool Matrix::is_invite_silenced(const std::string &room_id, int64_t timestamp) { + return silenced_invites.find(room_id + "|" + std::to_string(timestamp)) != silenced_invites.end(); + } + + void Matrix::silence_invite(const std::string &room_id, int64_t timestamp) { + std::string line = room_id + "|" + std::to_string(timestamp); + auto it = silenced_invites.find(line); + if(it != silenced_invites.end()) + return; + + Path matrix_dir = get_storage_dir().join("matrix"); + create_directory_recursive(matrix_dir); + Path silenced_invites_path = matrix_dir.join("silenced_invites"); + FILE *file = fopen(silenced_invites_path.data.c_str(), "ab"); + if(!file) + return; + + line += "\n"; + fwrite(line.data(), 1, line.size(), file); + fclose(file); + } + PluginResult Matrix::get_public_rooms(const std::string &server, const std::string &search_term, const std::string &since, BodyItems &rooms, std::string &next_batch) { rapidjson::Document filter_data(rapidjson::kObjectType); if(!search_term.empty()) @@ -5733,6 +5769,18 @@ namespace QuickMedia { #endif } + void Matrix::load_silenced_invites() { + std::string file_content; + if(file_get_content(get_storage_dir().join("matrix").join("silenced_invites"), file_content) != 0) + return; + + silenced_invites.clear(); + string_split(file_content, '\n', [&](const char *str, size_t size) { + silenced_invites.insert(std::string(str, size)); + return true; + }, false); + } + void Matrix::update() { mgl::Clock timer; std::optional> task; -- cgit v1.2.3