diff options
-rw-r--r-- | TODO | 2 | ||||
-rw-r--r-- | include/Body.hpp | 2 | ||||
-rw-r--r-- | include/BodyItem.hpp | 6 | ||||
-rw-r--r-- | include/QuickMedia.hpp | 2 | ||||
-rw-r--r-- | plugins/Matrix.hpp | 7 | ||||
-rw-r--r-- | src/Body.cpp | 12 | ||||
-rw-r--r-- | src/QuickMedia.cpp | 9 | ||||
-rw-r--r-- | src/plugins/Matrix.cpp | 82 |
8 files changed, 77 insertions, 45 deletions
@@ -119,7 +119,6 @@ Set curl download limits everywhere (when saving to file, downloading to json, e In the downloader if we already have the url in thumbnail/video cache, then copy it to the destination instead of redownloading it. This would also fix downloading images when viewing a manga page. Update timestamp of messages posted with matrix (and move them to the correct place in the timeline) when we receive the message from the server. This is needed when the localtime is messed up (for example when rebooting from windows into linux). When sfml dependency is removed use libvips for image manipulation. Its a very fast library, especially for thumbnail creation. -Notification race condition when fetching the first notifications page and receiving a notification immediately after the first sync? we might end up with a duplicate notification. Readd copying of Text in Body copy constructor. Find out why we need to make the text dirty on copy. Body items that are no longer visible should stop their thumbnail download. Make body width the same as the window width (for the main body when there isn't a room list beside it) and pass margin to body draw. This is needed to allow swiping body from the window (screen) edge. @@ -190,6 +189,5 @@ Add option to navigate studios/producers/author in AniList/MAL. Renable throttle detection after fixing it (it doesn't detect throttling well and it breaks for very long videos, such as 8 hour long videos). Show who deleted a message in matrix. Sync should replace all messages in the room (except for the selected room?) to reduce ram usage when in many rooms and when quickmedia has been running for a long time doing sync. -Fix notifications not being marked as read correctly (they remain red!). Show youtube annotations. Show indicator in body item if it has been bookmarked (to prevent accidental removal of an item that has already been bookmarked before).
\ No newline at end of file diff --git a/include/Body.hpp b/include/Body.hpp index e1e6fef..2a82406 100644 --- a/include/Body.hpp +++ b/include/Body.hpp @@ -71,6 +71,8 @@ namespace QuickMedia { void insert_item(std::shared_ptr<BodyItem> body_item, int index); void move_item(size_t src_index, size_t dst_index); // Returns the inserted position + size_t insert_item_by_timestamp_reverse(std::shared_ptr<BodyItem> body_item); + // Returns the inserted position size_t insert_item_by_timestamp(std::shared_ptr<BodyItem> body_item); // Note: keeps the selected item (moving the selected_item index if necessary) void insert_items_by_timestamps(BodyItems new_items); diff --git a/include/BodyItem.hpp b/include/BodyItem.hpp index 728c2b0..1d967b5 100644 --- a/include/BodyItem.hpp +++ b/include/BodyItem.hpp @@ -71,7 +71,7 @@ namespace QuickMedia { } // |new_timestamp| is in milliseconds - void set_timestamp(time_t new_timestamp) { + void set_timestamp(int64_t new_timestamp) { if(new_timestamp == timestamp) return; timestamp = new_timestamp; @@ -116,7 +116,7 @@ namespace QuickMedia { const std::string& get_description() const { return description; } const std::string& get_author() const { return author; } // In milliseconds - time_t get_timestamp() const { return timestamp; } + int64_t get_timestamp() const { return timestamp; } sf::Color get_title_color() const { return title_color; } sf::Color get_description_color() const { return description_color; } @@ -165,7 +165,7 @@ namespace QuickMedia { std::string title; std::string description; std::string author; - time_t timestamp; + int64_t timestamp; sf::Color title_color; sf::Color author_color; sf::Color description_color; diff --git a/include/QuickMedia.hpp b/include/QuickMedia.hpp index f3ac88b..dd54572 100644 --- a/include/QuickMedia.hpp +++ b/include/QuickMedia.hpp @@ -115,7 +115,7 @@ namespace QuickMedia { void page_loop_render(sf::RenderWindow &window, std::vector<Tab> &tabs, int selected_tab, TabAssociatedData &tab_associated_data, const Json::Value *json_chapters, Tabs &ui_tabs); using PageLoopSubmitHandler = std::function<void(const std::vector<Tab> &new_tabs)>; // Returns false if the page loop was escaped by user navigation (pressing escape) or if there was an error at startup - bool page_loop(std::vector<Tab> &tabs, int start_tab_index = 0, PageLoopSubmitHandler after_submit_handler = nullptr); + bool page_loop(std::vector<Tab> &tabs, int start_tab_index = 0, PageLoopSubmitHandler after_submit_handler = nullptr, bool go_to_previous_on_escape = true); void video_page_download_video(const std::string &url, sf::WindowHandle video_player_window = None); bool video_download_if_non_streamable(std::string &video_url, std::string &audio_url, bool &is_audio_only, bool &has_embedded_audio, PageType previous_page); void video_content_page(Page *parent_page, VideoPage *video_page, std::string video_title, bool download_if_streaming_fails, Body *parent_body, int play_index, int *parent_body_page = nullptr, const std::string &parent_page_search = ""); diff --git a/plugins/Matrix.hpp b/plugins/Matrix.hpp index 1566212..341a534 100644 --- a/plugins/Matrix.hpp +++ b/plugins/Matrix.hpp @@ -309,6 +309,7 @@ namespace QuickMedia { private: std::map<RoomData*, std::shared_ptr<BodyItem>> room_body_item_by_room; std::map<RoomData*, std::shared_ptr<Message>> last_message_by_room; + std::unordered_set<std::string> notifications_shown; UsersByRoom users_by_room; }; @@ -492,10 +493,14 @@ namespace QuickMedia { void add_notification(MatrixNotification notification); void set_room_as_read(RoomData *room); private: + bool has_fetched = false; Matrix *matrix; Body *notifications_body; MatrixRoomsPage *all_rooms_page; - std::unordered_map<std::string, std::vector<std::shared_ptr<BodyItem>>> room_notifications; + // room id[event_id[]] + std::unordered_map<std::string, std::unordered_map<std::string, std::shared_ptr<BodyItem>>> room_notifications; + // Notifications are here until the notifications has been fetched, so that page handler doesn't the notifications + std::unordered_map<std::string, std::unordered_map<std::string, MatrixNotification>> pending_room_notifications; }; class MatrixInviteUserPage : public Page { diff --git a/src/Body.cpp b/src/Body.cpp index 1762ba5..afa1dae 100644 --- a/src/Body.cpp +++ b/src/Body.cpp @@ -315,6 +315,18 @@ namespace QuickMedia { items.insert(items.begin() + dst_index - 1, std::move(item_to_move)); } + size_t Body::insert_item_by_timestamp_reverse(std::shared_ptr<BodyItem> body_item) { + apply_search_filter_for_item(body_item.get()); + for(size_t i = 0; i < items.size(); ++i) { + if(body_item->get_timestamp() > items[i]->get_timestamp()) { + items.insert(items.begin() + i, std::move(body_item)); + return i; + } + } + items.insert(items.begin(), std::move(body_item)); + return 0; + } + // TODO: Binary search and use hint to start search from start or end (for example when adding "previous" items or "next" items) size_t Body::insert_item_by_timestamp(std::shared_ptr<BodyItem> body_item) { apply_search_filter_for_item(body_item.get()); diff --git a/src/QuickMedia.cpp b/src/QuickMedia.cpp index 1b48743..f66e23f 100644 --- a/src/QuickMedia.cpp +++ b/src/QuickMedia.cpp @@ -1817,7 +1817,7 @@ namespace QuickMedia { } } - bool Program::page_loop(std::vector<Tab> &tabs, int start_tab_index, PageLoopSubmitHandler after_submit_handler) { + bool Program::page_loop(std::vector<Tab> &tabs, int start_tab_index, PageLoopSubmitHandler after_submit_handler, bool go_to_previous_on_escape) { if(tabs.empty()) { show_notification("QuickMedia", "No tabs provided!", Urgency::CRITICAL); return false; @@ -2169,7 +2169,7 @@ namespace QuickMedia { if(event.type == sf::Event::Resized || event.type == sf::Event::GainedFocus) redraw = true; else if(event.type == sf::Event::KeyPressed) { - if(event.key.code == sf::Keyboard::Escape) { + if(event.key.code == sf::Keyboard::Escape && go_to_previous_on_escape) { return false; } else if(event.key.code == sf::Keyboard::Enter) { if(!tabs[selected_tab].search_bar) { @@ -7084,10 +7084,7 @@ namespace QuickMedia { tabs.push_back(Tab{std::move(invites_body), std::move(matrix_invites_page), create_search_bar("Search...", SEARCH_DELAY_FILTER)}); tabs.push_back(Tab{std::move(room_directory_body), std::move(matrix_room_directory_page), create_search_bar("Server to search on...", SEARCH_DELAY_FILTER)}); - while(window.isOpen()) { - page_loop(tabs, 2); - } - + page_loop(tabs, 2, nullptr, false); matrix->stop_sync(); } diff --git a/src/plugins/Matrix.cpp b/src/plugins/Matrix.cpp index 21375e7..e95b74b 100644 --- a/src/plugins/Matrix.cpp +++ b/src/plugins/Matrix.cpp @@ -413,7 +413,8 @@ namespace QuickMedia { bool read = true; // 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) && message->related_event_type != RelatedEventType::EDIT && message->related_event_type != RelatedEventType::REDACTION) { - show_notification("QuickMedia matrix - " + extract_first_line_remove_newline_elipses(matrix->message_get_author_displayname(message.get()), AUTHOR_MAX_LENGTH) + " (" + room->get_name() + ")", body); + if(notifications_shown.insert(message->event_id).second) + show_notification("QuickMedia matrix - " + extract_first_line_remove_newline_elipses(matrix->message_get_author_displayname(message.get()), AUTHOR_MAX_LENGTH) + " (" + room->get_name() + ")", body); read = false; } @@ -452,7 +453,8 @@ namespace QuickMedia { } void MatrixQuickMedia::add_unread_notification(MatrixNotification notification) { - show_notification("QuickMedia matrix - " + notification.sender_user_id + " (" + notification.room->get_name() + ")", notification.body); + if(notifications_shown.insert(notification.event_id).second) + show_notification("QuickMedia matrix - " + notification.sender_user_id + " (" + notification.room->get_name() + ")", notification.body); } void MatrixQuickMedia::add_user(MatrixEventUserInfo user_info) { @@ -858,8 +860,7 @@ namespace QuickMedia { } void MatrixInvitesPage::add_body_item(std::shared_ptr<BodyItem> body_item) { - // TODO: Insert in reverse order (to show the latest invite at the top?) - body->insert_item_by_timestamp(std::move(body_item)); + body->insert_item_by_timestamp_reverse(std::move(body_item)); if(body->get_num_items() != prev_invite_count) { prev_invite_count = body->get_num_items(); title = "Invites (" + std::to_string(body->get_num_items()) + ")"; @@ -1066,17 +1067,34 @@ namespace QuickMedia { PluginResult MatrixNotificationsPage::get_page(const std::string&, int, BodyItems &result_items) { return matrix->get_previous_notifications([this, &result_items](const MatrixNotification ¬ification) { auto body_item = notification_to_body_item(notification); - room_notifications[notification.room->id].push_back(body_item); - result_items.push_back(body_item); + if(room_notifications[notification.room->id].insert(std::make_pair(notification.event_id, body_item)).second) + result_items.push_back(body_item); }); } PluginResult MatrixNotificationsPage::lazy_fetch(BodyItems &result_items) { - matrix->get_cached_notifications([this, &result_items](const MatrixNotification ¬ification) { + BodyItems new_body_items; + for(const auto &pending_room_notifications : pending_room_notifications) { + for(const auto ¬ification : pending_room_notifications.second) { + auto body_item = notification_to_body_item(notification.second); + if(room_notifications[notification.second.room->id].insert(std::make_pair(notification.second.event_id, body_item)).second) + new_body_items.push_back(std::move(body_item)); + } + } + pending_room_notifications.clear(); + + matrix->get_cached_notifications([this, &new_body_items](const MatrixNotification ¬ification) { auto body_item = notification_to_body_item(notification); - room_notifications[notification.room->id].push_back(body_item); - result_items.push_back(body_item); + if(room_notifications[notification.room->id].insert(std::make_pair(notification.event_id, body_item)).second) + new_body_items.push_back(body_item); + }); + + std::sort(new_body_items.begin(), new_body_items.end(), [](const std::shared_ptr<BodyItem> &body_item1, const std::shared_ptr<BodyItem> &body_item2) { + return body_item1->get_timestamp() > body_item2->get_timestamp(); }); + + result_items = std::move(new_body_items); + has_fetched = true; return PluginResult::OK; } @@ -1085,23 +1103,34 @@ namespace QuickMedia { } void MatrixNotificationsPage::add_notification(MatrixNotification notification) { + if(!has_fetched) { + pending_room_notifications[notification.room->id][notification.event_id] = std::move(notification); + return; + } + auto body_item = notification_to_body_item(notification); - room_notifications[notification.room->id].push_back(body_item); - notifications_body->insert_item_by_timestamp(body_item); + if(room_notifications[notification.room->id].insert(std::make_pair(notification.event_id, body_item)).second) + notifications_body->insert_item_by_timestamp_reverse(std::move(body_item)); } // TODO: Only loop unread items void MatrixNotificationsPage::set_room_as_read(RoomData *room) { - auto it = room_notifications.find(room->id); - if(it == room_notifications.end()) - return; + auto pending_it = pending_room_notifications.find(room->id); + if(pending_it != pending_room_notifications.end()) { + for(auto &room_notification : pending_it->second) { + room_notification.second.read = true; + } + } - for(const auto &room_notification : it->second) { - NotificationsExtraData *extra_data = static_cast<NotificationsExtraData*>(room_notification->extra.get()); - if(!extra_data->read) { - extra_data->read = true; - room_notification->set_author_color(get_current_theme().text_color); - room_notification->set_description_color(get_current_theme().text_color); + auto it = room_notifications.find(room->id); + if(it != room_notifications.end()) { + for(const auto &room_notification : it->second) { + NotificationsExtraData *extra_data = static_cast<NotificationsExtraData*>(room_notification.second->extra.get()); + if(!extra_data->read) { + extra_data->read = true; + room_notification.second->set_author_color(get_current_theme().text_color); + room_notification.second->set_description_color(get_current_theme().text_color); + } } } } @@ -2080,19 +2109,8 @@ 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) { + 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(notifications_by_event_id.insert(message->event_id).second) { - MatrixNotification notification; - notification.room = room_data; - notification.event_id = message->event_id; - notification.sender_user_id = message->user->user_id; - notification.body = remove_reply_formatting(message->body);; - notification.timestamp = message->timestamp; - notification.read = false; - notifications.insert(notifications.begin(), std::move(notification)); - } - } } } |