From ef1dd33682ae26b4af1343aaecf443e7cd883674 Mon Sep 17 00:00:00 2001 From: dec05eba Date: Thu, 1 Oct 2020 22:21:14 +0200 Subject: Matrix: implement mention/reply notifications --- src/QuickMedia.cpp | 150 +++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 106 insertions(+), 44 deletions(-) (limited to 'src/QuickMedia.cpp') diff --git a/src/QuickMedia.cpp b/src/QuickMedia.cpp index 909bdd7..da5453e 100644 --- a/src/QuickMedia.cpp +++ b/src/QuickMedia.cpp @@ -3331,50 +3331,58 @@ namespace QuickMedia { */ // This is needed to get initial data, with joined rooms etc. TODO: Remove this once its cached // and allow asynchronous update of rooms - RoomSyncMessages room_sync_messages; - if(matrix->sync(room_sync_messages) != PluginResult::OK) { - show_notification("QuickMedia", "Intial matrix sync failed", Urgency::CRITICAL); - current_page = Page::EXIT; - return; - } - - if(matrix->get_joined_rooms(tabs[ROOMS_TAB_INDEX].body->items) != PluginResult::OK) { - show_notification("QuickMedia", "Failed to get a list of joined rooms", Urgency::CRITICAL); - current_page = Page::EXIT; - return; - } + bool synced = false; struct RoomBodyData { std::shared_ptr body_item; bool last_message_read; + time_t last_read_message_timestamp; }; std::unordered_map body_items_by_room_id; - for(auto body_item : tabs[ROOMS_TAB_INDEX].body->items) { - // TODO: Set |last_message_read| depending on read markers (either remote matrix read markers or locally saved ones) - body_items_by_room_id[body_item->url] = { body_item, true }; - } - for(auto &[room, messages] : room_sync_messages) { - auto room_body_item_it = body_items_by_room_id.find(room->id); - if(room_body_item_it != body_items_by_room_id.end() && !messages.empty()) { - room_body_item_it->second.body_item->set_description(matrix->message_get_author_displayname(room, messages.back().get()) + ": " + extract_first_line(messages.back()->body)); + auto process_new_room_messages = [matrix, &body_items_by_room_id](RoomSyncMessages &room_sync_messages, bool only_show_mentions) mutable { + for(auto &[room, messages] : room_sync_messages) { + bool was_mentioned = false; + for(auto &message : messages) { + if(message->mentions_me) { + was_mentioned = true; + message->mentions_me = false; + // TODO: What if the message or username begins with "-"? also make the notification image be the avatar of the user + std::string desc = "QuickMedia Matrix\n\n" + message->body; + show_notification(matrix->message_get_author_displayname(room, message.get()), desc.c_str()); + } + } + + auto room_body_item_it = body_items_by_room_id.find(room->id); + if(room_body_item_it == body_items_by_room_id.end()) + continue; + + if(only_show_mentions) { + std::string room_desc; + if(!messages.empty()) + room_desc = matrix->message_get_author_displayname(room, messages.back().get()) + ": " + extract_first_line(messages.back()->body); + if(was_mentioned) { + room_desc += "\n** You were mentioned **"; // TODO: Better notification? + room_body_item_it->second.body_item->title_color = sf::Color(255, 100, 100); + room_body_item_it->second.last_message_read = false; + } + room_body_item_it->second.body_item->set_description(std::move(room_desc)); + } else if(!messages.empty()) { + std::string room_desc = "Unread: " + matrix->message_get_author_displayname(room, messages.back().get()) + ": " + extract_first_line(messages.back()->body); + if(was_mentioned) + room_desc += "\n** You were mentioned **"; // TODO: Better notification? + room_body_item_it->second.body_item->set_description(std::move(room_desc)); + room_body_item_it->second.body_item->title_color = sf::Color(255, 100, 100); + room_body_item_it->second.last_message_read = false; + } } - } + }; // TODO: the initial room to view should be the last viewed room when closing QuickMedia. // The room id should be saved in a file when changing viewed room. std::string current_room_id; - if(!tabs[ROOMS_TAB_INDEX].body->items.empty()) - current_room_id = tabs[ROOMS_TAB_INDEX].body->items[0]->url; - - // TODO: Allow empty initial room (if the user hasn't joined any room yet). - assert(!current_room_id.empty()); - RoomBodyData *current_room_body_data = nullptr; - auto room_body_item_it = body_items_by_room_id.find(current_room_id); - if(room_body_item_it != body_items_by_room_id.end()) - current_room_body_data = &room_body_item_it->second; // get_all_room_messages is not needed here because its done in the loop, where the initial timeout is 0ms @@ -3461,6 +3469,7 @@ namespace QuickMedia { struct SyncFutureResult { BodyItems body_items; + BodyItems rooms_body_items; RoomSyncMessages room_sync_messages; }; @@ -3503,9 +3512,7 @@ namespace QuickMedia { const float tab_vertical_offset = 10.0f; sf::Text room_name_text("", *font, 18); - if(current_room_body_data) - room_name_text.setString(current_room_body_data->body_item->get_title()); - const float room_name_text_height = std::floor(room_name_text.getLocalBounds().height); + const float room_name_text_height = 20.0f; const float room_name_text_padding_y = 10.0f; const float room_name_total_height = room_name_text_height + room_name_text_padding_y * 2.0f; const float room_avatar_height = 32.0f; @@ -3534,6 +3541,9 @@ namespace QuickMedia { Body url_selection_body(this, font.get(), bold_font.get(), cjk_font.get()); + sf::Clock read_marker_timer; + const sf::Int32 read_marker_timeout_ms = 3000; + auto launch_url = [this, &redraw](const std::string &url) mutable { if(url.empty()) return; @@ -3608,6 +3618,7 @@ namespace QuickMedia { } else if(event.key.code == sf::Keyboard::Left) { tabs[selected_tab].body->clear_thumbnails(); selected_tab = std::max(0, selected_tab - 1); + read_marker_timer.restart(); if(typing) { fprintf(stderr, "Stopped typing\n"); typing = false; @@ -3616,6 +3627,7 @@ namespace QuickMedia { } else if(event.key.code == sf::Keyboard::Right) { tabs[selected_tab].body->clear_thumbnails(); selected_tab = std::min((int)tabs.size() - 1, selected_tab + 1); + read_marker_timer.restart(); if(typing) { fprintf(stderr, "Stopped typing\n"); typing = false; @@ -3907,6 +3919,8 @@ namespace QuickMedia { } chat_input_height_full = chat_input.get_height() + chat_input_padding_y * 2.0f; + if(selected_tab != MESSAGES_TAB_INDEX) + chat_input_height_full = 0.0f; chat_input_shade.setSize(sf::Vector2f(window_size.x, chat_input_height_full)); chat_input_shade.setPosition(0.0f, window_size.y - chat_input_shade.getSize().y); @@ -3927,12 +3941,21 @@ namespace QuickMedia { sync_running = true; sync_timer.restart(); sync_future_room_id = current_room_id; - sync_future = std::async(std::launch::async, [this, &sync_future_room_id]() { + sync_future = std::async(std::launch::async, [this, &sync_future_room_id, synced]() { Matrix *matrix = static_cast(current_plugin); SyncFutureResult result; if(matrix->sync(result.room_sync_messages) == PluginResult::OK) { fprintf(stderr, "Synced matrix\n"); + + if(!synced) { + if(matrix->get_joined_rooms(result.rooms_body_items) != PluginResult::OK) { + show_notification("QuickMedia", "Failed to get a list of joined rooms", Urgency::CRITICAL); + current_page = Page::EXIT; + return result; + } + } + if(matrix->get_new_room_messages(sync_future_room_id, result.body_items) != PluginResult::OK) { fprintf(stderr, "Failed to get new matrix messages in room: %s\n", sync_future_room_id.c_str()); } @@ -3954,23 +3977,39 @@ namespace QuickMedia { if(scroll_to_end) tabs[MESSAGES_TAB_INDEX].body->select_last_item(); } - for(auto &[room, messages] : sync_result.room_sync_messages) { - auto room_body_item_it = body_items_by_room_id.find(room->id); - if(room_body_item_it != body_items_by_room_id.end() && !messages.empty()) { - room_body_item_it->second.body_item->set_description("Unread: " + matrix->message_get_author_displayname(room, messages.back().get()) + ": " + extract_first_line(messages.back()->body)); - room_body_item_it->second.body_item->title_color = sf::Color(255, 100, 100); - room_body_item_it->second.last_message_read = false; + + if(!synced) { + tabs[ROOMS_TAB_INDEX].body->items = std::move(sync_result.rooms_body_items); + + for(auto body_item : tabs[ROOMS_TAB_INDEX].body->items) { + // TODO: Set |last_message_read| depending on read markers (either remote matrix read markers or locally saved ones) + body_items_by_room_id[body_item->url] = { body_item, true, 0 }; } + + // TODO: the initial room to view should be the last viewed room when closing QuickMedia. + // The room id should be saved in a file when changing viewed room. + if(!tabs[ROOMS_TAB_INDEX].body->items.empty()) + current_room_id = tabs[ROOMS_TAB_INDEX].body->items[0]->url; + + auto room_body_item_it = body_items_by_room_id.find(current_room_id); + if(room_body_item_it != body_items_by_room_id.end()) { + current_room_body_data = &room_body_item_it->second; + room_name_text.setString(current_room_body_data->body_item->get_title()); + } + redraw = true; } + + process_new_room_messages(sync_result.room_sync_messages, !synced); sync_running = false; + synced = true; } if(fetching_previous_messages_running && previous_messages_future.valid() && previous_messages_future.wait_for(std::chrono::seconds(0)) == std::future_status::ready) { BodyItems new_body_items = previous_messages_future.get(); fprintf(stderr, "Finished fetching older messages, num new messages: %zu\n", new_body_items.size()); // Ignore finished fetch of messages if it happened in another room. When we navigate back to the room we will get the messages again - if(previous_messages_future_room_id == current_room_id) { - size_t num_new_messages = new_body_items.size(); + size_t num_new_messages = new_body_items.size(); + if(previous_messages_future_room_id == current_room_id && num_new_messages > 0) { int selected_item_index = tabs[MESSAGES_TAB_INDEX].body->get_selected_item(); tabs[MESSAGES_TAB_INDEX].body->prepend_items(std::move(new_body_items)); tabs[MESSAGES_TAB_INDEX].body->set_selected_item(selected_item_index + num_new_messages); @@ -4073,8 +4112,12 @@ namespace QuickMedia { if(tabs[selected_tab].type == ChatTabType::MESSAGES) { if(tabs[selected_tab].body->is_last_item_fully_visible()) { if(current_room_body_data && !current_room_body_data->last_message_read) { - if(strncmp(current_room_body_data->body_item->get_description().c_str(), "Unread: ", 8) == 0) - current_room_body_data->body_item->set_description(current_room_body_data->body_item->get_description().c_str() + 8); + std::string room_desc = current_room_body_data->body_item->get_description(); + if(strncmp(room_desc.c_str(), "Unread: ", 8) == 0) + room_desc = room_desc.substr(8); + if(room_desc.size() >= 26 && strncmp(room_desc.c_str() + room_desc.size() - 26, "\n** You were mentioned **", 26) == 0) + room_desc = room_desc.substr(0, room_desc.size() - 26); + current_room_body_data->body_item->set_description(std::move(room_desc)); // TODO: Show a line like nheko instead for unread messages, or something else current_room_body_data->body_item->title_color = sf::Color::White; current_room_body_data->last_message_read = true; @@ -4084,7 +4127,26 @@ namespace QuickMedia { } } + // TODO: Cache /sync, then we wont only see loading text + if(!synced) { + sf::Text loading_text("Loading...", *font, 24); + loading_text.setPosition(body_pos.x + body_size.x * 0.5f - loading_text.getLocalBounds().width * 0.5f, body_pos.y + body_size.y * 0.5f - loading_text.getLocalBounds().height * 0.5f); + window.draw(loading_text); + } + if(tabs[selected_tab].type == ChatTabType::MESSAGES) { + BodyItem *last_visible_item = tabs[selected_tab].body->get_last_fully_visible_item(); + if(chat_state != ChatState::URL_SELECTION && current_room_body_data && last_visible_item && read_marker_timer.getElapsedTime().asMilliseconds() >= read_marker_timeout_ms) { + Message *message = (Message*)last_visible_item->userdata; + if(message->timestamp > current_room_body_data->last_read_message_timestamp) { + current_room_body_data->last_read_message_timestamp = message->timestamp; + read_marker_timer.restart(); + if(matrix->set_read_marker(current_room_id, message) != PluginResult::OK) { + fprintf(stderr, "Warning: failed to set read marker to %s\n", message->event_id.c_str()); + } + } + } + window.draw(chat_input_shade); chat_input.draw(window); //chat_input.draw(window, false); window.draw(logo_sprite); -- cgit v1.2.3