diff options
-rw-r--r-- | include/QuickMedia.hpp | 2 | ||||
-rw-r--r-- | plugins/MangaGeneric.hpp | 8 | ||||
-rw-r--r-- | plugins/Matrix.hpp | 12 | ||||
-rw-r--r-- | src/Body.cpp | 2 | ||||
-rw-r--r-- | src/QuickMedia.cpp | 29 | ||||
-rw-r--r-- | src/plugins/MangaGeneric.cpp | 34 | ||||
-rw-r--r-- | src/plugins/Manganelo.cpp | 6 | ||||
-rw-r--r-- | src/plugins/Matrix.cpp | 41 | ||||
-rw-r--r-- | src/plugins/Soundcloud.cpp | 1 |
9 files changed, 96 insertions, 39 deletions
diff --git a/include/QuickMedia.hpp b/include/QuickMedia.hpp index b387968..9729c4b 100644 --- a/include/QuickMedia.hpp +++ b/include/QuickMedia.hpp @@ -112,7 +112,7 @@ namespace QuickMedia { void image_continuous_page(MangaImagesPage *images_page); void image_board_thread_page(ImageBoardThreadPage *thread_page, Body *thread_body); void chat_login_page(); - bool chat_page(MatrixChatPage *matrix_chat_page, RoomData *current_room, std::vector<Tab> &room_tabs, int room_selected_tab, TabAssociatedData &room_tab_associated_data); + bool chat_page(MatrixChatPage *matrix_chat_page, RoomData *current_room, std::vector<Tab> &room_tabs, int room_selected_tab); void after_matrix_login_page(); enum class LoadImageResult { diff --git a/plugins/MangaGeneric.hpp b/plugins/MangaGeneric.hpp index 4c99909..98d06d8 100644 --- a/plugins/MangaGeneric.hpp +++ b/plugins/MangaGeneric.hpp @@ -18,6 +18,11 @@ namespace QuickMedia { const char *url_contains = nullptr; }; + struct DescriptionQuery { + const char *html_query = nullptr; + const char *field_name = nullptr; + }; + // If |field_contains| is null, then any matching query is added. If |field_name| is "text", then the inner text is used. struct ThumbnailQuery { const char *html_query = nullptr; @@ -98,6 +103,8 @@ namespace QuickMedia { // This is required. MangaGenericSearchPage& text_handler(std::vector<TextQuery> queries); // This is optional. + MangaGenericSearchPage& description_handler(std::vector<DescriptionQuery> queries); + // This is optional. MangaGenericSearchPage& thumbnail_handler(std::vector<ThumbnailQuery> queries); // If |url_contains| is null, then any matching query is added. If |title_field| is "text", then the inner text is used. @@ -132,6 +139,7 @@ namespace QuickMedia { bool fail_on_http_error; SearchQuery search_query; std::vector<TextQuery> text_queries; + std::vector<DescriptionQuery> description_queries; std::vector<ThumbnailQuery> thumbnail_queries; ListChaptersQuery list_chapters_query; ListPageQuery list_page_query; diff --git a/plugins/Matrix.hpp b/plugins/Matrix.hpp index 974238f..0659983 100644 --- a/plugins/Matrix.hpp +++ b/plugins/Matrix.hpp @@ -235,7 +235,7 @@ namespace QuickMedia { virtual void add_unread_notification(RoomData *room, std::string event_id, std::string sender, std::string body) = 0; - virtual void update(MatrixPageType page_type) { (void)page_type; } + virtual void update(MatrixPageType page_type, Body *chat_body, bool messages_tab_visible) { (void)page_type; } virtual void clear_data() = 0; }; @@ -261,7 +261,7 @@ namespace QuickMedia { void add_unread_notification(RoomData *room, std::string event_id, std::string sender, std::string body) override; - void update(MatrixPageType page_type) override; + void update(MatrixPageType page_type, Body *chat_body, bool messages_tab_visible) override; void clear_data() override; @@ -271,8 +271,8 @@ namespace QuickMedia { MatrixRoomTagsPage *room_tags_page; MatrixInvitesPage *invites_page; private: - void update_room_description(RoomData *room, Messages &new_messages, bool is_initial_sync, bool sync_is_cache); - void update_pending_room_messages(MatrixPageType page_type); + void update_room_description(RoomData *room, Messages &new_messages, bool is_initial_sync, bool sync_is_cache, Body *chat_body, bool messages_tab_visible); + void update_pending_room_messages(MatrixPageType page_type, Body *chat_body, bool messages_tab_visible); private: struct RoomMessagesData { Messages messages; @@ -440,9 +440,11 @@ namespace QuickMedia { void update() override; const std::string room_id; - MatrixQuickMedia *matrix_delegate = nullptr; MatrixRoomsPage *rooms_page = nullptr; bool should_clear_data = false; + + Body *chat_body = nullptr; + bool messages_tab_visible = false; }; class MatrixRoomDirectoryPage : public Page { diff --git a/src/Body.cpp b/src/Body.cpp index c174188..0971e00 100644 --- a/src/Body.cpp +++ b/src/Body.cpp @@ -605,7 +605,7 @@ namespace QuickMedia { else if(offset_to_bottom > 0.0f) page_scroll += offset_to_bottom; } else { - if((attach_side == AttachSide::TOP && first_item_fully_visible) || (first_item_fully_visible && !last_item_fully_visible)) + if((attach_side == AttachSide::TOP && first_item_fully_visible)/* || (first_item_fully_visible && !last_item_fully_visible)*/) page_scroll -= offset_to_top; else if((attach_side == AttachSide::BOTTOM && last_item_fully_visible) || (last_item_fully_visible && !first_item_fully_visible)) page_scroll += offset_to_bottom; diff --git a/src/QuickMedia.cpp b/src/QuickMedia.cpp index f85f2da..0e8e197 100644 --- a/src/QuickMedia.cpp +++ b/src/QuickMedia.cpp @@ -1422,12 +1422,14 @@ namespace QuickMedia { int selected_index = tabs[selected_tab].body->get_selected_item(); video_content_page(tabs[selected_tab].page.get(), static_cast<VideoPage*>(new_tabs[0].page.get()), selected_item->get_title(), false, tabs[selected_tab].body->items, selected_index, &tab_associated_data[selected_tab].fetched_page, tab_associated_data[selected_tab].update_search_text); } else if(new_tabs.size() == 1 && new_tabs[0].page->get_type() == PageTypez::CHAT) { - body_set_selected_item(tabs[selected_tab].body.get(), selected_item.get()); + //body_set_selected_item(tabs[selected_tab].body.get(), selected_item.get()); current_page = PageType::CHAT; current_chat_room = matrix->get_room_by_id(selected_item->url); + MatrixRoomsPage *rooms_page = static_cast<MatrixRoomsPage*>(tabs[selected_tab].page.get()); while(window.isOpen()) { - bool move_room = chat_page(static_cast<MatrixChatPage*>(new_tabs[0].page.get()), current_chat_room, tabs, selected_tab, tab_associated_data[selected_tab]); + auto matrix_chat_page = std::make_unique<MatrixChatPage>(this, current_chat_room->id, rooms_page); + bool move_room = chat_page(matrix_chat_page.get(), current_chat_room, tabs, selected_tab); if(!move_room) break; @@ -3550,7 +3552,7 @@ namespace QuickMedia { user_update_display_info(body_item->embedded_item.get(), room, (Message*)body_item->embedded_item->userdata); } - bool Program::chat_page(MatrixChatPage *matrix_chat_page, RoomData *current_room, std::vector<Tab> &room_tabs, int room_selected_tab, TabAssociatedData &room_tab_associated_data) { + bool Program::chat_page(MatrixChatPage *matrix_chat_page, RoomData *current_room, std::vector<Tab> &room_tabs, int room_selected_tab) { assert(current_room); assert(strcmp(plugin_name, "matrix") == 0); if(!current_room) { @@ -3595,6 +3597,9 @@ namespace QuickMedia { const int MESSAGES_TAB_INDEX = 1; //const int USERS_TAB_INDEX = 2; + matrix_chat_page->chat_body = tabs[MESSAGES_TAB_INDEX].body.get(); + matrix_chat_page->messages_tab_visible = true; + int selected_tab = MESSAGES_TAB_INDEX; bool is_window_focused = window.hasFocus(); @@ -4193,18 +4198,20 @@ namespace QuickMedia { }); }; + bool remove_unread_marker = false; // TODO: How about instead fetching all messages we have, not only the visible ones? also fetch with multiple threads. - tabs[MESSAGES_TAB_INDEX].body->body_item_render_callback = [this, ¤t_room, &me, &fetch_message_future, &tabs, &is_window_focused, &chat_state, &setting_read_marker, &read_marker_timer, &read_marker_timeout_ms, &set_read_marker_future, &fetch_body_item, &fetch_message_tab](BodyItem *body_item) { + tabs[MESSAGES_TAB_INDEX].body->body_item_render_callback = [this, ¤t_room, &me, &remove_unread_marker, &fetch_message_future, &tabs, &is_window_focused, &chat_state, &setting_read_marker, &read_marker_timer, &read_marker_timeout_ms, &set_read_marker_future, &fetch_body_item, &fetch_message_tab](BodyItem *body_item) { Message *message = static_cast<Message*>(body_item->userdata); if(!message) return; - if(matrix->is_initial_sync_finished() && message_is_timeline(message) && is_window_focused && chat_state != ChatState::URL_SELECTION && !setting_read_marker && read_marker_timer.getElapsedTime().asMilliseconds() >= read_marker_timeout_ms) { + if(matrix->is_initial_sync_finished() && tabs[MESSAGES_TAB_INDEX].body->is_last_item_fully_visible() && message_is_timeline(message) && is_window_focused && chat_state != ChatState::URL_SELECTION && !setting_read_marker && read_marker_timer.getElapsedTime().asMilliseconds() >= read_marker_timeout_ms) { Message *read_message = message; if(message->replaced_by) read_message = message->replaced_by.get(); // TODO: What if two messages have the same timestamp? if(!read_message->event_id.empty() && read_message->timestamp > current_room->last_read_message_timestamp) { + remove_unread_marker = true; //read_marker_timeout_ms = read_marker_timeout_ms_default; current_room->last_read_message_timestamp = read_message->timestamp; // TODO: What if the message is no longer valid? @@ -4623,9 +4630,6 @@ namespace QuickMedia { } else if(event.key.code == sf::Keyboard::Escape) { move_room = false; goto chat_page_end; - } else if(event.key.code == sf::Keyboard::Enter) { - move_room = true; - goto chat_page_end; } continue; } @@ -4679,6 +4683,8 @@ namespace QuickMedia { } else if((event.key.code == sf::Keyboard::Left || (event.key.control && event.key.code == sf::Keyboard::H)) && selected_tab > 0) { tabs[selected_tab].body->clear_cache(); --selected_tab; + if(selected_tab == MESSAGES_TAB_INDEX) + matrix_chat_page->messages_tab_visible = true; read_marker_timer.restart(); redraw = true; if(typing && current_room) { @@ -4689,6 +4695,8 @@ namespace QuickMedia { } else if((event.key.code == sf::Keyboard::Right || (event.key.control && event.key.code == sf::Keyboard::L)) && selected_tab < (int)tabs.size() - 1) { tabs[selected_tab].body->clear_cache(); ++selected_tab; + if(selected_tab == MESSAGES_TAB_INDEX) + matrix_chat_page->messages_tab_visible = true; read_marker_timer.restart(); redraw = true; if(typing && current_room) { @@ -5212,7 +5220,7 @@ namespace QuickMedia { } if(selected_tab == MESSAGES_TAB_INDEX && current_room && current_room->body_item && !current_room->last_message_read) { - if(tabs[selected_tab].body->is_last_item_fully_visible()) { + if(remove_unread_marker && tabs[selected_tab].body->is_last_item_fully_visible()) { std::string room_desc = current_room->body_item->get_description(); if(strncmp(room_desc.c_str(), "Unread: ", 8) == 0) room_desc = room_desc.substr(8); @@ -5230,7 +5238,8 @@ namespace QuickMedia { current_room->last_message_read = true; // TODO: Maybe set this instead when the mention is visible on the screen? current_room->unread_notification_count = 0; - } else { + remove_unread_marker = false; + } else if(!tabs[selected_tab].body->is_last_item_fully_visible()) { window.draw(more_messages_below_rect); } } diff --git a/src/plugins/MangaGeneric.cpp b/src/plugins/MangaGeneric.cpp index ebfbdda..8a17040 100644 --- a/src/plugins/MangaGeneric.cpp +++ b/src/plugins/MangaGeneric.cpp @@ -13,7 +13,7 @@ namespace QuickMedia { enum class MergeType { THUMBNAIL, - UPLOAD_TIME + DESCRIPTION }; struct HtmlMergeUserdata { @@ -21,6 +21,7 @@ namespace QuickMedia { BodyItemContext body_item_image_context; const char *field_name = nullptr; const char *field_contains = nullptr; + const char *desc_prefix = nullptr; }; struct HtmlListPageImagesUserdata { @@ -85,9 +86,10 @@ namespace QuickMedia { { if(merge_userdata->type == MergeType::THUMBNAIL) { (*body_item_image_context.body_items)[body_item_image_context.index]->thumbnail_url = strip(field_value); - } else if(merge_userdata->type == MergeType::UPLOAD_TIME) { + } else if(merge_userdata->type == MergeType::DESCRIPTION) { std::string uploaded_date = strip(field_value); - (*body_item_image_context.body_items)[body_item_image_context.index]->set_description("Uploaded: " + uploaded_date); + (*body_item_image_context.body_items)[body_item_image_context.index]->set_description(merge_userdata->desc_prefix ? merge_userdata->desc_prefix : "" + uploaded_date); + (*body_item_image_context.body_items)[body_item_image_context.index]->set_description_color(sf::Color(179, 179, 179)); } body_item_image_context.index++; } @@ -181,8 +183,23 @@ namespace QuickMedia { if(result != 0) goto cleanup; + for(const DescriptionQuery &description_query : description_queries) { + assert(description_query.html_query && description_query.field_name); + if(description_query.html_query && description_query.field_name) { + HtmlMergeUserdata merge_userdata; + merge_userdata.type = MergeType::DESCRIPTION; + merge_userdata.body_item_image_context.body_items = &new_result_items; + merge_userdata.body_item_image_context.index = 0; + merge_userdata.field_name = description_query.field_name; + merge_userdata.field_contains = nullptr; + result = html_body_item_merge(&html_search, description_query.html_query, &merge_userdata); + if(result != 0) + goto cleanup; + } + } + for(const ThumbnailQuery &thumbnail_query : thumbnail_queries) { - assert(!thumbnail_query.html_query || thumbnail_query.field_name); + assert(thumbnail_query.html_query && thumbnail_query.field_name); if(thumbnail_query.html_query && thumbnail_query.field_name) { HtmlMergeUserdata merge_userdata; merge_userdata.type = MergeType::THUMBNAIL; @@ -217,6 +234,7 @@ namespace QuickMedia { new_body_item->url = target_url; } } + result_items.insert(result_items.end(), std::move_iterator(new_result_items.begin()), std::move_iterator(new_result_items.end())); } @@ -271,11 +289,12 @@ namespace QuickMedia { assert(!list_chapters_query.uploaded_time_html_query || list_chapters_query.uploaded_time_field_name); if(list_chapters_query.uploaded_time_html_query && list_chapters_query.uploaded_time_field_name) { HtmlMergeUserdata merge_userdata; - merge_userdata.type = MergeType::UPLOAD_TIME; + merge_userdata.type = MergeType::DESCRIPTION; merge_userdata.body_item_image_context.body_items = &chapters_items; merge_userdata.body_item_image_context.index = 0; merge_userdata.field_name = list_chapters_query.uploaded_time_field_name; merge_userdata.field_contains = list_chapters_query.uploaded_time_field_contains; + merge_userdata.desc_prefix = "Uploaded: "; result = html_body_item_merge(&html_search, list_chapters_query.uploaded_time_html_query, &merge_userdata); } @@ -605,6 +624,11 @@ namespace QuickMedia { return *this; } + MangaGenericSearchPage& MangaGenericSearchPage::description_handler(std::vector<DescriptionQuery> queries) { + description_queries = std::move(queries); + return *this; + } + MangaGenericSearchPage& MangaGenericSearchPage::thumbnail_handler(std::vector<ThumbnailQuery> queries) { thumbnail_queries = std::move(queries); return *this; diff --git a/src/plugins/Manganelo.cpp b/src/plugins/Manganelo.cpp index e63ff6c..6d0447a 100644 --- a/src/plugins/Manganelo.cpp +++ b/src/plugins/Manganelo.cpp @@ -62,6 +62,7 @@ namespace QuickMedia { if(text && class_attr && strstr(class_attr, "chapter-time") && item_data->index < item_data->body_items->size()) { std::string uploaded_date = strip(text); (*item_data->body_items)[item_data->index]->set_description("Uploaded: " + uploaded_date); + (*item_data->body_items)[item_data->index]->set_description_color(sf::Color(179, 179, 179)); item_data->index++; } }, &body_item_context); @@ -117,11 +118,16 @@ namespace QuickMedia { Json::Value name = child.get("name", ""); Json::Value nameunsigned = child.get("nameunsigned", ""); + Json::Value lastchapter = child.get("lastchapter", ""); if(name.isString() && name.asCString()[0] != '\0' && nameunsigned.isString() && nameunsigned.asCString()[0] != '\0') { std::string name_str = name.asString(); while(remove_html_span(name_str)) {} auto item = BodyItem::create(strip(name_str)); item->url = "https://manganelo.com/manga/" + url_param_encode(nameunsigned.asString()); + if(lastchapter.isString() && lastchapter.asCString()[0] != '\0') { + item->set_description("Last chapter: " + lastchapter.asString()); + item->set_description_color(sf::Color(179, 179, 179)); + } Json::Value image = child.get("image", ""); if(image.isString() && image.asCString()[0] != '\0') item->thumbnail_url = image.asString(); diff --git a/src/plugins/Matrix.cpp b/src/plugins/Matrix.cpp index e1f9b58..147dbdf 100644 --- a/src/plugins/Matrix.cpp +++ b/src/plugins/Matrix.cpp @@ -443,8 +443,8 @@ namespace QuickMedia { } } - void MatrixQuickMedia::update(MatrixPageType page_type) { - update_pending_room_messages(page_type); + void MatrixQuickMedia::update(MatrixPageType page_type, Body *chat_body, bool messages_tab_visible) { + update_pending_room_messages(page_type, chat_body, messages_tab_visible); std::lock_guard<std::mutex> room_body_lock(room_body_items_mutex); for(auto &it : unread_notifications) { for(auto &unread_notification : it.second) { @@ -499,7 +499,7 @@ namespace QuickMedia { return extract_first_line_elipses(message->body, 150); } - void MatrixQuickMedia::update_room_description(RoomData *room, Messages &new_messages, bool is_initial_sync, bool sync_is_cache) { + void MatrixQuickMedia::update_room_description(RoomData *room, Messages &new_messages, bool is_initial_sync, bool sync_is_cache, Body *chat_body, bool messages_tab_visible) { time_t read_marker_message_timestamp = 0; std::shared_ptr<UserInfo> me = matrix->get_me(room); std::string my_user_read_marker; @@ -544,16 +544,25 @@ namespace QuickMedia { return; if(last_unread_message && !sync_is_cache) { - std::string room_desc = "Unread: " + matrix->message_get_author_displayname(last_unread_message) + ": " + message_to_room_description_text(last_unread_message); + bool is_window_focused = program->is_window_focused(); + RoomData *current_room = program->get_current_chat_room(); + bool set_room_as_unread = !is_window_focused || room != current_room || (!chat_body || !chat_body->is_last_item_fully_visible()) || !messages_tab_visible; + + std::string room_desc; + if(set_room_as_unread) + room_desc += "Unread: "; + room_desc += matrix->message_get_author_displayname(last_unread_message) + ": " + message_to_room_description_text(last_unread_message); + int unread_notification_count = room->unread_notification_count; - if(unread_notification_count > 0) { + if(unread_notification_count > 0 && set_room_as_unread) { room_desc += "\n** " + std::to_string(unread_notification_count) + " unread mention(s) **"; // TODO: Better notification? room->body_item->set_description_color(sf::Color(255, 100, 100)); } else { room->body_item->set_description_color(sf::Color(179, 179, 179)); } room->body_item->set_description(std::move(room_desc)); - room->body_item->set_title_color(sf::Color(255, 100, 100)); + if(set_room_as_unread) + room->body_item->set_title_color(sf::Color(255, 100, 100)); room->last_message_read = false; rooms_page->move_room_to_top(room); @@ -564,7 +573,7 @@ namespace QuickMedia { } } - void MatrixQuickMedia::update_pending_room_messages(MatrixPageType page_type) { + void MatrixQuickMedia::update_pending_room_messages(MatrixPageType page_type, Body *chat_body, bool messages_tab_visible) { std::lock_guard<std::mutex> lock(pending_room_messages_mutex); bool is_window_focused = program->is_window_focused(); RoomData *current_room = program->get_current_chat_room(); @@ -580,14 +589,14 @@ namespace QuickMedia { for(auto &message : messages) { if(message->notification_mentions_me) { // TODO: What if the message or username begins with "-"? also make the notification image be the avatar of the user - if((!is_window_focused || room != current_room || page_type == MatrixPageType::ROOM_LIST) && message->related_event_type != RelatedEventType::EDIT && message->related_event_type != RelatedEventType::REDACTION) { + if((!is_window_focused || room != current_room) && message->related_event_type != RelatedEventType::EDIT && message->related_event_type != RelatedEventType::REDACTION) { show_notification("QuickMedia matrix - " + matrix->message_get_author_displayname(message.get()) + " (" + room->get_name() + ")", message->body); } } } } - update_room_description(room, messages, is_initial_sync, it.second.sync_is_cache); + update_room_description(room, messages, is_initial_sync, it.second.sync_is_cache, chat_body, messages_tab_visible); } pending_room_messages.clear(); } @@ -605,7 +614,6 @@ namespace QuickMedia { PluginResult MatrixRoomsPage::submit(const std::string &title, const std::string &url, std::vector<Tab> &result_tabs) { (void)title; auto chat_page = std::make_unique<MatrixChatPage>(program, url, this); - chat_page->matrix_delegate = matrix_delegate; result_tabs.push_back(Tab{nullptr, std::move(chat_page), nullptr}); return PluginResult::OK; } @@ -651,7 +659,7 @@ namespace QuickMedia { sort_room_body_items(body->items); //body_set_selected_item(body, selected_item); } - matrix_delegate->update(MatrixPageType::ROOM_LIST); + matrix_delegate->update(MatrixPageType::ROOM_LIST, nullptr, false); if(filter_on_update) { filter_on_update = false; if(search_bar) @@ -823,7 +831,7 @@ namespace QuickMedia { add_room_body_items_by_tags.clear(); body->set_selected_item(prev_selected_item, false); } - matrix_delegate->update(MatrixPageType::ROOM_LIST); + matrix_delegate->update(MatrixPageType::ROOM_LIST, nullptr, false); if(filter_on_update) { filter_on_update = false; if(search_bar) @@ -970,17 +978,16 @@ namespace QuickMedia { } MatrixChatPage::MatrixChatPage(Program *program, std::string room_id, MatrixRoomsPage *rooms_page) : Page(program), room_id(std::move(room_id)), rooms_page(rooms_page) { - if(rooms_page) - rooms_page->set_current_chat_page(this); + assert(rooms_page); + rooms_page->set_current_chat_page(this); } MatrixChatPage::~MatrixChatPage() { - if(rooms_page) - rooms_page->set_current_chat_page(nullptr); + rooms_page->set_current_chat_page(nullptr); } void MatrixChatPage::update() { - matrix_delegate->update(MatrixPageType::CHAT); + rooms_page->matrix_delegate->update(MatrixPageType::CHAT, chat_body, messages_tab_visible); if(rooms_page) rooms_page->update(); } diff --git a/src/plugins/Soundcloud.cpp b/src/plugins/Soundcloud.cpp index 3fcb3b8..a0184b4 100644 --- a/src/plugins/Soundcloud.cpp +++ b/src/plugins/Soundcloud.cpp @@ -141,6 +141,7 @@ namespace QuickMedia { } body_item->set_description(std::move(description)); + body_item->set_description_color(sf::Color(179, 179, 179)); return body_item; } |