diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index fb376f306..a0e0e5ef3 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -540,7 +540,8 @@ user id:int53 first_name:string last_name:string usernames:usernames phone_numbe botInfo share_text:string description:string photo:photo animation:animation menu_button:botMenuButton commands:vector default_group_administrator_rights:chatAdministratorRights default_channel_administrator_rights:chatAdministratorRights = BotInfo; //@description Contains full information about a user -//@photo User profile photo; may be null if empty or unknown. If non-null, then it is the same photo as in user.profile_photo and chat.photo +//@photo User profile photo; may be null if empty or unknown. If non-null and personal_photo is null, then it is the same photo as in user.profile_photo and chat.photo +//@personal_photo User profile photo set by the current user for the contact; may be null if empty or unknown. If non-null, then it is the same photo as in user.profile_photo and chat.photo //@is_blocked True, if the user is blocked by the current user //@can_be_called True, if the user can be called //@supports_video_calls True, if a video call can be created with the user @@ -552,7 +553,7 @@ botInfo share_text:string description:string photo:photo animation:animation men //@premium_gift_options The list of available options for gifting Telegram Premium to the user //@group_in_common_count Number of group chats where both the other user and the current user are a member; 0 for the current user //@bot_info For bots, information about the bot; may be null -userFullInfo photo:chatPhoto is_blocked:Bool can_be_called:Bool supports_video_calls:Bool has_private_calls:Bool has_private_forwards:Bool has_restricted_voice_and_video_note_messages:Bool need_phone_number_privacy_exception:Bool bio:formattedText premium_gift_options:vector group_in_common_count:int32 bot_info:botInfo = UserFullInfo; +userFullInfo photo:chatPhoto personal_photo:chatPhoto is_blocked:Bool can_be_called:Bool supports_video_calls:Bool has_private_calls:Bool has_private_forwards:Bool has_restricted_voice_and_video_note_messages:Bool need_phone_number_privacy_exception:Bool bio:formattedText premium_gift_options:vector group_in_common_count:int32 bot_info:botInfo = UserFullInfo; //@description Represents a list of users @total_count Approximate total number of users found @user_ids A list of user identifiers users total_count:int32 user_ids:vector = Users; diff --git a/td/telegram/ContactsManager.cpp b/td/telegram/ContactsManager.cpp index 980b94856..2dddb9f70 100644 --- a/td/telegram/ContactsManager.cpp +++ b/td/telegram/ContactsManager.cpp @@ -4237,6 +4237,7 @@ void ContactsManager::UserFull::store(StorerT &storer) const { bool has_description_photo = !description_photo.is_empty(); bool has_description_animation = description_animation_file_id.is_valid(); bool has_premium_gift_options = !premium_gift_options.empty(); + bool has_personal_photo = !personal_photo.is_empty(); BEGIN_STORE_FLAGS(); STORE_FLAG(has_about); STORE_FLAG(is_blocked); @@ -4256,6 +4257,7 @@ void ContactsManager::UserFull::store(StorerT &storer) const { STORE_FLAG(has_description_animation); STORE_FLAG(has_premium_gift_options); STORE_FLAG(voice_messages_forbidden); + STORE_FLAG(has_personal_photo); END_STORE_FLAGS(); if (has_about) { store(about, storer); @@ -4293,6 +4295,9 @@ void ContactsManager::UserFull::store(StorerT &storer) const { if (has_premium_gift_options) { store(premium_gift_options, storer); } + if (has_personal_photo) { + store(personal_photo, storer); + } } template @@ -4309,6 +4314,7 @@ void ContactsManager::UserFull::parse(ParserT &parser) { bool has_description_photo; bool has_description_animation; bool has_premium_gift_options; + bool has_personal_photo; BEGIN_PARSE_FLAGS(); PARSE_FLAG(has_about); PARSE_FLAG(is_blocked); @@ -4328,6 +4334,7 @@ void ContactsManager::UserFull::parse(ParserT &parser) { PARSE_FLAG(has_description_animation); PARSE_FLAG(has_premium_gift_options); PARSE_FLAG(voice_messages_forbidden); + PARSE_FLAG(has_personal_photo); END_PARSE_FLAGS(); if (has_about) { parse(about, parser); @@ -4365,6 +4372,9 @@ void ContactsManager::UserFull::parse(ParserT &parser) { if (has_premium_gift_options) { parse(premium_gift_options, parser); } + if (has_personal_photo) { + parse(personal_photo, parser); + } } template @@ -10676,8 +10686,11 @@ void ContactsManager::on_load_user_full_from_database(UserId user_id, string val User *u = get_user(user_id); CHECK(u != nullptr); - if (u->photo.id != user_full->photo.id.get()) { + auto user_full_photo_id = + !user_full->personal_photo.is_empty() ? user_full->personal_photo.id.get() : user_full->photo.id.get(); + if (u->photo.id != user_full_photo_id) { user_full->photo = Photo(); + user_full->personal_photo = Photo(); if (u->photo.id > 0) { user_full->expires_at = 0.0; } @@ -10685,6 +10698,9 @@ void ContactsManager::on_load_user_full_from_database(UserId user_id, string val if (!user_full->photo.is_empty()) { register_user_photo(u, user_id, user_full->photo); } + if (!user_full->personal_photo.is_empty()) { + register_user_photo(u, user_id, user_full->personal_photo); + } td_->group_call_manager_->on_update_dialog_about(DialogId(user_id), user_full->about, false); @@ -11676,18 +11692,34 @@ void ContactsManager::on_get_user_full(tl_object_ptr &&u } auto photo = get_photo(td_->file_manager_.get(), std::move(user->profile_photo_), DialogId(user_id)); + auto personal_photo = get_photo(td_->file_manager_.get(), std::move(user->personal_photo_), DialogId(user_id)); // do_update_user_photo should be a no-op if server sent consistent data - do_update_user_photo(u, user_id, as_profile_photo(td_->file_manager_.get(), user_id, u->access_hash, photo, false), - false, "on_get_user_full"); + if (!personal_photo.is_empty()) { + do_update_user_photo(u, user_id, + as_profile_photo(td_->file_manager_.get(), user_id, u->access_hash, personal_photo, true), + false, "on_get_user_full"); + } else { + do_update_user_photo(u, user_id, as_profile_photo(td_->file_manager_.get(), user_id, u->access_hash, photo, false), + false, "on_get_user_full"); + } if (photo != user_full->photo) { user_full->photo = std::move(photo); user_full->is_changed = true; } - if (user_full->photo.is_empty()) { - drop_user_photos(user_id, true, false, "on_get_user_full"); - } else { + if (personal_photo != user_full->personal_photo) { + user_full->personal_photo = std::move(personal_photo); + user_full->is_changed = true; + } + if (!user_full->photo.is_empty()) { register_user_photo(u, user_id, user_full->photo); } + if (!user_full->personal_photo.is_empty()) { + register_user_photo(u, user_id, user_full->personal_photo); + } + if (user_full->photo.is_empty() && user_full->personal_photo.is_empty()) { + // TODO drop separately + drop_user_photos(user_id, true, false, "on_get_user_full"); + } // User must be updated before UserFull if (u->is_changed) { @@ -12397,9 +12429,17 @@ void ContactsManager::do_update_user_photo(User *u, UserId user_id, ProfilePhoto } else { auto user_full = get_user_full(user_id); // must not load UserFull if (user_full != nullptr) { - if (u->photo.id != user_full->photo.id.get() && !user_full->photo.is_empty()) { - user_full->photo = Photo(); - user_full->is_changed = true; + auto user_full_photo_id = u->photo.is_personal ? user_full->personal_photo.id.get() : user_full->photo.id.get(); + if (u->photo.id == 0 || u->photo.id != user_full_photo_id) { + // if profile photo is unknown, we must drop both photos + if (!user_full->photo.is_empty()) { + user_full->photo = Photo(); + user_full->is_changed = true; + } + if (!user_full->personal_photo.is_empty()) { + user_full->personal_photo = Photo(); + user_full->is_changed = true; + } } if (user_full->is_update_user_full_sent) { update_user_full(user_full, user_id, "do_update_user_photo"); @@ -12742,13 +12782,9 @@ void ContactsManager::on_set_profile_photo(tl_object_ptrfile_manager_.get(), std::move(photo->photo_), DialogId(my_user_id))); + delete_profile_photo_from_cache(my_user_id, old_photo_id, false); + add_set_profile_photo_to_cache(my_user_id, + get_photo(td_->file_manager_.get(), std::move(photo->photo_), DialogId(my_user_id))); User *u = get_user(my_user_id); if (u != nullptr) { @@ -12774,7 +12810,7 @@ void ContactsManager::on_delete_profile_photo(int64 profile_photo_id, Promisecount != -1) { + if (is_me && user_photos != nullptr && user_photos->count != -1) { if (user_photos->offset == 0) { if (user_photos->photos.empty() || user_photos->photos[0].id.get() != photo.id.get()) { user_photos->photos.insert(user_photos->photos.begin(), photo); @@ -12805,24 +12843,34 @@ void ContactsManager::add_profile_photo_to_cache(UserId user_id, Photo &&photo) } // update ProfilePhoto in User - do_update_user_photo(u, user_id, as_profile_photo(td_->file_manager_.get(), user_id, u->access_hash, photo, false), - false, "add_profile_photo_to_cache"); + do_update_user_photo(u, user_id, as_profile_photo(td_->file_manager_.get(), user_id, u->access_hash, photo, !is_me), + false, "add_set_profile_photo_to_cache"); update_user(u, user_id); // update Photo in UserFull auto user_full = get_user_full_force(user_id); if (user_full != nullptr) { - if (user_full->photo != photo) { - user_full->photo = photo; - user_full->is_changed = true; - register_user_photo(u, user_id, photo); + if (is_me) { + if (user_full->photo != photo) { + user_full->photo = photo; + user_full->is_changed = true; + register_user_photo(u, user_id, photo); + } + } else { + if (user_full->personal_photo != photo) { + user_full->personal_photo = photo; + user_full->is_changed = true; + register_user_photo(u, user_id, photo); + } } - update_user_full(user_full, user_id, "add_profile_photo_to_cache"); + update_user_full(user_full, user_id, "add_set_profile_photo_to_cache"); } } bool ContactsManager::delete_profile_photo_from_cache(UserId user_id, int64 profile_photo_id, bool send_updates) { - CHECK(profile_photo_id != 0); + if (profile_photo_id == 0 || profile_photo_id == -2) { + return false; + } // we have subsequence of user photos in user_photos_ // ProfilePhoto in User and Photo in UserFull @@ -12858,9 +12906,14 @@ bool ContactsManager::delete_profile_photo_from_cache(UserId user_id, int64 prof user_photos != nullptr && user_photos->count != -1 && user_photos->offset == 0 && !user_photos->photos.empty(); auto user_full = get_user_full_force(user_id); - if (user_full != nullptr && user_full->photo.id.get() == profile_photo_id) { - // user_full->photo is empty or coincides with u->photo - CHECK(is_main_photo_deleted); + + // check that user_full photo is empty or coincides with u->photo + if (user_full != nullptr && (!user_full->personal_photo.is_empty() || !user_full->photo.is_empty())) { + auto user_full_photo_id = + !user_full->personal_photo.is_empty() ? user_full->personal_photo.id.get() : user_full->photo.id.get(); + if (user_full_photo_id == profile_photo_id) { + CHECK(is_main_photo_deleted); + } } // update ProfilePhoto in User @@ -12881,8 +12934,12 @@ bool ContactsManager::delete_profile_photo_from_cache(UserId user_id, int64 prof // update Photo in UserFull if (user_full != nullptr) { + if (user_full->personal_photo.id.get() == profile_photo_id) { + user_full->personal_photo = Photo(); + user_full->is_changed = true; + } if (have_new_photo) { - if (user_photos->photos[0] != user_full->photo) { + if (user_full->photo.id.get() == profile_photo_id && user_photos->photos[0] != user_full->photo) { user_full->photo = user_photos->photos[0]; user_full->is_changed = true; } @@ -12931,6 +12988,10 @@ void ContactsManager::drop_user_photos(UserId user_id, bool is_empty, bool drop_ user_full->photo = Photo(); user_full->is_changed = true; } + if (!user_full->personal_photo.is_empty()) { + user_full->personal_photo = Photo(); + user_full->is_changed = true; + } if (!is_empty) { if (user_full->expires_at > 0.0) { user_full->expires_at = 0.0; @@ -12957,6 +13018,7 @@ void ContactsManager::drop_user_full(UserId user_id) { user_full->expires_at = 0.0; user_full->photo = Photo(); + user_full->personal_photo = Photo(); user_full->is_blocked = false; user_full->can_be_called = false; user_full->supports_video_calls = false; @@ -17677,13 +17739,14 @@ tl_object_ptr ContactsManager::get_user_full_info_object(U bio_object = get_formatted_text_object(bio, true, 0); } auto voice_messages_forbidden = is_premium ? user_full->voice_messages_forbidden : false; - return make_tl_object(get_chat_photo_object(td_->file_manager_.get(), user_full->photo), - user_full->is_blocked, user_full->can_be_called, - user_full->supports_video_calls, user_full->has_private_calls, - !user_full->private_forward_name.empty(), voice_messages_forbidden, - user_full->need_phone_number_privacy_exception, std::move(bio_object), - get_premium_payment_options_object(user_full->premium_gift_options), - user_full->common_chat_count, std::move(bot_info)); + return make_tl_object( + get_chat_photo_object(td_->file_manager_.get(), user_full->photo), + get_chat_photo_object(td_->file_manager_.get(), user_full->personal_photo), user_full->is_blocked, + user_full->can_be_called, user_full->supports_video_calls, user_full->has_private_calls, + !user_full->private_forward_name.empty(), voice_messages_forbidden, + user_full->need_phone_number_privacy_exception, std::move(bio_object), + get_premium_payment_options_object(user_full->premium_gift_options), user_full->common_chat_count, + std::move(bot_info)); } td_api::object_ptr ContactsManager::get_update_unknown_basic_group_object(ChatId chat_id) { diff --git a/td/telegram/ContactsManager.h b/td/telegram/ContactsManager.h index 9299b21e8..ea7fd12c8 100644 --- a/td/telegram/ContactsManager.h +++ b/td/telegram/ContactsManager.h @@ -772,6 +772,7 @@ class ContactsManager final : public Actor { // do not forget to update drop_user_full and on_get_user_full struct UserFull { Photo photo; + Photo personal_photo; string about; string private_forward_name; @@ -1149,6 +1150,7 @@ class ContactsManager final : public Actor { static constexpr int32 USER_FULL_FLAG_HAS_GROUP_ADMINISTRATOR_RIGHTS = 1 << 17; static constexpr int32 USER_FULL_FLAG_HAS_BROADCAST_ADMINISTRATOR_RIGHTS = 1 << 18; static constexpr int32 USER_FULL_FLAG_HAS_VOICE_MESSAGES_FORBIDDEN = 1 << 20; + static constexpr int32 USER_FULL_FLAG_HAS_PERSONAL_PHOTO = 1 << 21; static constexpr int32 CHAT_FLAG_USER_IS_CREATOR = 1 << 0; static constexpr int32 CHAT_FLAG_USER_HAS_LEFT = 1 << 2; @@ -1352,7 +1354,7 @@ class ContactsManager final : public Actor { bool need_phone_number_privacy_exception) const; UserPhotos *add_user_photos(UserId user_id); - void add_profile_photo_to_cache(UserId user_id, Photo &&photo); + void add_set_profile_photo_to_cache(UserId user_id, Photo &&photo); bool delete_profile_photo_from_cache(UserId user_id, int64 profile_photo_id, bool send_updates); void drop_user_photos(UserId user_id, bool is_empty, bool drop_user_full_photo, const char *source); void drop_user_full(UserId user_id);