From c65a57e884de51cade584e3f01c7c5627aa6ebd8 Mon Sep 17 00:00:00 2001 From: dec05eba Date: Mon, 7 Nov 2022 14:23:49 +0100 Subject: Matrix: fix edit being replaced with old message when re-entering the room (without restart) Add quote colors to 4chan, monospace for codeblocks --- TODO | 4 ++-- plugins/Matrix.hpp | 7 ++++--- src/QuickMedia.cpp | 40 ++++++++++++++++++++++------------------ src/plugins/Fourchan.cpp | 19 +++++++++++-------- src/plugins/Matrix.cpp | 39 ++++++++++++++++++++++++++------------- tests/main.cpp | 7 ------- 6 files changed, 65 insertions(+), 51 deletions(-) diff --git a/TODO b/TODO index 75a710b..6a4ad06 100644 --- a/TODO +++ b/TODO @@ -2,7 +2,6 @@ Somehow deal with youtube banning ip when searching too often. When continuing to read manga from a different page from the first and there is no cache for the chapter, then start downloading from the current page instead of page 1. Show progress of manga in the history tab (current chapter out of total chapters). Animate page navigation. -Add greentext support for 4chan quotes. Add support for special formatting for posts by admins on imageboards. For image boards, track (You)'s and show notification when somebody replies to your post. Go to next chapter when reaching the end of the chapter in image endless mode. @@ -246,4 +245,5 @@ Downloading files should take into account the remove mime type if available. Fa Text images atlas. Do not render invalid unicode. Use matrix "from" with proper cache. -Text editing should take into consideration FORMATTED_TEXT_START/FORMATTED_TEXT_END. \ No newline at end of file +Text editing should take into consideration FORMATTED_TEXT_START/FORMATTED_TEXT_END. +Matrix edit message (happens even with matrix in master branch if it's to a reply) (by us, local) gets reverted back to the previous message when re-entering the room. Why? \ No newline at end of file diff --git a/plugins/Matrix.hpp b/plugins/Matrix.hpp index 61814ac..4afe28e 100644 --- a/plugins/Matrix.hpp +++ b/plugins/Matrix.hpp @@ -21,10 +21,9 @@ namespace QuickMedia { static const int AUTHOR_MAX_LENGTH = 48; std::string remove_reply_formatting(const std::string &str); - std::string message_get_body_remove_formatting(Message *message); 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); - std::string formatted_text_to_qm_text(const char *str, size_t size); + std::string formatted_text_to_qm_text(const char *str, size_t size, bool allow_formatted_text); struct TimestampedDisplayData { std::string data; @@ -234,7 +233,8 @@ namespace QuickMedia { using Rooms = std::vector; - bool message_contains_user_mention(const std::string &msg, const std::string &username); + bool message_contains_user_mention(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(); @@ -619,6 +619,7 @@ namespace QuickMedia { void append_system_message(RoomData *room_data, std::shared_ptr message); std::string body_to_formatted_body(RoomData *room, const std::string &body); + void on_exit_room(RoomData *room); // Calls the |MatrixDelegate| pending events. // Should be called from the main (ui) thread diff --git a/src/QuickMedia.cpp b/src/QuickMedia.cpp index 80ae146..d72f465 100644 --- a/src/QuickMedia.cpp +++ b/src/QuickMedia.cpp @@ -5307,7 +5307,7 @@ namespace QuickMedia { *body_item->embedded_item = *related_body_item; body_item->embedded_item->embedded_item = nullptr; body_item->embedded_item->reactions.clear(); - if(message->user->user_id != my_user_id && ((related_body_item->userdata && static_cast(related_body_item->userdata)->user.get() == me) || message_contains_user_mention(body_item->get_description(), my_display_name) || message_contains_user_mention(body_item->get_description(), my_user_id))) + if(message->user->user_id != my_user_id && ((related_body_item->userdata && static_cast(related_body_item->userdata)->user.get() == me) || message_contains_user_mention(body_item, my_display_name, my_user_id))) body_item->set_description_color(get_theme().attention_alert_text_color, true); else body_item->set_description_color(get_theme().text_color); @@ -5322,7 +5322,7 @@ namespace QuickMedia { static std::shared_ptr message_to_body_item(RoomData *room, Message *message, const std::string &my_display_name, const std::string &my_user_id) { auto body_item = BodyItem::create(""); body_item->set_author(extract_first_line_remove_newline_elipses(room->get_user_display_name(message->user), AUTHOR_MAX_LENGTH)); - body_item->set_description(strip(formatted_text_to_qm_text(message->body.c_str(), message->body.size()))); + body_item->set_description(strip(formatted_text_to_qm_text(message->body.c_str(), message->body.size(), true))); body_item->set_timestamp(message->timestamp); if(!message->thumbnail_url.empty()) { body_item->thumbnail_url = message->thumbnail_url; @@ -5352,7 +5352,7 @@ namespace QuickMedia { body_item->set_description_color(get_theme().faded_text_color); body_item->thumbnail_url.clear(); } - if(message->user->user_id != my_user_id && (message_contains_user_mention(body_item->get_description(), my_display_name) || message_contains_user_mention(body_item->get_description(), my_user_id))) + if(message->user->user_id != my_user_id && message_contains_user_mention(body_item.get(), my_display_name, my_user_id)) body_item->set_description_color(get_theme().attention_alert_text_color, true); return body_item; } @@ -5651,16 +5651,16 @@ namespace QuickMedia { } else { // TODO: Properly check reply message objects for mention of user instead of message data, but only when synapse fixes that notifications // are not triggered by reply to a message with our display name/user id. - Message *reply_to_message = static_cast(body_item->userdata); - std::string qm_formatted_text = formatted_text_to_qm_text(reply_to_message->body.c_str(), reply_to_message->body.size()); + Message *edited_message_ref = static_cast(body_item->userdata); + std::string qm_formatted_text = formatted_text_to_qm_text(message->body.c_str(), message->body.size(), true); body_item->set_description(std::move(qm_formatted_text)); - if(message->user != me && (message_contains_user_mention(reply_to_message->body, my_display_name) || message_contains_user_mention(reply_to_message->body, me->user_id))) + if(message->user != me && message_contains_user_mention(message.get(), my_display_name, me->user_id)) body_item->set_description_color(get_theme().attention_alert_text_color, true); else body_item->set_description_color(get_theme().text_color); - message->replaces = reply_to_message; - reply_to_message->replaced_by = message; + message->replaces = edited_message_ref; + edited_message_ref->replaced_by = message; } it = unreferenced_events.erase(it); } else { @@ -5692,16 +5692,16 @@ namespace QuickMedia { } else { // TODO: Properly check reply message objects for mention of user instead of message data, but only when synapse fixes that notifications // are not triggered by reply to a message with our display name/user id. - Message *reply_to_message = static_cast(body_item->userdata); - std::string qm_formatted_text = formatted_text_to_qm_text(reply_to_message->body.c_str(), reply_to_message->body.size()); + Message *edited_message_ref = static_cast(body_item->userdata); + std::string qm_formatted_text = formatted_text_to_qm_text(message->body.c_str(), message->body.size(), true); body_item->set_description(std::move(qm_formatted_text)); - if(message->user != me && (message_contains_user_mention(reply_to_message->body, my_display_name) || message_contains_user_mention(reply_to_message->body, me->user_id))) + if(message->user != me && message_contains_user_mention(message.get(), my_display_name, me->user_id)) body_item->set_description_color(get_theme().attention_alert_text_color, true); else body_item->set_description_color(get_theme().text_color); - message->replaces = reply_to_message; - reply_to_message->replaced_by = message; + message->replaces = edited_message_ref; + edited_message_ref->replaced_by = message; } } else { unreferenced_events.push_back(message); @@ -6194,14 +6194,17 @@ namespace QuickMedia { tabs[MESSAGES_TAB_INDEX].body->select_last_item(); return true; } else if(chat_state == ChatState::EDITING) { - void *related_to_message = currently_operating_on_item->userdata; + Message *related_to_message = (Message*)currently_operating_on_item->userdata; message->related_event_type = RelatedEventType::EDIT; - message->related_event_id = static_cast(related_to_message)->event_id; + message->related_event_id = related_to_message->event_id; + // TODO: + //related_to_message->replaced_by = message; + //message->replaces = related_to_message; size_t body_item_index = 0; auto body_item = find_body_item_by_event_id(tabs[MESSAGES_TAB_INDEX].body->get_items().data(), tabs[MESSAGES_TAB_INDEX].body->get_items().size(), message->related_event_id, &body_item_index); if(body_item) { const std::string formatted_text = matrix->body_to_formatted_body(current_room, text); - std::string qm_formatted_text = formatted_text_to_qm_text(formatted_text.c_str(), formatted_text.size()); + std::string qm_formatted_text = formatted_text_to_qm_text(formatted_text.c_str(), formatted_text.size(), true); auto body_item_shared_ptr = tabs[MESSAGES_TAB_INDEX].body->get_item_by_index(body_item_index); body_item_shared_ptr->set_description(std::move(qm_formatted_text)); @@ -6298,7 +6301,7 @@ namespace QuickMedia { if(related_body_item) { *body_item = *related_body_item; body_item->reactions.clear(); - if(message_contains_user_mention(related_body_item->get_description(), current_room->get_user_display_name(me)) || message_contains_user_mention(related_body_item->get_description(), me->user_id)) + if(message_contains_user_mention(related_body_item.get(), current_room->get_user_display_name(me), me->user_id)) body_item->set_description_color(get_theme().attention_alert_text_color, true); else body_item->set_description_color(get_theme().text_color); @@ -6698,7 +6701,7 @@ namespace QuickMedia { }); }; - auto cleanup_tasks = [&set_read_marker_future, &fetch_message_future, &fetch_users_future, &typing_state_queue, &typing_state_thread, &post_task_queue, &provisional_message_queue, &fetched_messages_set, &sent_messages, &pending_sent_replies, &post_thread, &tabs, MESSAGES_TAB_INDEX, PINNED_TAB_INDEX, USERS_TAB_INDEX]() { + auto cleanup_tasks = [&]() { set_read_marker_future.cancel(); fetch_message_future.cancel(); fetch_users_future.cancel(); @@ -6730,6 +6733,7 @@ namespace QuickMedia { } //tabs.clear(); + matrix->on_exit_room(current_room); }; // TODO: Remove this once synapse bug has been resolved where /sync does not include user info for new messages when using message filter that limits number of messages for initial sync, diff --git a/src/plugins/Fourchan.cpp b/src/plugins/Fourchan.cpp index 4cfaeaa..c58d942 100644 --- a/src/plugins/Fourchan.cpp +++ b/src/plugins/Fourchan.cpp @@ -260,35 +260,38 @@ namespace QuickMedia { comment_text += std::move(cp.text); break; case CommentPiece::Type::QUOTE: - comment_text += std::move(cp.text); + comment_text += Text::formatted_text(std::move(cp.text), mgl::Color(120, 153, 34), FORMATTED_TEXT_FLAG_COLOR); break; case CommentPiece::Type::QUOTE_CONTINUE: - comment_text += std::move(cp.text); + comment_text += Text::formatted_text(std::move(cp.text), mgl::Color(120, 153, 34), FORMATTED_TEXT_FLAG_COLOR); break; case CommentPiece::Type::QUOTELINK: { - comment_text += std::move(cp.text); auto it = comment_by_postno.find(cp.quote_postnumber); if(it == comment_by_postno.end()) { // TODO: Link this quote to a 4chan archive that still has the quoted comment (if available) - comment_text += " (Dead)"; + comment_text += Text::formatted_text(std::move(cp.text) + " (DEAD)", get_theme().attention_alert_text_color, FORMATTED_TEXT_FLAG_COLOR); } else { result_items[body_item_index]->replies_to.push_back(it->second); result_items[it->second]->replies.push_back(body_item_index); result_items[it->second]->add_reaction(">>" + result_items[body_item_index]->post_number, nullptr, get_theme().replies_text_color); + if(it->second == 0) + comment_text += Text::formatted_text(std::move(cp.text) + " (OP)", get_theme().attention_alert_text_color, FORMATTED_TEXT_FLAG_COLOR); + else + comment_text += Text::formatted_text(std::move(cp.text), get_theme().attention_alert_text_color, FORMATTED_TEXT_FLAG_COLOR); } break; } case CommentPiece::Type::DEADLINK: // TODO: Link this quote to a 4chan archive that still has the quoted comment (if available) - comment_text += std::move(cp.text) + " (Dead)"; + comment_text += Text::formatted_text(std::move(cp.text) + " (DEAD)", get_theme().attention_alert_text_color, FORMATTED_TEXT_FLAG_COLOR); break; case CommentPiece::Type::CROSSBOARD_LINK: // TODO: Link this to another thread and allow navigating to it - comment_text += std::move(cp.text) + " (Cross-thread)"; + comment_text += Text::formatted_text(std::move(cp.text) + " (Cross-thread)", get_theme().attention_alert_text_color, FORMATTED_TEXT_FLAG_COLOR); break; case CommentPiece::Type::CODEBLOCK: - // TODO: Use a different colored background and use a monospace font - comment_text += std::move(cp.text); + // TODO: Use a different colored background + comment_text += Text::formatted_text(std::move(cp.text), mgl::Color(255, 255, 255, 255), FORMATTED_TEXT_FLAG_MONOSPACE); break; } }); diff --git a/src/plugins/Matrix.cpp b/src/plugins/Matrix.cpp index 14a5bbe..a9284f5 100644 --- a/src/plugins/Matrix.cpp +++ b/src/plugins/Matrix.cpp @@ -599,7 +599,7 @@ namespace QuickMedia { } static std::string message_to_room_description_text(Message *message) { - std::string body = strip(formatted_text_to_qm_text(message->body.c_str(), message->body.size())); + std::string body = strip(formatted_text_to_qm_text(message->body.c_str(), message->body.size(), true)); if(message->type == MessageType::REACTION) return "Reacted with: " + body; else if(message->related_event_type == RelatedEventType::REPLY) @@ -2102,7 +2102,7 @@ namespace QuickMedia { } // TODO: Do not show notification if mention is a reply to somebody else that replies to me? also dont show notification everytime a mention is edited - bool message_contains_user_mention(const std::string &msg, const std::string &username) { + static bool message_contains_user_mention(const std::string &msg, const std::string &username) { if(msg.empty() || username.empty()) return false; @@ -2129,6 +2129,16 @@ namespace QuickMedia { return false; } + bool message_contains_user_mention(const Message *message, const std::string &username, const std::string &user_id) { + const std::string formatted_text = formatted_text_to_qm_text(message->body.c_str(), message->body.size(), false); + return message_contains_user_mention(formatted_text, username) || message_contains_user_mention(formatted_text, user_id); + } + + bool message_contains_user_mention(const BodyItem *body_item, const std::string &username, const std::string &user_id) { + const std::string formatted_text = Text::to_printable_string(body_item->get_description()); + return message_contains_user_mention(formatted_text, username) || message_contains_user_mention(formatted_text, user_id); + } + bool message_is_timeline(Message *message) { return message->type >= MessageType::TEXT && message->type <= MessageType::FILE; } @@ -2186,8 +2196,10 @@ namespace QuickMedia { for(auto &message : new_messages) { // TODO: Is @room ok? shouldn't we also check if the user has permission to do @room? (only when notifications are limited to @mentions) // TODO: Is comparing against read marker timestamp ok enough? - if(me && message->timestamp > read_marker_message_timestamp) - message->notification_mentions_me = message_contains_user_mention(message->body, my_display_name) || message_contains_user_mention(message->body, me->user_id) || message_contains_user_mention(message->body, "@room"); + if(me && message->timestamp > read_marker_message_timestamp) { + std::string message_str = formatted_text_to_qm_text(message->body.c_str(), message->body.size(), false); + message->notification_mentions_me = message_contains_user_mention(message_str, my_display_name) || message_contains_user_mention(message_str, me->user_id) || message_contains_user_mention(message_str, "@room"); + } } } @@ -2277,6 +2289,7 @@ namespace QuickMedia { bool inside_font_tag = false; bool font_tag_has_custom_color = false; bool inside_code_tag = false; + bool allow_formatted_text = false; mgl::Color font_color = mgl::Color(255, 255, 255, 255); }; @@ -2327,7 +2340,7 @@ namespace QuickMedia { if(parse_userdata.inside_code_tag) formatted_text_flags |= FORMATTED_TEXT_FLAG_MONOSPACE; - if(formatted_text_flags != FORMATTED_TEXT_FLAG_NONE) + if(formatted_text_flags != FORMATTED_TEXT_FLAG_NONE && parse_userdata.allow_formatted_text) parse_userdata.result += Text::formatted_text(text_to_add, parse_userdata.font_color, formatted_text_flags); else parse_userdata.result += std::move(text_to_add); @@ -2338,8 +2351,9 @@ namespace QuickMedia { return 0; } - std::string formatted_text_to_qm_text(const char *str, size_t size) { + std::string formatted_text_to_qm_text(const char *str, size_t size, bool allow_formatted_text) { FormattedTextParseUserdata parse_userdata; + parse_userdata.allow_formatted_text = allow_formatted_text; html_parser_parse(str, size, formattext_text_parser_callback, &parse_userdata); return std::move(parse_userdata.result); } @@ -3429,6 +3443,10 @@ namespace QuickMedia { return formatted_body; } + void Matrix::on_exit_room(RoomData *room) { + my_events_transaction_ids.clear(); + } + PluginResult Matrix::post_message(RoomData *room, const std::string &body, std::string &event_id_response, const std::optional &file_info, const std::optional &thumbnail_info, const std::string &msgtype) { std::string transaction_id = create_transaction_id(); if(transaction_id.empty()) @@ -3520,17 +3538,12 @@ namespace QuickMedia { if(msg_begin != std::string::npos) return str.substr(msg_begin + 2); } + } else { + return formatted_text_to_qm_text(str.c_str(), str.size(), false); } return str; } - std::string message_get_body_remove_formatting(Message *message) { - if(message->related_event_type == RelatedEventType::REPLY || message->related_event_type == RelatedEventType::EDIT) - return remove_reply_formatting(message->body); - else - return message->body; - } - static std::string block_quote(const std::string &str) { std::string result; for(char c : str) { diff --git a/tests/main.cpp b/tests/main.cpp index c076e14..41ef45d 100644 --- a/tests/main.cpp +++ b/tests/main.cpp @@ -2,7 +2,6 @@ #include #include "../include/NetUtils.hpp" #include "../plugins/utils/EpisodeNameParser.hpp" -#include "../plugins/Matrix.hpp" #include "../generated/Emoji.hpp" #define assert_fail(str) do { fprintf(stderr, "Assert failed on line %d, reason: %s\n", __LINE__, (str)); exit(1); } while(0) @@ -113,11 +112,5 @@ int main() { assert_equals(emoji_sequence_length, 4); assert_equals(emoji_sequence_byte_length, 13); - const std::string formatted_text = "\n

>amazin
>asd

\n
\n

feaf

\n"; - const std::string qm_text = formatted_text_to_qm_text(formatted_text.c_str(), formatted_text.size()); - for(char c : qm_text) { - fprintf(stderr, "%c(%02x) ", c, *(uint8_t*)&c); - } - return 0; } -- cgit v1.2.3