diff --git a/td/generate/scheme/td_api.tl b/td/generate/scheme/td_api.tl index eb9878f13..ac5e62b76 100644 --- a/td/generate/scheme/td_api.tl +++ b/td/generate/scheme/td_api.tl @@ -2210,6 +2210,9 @@ updateFavoriteStickers sticker_ids:vector = Update; //@description The list of saved animations was updated @animation_ids The new list of file identifiers of saved animations updateSavedAnimations animation_ids:vector = Update; +//@description Some language pack strings was updated @language_pack Changed language pack @language_code Code of the language, which was updated @strings List of changed language pack strings +updateLanguagePack language_pack:string language_code:string strings:vector = Update; + //@description The connection state has changed @state The new connection state updateConnectionState state:ConnectionState = Update; diff --git a/td/generate/scheme/td_api.tlo b/td/generate/scheme/td_api.tlo index b442faaa5..d72152a79 100644 Binary files a/td/generate/scheme/td_api.tlo and b/td/generate/scheme/td_api.tlo differ diff --git a/td/telegram/Global.h b/td/telegram/Global.h index 5d11722fd..4b7f69d20 100644 --- a/td/telegram/Global.h +++ b/td/telegram/Global.h @@ -41,6 +41,7 @@ class ConfigManager; class ConnectionCreator; class ContactsManager; class FileManager; +class LanguagePackManager; class MtprotoHeader; class MessagesManager; class NetQueryDispatcher; @@ -159,6 +160,12 @@ class Global : public ActorContext { void set_file_manager(ActorId file_manager) { file_manager_ = std::move(file_manager); } + ActorId language_pack_manager() const { + return language_pack_manager_; + } + void set_language_pack_manager(ActorId language_pack_manager) { + language_pack_manager_ = language_pack_manager; + } ActorId messages_manager() const { return messages_manager_; } @@ -309,6 +316,7 @@ class Global : public ActorContext { ActorId contacts_manager_; ActorId file_manager_; ActorId messages_manager_; + ActorId language_pack_manager_; ActorId password_manager_; ActorId secret_chats_manager_; ActorId call_manager_; diff --git a/td/telegram/LanguagePackManager.cpp b/td/telegram/LanguagePackManager.cpp index ece5be945..97bff7d88 100644 --- a/td/telegram/LanguagePackManager.cpp +++ b/td/telegram/LanguagePackManager.cpp @@ -10,6 +10,7 @@ #include "td/telegram/Global.h" #include "td/telegram/logevent/LogEvent.h" #include "td/telegram/net/NetQueryDispatcher.h" +#include "td/telegram/Td.h" #include "td/utils/logging.h" #include "td/utils/misc.h" @@ -22,9 +23,9 @@ namespace td { void LanguagePackManager::start_up() { language_pack_ = G()->shared_config().get_option_string("language_pack"); language_code_ = G()->shared_config().get_option_string("language_code"); - language_pack_version_ = G()->shared_config().get_option_integer("language_pack_version", -1); - LOG(INFO) << "Use language pack " << language_pack_ << " with language " << language_code_ << " of version " - << language_pack_version_; + LOG(INFO) << "Use language pack " << language_pack_ << " with language " << language_code_; + + // TODO load language_pack_version from database } void LanguagePackManager::on_language_pack_changed() { @@ -47,23 +48,81 @@ void LanguagePackManager::on_language_code_changed() { inc_generation(); } -void LanguagePackManager::on_language_pack_version_changed() { - auto new_language_pack_version = G()->shared_config().get_option_integer("language_pack_version"); - if (new_language_pack_version == language_pack_version_) { - return; - } - if (language_pack_version_ == -1) { +void LanguagePackManager::on_language_pack_version_changed(int32 new_version) { + Language *language = get_language(language_pack_, language_code_); + auto version = language == nullptr ? -1 : language->version_; + if (version == -1) { return; } - // TODO update language pack - language_pack_version_ = new_language_pack_version; + auto new_language_pack_version = + new_version >= 0 ? new_version : G()->shared_config().get_option_integer("language_pack_version", -1); + if (new_language_pack_version <= version) { + return; + } + + std::lock_guard lock(language->mutex_); + if (language->has_get_difference_query_) { + return; + } + + language->has_get_difference_query_ = true; + auto request_promise = + PromiseCreator::lambda([actor_id = actor_id(this), language_pack = language_pack_, language_code = language_code_, + from_version = version](Result r_query) mutable { + auto r_result = fetch_result(std::move(r_query)); + if (r_result.is_error()) { + send_closure(actor_id, &LanguagePackManager::on_failed_get_difference, std::move(language_pack), + std::move(language_code)); + return; + } + + auto result = r_result.move_as_ok(); + LOG(INFO) << "Receive language pack difference for language " << result->lang_code_ << " from version " + << result->from_version_ << " with version " << result->version_ << " of size " + << result->strings_.size(); + LOG_IF(ERROR, result->lang_code_ != language_code) + << "Receive strings for " << result->lang_code_ << " instead of " << language_code; + LOG_IF(ERROR, result->from_version_ != from_version) + << "Receive strings from " << result->from_version_ << " instead of " << from_version; + send_closure(actor_id, &LanguagePackManager::on_get_language_pack_strings, std::move(language_pack), + std::move(language_code), result->version_, true, vector(), std::move(result->strings_), + Promise>()); + }); + send_with_promise(G()->net_query_creator().create(create_storer(telegram_api::langpack_getDifference(version))), + std::move(request_promise)); +} + +void LanguagePackManager::on_update_language_pack(tl_object_ptr difference) { + LOG(INFO) << "Receive update language pack difference for language " << difference->lang_code_ << " from version " + << difference->from_version_ << " with version " << difference->version_ << " of size " + << difference->strings_.size(); + if (difference->lang_code_ != language_code_) { + LOG(WARNING) << "Ignore difference for language " << difference->lang_code_; + return; + } + + Language *language = get_language(language_pack_, language_code_); + auto version = language == nullptr ? -1 : language->version_; + if (difference->version_ <= version) { + LOG(INFO) << "Skip applying already applied updates"; + return; + } + if (version == -1 || version < difference->from_version_) { + LOG(INFO) << "Can't apply difference"; + return on_language_pack_version_changed(difference->version_); + } + + on_get_language_pack_strings(language_pack_, std::move(difference->lang_code_), difference->version_, true, + vector(), std::move(difference->strings_), + Promise>()); } void LanguagePackManager::inc_generation() { generation_++; G()->shared_config().set_option_empty("language_pack_version"); - language_pack_version_ = -1; + + // TODO preload language and load language_pack_version from database } LanguagePackManager::Language *LanguagePackManager::get_language(const string &language_pack, @@ -221,7 +280,7 @@ void LanguagePackManager::get_language_pack_strings(string language_code, vector << "Receive strings for " << result->lang_code_ << " instead of " << language_code; LOG_IF(ERROR, result->from_version_ != 0) << "Receive lang pack from version " << result->from_version_; send_closure(actor_id, &LanguagePackManager::on_get_language_pack_strings, std::move(language_pack), - std::move(language_code), result->version_, vector(), std::move(result->strings_), + std::move(language_code), result->version_, false, vector(), std::move(result->strings_), std::move(promise)); }); send_with_promise(G()->net_query_creator().create(create_storer(telegram_api::langpack_getLangPack(language_code))), @@ -236,7 +295,7 @@ void LanguagePackManager::get_language_pack_strings(string language_code, vector } send_closure(actor_id, &LanguagePackManager::on_get_language_pack_strings, std::move(language_pack), - std::move(language_code), -1, std::move(keys), r_result.move_as_ok(), std::move(promise)); + std::move(language_code), -1, false, std::move(keys), r_result.move_as_ok(), std::move(promise)); }); send_with_promise(G()->net_query_creator().create( create_storer(telegram_api::langpack_getStrings(language_code, std::move(keys)))), @@ -245,10 +304,11 @@ void LanguagePackManager::get_language_pack_strings(string language_code, vector } void LanguagePackManager::on_get_language_pack_strings( - string language_pack, string language_code, int32 version, vector keys, + string language_pack, string language_code, int32 version, bool is_diff, vector keys, vector> results, Promise> promise) { Language *language = get_language(language_pack, language_code); + bool is_version_changed = false; if (language == nullptr || (language->version_ < version || !keys.empty())) { if (language == nullptr) { language = add_language(language_pack, language_code); @@ -256,9 +316,13 @@ void LanguagePackManager::on_get_language_pack_strings( } std::lock_guard lock(language->mutex_); if (language->version_ < version || !keys.empty()) { - if (language->version_ < version) { + vector> strings; + if (language->version_ < version && !(is_diff && language->version_ == -1)) { + LOG(INFO) << "Set language " << language_code << " version to " << version; language->version_ = version; + is_version_changed = true; } + for (auto &result : results) { CHECK(result != nullptr); switch (result->get_id()) { @@ -267,6 +331,9 @@ void LanguagePackManager::on_get_language_pack_strings( language->ordinary_strings_[str->key_] = std::move(str->value_); language->pluralized_strings_.erase(str->key_); language->deleted_strings_.erase(str->key_); + if (is_diff) { + strings.push_back(get_language_pack_string_object(*language->ordinary_strings_.find(str->key_))); + } break; } case telegram_api::langPackStringPluralized::ID: { @@ -276,6 +343,9 @@ void LanguagePackManager::on_get_language_pack_strings( std::move(str->few_value_), std::move(str->many_value_), std::move(str->other_value_)}; language->ordinary_strings_.erase(str->key_); language->deleted_strings_.erase(str->key_); + if (is_diff) { + strings.push_back(get_language_pack_string_object(*language->pluralized_strings_.find(str->key_))); + } break; } case telegram_api::langPackStringDeleted::ID: { @@ -283,6 +353,9 @@ void LanguagePackManager::on_get_language_pack_strings( language->ordinary_strings_.erase(str->key_); language->pluralized_strings_.erase(str->key_); language->deleted_strings_.insert(std::move(str->key_)); + if (is_diff) { + strings.push_back(get_language_pack_string_object(str->key_)); + } break; } default: @@ -294,14 +367,48 @@ void LanguagePackManager::on_get_language_pack_strings( if (!language_has_string_unsafe(language, key)) { LOG(ERROR) << "Doesn't receive key " << key << " from server"; language->deleted_strings_.insert(key); + if (is_diff) { + strings.push_back(get_language_pack_string_object(key)); + } } } + if (is_diff) { + // do we need to send update to all client instances? + send_closure(G()->td(), &Td::send_update, + td_api::make_object(language_pack, language_code, std::move(strings))); + } + // TODO save language } } + if (is_diff) { + CHECK(language != nullptr); + std::lock_guard lock(language->mutex_); + if (language->has_get_difference_query_) { + language->has_get_difference_query_ = false; + is_version_changed = true; + } + } + if (is_version_changed && language_pack == language_pack_ && language_code == language_code_) { + send_closure_later(actor_id(this), &LanguagePackManager::on_language_pack_version_changed, -1); + } - promise.set_value(get_language_pack_strings_object(language, keys)); + if (promise) { + promise.set_value(get_language_pack_strings_object(language, keys)); + } +} + +void LanguagePackManager::on_failed_get_difference(string language_pack, string language_code) { + Language *language = get_language(language_pack, language_code); + CHECK(language != nullptr); + std::lock_guard lock(language->mutex_); + if (language->has_get_difference_query_) { + language->has_get_difference_query_ = false; + if (language_pack == language_pack_ && language_code == language_code_) { + send_closure_later(actor_id(this), &LanguagePackManager::on_language_pack_version_changed, -1); + } + } } void LanguagePackManager::on_result(NetQueryPtr query) { diff --git a/td/telegram/LanguagePackManager.h b/td/telegram/LanguagePackManager.h index 4799ca552..cbe388edc 100644 --- a/td/telegram/LanguagePackManager.h +++ b/td/telegram/LanguagePackManager.h @@ -33,13 +33,15 @@ class LanguagePackManager : public NetQueryCallback { void on_language_code_changed(); - void on_language_pack_version_changed(); + void on_language_pack_version_changed(int32 new_version); void get_languages(Promise> promise); void get_language_pack_strings(string language_code, vector keys, Promise> promise); + void on_update_language_pack(tl_object_ptr difference); + private: struct PluralizedString { string zero_value_; @@ -53,6 +55,7 @@ class LanguagePackManager : public NetQueryCallback { struct Language { std::mutex mutex_; // TODO RwMutex std::atomic version_{-1}; + bool has_get_difference_query_ = false; std::unordered_map ordinary_strings_; std::unordered_map pluralized_strings_; std::unordered_set deleted_strings_; @@ -69,8 +72,6 @@ class LanguagePackManager : public NetQueryCallback { string language_code_; uint32 generation_ = 0; - int32 language_pack_version_ = -1; - static std::mutex language_packs_mutex_; static std::unordered_map> language_packs_; @@ -93,10 +94,12 @@ class LanguagePackManager : public NetQueryCallback { void inc_generation(); - void on_get_language_pack_strings(string language_pack, string language_code, int32 version, vector keys, - vector> results, + void on_get_language_pack_strings(string language_pack, string language_code, int32 version, bool is_diff, + vector keys, vector> results, Promise> promise); + void on_failed_get_difference(string language_pack, string language_code); + void on_result(NetQueryPtr query) override; void start_up() override; diff --git a/td/telegram/Td.cpp b/td/telegram/Td.cpp index f667a2429..b265f8a57 100644 --- a/td/telegram/Td.cpp +++ b/td/telegram/Td.cpp @@ -3512,7 +3512,7 @@ void Td::on_config_option_updated(const string &name) { G()->net_query_dispatcher().update_mtproto_header(); } } else if (name == "language_pack_version") { - send_closure(language_pack_manager_, &LanguagePackManager::on_language_pack_version_changed); + send_closure(language_pack_manager_, &LanguagePackManager::on_language_pack_version_changed, -1); return; } else if (is_internal_config_option(name)) { return; @@ -4075,6 +4075,7 @@ Status Td::init(DbKey key) { device_token_manager_ = create_actor("DeviceTokenManager", create_reference()); hashtag_hints_ = create_actor("HashtagHints", "text", create_reference()); language_pack_manager_ = create_actor("LanguagePackManager", create_reference()); + G()->set_language_pack_manager(language_pack_manager_.get()); password_manager_ = create_actor("PasswordManager", create_reference()); G()->set_password_manager(password_manager_.get()); privacy_manager_ = create_actor("PrivacyManager", create_reference()); diff --git a/td/telegram/UpdatesManager.cpp b/td/telegram/UpdatesManager.cpp index 80895b24e..ff0716e8f 100644 --- a/td/telegram/UpdatesManager.cpp +++ b/td/telegram/UpdatesManager.cpp @@ -19,6 +19,7 @@ #include "td/telegram/DialogId.h" #include "td/telegram/Global.h" #include "td/telegram/InlineQueriesManager.h" +#include "td/telegram/LanguagePackManager.h" #include "td/telegram/Location.h" #include "td/telegram/MessageId.h" #include "td/telegram/MessagesManager.h" @@ -1816,12 +1817,16 @@ void UpdatesManager::on_update(tl_object_ptr td_->contacts_manager_->on_update_contacts_reset(); } -// unsupported updates - void UpdatesManager::on_update(tl_object_ptr update, bool /*force_apply*/) { + send_closure(G()->language_pack_manager(), &LanguagePackManager::on_language_pack_version_changed, + std::numeric_limits::max()); } void UpdatesManager::on_update(tl_object_ptr update, bool /*force_apply*/) { + send_closure(G()->language_pack_manager(), &LanguagePackManager::on_update_language_pack, + std::move(update->difference_)); } +// unsupported updates + } // namespace td diff --git a/td/telegram/UpdatesManager.h b/td/telegram/UpdatesManager.h index 7a92f8c5c..473589527 100644 --- a/td/telegram/UpdatesManager.h +++ b/td/telegram/UpdatesManager.h @@ -267,10 +267,10 @@ class UpdatesManager : public Actor { void on_update(tl_object_ptr update, bool /*force_apply*/); - // unsupported updates - void on_update(tl_object_ptr update, bool /*force_apply*/); void on_update(tl_object_ptr update, bool /*force_apply*/); + + // unsupported updates }; } // namespace td