diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index 5e1474075..bfb49a3e3 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -750,6 +750,18 @@ premiumGiveawayInfoCompleted creation_date:int32 actual_winners_selection_date:i //@dark_theme_colors The list of 1-3 colors in RGB format, describing the accent color, as expected to be shown in dark themes accentColor id:int32 built_in_accent_color_id:int32 light_theme_colors:vector dark_theme_colors:vector = AccentColor; +//@description Contains information about supported accent colors for user profile photo background in RGB format +//@palette_colors The list of 1-2 colors in RGB format, describing the colors, as expected to be shown in the color palette settings +//@background_colors The list of 1-2 colors in RGB format, describing the colors, as expected to be used for the profile photo background +//@story_colors The list of 2 colors in RGB format, describing the colors of the gradient to be used for the unread active story indicator around profile photo +profileAccentColors palette_colors:vector background_colors:vector story_colors:vector = ProfileAccentColors; + +//@description Contains information about supported accent color for user profile photo background +//@id Profile accent color identifier +//@light_theme_colors The list of 1-3 colors in RGB format, describing the accent color, as expected to be shown in light themes +//@dark_theme_colors The list of 1-3 colors in RGB format, describing the accent color, as expected to be shown in dark themes +profileAccentColor id:int32 light_theme_colors:profileAccentColors dark_theme_colors:profileAccentColors = ProfileAccentColor; + //@description Describes a custom emoji to be shown instead of the Telegram Premium badge //@custom_emoji_id Identifier of the custom emoji in stickerFormatTgs format //@expiration_date Point in time (Unix timestamp) when the status will expire; 0 if never @@ -4522,7 +4534,7 @@ premiumFeatureUpgradedStories = PremiumFeature; //@description The ability to boost chats premiumFeatureChatBoost = PremiumFeature; -//@description The ability to choose accent color +//@description The ability to choose accent color for replies and user profile premiumFeatureAccentColor = PremiumFeature; @@ -6389,6 +6401,11 @@ updateChatThemes chat_themes:vector = Update; //@available_accent_color_ids The list of accent color identifiers, which can be set through setAccentColor and setChatAccentColor. The colors must be shown in the specififed order updateAccentColors colors:vector available_accent_color_ids:vector = Update; +//@description The list of supported accent colors for user profiles has changed +//@colors Information about supported colors +//@available_accent_color_ids The list of accent color identifiers, which can be set through setProfileAccentColor. The colors must be shown in the specififed order +updateProfileAccentColors colors:vector available_accent_color_ids:vector = Update; + //@description Some language pack strings have been updated @localization_target Localization target to which the language pack belongs @language_pack_id Identifier of the updated language pack @strings List of changed language pack strings; empty if all strings have changed updateLanguagePackStrings localization_target:string language_pack_id:string strings:vector = Update; diff --git a/td/telegram/ThemeManager.cpp b/td/telegram/ThemeManager.cpp index 50bc7cc59..62ddb7a33 100644 --- a/td/telegram/ThemeManager.cpp +++ b/td/telegram/ThemeManager.cpp @@ -78,6 +78,32 @@ class GetPeerColorsQuery final : public Td::ResultHandler { } }; +class GetPeerProfileColorsQuery final : public Td::ResultHandler { + Promise> promise_; + + public: + explicit GetPeerProfileColorsQuery(Promise> &&promise) + : promise_(std::move(promise)) { + } + + void send(int32 hash) { + send_query(G()->net_query_creator().create(telegram_api::help_getPeerProfileColors(hash))); + } + + void on_result(BufferSlice packet) final { + auto result_ptr = fetch_result(packet); + if (result_ptr.is_error()) { + return on_error(result_ptr.move_as_error()); + } + + promise_.set_value(result_ptr.move_as_ok()); + } + + void on_error(Status status) final { + promise_.set_error(std::move(status)); + } +}; + bool operator==(const ThemeManager::ThemeSettings &lhs, const ThemeManager::ThemeSettings &rhs) { return lhs.accent_color == rhs.accent_color && lhs.message_accent_color == rhs.message_accent_color && lhs.background_info == rhs.background_info && lhs.base_theme == rhs.base_theme && @@ -88,6 +114,15 @@ bool operator!=(const ThemeManager::ThemeSettings &lhs, const ThemeManager::Them return !(lhs == rhs); } +bool operator==(const ThemeManager::ProfileAccentColor &lhs, const ThemeManager::ProfileAccentColor &rhs) { + return lhs.palette_colors_ == rhs.palette_colors_ && lhs.background_colors_ == rhs.background_colors_ && + lhs.story_colors_ == rhs.story_colors_; +} + +bool operator!=(const ThemeManager::ProfileAccentColor &lhs, const ThemeManager::ProfileAccentColor &rhs) { + return !(lhs == rhs); +} + template void ThemeManager::ThemeSettings::store(StorerT &storer) const { using td::store; @@ -217,8 +252,76 @@ void ThemeManager::AccentColors::parse(ParserT &parser) { } } +template +void ThemeManager::ProfileAccentColor::store(StorerT &storer) const { + td::store(palette_colors_, storer); + td::store(background_colors_, storer); + td::store(story_colors_, storer); +} + +template +void ThemeManager::ProfileAccentColor::parse(ParserT &parser) { + td::parse(palette_colors_, parser); + td::parse(background_colors_, parser); + td::parse(story_colors_, parser); +} + +template +void ThemeManager::ProfileAccentColors::store(StorerT &storer) const { + bool has_hash = hash_ != 0; + BEGIN_STORE_FLAGS(); + STORE_FLAG(has_hash); + END_STORE_FLAGS(); + td::store(static_cast(light_colors_.size()), storer); + for (auto &it : light_colors_) { + td::store(it.first, storer); + td::store(it.second, storer); + } + td::store(static_cast(dark_colors_.size()), storer); + for (auto &it : dark_colors_) { + td::store(it.first, storer); + td::store(it.second, storer); + } + td::store(accent_color_ids_, storer); + if (has_hash) { + td::store(hash_, storer); + } +} + +template +void ThemeManager::ProfileAccentColors::parse(ParserT &parser) { + bool has_hash; + BEGIN_PARSE_FLAGS(); + PARSE_FLAG(has_hash); + END_PARSE_FLAGS(); + int32 size; + td::parse(size, parser); + for (int32 i = 0; i < size; i++) { + AccentColorId accent_color_id; + ProfileAccentColor colors; + td::parse(accent_color_id, parser); + td::parse(colors, parser); + CHECK(accent_color_id.is_valid()); + light_colors_.emplace(accent_color_id, std::move(colors)); + } + td::parse(size, parser); + for (int32 i = 0; i < size; i++) { + AccentColorId accent_color_id; + ProfileAccentColor colors; + td::parse(accent_color_id, parser); + td::parse(colors, parser); + CHECK(accent_color_id.is_valid()); + dark_colors_.emplace(accent_color_id, std::move(colors)); + } + td::parse(accent_color_ids_, parser); + if (has_hash) { + td::parse(hash_, parser); + } +} + ThemeManager::ThemeManager(Td *td, ActorShared<> parent) : td_(td), parent_(std::move(parent)) { load_accent_colors(); + load_profile_accent_colors(); } void ThemeManager::start_up() { @@ -259,6 +362,23 @@ void ThemeManager::load_accent_colors() { } } +void ThemeManager::load_profile_accent_colors() { + if (!td_->auth_manager_->is_authorized() || td_->auth_manager_->is_bot()) { + return; + } + + auto log_event_string = G()->td_db()->get_binlog_pmc()->get(get_profile_accent_colors_database_key()); + if (!log_event_string.empty()) { + auto status = log_event_parse(profile_accent_colors_, log_event_string); + if (status.is_ok()) { + send_update_profile_accent_colors(); + } else { + LOG(ERROR) << "Failed to parse profile accent colors from binlog: " << status; + profile_accent_colors_ = ProfileAccentColors(); + } + } +} + void ThemeManager::init() { load_chat_themes(); if (td_->auth_manager_->is_authorized() && !td_->auth_manager_->is_bot()) { @@ -268,6 +388,9 @@ void ThemeManager::init() { if (accent_colors_.hash_ == 0) { reload_accent_colors(); } + if (profile_accent_colors_.hash_ == 0) { + reload_profile_accent_colors(); + } } } @@ -359,6 +482,38 @@ bool ThemeManager::on_update_accent_colors(FlatHashMap light_colors, + FlatHashMap dark_colors, + vector accent_color_ids) { + auto are_equal = [](const FlatHashMap &lhs, + const FlatHashMap &rhs) { + for (auto &lhs_it : lhs) { + auto rhs_it = rhs.find(lhs_it.first); + if (rhs_it == rhs.end() || rhs_it->second != lhs_it.second) { + return false; + } + } + return true; + }; + if (accent_color_ids == profile_accent_colors_.accent_color_ids_ && + are_equal(light_colors, profile_accent_colors_.light_colors_) && + are_equal(dark_colors, profile_accent_colors_.dark_colors_)) { + return false; + } + for (auto &it : light_colors) { + profile_accent_colors_.light_colors_[it.first] = std::move(it.second); + } + for (auto &it : dark_colors) { + profile_accent_colors_.dark_colors_[it.first] = std::move(it.second); + } + profile_accent_colors_.accent_color_ids_ = std::move(accent_color_ids); + + save_profile_accent_colors(); + send_update_profile_accent_colors(); + return true; +} + namespace { template static auto get_color_json(int32 color); @@ -427,6 +582,14 @@ int32 ThemeManager::get_accent_color_id_object(AccentColorId accent_color_id, return fallback_accent_color_id.get(); } +int32 ThemeManager::get_profile_accent_color_id_object(AccentColorId accent_color_id) const { + CHECK(accent_color_id.is_valid()); + if (td_->auth_manager_->is_bot() || profile_accent_colors_.light_colors_.count(accent_color_id) != 0) { + return accent_color_id.get(); + } + return 5; // blue +} + td_api::object_ptr ThemeManager::get_theme_settings_object(const ThemeSettings &settings) const { auto fill = [colors = settings.message_colors]() mutable -> td_api::object_ptr { if (colors.size() >= 3) { @@ -492,6 +655,33 @@ td_api::object_ptr ThemeManager::AccentColors::get_u return td_api::make_object(std::move(colors), std::move(available_accent_color_ids)); } +td_api::object_ptr ThemeManager::get_update_profile_accent_colors_object() const { + return profile_accent_colors_.get_update_profile_accent_colors_object(); +} + +td_api::object_ptr ThemeManager::ProfileAccentColor::get_profile_accent_colors_object() + const { + return td_api::make_object( + vector(palette_colors_), vector(background_colors_), vector(story_colors_)); +} + +td_api::object_ptr +ThemeManager::ProfileAccentColors::get_update_profile_accent_colors_object() const { + vector> colors; + for (auto &it : light_colors_) { + auto light_colors = it.second.get_profile_accent_colors_object(); + auto dark_it = dark_colors_.find(it.first); + auto dark_colors = dark_it != dark_colors_.end() ? dark_it->second.get_profile_accent_colors_object() + : it.second.get_profile_accent_colors_object(); + colors.push_back(td_api::make_object(it.first.get(), std::move(light_colors), + std::move(dark_colors))); + } + auto available_accent_color_ids = + transform(accent_color_ids_, [](AccentColorId accent_color_id) { return accent_color_id.get(); }); + return td_api::make_object(std::move(colors), + std::move(available_accent_color_ids)); +} + string ThemeManager::get_chat_themes_database_key() { return "chat_themes"; } @@ -500,6 +690,10 @@ string ThemeManager::get_accent_colors_database_key() { return "accent_colors"; } +string ThemeManager::get_profile_accent_colors_database_key() { + return "profile_accent_colors"; +} + void ThemeManager::save_chat_themes() { G()->td_db()->get_binlog_pmc()->set(get_chat_themes_database_key(), log_event_store(chat_themes_).as_slice().str()); } @@ -509,6 +703,11 @@ void ThemeManager::save_accent_colors() { log_event_store(accent_colors_).as_slice().str()); } +void ThemeManager::save_profile_accent_colors() { + G()->td_db()->get_binlog_pmc()->set(get_profile_accent_colors_database_key(), + log_event_store(profile_accent_colors_).as_slice().str()); +} + void ThemeManager::send_update_chat_themes() const { send_closure(G()->td(), &Td::send_update, get_update_chat_themes_object()); } @@ -517,6 +716,10 @@ void ThemeManager::send_update_accent_colors() const { send_closure(G()->td(), &Td::send_update, get_update_accent_colors_object()); } +void ThemeManager::send_update_profile_accent_colors() const { + send_closure(G()->td(), &Td::send_update, get_update_profile_accent_colors_object()); +} + void ThemeManager::reload_chat_themes() { auto request_promise = PromiseCreator::lambda( [actor_id = actor_id(this)](Result> result) { @@ -643,6 +846,70 @@ void ThemeManager::on_get_accent_colors(Result> result) { + send_closure(actor_id, &ThemeManager::on_get_profile_accent_colors, std::move(result)); + }); + + td_->create_handler(std::move(request_promise))->send(profile_accent_colors_.hash_); +} + +ThemeManager::ProfileAccentColor ThemeManager::get_profile_accent_color( + telegram_api::object_ptr &&color_set) const { + CHECK(color_set != nullptr); + CHECK(color_set->get_id() == telegram_api::help_peerColorProfileSet::ID); + auto colors = telegram_api::move_object_as(color_set); + ProfileAccentColor color; + color.palette_colors_ = std::move(colors->palette_colors_); + color.background_colors_ = std::move(colors->bg_colors_); + color.story_colors_ = std::move(colors->story_colors_); + return color; +} + +void ThemeManager::on_get_profile_accent_colors( + Result> result) { + if (result.is_error()) { + return; + } + + auto peer_colors_ptr = result.move_as_ok(); + LOG(DEBUG) << "Receive " << to_string(peer_colors_ptr); + if (peer_colors_ptr->get_id() == telegram_api::help_peerColorsNotModified::ID) { + return; + } + CHECK(peer_colors_ptr->get_id() == telegram_api::help_peerColors::ID); + auto peer_colors = telegram_api::move_object_as(peer_colors_ptr); + FlatHashMap light_colors; + FlatHashMap dark_colors; + vector accent_color_ids; + for (auto &option : peer_colors->colors_) { + AccentColorId accent_color_id(option->color_id_); + if (option->colors_ == nullptr || option->colors_->get_id() != telegram_api::help_peerColorProfileSet::ID || + option->dark_colors_ == nullptr || + option->dark_colors_->get_id() != telegram_api::help_peerColorProfileSet::ID || !accent_color_id.is_valid() || + td::contains(accent_color_ids, accent_color_id)) { + LOG(ERROR) << "Receive " << to_string(option); + continue; + } + if (!option->hidden_) { + accent_color_ids.push_back(accent_color_id); + } + light_colors[accent_color_id] = get_profile_accent_color(std::move(option->colors_)); + dark_colors[accent_color_id] = get_profile_accent_color(std::move(option->dark_colors_)); + } + + bool is_changed = false; + if (profile_accent_colors_.hash_ != peer_colors->hash_) { + profile_accent_colors_.hash_ = peer_colors->hash_; + is_changed = true; + } + if (!on_update_profile_accent_colors(std::move(light_colors), std::move(dark_colors), std::move(accent_color_ids)) && + is_changed) { + save_profile_accent_colors(); + } +} + ThemeManager::BaseTheme ThemeManager::get_base_theme( const telegram_api::object_ptr &base_theme) { CHECK(base_theme != nullptr); @@ -689,6 +956,9 @@ void ThemeManager::get_current_state(vector> if (!accent_colors_.accent_color_ids_.empty()) { updates.push_back(get_update_accent_colors_object()); } + if (!profile_accent_colors_.accent_color_ids_.empty()) { + updates.push_back(get_update_profile_accent_colors_object()); + } } } // namespace td diff --git a/td/telegram/ThemeManager.h b/td/telegram/ThemeManager.h index 556bd4e57..8e50c68a5 100644 --- a/td/telegram/ThemeManager.h +++ b/td/telegram/ThemeManager.h @@ -34,12 +34,16 @@ class ThemeManager final : public Actor { void reload_accent_colors(); + void reload_profile_accent_colors(); + static string get_theme_parameters_json_string(const td_api::object_ptr &theme, bool for_web_view); int32 get_accent_color_id_object(AccentColorId accent_color_id, AccentColorId fallback_accent_color_id = AccentColorId()) const; + int32 get_profile_accent_color_id_object(AccentColorId accent_color_id) const; + void get_current_state(vector> &updates) const; private: @@ -104,6 +108,39 @@ class ThemeManager final : public Actor { void parse(ParserT &parser); }; + struct ProfileAccentColor { + vector palette_colors_; + vector background_colors_; + vector story_colors_; + + td_api::object_ptr get_profile_accent_colors_object() const; + + template + void store(StorerT &storer) const; + + template + void parse(ParserT &parser); + }; + + friend bool operator==(const ProfileAccentColor &lhs, const ProfileAccentColor &rhs); + + friend bool operator!=(const ProfileAccentColor &lhs, const ProfileAccentColor &rhs); + + struct ProfileAccentColors { + FlatHashMap light_colors_; + FlatHashMap dark_colors_; + vector accent_color_ids_; + int32 hash_ = 0; + + td_api::object_ptr get_update_profile_accent_colors_object() const; + + template + void store(StorerT &storer) const; + + template + void parse(ParserT &parser); + }; + void start_up() final; void tear_down() final; @@ -112,6 +149,8 @@ class ThemeManager final : public Actor { void load_accent_colors(); + void load_profile_accent_colors(); + static bool is_dark_base_theme(BaseTheme base_theme); void on_get_chat_themes(Result> result); @@ -122,6 +161,15 @@ class ThemeManager final : public Actor { void on_get_accent_colors(Result> result); + bool on_update_profile_accent_colors(FlatHashMap light_colors, + FlatHashMap dark_colors, + vector accent_color_ids); + + ProfileAccentColor get_profile_accent_color( + telegram_api::object_ptr &&color_set) const; + + void on_get_profile_accent_colors(Result> result); + td_api::object_ptr get_theme_settings_object(const ThemeSettings &settings) const; td_api::object_ptr get_chat_theme_object(const ChatTheme &theme) const; @@ -132,10 +180,14 @@ class ThemeManager final : public Actor { string get_accent_colors_database_key(); + string get_profile_accent_colors_database_key(); + void save_chat_themes(); void save_accent_colors(); + void save_profile_accent_colors(); + void send_update_chat_themes() const; static BaseTheme get_base_theme(const telegram_api::object_ptr &base_theme); @@ -144,12 +196,18 @@ class ThemeManager final : public Actor { td_api::object_ptr get_update_accent_colors_object() const; + td_api::object_ptr get_update_profile_accent_colors_object() const; + void send_update_accent_colors() const; + void send_update_profile_accent_colors() const; + ChatThemes chat_themes_; AccentColors accent_colors_; + ProfileAccentColors profile_accent_colors_; + Td *td_; ActorShared<> parent_; }; diff --git a/td/telegram/UpdatesManager.cpp b/td/telegram/UpdatesManager.cpp index 30aab9fba..152d9fe62 100644 --- a/td/telegram/UpdatesManager.cpp +++ b/td/telegram/UpdatesManager.cpp @@ -2263,6 +2263,7 @@ void UpdatesManager::try_reload_data() { td_->story_manager_->reload_all_read_stories(); td_->theme_manager_->reload_accent_colors(); td_->theme_manager_->reload_chat_themes(); + td_->theme_manager_->reload_profile_accent_colors(); schedule_data_reload(); }