From 4af367373f6f92b0fc1d79b2871fa942e1eb86fa Mon Sep 17 00:00:00 2001 From: dec05eba Date: Mon, 9 Nov 2020 00:08:53 +0100 Subject: Matrix: update user display name/avatar when updated in /sync; fix backspace search delay --- TODO | 6 +- include/SearchBar.hpp | 4 +- plugins/Matrix.hpp | 18 ++++-- src/QuickMedia.cpp | 36 +++++------- src/SearchBar.cpp | 22 +++++--- src/plugins/Matrix.cpp | 148 +++++++++++++++++++++++++++++++------------------ 6 files changed, 139 insertions(+), 95 deletions(-) diff --git a/TODO b/TODO index 7eb4689..2f0ec6b 100644 --- a/TODO +++ b/TODO @@ -68,7 +68,6 @@ Add url preview for matrix (using matrix api, fallback to client url preview (us IMPORTANT: Cleanup old messages in matrix (from matrix plugin), and instead either save them to disk or refetch them from server when going up to read old messages. (High memory usage, high disk space) Use memberName() instead of key() when iterating json object. key() creates a copy, memberName() doesn't. Do not try to reload/redownload thumbnail that fails to download after its cleared when its no longer visible on screen and then becomes visible. -Sort matrix events by timestamp (not messages). This affects the applying of certain actions, such as changing avatar, room name, etc. Show google recaptcha on youtube when search/play fails, which can happen when using tor. Show notifications when we receive a message in a matrix room even if we are not mentioned. This happens when we have set to receive notifications for all messages. If there are multiple users with the same name in a matrix room, then display the user id beside the displayname. @@ -85,7 +84,6 @@ Modify sfml to use GL_COMPRESSED_LUMINANCE and other texture compression modes ( Decrease memory usage even further (mostly in matrix /sync when part of large rooms) by using rapidjson SAX style API to stream json string into SAX style parsing. Sometimes we fail to get images in mangadex, most common reason being that the manga is licensed and we can't view the manga on mangadex. QuickMedia should implement mangaplus and redirect us to mangaplus plugin to view the manga, or simply show that we cant view the manga because its licensed. Show redacted messages even when part of the initial sync in matrix. Right now they are hidden while new sync redacted messages are not. -Update displayname/avatar in matrix when updated in /sync. Fix inconsistent behavior when editing a message that is replied to in matrix. Right now if the replied to message already exits in the body then its used directly and when editing that message the reply message shows the edit embedded, but not if the edit is of an body item that is created because we dont already have it, to fix this we could perhaps replace the newly created body items for replies when loading old messages and one of the old messages is also one of the embedded messages (by event id). Add button to skip to next video. MPV has this feature when setting "next" video (can be done over IPC). @@ -125,11 +123,11 @@ Add a notifications tab to show messages that mention us in all rooms (and then Disable message input in matrix when muted. Preview rooms? Instantly show messages posted by me until we receive our message from the server (use post response which includes event id). -Get user displayname, avatar, room name, etc updates from /sync and update them in gui. Test if glScissor doesn't break loading of embedded body item. Handle matrix token being invalidated while running. Update upload limit if its updated on the server (can it be updated while the server is running?). Editing a reply removes reply formatting (both in body and formatted_body). Element also does this when you edit a reply twice. This breaks element mobile that is unable to display replied-to messages without correct formatting (doesn't fetch the replied-to message). Implement m.room.tombstone. Show a marker when a room uses encryption. -Remove replied-to message text in room preview. That shows ignored users text and we want to see the reply message instead anyways. \ No newline at end of file +Remove replied-to message text in room preview. That shows ignored users text and we want to see the reply message instead anyways. +Update room name/avatar with new data in /sync. \ No newline at end of file diff --git a/include/SearchBar.hpp b/include/SearchBar.hpp index 78420ec..4c9757c 100644 --- a/include/SearchBar.hpp +++ b/include/SearchBar.hpp @@ -26,7 +26,6 @@ namespace QuickMedia { void on_event(sf::Event &event); void update(); void onWindowResize(const sf::Vector2f &window_size); - void onTextEntered(sf::Uint32 codepoint); void clear(); void append_text(const std::string &text_to_add); bool is_cursor_at_start_of_line() const; @@ -48,6 +47,7 @@ namespace QuickMedia { int autocomplete_search_delay; bool caret_visible; private: + void onTextEntered(sf::Uint32 codepoint); void clear_autocomplete_if_text_not_substring(); void clear_autocomplete_if_last_char_not_substr(); private: @@ -66,7 +66,7 @@ namespace QuickMedia { bool needs_update; bool input_masked; bool typing; - int backspace_seq_count; + bool backspace_pressed; float vertical_pos; sf::Clock time_since_search_update; }; diff --git a/plugins/Matrix.hpp b/plugins/Matrix.hpp index 0248e09..362abeb 100644 --- a/plugins/Matrix.hpp +++ b/plugins/Matrix.hpp @@ -14,12 +14,15 @@ namespace QuickMedia { struct UserInfo { friend struct RoomData; + UserInfo(RoomData *room, std::string user_id); + UserInfo(RoomData *room, std::string user_id, std::string display_name, std::string avatar_url); - std::string user_id; + RoomData *room; + const sf::Color display_name_color; + const std::string user_id; + private: std::string display_name; std::string avatar_url; - sf::Color display_name_color; - private: std::string read_marker_event_id; }; @@ -60,7 +63,14 @@ namespace QuickMedia { void add_user(std::shared_ptr user); void set_user_read_marker(std::shared_ptr &user, const std::string &event_id); - std::string get_user_read_marker(std::shared_ptr &user); + std::string get_user_read_marker(const std::shared_ptr &user); + + std::string get_user_display_name(const std::shared_ptr &user); + std::string get_user_avatar_url(const std::shared_ptr &user); + + // Set to empty to remove (in which case the display name will be the user id) + void set_user_display_name(std::shared_ptr &user, std::string display_name); + void set_user_avatar_url(std::shared_ptr &user, std::string avatar_url); // Ignores duplicates void prepend_messages_reverse(const std::vector> &new_messages); diff --git a/src/QuickMedia.cpp b/src/QuickMedia.cpp index 0ec8d1d..fdaf3d1 100644 --- a/src/QuickMedia.cpp +++ b/src/QuickMedia.cpp @@ -657,8 +657,6 @@ namespace QuickMedia { } } else if(handle_searchbar) { assert(search_bar); - if(event.type == sf::Event::TextEntered) - search_bar->onTextEntered(event.text.unicode); search_bar->on_event(event); } } @@ -1126,8 +1124,6 @@ namespace QuickMedia { } if(tabs[selected_tab].search_bar) { - if(event.type == sf::Event::TextEntered) - tabs[selected_tab].search_bar->onTextEntered(event.text.unicode); tabs[selected_tab].search_bar->on_event(event); } @@ -2988,8 +2984,6 @@ namespace QuickMedia { redraw = true; } else if(event.type == sf::Event::GainedFocus) { redraw = true; - } else if(event.type == sf::Event::TextEntered) { - inputs[focused_input]->onTextEntered(event.text.unicode); } else if(event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::Tab) { for(int i = 0; i < num_inputs; ++i) { inputs[i]->caret_visible = false; @@ -3047,9 +3041,9 @@ namespace QuickMedia { static const sf::Vector2i CHAT_MESSAGE_THUMBNAIL_MAX_SIZE(600, 337); - static std::shared_ptr message_to_body_item(Message *message, UserInfo *me) { + static std::shared_ptr message_to_body_item(RoomData *room, Message *message, const std::string &my_display_name, const std::string &my_user_id) { auto body_item = BodyItem::create(""); - body_item->set_author(message->user->display_name); + body_item->set_author(room->get_user_display_name(message->user)); body_item->set_description(message_get_body_remove_formatting(message)); body_item->set_timestamp(message->timestamp); if(!message->thumbnail_url.empty()) { @@ -3059,7 +3053,7 @@ namespace QuickMedia { body_item->thumbnail_url = message->url; body_item->thumbnail_size = message->thumbnail_size; } else { - body_item->thumbnail_url = message->user->avatar_url; + body_item->thumbnail_url = room->get_user_avatar_url(message->user); body_item->thumbnail_mask_type = ThumbnailMaskType::CIRCLE; // if construct is not configured to use ImageMagic then it wont give thumbnails of size 32x32 even when requested and the spec says that the server SHOULD do that body_item->thumbnail_size = sf::Vector2i(32, 32); @@ -3070,15 +3064,15 @@ namespace QuickMedia { body_item->userdata = (void*)message; // Note: message has to be valid as long as body_item is used! if(message->related_event_type == RelatedEventType::REDACTION || message->related_event_type == RelatedEventType::EDIT) body_item->visible = false; - if(me && (message_contains_user_mention(message->body, me->display_name) || message_contains_user_mention(message->body, me->user_id))) + if(message_contains_user_mention(message->body, my_display_name) || message_contains_user_mention(message->body, my_user_id)) body_item->set_description_color(sf::Color(255, 100, 100)); return body_item; } - static BodyItems messages_to_body_items(const Messages &messages, UserInfo *me) { + static BodyItems messages_to_body_items(RoomData *room, const Messages &messages, const std::string &my_display_name, const std::string &my_user_id) { BodyItems result_items(messages.size()); for(size_t i = 0; i < messages.size(); ++i) { - result_items[i] = message_to_body_item(messages[i].get(), me); + result_items[i] = message_to_body_item(room, messages[i].get(), my_display_name, my_user_id); } return result_items; } @@ -3190,10 +3184,10 @@ namespace QuickMedia { // TODO: What if these never end up referencing events? clean up automatically after a while? std::unordered_map unreferenced_event_by_room; - auto set_body_as_deleted = [](Message *message, BodyItem *body_item) { + auto set_body_as_deleted = [¤t_room](Message *message, BodyItem *body_item) { body_item->embedded_item = nullptr; body_item->embedded_item_status = FetchStatus::NONE; - body_item->thumbnail_url = message->user->avatar_url; + body_item->thumbnail_url = current_room->get_user_avatar_url(message->user); body_item->thumbnail_mask_type = ThumbnailMaskType::CIRCLE; body_item->set_description_color(sf::Color::White); body_item->thumbnail_size = sf::Vector2i(32, 32); @@ -3306,7 +3300,8 @@ namespace QuickMedia { for(auto &message : all_messages) { fetched_messages_set.insert(message->event_id); } - tabs[MESSAGES_TAB_INDEX].body->insert_items_by_timestamps(messages_to_body_items(all_messages, matrix->get_me(current_room).get())); + auto me = matrix->get_me(current_room); + tabs[MESSAGES_TAB_INDEX].body->insert_items_by_timestamps(messages_to_body_items(current_room, all_messages, current_room->get_user_display_name(me), me->user_id)); modify_related_messages_in_current_room(all_messages); tabs[MESSAGES_TAB_INDEX].body->select_last_item(); @@ -3577,7 +3572,7 @@ namespace QuickMedia { } }; - auto add_new_messages_to_current_room = [this, &tabs, &selected_tab, ¤t_room](Messages &messages) { + auto add_new_messages_to_current_room = [&me, &tabs, &selected_tab, ¤t_room](Messages &messages) { if(messages.empty()) return; @@ -3587,7 +3582,7 @@ namespace QuickMedia { scroll_to_end = true; BodyItem *selected_item = tabs[MESSAGES_TAB_INDEX].body->get_selected(); - tabs[MESSAGES_TAB_INDEX].body->insert_items_by_timestamps(messages_to_body_items(messages, matrix->get_me(current_room).get())); + tabs[MESSAGES_TAB_INDEX].body->insert_items_by_timestamps(messages_to_body_items(current_room, messages, current_room->get_user_display_name(me), me->user_id)); if(selected_item && !scroll_to_end) { int selected_item_index = tabs[MESSAGES_TAB_INDEX].body->get_index_by_body_item(selected_item); if(selected_item_index != -1) @@ -3896,7 +3891,6 @@ namespace QuickMedia { if((chat_state == ChatState::TYPING_MESSAGE || chat_state == ChatState::REPLYING || chat_state == ChatState::EDITING) && selected_tab == MESSAGES_TAB_INDEX && !frame_skip_text_entry) { frame_skip_text_entry = false; if(event.type == sf::Event::TextEntered) { - //chat_input.onTextEntered(event.text.unicode); // TODO: Also show typing event when ctrl+v pasting? if(event.text.unicode != 13) { // Return key start_typing_timer.restart(); @@ -4094,7 +4088,7 @@ namespace QuickMedia { size_t num_new_messages = new_messages.size(); if(num_new_messages > 0) { BodyItem *selected_item = tabs[MESSAGES_TAB_INDEX].body->get_selected(); - BodyItems new_body_items = messages_to_body_items(new_messages, matrix->get_me(current_room).get()); + BodyItems new_body_items = messages_to_body_items(current_room, new_messages, current_room->get_user_display_name(me), me->user_id); size_t num_new_body_items = new_body_items.size(); tabs[MESSAGES_TAB_INDEX].body->insert_items_by_timestamps(std::move(new_body_items)); if(selected_item) { @@ -4124,7 +4118,7 @@ namespace QuickMedia { if(fetch_message_tab == PINNED_TAB_INDEX) { PinnedEventData *event_data = static_cast(fetch_body_item->userdata); if(message) { - *fetch_body_item = *message_to_body_item(message.get(), matrix->get_me(current_room).get()); + *fetch_body_item = *message_to_body_item(current_room, message.get(), current_room->get_user_display_name(me), me->user_id); event_data->status = FetchStatus::FINISHED_LOADING; event_data->message = message.get(); fetch_body_item->userdata = event_data; @@ -4134,7 +4128,7 @@ namespace QuickMedia { } } else if(fetch_message_tab == MESSAGES_TAB_INDEX) { if(message) { - fetch_body_item->embedded_item = message_to_body_item(message.get(), matrix->get_me(current_room).get()); + fetch_body_item->embedded_item = message_to_body_item(current_room, message.get(), current_room->get_user_display_name(me), me->user_id); fetch_body_item->embedded_item_status = FetchStatus::FINISHED_LOADING; } else { fetch_body_item->embedded_item_status = FetchStatus::FAILED_TO_LOAD; diff --git a/src/SearchBar.cpp b/src/SearchBar.cpp index 0c72805..dea9951 100644 --- a/src/SearchBar.cpp +++ b/src/SearchBar.cpp @@ -37,7 +37,7 @@ namespace QuickMedia { needs_update(true), input_masked(input_masked), typing(false), - backspace_seq_count(0), + backspace_pressed(false), vertical_pos(0.0f) { text.setFillColor(text_placeholder_color); @@ -88,15 +88,20 @@ namespace QuickMedia { } void SearchBar::on_event(sf::Event &event) { - if(event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::V && event.key.control) { + if(event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::Backspace) + backspace_pressed = true; + else if(event.type == sf::Event::KeyReleased && event.key.code == sf::Keyboard::Backspace) + backspace_pressed = false; + if(event.type == sf::Event::KeyPressed && event.key.code == sf::Keyboard::V && event.key.control) append_text(sf::Clipboard::getString()); - } + if(event.type == sf::Event::TextEntered) + onTextEntered(event.text.unicode); } void SearchBar::update() { sf::Int32 elapsed_time = time_since_search_update.getElapsedTime().asMilliseconds(); int timeout = text_autosearch_delay; - if(backspace_seq_count == 1) + if(backspace_pressed) timeout = 750; if(updated_search && elapsed_time >= timeout) { updated_search = false; @@ -125,7 +130,7 @@ namespace QuickMedia { draw_logo = false; } - float font_height = text.getLocalBounds().height + 12.0f; + float font_height = text.getCharacterSize() + 7.0f; float rect_height = std::floor(font_height + background_margin_vertical * 2.0f); float offset_x = padding_horizontal; @@ -177,11 +182,10 @@ namespace QuickMedia { } updated_search = true; updated_autocomplete = true; - ++backspace_seq_count; time_since_search_update.restart(); } } else if(codepoint == 13) { // Return - backspace_seq_count = 0; + backspace_pressed = false; if(onTextSubmitCallback) { auto u8 = text.getString().toUtf8(); std::string *u8_str = (std::string*)&u8; @@ -205,7 +209,7 @@ namespace QuickMedia { needs_update = true; updated_search = false; updated_autocomplete = false; - backspace_seq_count = 0; + backspace_pressed = false; } void SearchBar::append_text(const std::string &text_to_add) { @@ -231,7 +235,7 @@ namespace QuickMedia { updated_search = true; updated_autocomplete = true; time_since_search_update.restart(); - backspace_seq_count = 0; + backspace_pressed = false; if(str[str.getSize() - 1] == '\n') needs_update = true; } diff --git a/src/plugins/Matrix.cpp b/src/plugins/Matrix.cpp index 7c170c9..0c94dd5 100644 --- a/src/plugins/Matrix.cpp +++ b/src/plugins/Matrix.cpp @@ -84,6 +84,30 @@ namespace QuickMedia { } } + static sf::Color user_id_to_color(const std::string &user_id) { + uint32_t color = 2166136261; + for(unsigned char c : user_id) { + color = (color * 16777619) ^ c; + } + sf::Uint8 *col = (sf::Uint8*)&color; + sf::Color result(col[0], col[1], col[2]); + result.r = std::min(255, 80 + (int)result.r); + result.g = std::min(255, 80 + (int)result.g); + result.b = std::min(255, 80 + (int)result.b); + result.a = 255; + return result; + } + + UserInfo::UserInfo(RoomData *room, std::string user_id) : + room(room), display_name_color(user_id_to_color(user_id)), user_id(std::move(user_id)), display_name(user_id) { + + } + + UserInfo::UserInfo(RoomData *room, std::string user_id, std::string display_name, std::string avatar_url) : + room(room), display_name_color(user_id_to_color(user_id)), user_id(std::move(user_id)), display_name(std::move(display_name)), avatar_url(std::move(avatar_url)) { + + } + std::shared_ptr RoomData::get_user_by_id(const std::string &user_id) { std::lock_guard lock(room_mutex); auto user_it = user_info_by_user_id.find(user_id); @@ -102,11 +126,33 @@ namespace QuickMedia { user->read_marker_event_id = event_id; } - std::string RoomData::get_user_read_marker(std::shared_ptr &user) { + std::string RoomData::get_user_read_marker(const std::shared_ptr &user) { std::lock_guard lock(user_mutex); return user->read_marker_event_id; } + std::string RoomData::get_user_display_name(const std::shared_ptr &user) { + std::lock_guard lock(user_mutex); + return user->display_name; + } + + std::string RoomData::get_user_avatar_url(const std::shared_ptr &user) { + std::lock_guard lock(user_mutex); + return user->avatar_url; + } + + void RoomData::set_user_display_name(std::shared_ptr &user, std::string display_name) { + std::lock_guard lock(user_mutex); + user->display_name = std::move(display_name); + if(user->display_name.empty()) + user->display_name = user->user_id; + } + + void RoomData::set_user_avatar_url(std::shared_ptr &user, std::string avatar_url) { + std::lock_guard lock(user_mutex); + user->avatar_url = std::move(avatar_url); + } + void RoomData::prepend_messages_reverse(const std::vector> &new_messages) { std::lock_guard lock(room_mutex); for(auto it = new_messages.begin(); it != new_messages.end(); ++it) { @@ -280,8 +326,9 @@ namespace QuickMedia { } void MatrixQuickMedia::add_invite(const std::string &room_id, const Invite &invite) { + std::string invited_by_display_name = invite.invited_by->room->get_user_display_name(invite.invited_by); auto body_item = BodyItem::create(invite.room_name); - body_item->set_description("Invited by " + invite.invited_by->display_name + " (" + invite.invited_by->user_id + ")"); + body_item->set_description("Invited by " + invited_by_display_name + " (" + invite.invited_by->user_id + ")"); body_item->url = room_id; body_item->thumbnail_url = invite.room_avatar_url; body_item->thumbnail_mask_type = ThumbnailMaskType::CIRCLE; @@ -289,7 +336,7 @@ namespace QuickMedia { body_item->set_timestamp(invite.timestamp); invites_page->add_body_item(std::move(body_item)); if(invite.new_invite) - show_notification("QuickMedia matrix - " + invite.room_name, "You were invited to " + invite.room_name + " by " + invite.invited_by->display_name + " (" + invite.invited_by->user_id + ")"); + show_notification("QuickMedia matrix - " + invite.room_name, "You were invited to " + invite.room_name + " by " + invited_by_display_name + " (" + invite.invited_by->user_id + ")"); } void MatrixQuickMedia::remove_invite(const std::string &room_id) { @@ -1381,20 +1428,6 @@ namespace QuickMedia { return PluginResult::OK; } - static sf::Color user_id_to_color(const std::string &user_id) { - uint32_t color = 2166136261; - for(unsigned char c : user_id) { - color = (color * 16777619) ^ c; - } - sf::Uint8 *col = (sf::Uint8*)&color; - sf::Color result(col[0], col[1], col[2]); - result.r = std::min(255, 80 + (int)result.r); - result.g = std::min(255, 80 + (int)result.g); - result.b = std::min(255, 80 + (int)result.b); - result.a = 255; - return result; - } - void Matrix::events_add_user_info(const rapidjson::Value &events_json, RoomData *room_data) { if(!events_json.IsArray()) return; @@ -1439,14 +1472,11 @@ namespace QuickMedia { const rapidjson::Value &display_name_json = GetMember(json, "displayname"); - auto user_info = std::make_shared(); - user_info->user_id = user_id; - user_info->avatar_url = thumbnail_url_extract_media_id(avatar_url_str); - if(!user_info->avatar_url.empty()) - user_info->avatar_url = homeserver + "/_matrix/media/r0/thumbnail/" + user_info->avatar_url + "?width=32&height=32&method=crop"; // TODO: Remove the constant strings around to reduce memory usage (6.3mb) - user_info->display_name = display_name_json.IsString() ? display_name_json.GetString() : user_id; - user_info->display_name_color = user_id_to_color(user_id); - + std::string display_name = display_name_json.IsString() ? display_name_json.GetString() : user_id; + std::string avatar_url = thumbnail_url_extract_media_id(avatar_url_str); + if(!avatar_url.empty()) + avatar_url = homeserver + "/_matrix/media/r0/thumbnail/" + avatar_url + "?width=32&height=32&method=crop"; // TODO: Remove the constant strings around to reduce memory usage (6.3mb) + auto user_info = std::make_shared(room_data, user_id, std::move(display_name), std::move(avatar_url)); // Overwrites user data room_data->add_user(user_info); return user_info; @@ -1590,6 +1620,7 @@ namespace QuickMedia { // TODO: Preallocate std::vector> new_messages; auto me = get_me(room_data); + std::string my_display_name = room_data->get_user_display_name(me); for(const rapidjson::Value &event_item_json : events_json.GetArray()) { std::shared_ptr new_message = parse_message_event(event_item_json, room_data); @@ -1618,7 +1649,7 @@ namespace QuickMedia { // 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) - message->mentions_me = message_contains_user_mention(message->body, me->display_name) || message_contains_user_mention(message->body, me->user_id) || message_contains_user_mention(message->body, "@room"); + message->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"); } } @@ -1726,6 +1757,8 @@ namespace QuickMedia { if(strcmp(type_json.GetString(), "m.room.message") == 0) { } else if(strcmp(type_json.GetString(), "m.room.member") == 0) { + std::string user_display_name = room_data->get_user_display_name(user); + std::string sender_display_name = room_data->get_user_display_name(user_sender); std::string body; const rapidjson::Value &membership_json = GetMember(*content_json, "membership"); if(strcmp(membership_json.GetString(), "join") == 0) { @@ -1737,31 +1770,40 @@ namespace QuickMedia { const rapidjson::Value &prev_avatar_url_json = GetMember(prev_content_json, "avatar_url"); const rapidjson::Value &new_displayname_json = GetMember(*content_json, "displayname"); const rapidjson::Value &new_avatar_url_json = GetMember(*content_json, "avatar_url"); - if(new_displayname_json.IsString() && (!prev_displayname_json.IsString() || strcmp(new_displayname_json.GetString(), prev_displayname_json.GetString()) != 0)) - body = user->display_name + " changed their display name to " + std::string(new_displayname_json.GetString()); - else if(!new_displayname_json.IsString() && prev_displayname_json.IsString()) - body = user->display_name + " removed their display name"; - else if(new_avatar_url_json.IsString() && (!prev_avatar_url_json.IsString() || strcmp(new_avatar_url_json.GetString(), prev_avatar_url_json.GetString()) != 0)) - body = user->display_name + " changed their profile picture"; - else if(!new_avatar_url_json.IsString() && prev_avatar_url_json.IsString()) - body = user->display_name + " removed their profile picture."; - else - body = user->display_name + " joined the room"; + if(new_displayname_json.IsString() && (!prev_displayname_json.IsString() || strcmp(new_displayname_json.GetString(), prev_displayname_json.GetString()) != 0)) { + std::string new_displayname_str = std::string(new_displayname_json.GetString()); + body = user_display_name + " changed their display name to " + new_displayname_str; + room_data->set_user_display_name(user, std::move(new_displayname_str)); + } else if(!new_displayname_json.IsString() && prev_displayname_json.IsString()) { + body = user_display_name + " removed their display name"; + room_data->set_user_display_name(user, ""); + } else if(new_avatar_url_json.IsString() && (!prev_avatar_url_json.IsString() || strcmp(new_avatar_url_json.GetString(), prev_avatar_url_json.GetString()) != 0)) { + body = user_display_name + " changed their profile picture"; + std::string new_avatar_url_str = thumbnail_url_extract_media_id(new_avatar_url_json.GetString()); + if(!new_avatar_url_str.empty()) + new_avatar_url_str = homeserver + "/_matrix/media/r0/thumbnail/" + new_avatar_url_str + "?width=32&height=32&method=crop"; // TODO: Remove the constant strings around to reduce memory usage (6.3mb) + room_data->set_user_avatar_url(user, std::move(new_avatar_url_str)); + } else if(!new_avatar_url_json.IsString() && prev_avatar_url_json.IsString()) { + body = user_display_name + " removed their profile picture."; + room_data->set_user_avatar_url(user, ""); + } else { + body = user_display_name + " joined the room"; + } } else { - body = user->display_name + " joined the room"; + body = user_display_name + " joined the room"; } } else { - body = user->display_name + " joined the room"; + body = user_display_name + " joined the room"; } } else if(strcmp(membership_json.GetString(), "leave") == 0) { if(sent_by_somebody_else) - body = user->display_name + " was kicked from the room by " + user_sender->display_name; + body = user_display_name + " was kicked from the room by " + sender_display_name; else - body = user->display_name + " left the room"; + body = user_display_name + " left the room"; } else if(strcmp(membership_json.GetString(), "invite") == 0) { - body = user->display_name + " was invited to the room by " + user_sender->display_name; + body = user_display_name + " was invited to the room by " + sender_display_name; } else if(strcmp(membership_json.GetString(), "ban") == 0) { - body = user->display_name + " was banned from the room by " + user_sender->display_name; + body = user_display_name + " was banned from the room by " + sender_display_name; } else { body = "unimplemented membership: " + std::string(membership_json.GetString()); } @@ -1831,7 +1873,7 @@ namespace QuickMedia { message->type = MessageType::FILE; } else if(strcmp(content_type.GetString(), "m.emote") == 0) { // this is a /me message, TODO: show /me messages differently message->type = MessageType::TEXT; - prefix = "*" + user->display_name + "* "; + prefix = "*" + room_data->get_user_display_name(user) + "* "; } else if(strcmp(content_type.GetString(), "m.notice") == 0) { // TODO: show notices differently message->type = MessageType::TEXT; prefix = "* NOTICE * "; @@ -1867,16 +1909,16 @@ namespace QuickMedia { return user_id.substr(1, index - 1); } - static std::string combine_user_display_names_for_room_name(const std::vector> &user_info, const std::string &fallback_user_id) { + static std::string combine_user_display_names_for_room_name(std::vector> &user_info, const std::string &fallback_user_id) { std::string result; if(user_info.size() == 0) result = extract_user_name_from_user_id(fallback_user_id); else if(user_info.size() == 1) - result = user_info[0]->display_name; + result = user_info[0]->room->get_user_display_name(user_info[0]); else if(user_info.size() == 2) - result = user_info[0]->display_name + " and " + user_info[1]->display_name; + result = user_info[0]->room->get_user_display_name(user_info[0]) + " and " + user_info[1]->room->get_user_display_name(user_info[1]); else if(user_info.size() > 2) - result = user_info[0]->display_name + ", " + user_info[1]->display_name + " and " + std::to_string(user_info.size() - 2) + " other(s)"; + result = user_info[0]->room->get_user_display_name(user_info[0]) + ", " + user_info[1]->room->get_user_display_name(user_info[1]) + " and " + std::to_string(user_info.size() - 2) + " other(s)"; return result; } @@ -1956,10 +1998,10 @@ namespace QuickMedia { if(users_excluding_me.empty()) { auto user = get_user_by_id(room_data, creator_json.GetString()); if(user) - room_data->set_avatar_url(user->avatar_url); + room_data->set_avatar_url(room_data->get_user_avatar_url(user)); } else { // TODO: If there are multiple users, then we want to use some other type of avatar, not the first users avatar - room_data->set_avatar_url(users_excluding_me.front()->avatar_url); + room_data->set_avatar_url(room_data->get_user_avatar_url(users_excluding_me.front())); } has_room_avatar_url = true; } @@ -3128,8 +3170,7 @@ namespace QuickMedia { } std::string Matrix::message_get_author_displayname(Message *message) const { - // TODO: Thread safe? - return message->user->display_name; + return message->user->room->get_user_display_name(message->user); } PluginResult Matrix::get_config(int *upload_size) { @@ -3255,10 +3296,7 @@ namespace QuickMedia { return parse_user_info(json_root, user_id, room); #else fprintf(stderr, "Unknown user: %s, creating locally... synapse bug?\n", user_id.c_str()); - auto user_info = std::make_shared(); - user_info->user_id = user_id; - user_info->display_name = user_id; - user_info->display_name_color = user_id_to_color(user_id); + auto user_info = std::make_shared(room, user_id); room->add_user(user_info); return user_info; #endif -- cgit v1.2.3