From cb60c5304db0b25d6ce2460fded4cd42a1dcf0dc Mon Sep 17 00:00:00 2001 From: dec05eba Date: Thu, 1 Oct 2020 11:03:30 +0200 Subject: Matrix: show indication for unread messages and show the last message in the rooms list --- src/QuickMedia.cpp | 81 +++++++++++++++++++++++++++++++++++++++++++------- src/plugins/Matrix.cpp | 25 ++++++++++++---- 2 files changed, 90 insertions(+), 16 deletions(-) (limited to 'src') diff --git a/src/QuickMedia.cpp b/src/QuickMedia.cpp index cb58a27..15ae39c 100644 --- a/src/QuickMedia.cpp +++ b/src/QuickMedia.cpp @@ -3279,6 +3279,16 @@ namespace QuickMedia { source.a + diff_a * progress); } + static std::string extract_first_line(const std::string &str) { + size_t index = str.find('\n'); + if(index == std::string::npos) + return str; + else if(index == 0) + return ""; + else + return str.substr(0, index - 1); + } + void Program::chat_page() { assert(current_plugin->name == "matrix"); Matrix *matrix = static_cast(current_plugin); @@ -3320,7 +3330,8 @@ 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 - if(matrix->sync() != PluginResult::OK) { + 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; @@ -3332,6 +3343,24 @@ namespace QuickMedia { return; } + struct RoomBodyData { + std::shared_ptr body_item; + bool last_message_read; + }; + + 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)); + } + } + // 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; @@ -3341,6 +3370,11 @@ namespace QuickMedia { // 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 enum class ChatState { @@ -3423,7 +3457,12 @@ namespace QuickMedia { return false; }; - std::future sync_future; + struct SyncFutureResult { + BodyItems body_items; + RoomSyncMessages room_sync_messages; + }; + + std::future sync_future; bool sync_running = false; std::string sync_future_room_id; sf::Clock sync_timer; @@ -3642,6 +3681,10 @@ namespace QuickMedia { std::string err_msg = "Failed to get messages in room: " + current_room_id; show_notification("QuickMedia", err_msg, Urgency::CRITICAL); } + + 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; } } } @@ -3757,30 +3800,38 @@ namespace QuickMedia { sync_future = std::async(std::launch::async, [this, &sync_future_room_id]() { Matrix *matrix = static_cast(current_plugin); - BodyItems result_items; - if(matrix->sync() == PluginResult::OK) { + SyncFutureResult result; + if(matrix->sync(result.room_sync_messages) == PluginResult::OK) { fprintf(stderr, "Synced matrix\n"); - if(matrix->get_new_room_messages(sync_future_room_id, result_items) != PluginResult::OK) { + 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()); } } else { fprintf(stderr, "Failed to sync matrix\n"); } - return result_items; + return result; }); } if(sync_future.valid() && sync_future.wait_for(std::chrono::seconds(0)) == std::future_status::ready) { - BodyItems new_body_items = sync_future.get(); + SyncFutureResult sync_result = sync_future.get(); // Ignore finished sync if it happened in another room. When we navigate back to the room we will get the messages again if(sync_future_room_id == current_room_id) { int num_items = tabs[MESSAGES_TAB_INDEX].body->items.size(); bool scroll_to_end = (num_items == 0 || (num_items > 0 && tabs[MESSAGES_TAB_INDEX].body->get_selected_item() == num_items - 1)); - tabs[MESSAGES_TAB_INDEX].body->append_items(std::move(new_body_items)); + tabs[MESSAGES_TAB_INDEX].body->append_items(std::move(sync_result.body_items)); 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; + } + } sync_running = false; } @@ -3872,8 +3923,18 @@ namespace QuickMedia { tabs[MESSAGES_TAB_INDEX].body->draw_item(window, currently_operating_on_item.get(), body_item_pos, body_item_size); } - if(tabs[selected_tab].type == ChatTabType::MESSAGES && !tabs[selected_tab].body->is_last_item_fully_visible()) { - window.draw(more_messages_below_rect); + 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); + // 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; + } + } else { + window.draw(more_messages_below_rect); + } } if(tabs[selected_tab].type == ChatTabType::MESSAGES) { diff --git a/src/plugins/Matrix.cpp b/src/plugins/Matrix.cpp index b5eb617..ed3b0af 100644 --- a/src/plugins/Matrix.cpp +++ b/src/plugins/Matrix.cpp @@ -34,7 +34,7 @@ namespace QuickMedia { return PluginResult::OK; } - PluginResult Matrix::sync() { + PluginResult Matrix::sync(RoomSyncMessages &room_messages) { std::vector additional_args = { { "-H", "Authorization: Bearer " + access_token }, { "-m", "35" } @@ -73,7 +73,7 @@ namespace QuickMedia { return PluginResult::ERR; } - PluginResult result = sync_response_to_body_items(json_root); + PluginResult result = sync_response_to_body_items(json_root, room_messages); if(result != PluginResult::OK) return result; @@ -244,7 +244,7 @@ namespace QuickMedia { return PluginResult::OK; } - PluginResult Matrix::sync_response_to_body_items(const Json::Value &root) { + PluginResult Matrix::sync_response_to_body_items(const Json::Value &root, RoomSyncMessages &room_messages) { if(!root.isObject()) return PluginResult::ERR; @@ -314,7 +314,7 @@ namespace QuickMedia { const Json::Value &events_json = timeline_json["events"]; events_add_user_info(events_json, room_it->second.get()); - events_add_messages(events_json, room_it->second.get(), MessageDirection::AFTER); + events_add_messages(events_json, room_it->second.get(), MessageDirection::AFTER, &room_messages); events_set_room_name(events_json, room_it->second.get()); } @@ -400,10 +400,13 @@ namespace QuickMedia { return ""; } - void Matrix::events_add_messages(const Json::Value &events_json, RoomData *room_data, MessageDirection message_dir) { + void Matrix::events_add_messages(const Json::Value &events_json, RoomData *room_data, MessageDirection message_dir, RoomSyncMessages *room_messages) { if(!events_json.isArray()) return; + std::vector> *room_sync_messages = nullptr; + if(room_messages) + room_sync_messages = &(*room_messages)[room_data]; std::vector> new_messages; for(const Json::Value &event_item_json : events_json) { @@ -484,6 +487,8 @@ namespace QuickMedia { message->replaces_event_id = std::move(replaces_event_id); new_messages.push_back(message); room_data->message_by_event_id[event_id_str] = message; + if(room_sync_messages) + room_sync_messages->push_back(message); } // TODO: Loop and std::move instead? doesn't insert create copies? @@ -626,7 +631,7 @@ namespace QuickMedia { events_set_room_name(state_json, room_data); const Json::Value &chunk_json = json_root["chunk"]; - events_add_messages(chunk_json, room_data, MessageDirection::BEFORE); + events_add_messages(chunk_json, room_data, MessageDirection::BEFORE, nullptr); const Json::Value &end_json = json_root["end"]; if(!end_json.isString()) { @@ -1431,4 +1436,12 @@ namespace QuickMedia { Message *message_typed = (Message*)message; return user_id == room_it->second->user_info[message_typed->user_id].user_id; } + + std::string Matrix::message_get_author_displayname(RoomData *room_data, Message *message) const { + if(message->user_id >= room_data->user_info.size()) { + fprintf(stderr, "Warning: no user with the index: %zu\n", message->user_id); + return ""; + } + return room_data->user_info[message->user_id].display_name; + } } \ No newline at end of file -- cgit v1.2.3