From d274d3a6dfc0864ec6a44e7d6948c2d873eb6f76 Mon Sep 17 00:00:00 2001 From: dec05eba Date: Fri, 11 Nov 2022 10:50:39 +0100 Subject: Add image (custom emoji) alt text for copy-pasting, limit custom emoji size in room description, change max size to 32, 32, cache custom emoji locally --- src/plugins/Matrix.cpp | 101 +++++++++++++++++++++++++++++++++++-------------- 1 file changed, 72 insertions(+), 29 deletions(-) (limited to 'src/plugins') diff --git a/src/plugins/Matrix.cpp b/src/plugins/Matrix.cpp index fd5026a..e53cf71 100644 --- a/src/plugins/Matrix.cpp +++ b/src/plugins/Matrix.cpp @@ -28,7 +28,7 @@ namespace QuickMedia { static const mgl::vec2i thumbnail_max_size(600, 337); - static const mgl::vec2i custom_emoji_max_size(64, 64); + static const mgl::vec2i custom_emoji_max_size(32, 32); static const char* SERVICE_NAME = "matrix"; static const char* OTHERS_ROOM_TAG = "tld.name.others"; // Filter without account data. TODO: We include pinned events but limit events to 1. That means if the last event is a pin, @@ -640,8 +640,8 @@ namespace QuickMedia { return message->body; } - static std::string message_to_room_description_text(Matrix *matrix, Message *message) { - std::string body = strip(formatted_text_to_qm_text(matrix, message->body.c_str(), message->body.size(), true)); + static std::string message_to_room_description_text(Matrix *matrix, Message *message, mgl::vec2i image_max_size = mgl::vec2i(0, 0)) { + std::string body = strip(formatted_text_to_qm_text(matrix, message->body.c_str(), message->body.size(), true, image_max_size)); if(message->type == MessageType::REACTION) return "Reacted with: " + body; else if(message->related_event_type == RelatedEventType::REPLY) @@ -708,7 +708,7 @@ namespace QuickMedia { room_desc += "Unread: "; if(last_unread_message) - room_desc += 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); + room_desc += 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); int unread_notification_count = room->unread_notification_count; if(unread_notification_count > 0 && set_room_as_unread) { @@ -728,7 +728,7 @@ namespace QuickMedia { rooms_page->move_room_to_top(room); room_tags_page->move_room_to_top(room); } else if(last_new_message) { - room->body_item->set_description(extract_first_line_remove_newline_elipses(matrix->message_get_author_displayname(last_new_message.get()), AUTHOR_MAX_LENGTH) + ": " + message_to_room_description_text(matrix, last_new_message.get())); + room->body_item->set_description(extract_first_line_remove_newline_elipses(matrix->message_get_author_displayname(last_new_message.get()), AUTHOR_MAX_LENGTH) + ": " + message_to_room_description_text(matrix, last_new_message.get(), custom_emoji_max_size)); room->body_item->set_description_color(get_theme().faded_text_color); room->body_item->set_description_max_lines(3); @@ -1503,12 +1503,12 @@ namespace QuickMedia { sync_is_cache = false; sync_running = true; + load_custom_emoji_from_cache(); + sync_thread = std::thread([this, matrix_cache_dir]() { sync_is_cache = true; - bool cache_available = false; FILE *sync_cache_file = fopen(matrix_cache_dir.data.c_str(), "rb"); if(sync_cache_file) { - cache_available = true; rapidjson::Document doc; char read_buffer[8192]; rapidjson::FileReadStream is(sync_cache_file, read_buffer, sizeof(read_buffer)); @@ -1949,6 +1949,47 @@ namespace QuickMedia { return PluginResult::OK; } + void Matrix::parse_custom_emoji(const rapidjson::Value &custom_emoji_json) { + if(!custom_emoji_json.IsObject()) + return; + + std::lock_guard lock(room_data_mutex); + custom_emoji_by_key.clear(); + for(auto const &emoji_json : custom_emoji_json.GetObject()) { + if(!emoji_json.name.IsString() || !emoji_json.value.IsObject()) + continue; + + const rapidjson::Value &url_json = GetMember(emoji_json.value, "url"); + const rapidjson::Value &width_json = GetMember(emoji_json.value, "width"); + const rapidjson::Value &height_json = GetMember(emoji_json.value, "height"); + if(!url_json.IsString()) + continue; + + CustomEmoji custom_emoji; + custom_emoji.url = url_json.GetString(); + if(width_json.IsInt() && height_json.IsInt()) { + custom_emoji.size.x = width_json.GetInt(); + custom_emoji.size.y = height_json.GetInt(); + } + custom_emoji_by_key[emoji_json.name.GetString()] = std::move(custom_emoji); + } + } + + void Matrix::load_custom_emoji_from_cache() { + std::string custom_emoji_file_content; + if(file_get_content(get_cache_dir().join("matrix").join("custom_emoji.json"), custom_emoji_file_content) != 0) + return; + + rapidjson::Document json_root; + rapidjson::ParseResult parse_result = json_root.Parse(custom_emoji_file_content.c_str(), custom_emoji_file_content.size()); + if(parse_result.IsError()) { + fprintf(stderr, "Warning: failed to parse custom_emoji.json, error: %d\n", parse_result.Code()); + return; + } + + parse_custom_emoji(json_root); + } + PluginResult Matrix::parse_sync_account_data(const rapidjson::Value &account_data_json) { if(!account_data_json.IsObject()) return PluginResult::OK; @@ -2004,26 +2045,12 @@ namespace QuickMedia { } } } else if(strcmp(type_json.GetString(), "qm.emoji") == 0) { - std::lock_guard lock(room_data_mutex); - for(auto const &emoji_json : content_json.GetObject()) { - if(!emoji_json.name.IsString() || !emoji_json.value.IsObject()) - continue; - - const rapidjson::Value &url_json = GetMember(emoji_json.value, "url"); - const rapidjson::Value &width_json = GetMember(emoji_json.value, "width"); - const rapidjson::Value &height_json = GetMember(emoji_json.value, "height"); - if(!url_json.IsString()) - continue; - - CustomEmoji custom_emoji; - custom_emoji.url = url_json.GetString(); - if(width_json.IsInt() && height_json.IsInt()) { - custom_emoji.size.x = width_json.GetInt(); - custom_emoji.size.y = height_json.GetInt(); - } - custom_emoji_by_key[emoji_json.name.GetString()] = std::move(custom_emoji); - } - } + parse_custom_emoji(content_json); + 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())); + } } return PluginResult::OK; @@ -2585,9 +2612,11 @@ namespace QuickMedia { bool supports_syntax_highlight = false; bool inside_img_tag = false; std::string_view img_src; + std::string_view img_alt; mgl::vec2i img_size; mgl::Color font_color = mgl::Color(255, 255, 255, 255); Matrix *matrix = nullptr; + mgl::vec2i image_max_size; }; static int accumulate_string(char *data, int size, void *userdata) { @@ -2615,6 +2644,7 @@ namespace QuickMedia { } else if(html_parser->tag_name.size == 3 && memcmp(html_parser->tag_name.data, "img", 3) == 0) { parse_userdata.inside_img_tag = true; parse_userdata.img_src = std::string_view(); + parse_userdata.img_alt = std::string_view(); parse_userdata.img_size = { 0, 0 }; } break; @@ -2637,7 +2667,9 @@ namespace QuickMedia { // TODO: Better solution when size not given? if(img_size.x == 0 || img_size.y == 0) img_size = custom_emoji_max_size; - parse_userdata.result += Text::formatted_image(parse_userdata.matrix->get_media_url(image_url), false, img_size); + if(parse_userdata.image_max_size.x > 0 && parse_userdata.image_max_size.y > 0) + img_size = clamp_to_size(img_size, parse_userdata.image_max_size); + parse_userdata.result += Text::formatted_image(parse_userdata.matrix->get_media_url(image_url), false, img_size, std::string(parse_userdata.img_alt)); } parse_userdata.inside_img_tag = false; } @@ -2659,6 +2691,8 @@ namespace QuickMedia { } else if(html_parser->attribute_key.size == 6 && memcmp(html_parser->attribute_key.data, "height", 6) == 0) { const std::string height(html_parser->attribute_value.data, html_parser->attribute_value.size); parse_userdata.img_size.y = atoi(height.c_str()); + } else if(html_parser->attribute_key.size == 3 && memcmp(html_parser->attribute_key.data, "alt", 3) == 0) { + parse_userdata.img_alt = std::string_view(html_parser->attribute_value.data, html_parser->attribute_value.size); } } else if(!parse_userdata.allow_formatted_text && parse_userdata.inside_img_tag && parse_userdata.mx_reply_depth == 0 && html_parser->attribute_key.size == 3 && memcmp(html_parser->attribute_key.data, "alt", 3) == 0) { std::string text_to_add(html_parser->attribute_value.data, html_parser->attribute_value.size); @@ -2712,11 +2746,12 @@ namespace QuickMedia { return 0; } - std::string formatted_text_to_qm_text(Matrix *matrix, const char *str, size_t size, bool allow_formatted_text) { + std::string formatted_text_to_qm_text(Matrix *matrix, const char *str, size_t size, bool allow_formatted_text, mgl::vec2i image_max_size) { FormattedTextParseUserdata parse_userdata; parse_userdata.allow_formatted_text = allow_formatted_text; parse_userdata.supports_syntax_highlight = is_program_executable_by_name("source-highlight"); parse_userdata.matrix = matrix; + parse_userdata.image_max_size = image_max_size; html_parser_parse(str, size, formattext_text_parser_callback, &parse_userdata); return std::move(parse_userdata.result); } @@ -4387,6 +4422,8 @@ namespace QuickMedia { if(download_result != DownloadResult::OK) return download_result_to_plugin_result(download_result); + // TODO: Is this even needed? (including in other /account_data/qm.emoji functions), wont we get qm.emoji update in sync? + file_overwrite_atomic(get_cache_dir().join("matrix").join("custom_emoji.json"), std::string(buffer.GetString(), buffer.GetSize())); std::lock_guard lock(room_data_mutex); custom_emoji_by_key[key] = std::move(custom_emoji); return PluginResult::OK; @@ -4428,6 +4465,7 @@ namespace QuickMedia { if(download_result != DownloadResult::OK) return false; + file_overwrite_atomic(get_cache_dir().join("matrix").join("custom_emoji.json"), std::string(buffer.GetString(), buffer.GetSize())); std::lock_guard lock(room_data_mutex); auto it = custom_emoji_by_key.find(key); if(it != custom_emoji_by_key.end()) @@ -4445,6 +4483,10 @@ namespace QuickMedia { if(it == custom_emoji_list_copy.end()) return false; + auto existing_new_it = custom_emoji_list_copy.find(new_key); + if(existing_new_it != custom_emoji_list_copy.end()) + return false; + auto custom_emoji_copy = it->second; custom_emoji_list_copy.erase(it); custom_emoji_list_copy[new_key] = std::move(custom_emoji_copy); @@ -4473,6 +4515,7 @@ namespace QuickMedia { if(download_result != DownloadResult::OK) return false; + file_overwrite_atomic(get_cache_dir().join("matrix").join("custom_emoji.json"), std::string(buffer.GetString(), buffer.GetSize())); std::lock_guard lock(room_data_mutex); auto it = custom_emoji_by_key.find(key); if(it != custom_emoji_by_key.end()) { -- cgit v1.2.3