From 089818f9078c53de7ff9e6596eb7eb82cc8d6727 Mon Sep 17 00:00:00 2001 From: dec05eba Date: Wed, 2 Nov 2022 19:29:18 +0100 Subject: Matrix: update room info in ui when receiving updates --- TODO | 5 +- plugins/Matrix.hpp | 19 +++++++- src/QuickMedia.cpp | 65 ++++++++++++++++--------- src/plugins/Matrix.cpp | 130 +++++++++++++++++++++++++++++++++++++++++++++---- 4 files changed, 181 insertions(+), 38 deletions(-) diff --git a/TODO b/TODO index 906525d..6821418 100644 --- a/TODO +++ b/TODO @@ -85,7 +85,6 @@ then add a gap between old messages from before sync and after sync so we can fe Fetching of previous messages should also be saved in the /sync file and messages fetched with get_message_by_id, which would cache embedded items and pinned messages; also cache users. If manga page fails to download then show "failed to download image" as text and bind F5 to refresh (retry download). Use avatar_url; }; + struct MatrixEventRoomInfo { + RoomData *room; + std::optional name; + std::optional topic; + std::optional avatar_url; + }; + enum class MatrixEventType { ADD_USER, REMOVE_USER, - USER_INFO + USER_INFO, + ROOM_INFO }; struct Message { @@ -272,6 +280,7 @@ namespace QuickMedia { virtual void add_user(MatrixEventUserInfo user_info) = 0; virtual void remove_user(MatrixEventUserInfo user_info) = 0; virtual void set_user_info(MatrixEventUserInfo user_info) = 0; + virtual void set_room_info(MatrixEventRoomInfo room_info) = 0; virtual void clear_data() = 0; }; @@ -303,6 +312,7 @@ namespace QuickMedia { void add_user(MatrixEventUserInfo user_info) override; void remove_user(MatrixEventUserInfo user_info) override; void set_user_info(MatrixEventUserInfo user_info) override; + void set_room_info(MatrixEventRoomInfo room_info) override; void for_each_user_in_room(RoomData *room, std::function callback); void set_room_as_read(RoomData *room); @@ -430,6 +440,8 @@ namespace QuickMedia { std::string filename; }; + using MatrixRoomInfoUpdateCallback = std::function; + class MatrixChatPage : public Page { public: MatrixChatPage(Program *program, std::string room_id, MatrixRoomsPage *rooms_page, std::string jump_to_event_id = ""); @@ -441,8 +453,9 @@ namespace QuickMedia { void add_user(MatrixEventUserInfo user_info); void remove_user(MatrixEventUserInfo user_info); void set_user_info(MatrixEventUserInfo user_info); + void set_room_info(MatrixEventRoomInfo room_info); - void set_current_room(RoomData *room, Body *users_body); + void set_current_room(RoomData *room, Body *users_body, MatrixRoomInfoUpdateCallback room_info_update_callback); size_t get_num_users_in_current_room() const; void set_room_as_read(RoomData *room); @@ -458,6 +471,7 @@ namespace QuickMedia { private: RoomData *current_room = nullptr; Body *users_body = nullptr; + MatrixRoomInfoUpdateCallback room_info_update_callback; }; class MatrixRoomDirectoryPage : public Page { @@ -609,6 +623,7 @@ namespace QuickMedia { void update(); private: void trigger_event(RoomData *room, MatrixEventType type, MatrixEventUserInfo user_info); + void trigger_event(RoomData *room, MatrixEventType type, MatrixEventRoomInfo room_info); void formatted_body_add_line(RoomData *room, std::string &formatted_body, const std::string &line_str); void replace_mentions(RoomData *room, std::string &text); diff --git a/src/QuickMedia.cpp b/src/QuickMedia.cpp index fbf2006..432dccb 100644 --- a/src/QuickMedia.cpp +++ b/src/QuickMedia.cpp @@ -5462,6 +5462,23 @@ namespace QuickMedia { bool move_room = false; + const float room_name_text_height = std::floor(18.0f * get_config().scale * get_config().font_scale); + mgl::Text room_name_text("", *FontLoader::get_font(FontLoader::FontType::LATIN_BOLD, room_name_text_height)); + const float room_name_text_padding_y = std::floor(10.0f * get_config().scale); + const float room_name_total_height = room_name_text_height + room_name_text_padding_y * 2.0f; + const float room_avatar_height = 32.0f; + + const float room_topic_text_height = std::floor(12.0f * get_config().scale * get_config().font_scale); + mgl::Text room_topic_text("", *FontLoader::get_font(FontLoader::FontType::LATIN, room_topic_text_height)); + room_topic_text.set_color(get_theme().faded_text_color); + + mgl::Text room_label(matrix_chat_page->rooms_page->get_title(), *FontLoader::get_font(FontLoader::FontType::LATIN_BOLD, 18 * get_config().scale * get_config().font_scale)); + room_label.set_position(mgl::vec2f(15.0f, room_name_text_padding_y + 4.0f)); + + mgl::Sprite room_avatar_sprite; + auto room_avatar_thumbnail_data = std::make_shared(); + bool avatar_applied = false; + std::vector tabs; ChatTab pinned_tab; @@ -5494,7 +5511,27 @@ namespace QuickMedia { matrix_chat_page->chat_body = tabs[MESSAGES_TAB_INDEX].body.get(); matrix_chat_page->messages_tab_visible = true; - matrix_chat_page->set_current_room(current_room, tabs[USERS_TAB_INDEX].body.get()); + matrix_chat_page->set_current_room(current_room, tabs[USERS_TAB_INDEX].body.get(), + [&](const MatrixEventRoomInfo &room_info) { + if(room_info.name) { + window.set_title(("QuickMedia - matrix - " + room_info.name.value()).c_str()); + std::string room_name = current_room->get_name(); + string_replace_all(room_name, '\n', ' '); + room_name_text.set_string(std::move(room_name)); + } + + if(room_info.topic) { + std::string room_topic = current_room->get_topic(); + string_replace_all(room_topic, '\n', ' '); + room_topic_text.set_string(std::move(room_topic)); + } + + if(room_info.avatar_url) { + room_avatar_sprite.set_texture(nullptr); + room_avatar_thumbnail_data.reset(); + avatar_applied = false; + } + }); size_t prev_num_users_in_room = 0; bool redraw = true; @@ -5560,22 +5597,6 @@ namespace QuickMedia { logo_sprite.set_scale(mgl::vec2f(0.8f * get_config().scale, 0.8f * get_config().scale)); mgl::vec2f logo_size(plugin_logo.get_size().x * logo_sprite.get_scale().x, plugin_logo.get_size().y * logo_sprite.get_scale().y); - const float room_name_text_height = std::floor(18.0f * get_config().scale * get_config().font_scale); - mgl::Text room_name_text("", *FontLoader::get_font(FontLoader::FontType::LATIN_BOLD, room_name_text_height)); - const float room_name_text_padding_y = std::floor(10.0f * get_config().scale); - const float room_name_total_height = room_name_text_height + room_name_text_padding_y * 2.0f; - const float room_avatar_height = 32.0f; - - const float room_topic_text_height = std::floor(12.0f * get_config().scale * get_config().font_scale); - mgl::Text room_topic_text("", *FontLoader::get_font(FontLoader::FontType::LATIN, room_topic_text_height)); - room_topic_text.set_color(get_theme().faded_text_color); - - mgl::Text room_label(matrix_chat_page->rooms_page->get_title(), *FontLoader::get_font(FontLoader::FontType::LATIN_BOLD, 18 * get_config().scale * get_config().font_scale)); - room_label.set_position(mgl::vec2f(15.0f, room_name_text_padding_y + 4.0f)); - - mgl::Sprite room_avatar_sprite; - auto room_avatar_thumbnail_data = std::make_shared(); - bool draw_room_list = show_room_side_panel; // TODO: What if these never end up referencing events? clean up automatically after a while? @@ -6364,8 +6385,6 @@ namespace QuickMedia { const float logo_padding_x = std::floor(10.0f * get_config().scale * get_config().spacing_scale); const float chat_input_padding_x = std::floor(10.0f * get_config().scale * get_config().spacing_scale); const float chat_input_padding_y = std::floor(10.0f * get_config().scale * get_config().spacing_scale); - - bool avatar_applied = false; auto jump_to_message = [&](const std::string &event_id) { const int selected_tab = ui_tabs.get_selected(); @@ -7245,7 +7264,7 @@ namespace QuickMedia { break; } case PageType::CHAT_LOGIN: { - matrix_chat_page->set_current_room(nullptr, nullptr); + matrix_chat_page->set_current_room(nullptr, nullptr, nullptr); fetch_messages_future.cancel(); cleanup_tasks(); tabs.clear(); @@ -7660,7 +7679,7 @@ namespace QuickMedia { if(matrix && !matrix->is_initial_sync_finished()) { std::string err_msg; if(matrix->did_initial_sync_fail(err_msg)) { - matrix_chat_page->set_current_room(nullptr, nullptr); + matrix_chat_page->set_current_room(nullptr, nullptr, nullptr); fetch_messages_future.cancel(); cleanup_tasks(); tabs.clear(); @@ -7696,7 +7715,7 @@ namespace QuickMedia { while(!matrix->is_initial_sync_finished()) { std::this_thread::sleep_for(std::chrono::milliseconds(10)); if(matrix->did_initial_sync_fail(err_msg)) { - matrix_chat_page->set_current_room(nullptr, nullptr); + matrix_chat_page->set_current_room(nullptr, nullptr, nullptr); fetch_messages_future.cancel(); cleanup_tasks(); tabs.clear(); @@ -7753,7 +7772,7 @@ namespace QuickMedia { } chat_page_end: - matrix_chat_page->set_current_room(nullptr, nullptr); + matrix_chat_page->set_current_room(nullptr, nullptr, nullptr); fetch_messages_future.cancel(); cleanup_tasks(); window.set_title("QuickMedia - matrix"); diff --git a/src/plugins/Matrix.cpp b/src/plugins/Matrix.cpp index 7318383..db37a97 100644 --- a/src/plugins/Matrix.cpp +++ b/src/plugins/Matrix.cpp @@ -510,6 +510,25 @@ namespace QuickMedia { chat_page->set_user_info(std::move(user_info)); } + void MatrixQuickMedia::set_room_info(MatrixEventRoomInfo room_info) { + auto it = room_body_item_by_room.find(room_info.room); + if(it == room_body_item_by_room.end()) + return; + + if(room_info.name) { + std::string room_name = room_info.name.value_or(room_info.room->id); + string_replace_all(room_name, '\n', ' '); + it->second->set_title(std::move(room_name)); + //body_item->thumbnail_url = room->get_avatar_url(); + } + + if(room_info.avatar_url) + it->second->thumbnail_url = room_info.avatar_url.value(); + + if(chat_page) + chat_page->set_room_info(std::move(room_info)); + } + void MatrixQuickMedia::for_each_user_in_room(RoomData *room, std::function callback) { auto &users = users_by_room[room]; for(const auto &user : users) { @@ -980,9 +999,17 @@ namespace QuickMedia { } } - void MatrixChatPage::set_current_room(RoomData *room, Body *users_body) { + void MatrixChatPage::set_room_info(MatrixEventRoomInfo room_info) { + if(!current_room || !room_info_update_callback || room_info.room != current_room) + return; + + room_info_update_callback(room_info); + } + + void MatrixChatPage::set_current_room(RoomData *room, Body *users_body, MatrixRoomInfoUpdateCallback room_info_update_callback) { this->current_room = room; this->users_body = users_body; + this->room_info_update_callback = std::move(room_info_update_callback); if(!room || !users_body) return; @@ -2521,6 +2548,53 @@ namespace QuickMedia { message->timestamp = timestamp; message->transaction_id = std::move(transaction_id); return message; + } else if(strcmp(type_json.GetString(), "m.room.name") == 0) { + std::string sender_display_name = extract_first_line_remove_newline_elipses(room_data->get_user_display_name(user_sender), AUTHOR_MAX_LENGTH); + const rapidjson::Value &new_room_name = GetMember(*content_json, "name"); + + auto message = std::make_shared(); + message->type = MessageType::SYSTEM; + message->user = user; + message->event_id = event_id_str; + if(new_room_name.IsString() && new_room_name.GetStringLength() > 0) + message->body = sender_display_name + " changed the room name to \"" + new_room_name.GetString() + "\""; + else + message->body = sender_display_name + " removed the room name"; + message->related_event_id = std::move(related_event_id); + message->related_event_type = related_event_type; + message->timestamp = timestamp; + message->transaction_id = std::move(transaction_id); + return message; + } else if(strcmp(type_json.GetString(), "m.room.topic") == 0) { + std::string sender_display_name = extract_first_line_remove_newline_elipses(room_data->get_user_display_name(user_sender), AUTHOR_MAX_LENGTH); + const rapidjson::Value &new_room_topic = GetMember(*content_json, "topic"); + + auto message = std::make_shared(); + message->type = MessageType::SYSTEM; + message->user = user; + message->event_id = event_id_str; + if(new_room_topic.IsString() && new_room_topic.GetStringLength() > 0) + message->body = sender_display_name + " changed the room topic to \"" + new_room_topic.GetString() + "\""; + else + message->body = sender_display_name + " removed the room topic"; + message->related_event_id = std::move(related_event_id); + message->related_event_type = related_event_type; + message->timestamp = timestamp; + message->transaction_id = std::move(transaction_id); + return message; + } else if(strcmp(type_json.GetString(), "m.room.avatar") == 0) { + std::string sender_display_name = extract_first_line_remove_newline_elipses(room_data->get_user_display_name(user_sender), AUTHOR_MAX_LENGTH); + + auto message = std::make_shared(); + message->type = MessageType::SYSTEM; + message->user = user; + message->event_id = event_id_str; + message->body = sender_display_name + " changed the room avatar"; + message->related_event_id = std::move(related_event_id); + message->related_event_type = related_event_type; + message->timestamp = timestamp; + message->transaction_id = std::move(transaction_id); + return message; } else { auto message = std::make_shared(); message->type = MessageType::UNIMPLEMENTED; @@ -2637,6 +2711,10 @@ namespace QuickMedia { if(!events_json.IsArray()) return; + bool update_room_name = false; + bool update_room_topic = false; + bool update_room_avatar_url = false; + for(const rapidjson::Value &event_item_json : events_json.GetArray()) { if(!event_item_json.IsObject()) continue; @@ -2661,7 +2739,7 @@ namespace QuickMedia { if(!name_json.IsString()) continue; - room_data->set_name(name_json.GetString(), item_timestamp); // TODO: Update room name in gui + update_room_name |= room_data->set_name(name_json.GetString(), item_timestamp); room_data->name_is_fallback = false; } else if(strcmp(type_json.GetString(), "m.room.avatar") == 0) { const rapidjson::Value &content_json = GetMember(event_item_json, "content"); @@ -2672,7 +2750,7 @@ namespace QuickMedia { if(!url_json.IsString() || strncmp(url_json.GetString(), "mxc://", 6) != 0) continue; - room_data->set_avatar_url(get_thumbnail_url(homeserver, thumbnail_url_extract_media_id(url_json.GetString())), item_timestamp); // TODO: Update avatar url in gui + update_room_avatar_url |= room_data->set_avatar_url(get_thumbnail_url(homeserver, thumbnail_url_extract_media_id(url_json.GetString())), item_timestamp); room_data->avatar_is_fallback = false; } else if(strcmp(type_json.GetString(), "m.room.topic") == 0) { const rapidjson::Value &content_json = GetMember(event_item_json, "content"); @@ -2683,21 +2761,32 @@ namespace QuickMedia { if(!topic_json.IsString()) continue; - room_data->set_topic(topic_json.GetString(), item_timestamp); // TODO: Update topic in gui + update_room_topic |= room_data->set_topic(topic_json.GetString(), item_timestamp); } } + + MatrixEventRoomInfo room_info_event; + room_info_event.room = room_data; + room_info_event.name = update_room_name ? std::optional(room_data->get_name()) : std::nullopt; + room_info_event.topic = update_room_topic ? std::optional(room_data->get_topic()) : std::nullopt; + room_info_event.avatar_url = update_room_avatar_url ? std::optional(room_data->get_avatar_url()) : std::nullopt; + if(update_room_name || update_room_topic || update_room_avatar_url) + trigger_event(room_data, MatrixEventType::ROOM_INFO, std::move(room_info_event)); } void Matrix::set_room_info_to_users_if_empty(RoomData *room, const std::string &room_creator_user_id) { - bool has_room_name = room->has_name(); - bool has_room_avatar_url = room->has_avatar_url(); + const bool has_room_name = room->has_name(); + const bool has_room_avatar_url = room->has_avatar_url(); + + bool update_room_name = false; + bool update_room_avatar_url = false; std::vector> users_excluding_me; if(!has_room_name || !has_room_avatar_url || room->name_is_fallback || room->avatar_is_fallback) users_excluding_me = room->get_users_excluding_me(my_user_id); if(!has_room_name) { - room->set_name(combine_user_display_names_for_room_name(users_excluding_me, room_creator_user_id), 0); // TODO: Update in gui + update_room_name |= room->set_name(combine_user_display_names_for_room_name(users_excluding_me, room_creator_user_id), 0); room->name_is_fallback = true; } @@ -2705,13 +2794,20 @@ namespace QuickMedia { if(users_excluding_me.empty()) { auto user = get_user_by_id(room, room_creator_user_id); if(user) - room->set_avatar_url(room->get_user_avatar_url(user), 0); // TODO: Update in gui + update_room_avatar_url |= room->set_avatar_url(room->get_user_avatar_url(user), 0); } else { // TODO: If there are multiple users, then we want to use some other type of avatar, not the first users avatar - room->set_avatar_url(room->get_user_avatar_url(users_excluding_me.front()), 0); // TODO: Update in gui + update_room_avatar_url |= room->set_avatar_url(room->get_user_avatar_url(users_excluding_me.front()), 0); } room->avatar_is_fallback = true; } + + MatrixEventRoomInfo room_info_event; + room_info_event.room = room; + room_info_event.name = update_room_name ? std::optional(room->get_name()) : std::nullopt; + room_info_event.avatar_url = update_room_avatar_url ? std::optional(room->get_avatar_url()) : std::nullopt; + if(update_room_name || update_room_avatar_url) + trigger_event(room, MatrixEventType::ROOM_INFO, std::move(room_info_event)); } void Matrix::events_add_pinned_events(const rapidjson::Value &events_json, RoomData *room_data) { @@ -4902,6 +4998,22 @@ namespace QuickMedia { }); break; } + default: + break; + } + } + + void Matrix::trigger_event(RoomData *room, MatrixEventType type, MatrixEventRoomInfo room_info) { + room_info.room = room; + switch(type) { + case MatrixEventType::ROOM_INFO: { + ui_thread_tasks.push([this, room_info{std::move(room_info)}]{ + delegate->set_room_info(std::move(room_info)); + }); + break; + } + default: + break; } } } \ No newline at end of file -- cgit v1.2.3