From 3609429b3d8fccd99994dde015b6516376e6835b Mon Sep 17 00:00:00 2001 From: dec05eba Date: Thu, 20 May 2021 05:20:49 +0200 Subject: Move chat page room list logic to matrix delegate --- src/Body.cpp | 14 +-- src/QuickMedia.cpp | 118 +++---------------------- src/plugins/Matrix.cpp | 231 +++++++++++++++++++++++++++++++++++++++---------- 3 files changed, 207 insertions(+), 156 deletions(-) (limited to 'src') diff --git a/src/Body.cpp b/src/Body.cpp index d51ea29..72974fa 100644 --- a/src/Body.cpp +++ b/src/Body.cpp @@ -487,10 +487,10 @@ namespace QuickMedia { // TODO: Use a render target for the whole body so all images can be put into one. // TODO: Load thumbnails with more than one thread. void Body::draw(sf::RenderWindow &window, sf::Vector2f pos, sf::Vector2f size, const Json::Value &content_progress) { - if(items_dirty) { - items_dirty = false; - if(using_filter) + if(items_dirty != DirtyState::FALSE) { + if(using_filter || items_dirty == DirtyState::FORCE_TRUE) filter_search_fuzzy(current_filter, false); + items_dirty = DirtyState::FALSE; } sf::Vector2f scissor_pos = pos; @@ -1547,7 +1547,11 @@ namespace QuickMedia { clamp_selected_item_to_body_count = 1; } - void Body::items_set_dirty() { - items_dirty = true; + void Body::items_set_dirty(bool force) { + items_dirty = force ? DirtyState::FORCE_TRUE : DirtyState::TRUE; + } + + void Body::apply_search_filter_for_item(BodyItem *body_item) { + filter_search_fuzzy_item(current_filter, body_item); } } diff --git a/src/QuickMedia.cpp b/src/QuickMedia.cpp index 5bbcfd7..a9956d3 100644 --- a/src/QuickMedia.cpp +++ b/src/QuickMedia.cpp @@ -3952,11 +3952,13 @@ namespace QuickMedia { Tabs ui_tabs(&rounded_rectangle_shader, sf::Color::Transparent); const int PINNED_TAB_INDEX = ui_tabs.add_tab("Pinned messages (0)"); const int MESSAGES_TAB_INDEX = ui_tabs.add_tab("Messages"); - const int USERS_TAB_INDEX = ui_tabs.add_tab("Users"); + const int USERS_TAB_INDEX = ui_tabs.add_tab("Users (0)"); ui_tabs.set_selected(MESSAGES_TAB_INDEX); 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()); + size_t prev_num_users_in_room = 0; bool redraw = true; @@ -4994,91 +4996,6 @@ namespace QuickMedia { //tabs.clear(); }; - auto on_add_user_event = [&ui_tabs, &tabs, USERS_TAB_INDEX](MatrixAddUserEvent *event) { - // Ignore if the user already exists in the room - // TODO: Remove the need for this - for(auto &body_item : tabs[USERS_TAB_INDEX].body->items) { - if(body_item->url == event->user_info.user_id) - return; - } - - std::string display_name = event->user_info.display_name.value_or(event->user_info.user_id); - size_t insert_position = get_body_item_sorted_insert_position_by_author(tabs[USERS_TAB_INDEX].body->items, display_name, 0); - - auto body_item = BodyItem::create(""); - body_item->url = event->user_info.user_id; - body_item->set_author(std::move(display_name)); - body_item->set_author_color(user_id_to_color(event->user_info.user_id)); - body_item->set_description(event->user_info.user_id); - body_item->set_description_color(sf::Color(179, 179, 179)); - if(event->user_info.avatar_url) - body_item->thumbnail_url = event->user_info.avatar_url.value(); - body_item->thumbnail_mask_type = ThumbnailMaskType::CIRCLE; - body_item->thumbnail_size = AVATAR_THUMBNAIL_SIZE; - tabs[USERS_TAB_INDEX].body->items.insert(tabs[USERS_TAB_INDEX].body->items.begin() + insert_position, std::move(body_item)); - tabs[USERS_TAB_INDEX].body->items_set_dirty(); - - ui_tabs.set_text(USERS_TAB_INDEX, "Users (" + std::to_string(tabs[USERS_TAB_INDEX].body->items.size()) + ")"); - }; - - // TODO: Actually trigger this when a user leaves the room. Also remove the user from the room in the matrix plugin - auto on_remove_user_event = [&ui_tabs, &tabs, USERS_TAB_INDEX](MatrixRemoveUserEvent *event) { - for(auto it = tabs[USERS_TAB_INDEX].body->items.begin(), end = tabs[USERS_TAB_INDEX].body->items.end(); it != end; ++it) { - if((*it)->url == event->user_info.user_id) { - tabs[USERS_TAB_INDEX].body->items.erase(it); - ui_tabs.set_text(USERS_TAB_INDEX, "Users (" + std::to_string(tabs[USERS_TAB_INDEX].body->items.size()) + ")"); - return; - } - } - }; - - auto on_user_info_event = [&tabs, USERS_TAB_INDEX](MatrixUserInfoEvent *event) { - for(auto it = tabs[USERS_TAB_INDEX].body->items.begin(), end = tabs[USERS_TAB_INDEX].body->items.end(); it != end; ++it) { - if((*it)->url == event->user_info.user_id) { - if(event->user_info.avatar_url) - (*it)->thumbnail_url = event->user_info.avatar_url.value(); - - if(event->user_info.display_name) { - std::string display_name; - if(event->user_info.display_name.value().empty()) - display_name = event->user_info.user_id; - else - display_name = event->user_info.display_name.value(); - - (*it)->set_author(std::move(display_name)); - - auto user_body_item = *it; - tabs[USERS_TAB_INDEX].body->items.erase(it); - - // TODO: extract_first_line_remove_newline_elipses(room->get_user_display_name(message->user), AUTHOR_MAX_LENGTH), - // But that should be done in Text because we need author to be 100% the same as in the input to reorder users - size_t insert_position = get_body_item_sorted_insert_position_by_author(tabs[USERS_TAB_INDEX].body->items, user_body_item->get_author(), 0); - tabs[USERS_TAB_INDEX].body->items.insert(tabs[USERS_TAB_INDEX].body->items.begin() + insert_position, std::move(user_body_item)); - tabs[USERS_TAB_INDEX].body->items_set_dirty(); - } - - return; - } - } - }; - - matrix->enable_event_queue(current_room); - { - auto users_in_room = current_room->get_users(); - for(auto &user : users_in_room) { - std::string display_name = current_room->get_user_display_name(user); - std::string avatar_url = current_room->get_user_avatar_url(user); - - MatrixEventUserInfo user_info; - user_info.user_id = user->user_id; - user_info.display_name = std::move(display_name); - user_info.avatar_url = std::move(avatar_url); - - MatrixAddUserEvent add_user_event(std::move(user_info)); - on_add_user_event(&add_user_event); - } - } - // TODO: Remove this once synapse bug has been resolved where /sync does not include user info for new messages when using message filter that limits number of messages for initial sync, // and then only call this when viewing the users tab for the first time. // Note that this is not needed when new users join the room, as those will be included in the sync timeline (with membership events) @@ -5390,22 +5307,11 @@ namespace QuickMedia { matrix->update(); mention.update(); - - std::unique_ptr matrix_event; - while((matrix_event = matrix->pop_event()) != nullptr) { - if(matrix_event) { - switch(matrix_event->type) { - case MatrixEvent::Type::ADD_USER: - on_add_user_event(static_cast(matrix_event.get())); - break; - case MatrixEvent::Type::REMOVE_USER: - on_remove_user_event(static_cast(matrix_event.get())); - break; - case MatrixEvent::Type::USER_INFO: - on_user_info_event(static_cast(matrix_event.get())); - break; - } - } + + const size_t num_users_in_room = matrix_chat_page->get_num_users_in_current_room(); + if(num_users_in_room != prev_num_users_in_room) { + prev_num_users_in_room = num_users_in_room; + ui_tabs.set_text(USERS_TAB_INDEX, "Users (" + std::to_string(num_users_in_room) + ")"); } while((provisional_message = provisional_message_queue.pop_if_available()) != std::nullopt) { @@ -5451,7 +5357,7 @@ namespace QuickMedia { break; } case PageType::CHAT_LOGIN: { - matrix->disable_event_queue(); + matrix_chat_page->set_current_room(nullptr, nullptr); previous_messages_future.cancel(); cleanup_tasks(); tabs.clear(); @@ -5818,7 +5724,7 @@ namespace QuickMedia { if(matrix && !matrix->is_initial_sync_finished()) { std::string err_msg; if(matrix->did_initial_sync_fail(err_msg)) { - matrix->disable_event_queue(); + matrix_chat_page->set_current_room(nullptr, nullptr); previous_messages_future.cancel(); cleanup_tasks(); tabs.clear(); @@ -5848,7 +5754,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->disable_event_queue(); + matrix_chat_page->set_current_room(nullptr, nullptr); previous_messages_future.cancel(); cleanup_tasks(); tabs.clear(); @@ -5899,7 +5805,7 @@ namespace QuickMedia { } chat_page_end: - matrix->disable_event_queue(); + matrix_chat_page->set_current_room(nullptr, nullptr); previous_messages_future.cancel(); cleanup_tasks(); window.setTitle("QuickMedia - matrix"); diff --git a/src/plugins/Matrix.cpp b/src/plugins/Matrix.cpp index 8a0f486..afa8738 100644 --- a/src/plugins/Matrix.cpp +++ b/src/plugins/Matrix.cpp @@ -437,6 +437,50 @@ namespace QuickMedia { show_notification("QuickMedia matrix - " + sender + " (" + room->get_name() + ")", body); } + static UsersByRoom::iterator find_user_data_by_id(UsersByRoom &users_by_room, const MatrixEventUserInfo &user_info) { + RoomData *room = user_info.room; + auto range = users_by_room.equal_range(room); + for(auto &it = range.first; it != range.second; ++it) { + if(it->second.user_id == user_info.user_id) + return it; + } + return users_by_room.end(); + } + + void MatrixQuickMedia::add_user(MatrixEventUserInfo user_info) { + auto it = find_user_data_by_id(users_by_room, user_info); + if(it == users_by_room.end()) { + users_by_room.insert(std::make_pair(user_info.room, user_info)); + if(chat_page) + chat_page->add_user(std::move(user_info)); + } + } + + void MatrixQuickMedia::remove_user(MatrixEventUserInfo user_info) { + auto it = find_user_data_by_id(users_by_room, user_info); + if(it != users_by_room.end()) { + users_by_room.erase(it); + if(chat_page) + chat_page->remove_user(std::move(user_info)); + } + } + + void MatrixQuickMedia::set_user_info(MatrixEventUserInfo user_info) { + auto it = find_user_data_by_id(users_by_room, user_info); + if(it != users_by_room.end()) { + it->second = user_info; + if(chat_page) + chat_page->set_user_info(std::move(user_info)); + } + } + + void MatrixQuickMedia::for_each_user_in_room(RoomData *room, std::function callback) { + auto range = users_by_room.equal_range(room); + for(auto &it = range.first; it != range.second; ++it) { + callback(it->second); + } + } + static void sort_room_body_items(std::vector> &room_body_items) { std::sort(room_body_items.begin(), room_body_items.end(), [](const std::shared_ptr &body_item1, const std::shared_ptr &body_item2) { RoomData *room1 = static_cast(body_item1->userdata); @@ -597,6 +641,10 @@ namespace QuickMedia { return PluginResult::OK; } + void MatrixRoomsPage::on_navigate_to_page(Body *body) { + body->items_set_dirty(true); + } + void MatrixRoomsPage::add_body_item(std::shared_ptr body_item) { insert_room_body_item_by_timestamp(body->items, body_item); } @@ -667,6 +715,10 @@ namespace QuickMedia { return PluginResult::OK; } + void MatrixRoomTagsPage::on_navigate_to_page(Body *body) { + body->items_set_dirty(true); + } + void MatrixRoomTagsPage::add_room_body_item_to_tag(std::shared_ptr body_item, const std::string &tag) { TagData *tag_data; auto tag_body_it = tag_body_items_by_name.find(tag); @@ -679,8 +731,8 @@ namespace QuickMedia { tag_body_item->url = tag; tag_body_items_by_name.insert(std::make_pair(tag, TagData{tag_body_item, {}})); // TODO: Sort by tag priority + body->apply_search_filter_for_item(tag_body_item.get()); body->items.push_back(tag_body_item); - body->items_set_dirty(); tag_data = &tag_body_items_by_name[tag]; tag_data->tag_item = tag_body_item; } @@ -783,8 +835,8 @@ namespace QuickMedia { void MatrixInvitesPage::add_body_item(std::shared_ptr body_item) { // TODO: Insert in reverse order (to show the latest invite at the top?) + body->apply_search_filter_for_item(body_item.get()); body->insert_item_by_timestamp(std::move(body_item)); - body->items_set_dirty(); if(body->items.size() != prev_invite_count) { prev_invite_count = body->items.size(); title = "Invites (" + std::to_string(body->items.size()) + ")"; @@ -812,6 +864,108 @@ namespace QuickMedia { rooms_page->matrix_delegate->chat_page = nullptr; } + // Returns |default_value| if the input items is empty + static size_t get_body_item_sorted_insert_position_by_author(BodyItems &body_items, const std::string &display_name, size_t default_value) { + for(size_t i = 0; i < body_items.size(); ++i) { + auto &body_item = body_items[i]; + if(strcasecmp(display_name.c_str(), body_item->get_author().c_str()) <= 0) + return i; + } + return default_value; + } + + static void add_user_to_body_by_user_info(Body *users_body, const MatrixEventUserInfo &user_info) { + std::string display_name = user_info.display_name.value_or(user_info.user_id); + size_t insert_index = get_body_item_sorted_insert_position_by_author(users_body->items, display_name, 0); + + auto body_item = BodyItem::create(""); + body_item->url = user_info.user_id; + body_item->set_author(std::move(display_name)); + body_item->set_author_color(user_id_to_color(user_info.user_id)); + body_item->set_description(user_info.user_id); + body_item->set_description_color(sf::Color(179, 179, 179)); + if(user_info.avatar_url) + body_item->thumbnail_url = user_info.avatar_url.value(); + body_item->thumbnail_mask_type = ThumbnailMaskType::CIRCLE; + body_item->thumbnail_size = sf::Vector2i(32, 32); + + users_body->apply_search_filter_for_item(body_item.get()); + users_body->items.insert(users_body->items.begin() + insert_index, std::move(body_item)); + } + + void MatrixChatPage::add_user(MatrixEventUserInfo user_info) { + if(!current_room || !users_body || user_info.room != current_room) + return; + + // Ignore if the user already exists in the room + for(auto &body_item : users_body->items) { + if(body_item->url == user_info.user_id) + return; + } + + add_user_to_body_by_user_info(users_body, user_info); + } + + void MatrixChatPage::remove_user(MatrixEventUserInfo user_info) { + if(!current_room || !users_body || user_info.room != current_room) + return; + + for(auto it = users_body->items.begin(), end = users_body->items.end(); it != end; ++it) { + if((*it)->url == user_info.user_id) { + users_body->items.erase(it); + return; + } + } + } + + void MatrixChatPage::set_user_info(MatrixEventUserInfo user_info) { + if(!current_room || !users_body || user_info.room != current_room) + return; + + for(auto it = users_body->items.begin(), end = users_body->items.end(); it != end; ++it) { + if((*it)->url == user_info.user_id) { + if(user_info.avatar_url) + (*it)->thumbnail_url = user_info.avatar_url.value(); + + if(user_info.display_name) { + std::string display_name; + if(user_info.display_name.value().empty()) + display_name = user_info.user_id; + else + display_name = user_info.display_name.value(); + + (*it)->set_author(std::move(display_name)); + + auto user_body_item = *it; + users_body->items.erase(it); + + // TODO: extract_first_line_remove_newline_elipses(room->get_user_display_name(message->user), AUTHOR_MAX_LENGTH), + // But that should be done in Text because we need author to be 100% the same as in the input to reorder users + users_body->apply_search_filter_for_item(user_body_item.get()); + size_t insert_index = get_body_item_sorted_insert_position_by_author(users_body->items, user_body_item->get_author(), 0); + users_body->items.insert(users_body->items.begin() + insert_index, std::move(user_body_item)); + } + + return; + } + } + } + + void MatrixChatPage::set_current_room(RoomData *room, Body *users_body) { + this->current_room = room; + this->users_body = users_body; + if(!room || !users_body) + return; + + rooms_page->matrix_delegate->for_each_user_in_room(room, [users_body](const MatrixEventUserInfo &user_info) { + add_user_to_body_by_user_info(users_body, user_info); + }); + } + + size_t MatrixChatPage::get_num_users_in_current_room() const { + return users_body ? users_body->items.size() : 0; + } + PluginResult MatrixRoomDirectoryPage::submit(const std::string &title, const std::string&, std::vector &result_tabs) { std::string server_name = title; @@ -1516,11 +1670,9 @@ namespace QuickMedia { event_user_info.avatar_url = avatar_url; if(is_new_user) { - auto event = std::make_unique(std::move(event_user_info)); - trigger_event(room_data, std::move(event)); + trigger_event(room_data, MatrixEventType::ADD_USER, std::move(event_user_info)); } else { - auto event = std::make_unique(std::move(event_user_info)); - trigger_event(room_data, std::move(event)); + trigger_event(room_data, MatrixEventType::USER_INFO, std::move(event_user_info)); } return user_info; @@ -1779,8 +1931,7 @@ namespace QuickMedia { if(is_new_user) { MatrixEventUserInfo user_info; user_info.user_id = user->user_id; - auto event = std::make_unique(std::move(user_info)); - trigger_event(room_data, std::move(event)); + trigger_event(room_data, MatrixEventType::ADD_USER, std::move(user_info)); } auto user_sender = user; @@ -1791,8 +1942,7 @@ namespace QuickMedia { if(is_new_user) { MatrixEventUserInfo user_info; user_info.user_id = user_sender->user_id; - auto event = std::make_unique(std::move(user_info)); - trigger_event(room_data, std::move(event)); + trigger_event(room_data, MatrixEventType::ADD_USER, std::move(user_info)); } } @@ -1934,8 +2084,7 @@ namespace QuickMedia { user_info.display_name = std::move(new_display_name); user_info.avatar_url = std::move(new_avatar_url); - auto event = std::make_unique(std::move(user_info)); - trigger_event(room_data, std::move(event)); + trigger_event(room_data, MatrixEventType::USER_INFO, std::move(user_info)); } } else { body = user_display_name + " joined the room"; @@ -3781,7 +3930,7 @@ namespace QuickMedia { if(room_it == room_data_by_id.end()) return; - // We want to clear data instead of removing the object iself becasue we want to instance to still be valid, + // We want to clear data instead of removing the object iself because we want to instance to still be valid, // also in the future we can have a "history" tag for rooms we have just left rooms[room_it->second]->clear_data(); room_data_by_id.erase(room_it); @@ -3898,11 +4047,9 @@ namespace QuickMedia { user_info.avatar_url = avatar_url; if(is_new_user) { - auto event = std::make_unique(std::move(user_info)); - trigger_event(room, std::move(event)); + trigger_event(room, MatrixEventType::ADD_USER, std::move(user_info)); } else { - auto event = std::make_unique(std::move(user_info)); - trigger_event(room, std::move(event)); + trigger_event(room, MatrixEventType::USER_INFO, std::move(user_info)); } } #else @@ -3974,19 +4121,6 @@ namespace QuickMedia { #endif } - void Matrix::enable_event_queue(RoomData *room) { - std::lock_guard lock(event_queue_mutex); - assert(!current_event_queue_room); - current_event_queue_room = room; - } - - void Matrix::disable_event_queue() { - std::lock_guard lock(event_queue_mutex); - assert(current_event_queue_room); - current_event_queue_room = nullptr; - event_queue.clear(); - } - void Matrix::update() { std::optional> task; while((task = ui_thread_tasks.pop_if_available()) != std::nullopt) { @@ -3994,20 +4128,27 @@ namespace QuickMedia { } } - std::unique_ptr Matrix::pop_event() { - std::lock_guard lock(event_queue_mutex); - if(!current_event_queue_room || event_queue.empty()) - return nullptr; - - auto event_data = std::move(event_queue.front()); - event_queue.pop_front(); - return event_data; - } - - void Matrix::trigger_event(RoomData *room, std::unique_ptr event) { - std::lock_guard lock(event_queue_mutex); - if(sync_is_cache || !current_event_queue_room || current_event_queue_room != room) - return; - event_queue.push_back(std::move(event)); + void Matrix::trigger_event(RoomData *room, MatrixEventType type, MatrixEventUserInfo user_info) { + user_info.room = room; + switch(type) { + case MatrixEventType::ADD_USER: { + ui_thread_tasks.push([this, user_info{std::move(user_info)}]{ + delegate->add_user(std::move(user_info)); + }); + break; + } + case MatrixEventType::REMOVE_USER: { + ui_thread_tasks.push([this, user_info{std::move(user_info)}]{ + delegate->remove_user(std::move(user_info)); + }); + break; + } + case MatrixEventType::USER_INFO: { + ui_thread_tasks.push([this, user_info{std::move(user_info)}]{ + delegate->set_user_info(std::move(user_info)); + }); + break; + } + } } } \ No newline at end of file -- cgit v1.2.3