Support cloud languages list and switching.

Add Lang::Current().updated() observable for retranslating the UI.
This commit is contained in:
John Preston 2017-04-18 18:21:03 +03:00
parent 139d4e72b5
commit f5dfeb0c50
16 changed files with 415 additions and 179 deletions

View File

@ -357,9 +357,9 @@ class Observable : public internal::BaseObservable<EventType, Handler, base::typ
public: public:
Observable() = default; Observable() = default;
Observable(const Observable &other) = delete; Observable(const Observable &other) = delete;
Observable(Observable &&other) = default; Observable(Observable &&other) = delete;
Observable &operator=(const Observable &other) = delete; Observable &operator=(const Observable &other) = delete;
Observable &operator=(Observable &&other) = default; Observable &operator=(Observable &&other) = delete;
}; };

View File

@ -28,81 +28,119 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "mainwidget.h" #include "mainwidget.h"
#include "mainwindow.h" #include "mainwindow.h"
#include "lang/lang_instance.h" #include "lang/lang_instance.h"
#include "lang/lang_cloud_manager.h"
#include "styles/style_boxes.h" #include "styles/style_boxes.h"
class LanguageBox::Inner : public TWidget {
public:
Inner(QWidget *parent, gsl::not_null<Languages*> languages);
void setSelected(int index);
void refresh();
private:
void languageChanged(int languageIndex);
gsl::not_null<Languages*> _languages;
std::shared_ptr<Ui::RadiobuttonGroup> _group;
std::vector<object_ptr<Ui::Radiobutton>> _buttons;
};
LanguageBox::Inner::Inner(QWidget *parent, gsl::not_null<Languages*> languages) : TWidget(parent)
, _languages(languages) {
_group = std::make_shared<Ui::RadiobuttonGroup>(0);
_group->setChangedCallback([this](int value) { languageChanged(value); });
}
void LanguageBox::Inner::setSelected(int index) {
_group->setValue(index);
}
void LanguageBox::Inner::refresh() {
for (auto &button : _buttons) {
button.destroy();
}
_buttons.clear();
auto y = st::boxOptionListPadding.top();
_buttons.reserve(_languages->size());
auto index = 0;
for_const (auto &language, *_languages) {
_buttons.emplace_back(this, _group, index++, language.name, st::langsButton);
auto button = _buttons.back().data();
button->moveToLeft(st::boxPadding.left() + st::boxOptionListPadding.left(), y + st::langsButton.margin.top());
button->show();
y += button->heightNoMargins() + st::boxOptionListSkip;
}
auto newHeight = y + st::boxOptionListPadding.bottom() + st::boxPadding.bottom();
resize(st::langsWidth, newHeight);
}
void LanguageBox::Inner::languageChanged(int languageIndex) {
Expects(languageIndex >= 0 && languageIndex < _languages->size());
auto languageId = (*_languages)[languageIndex].id;
if (languageId != qsl("custom")) {
Lang::CurrentCloudManager().switchToLanguage(languageId);
}
}
void LanguageBox::prepare() { void LanguageBox::prepare() {
refreshLangItems();
subscribe(Lang::Current().updated(), [this] {
refreshLangItems();
});
_inner = setInnerWidget(object_ptr<Inner>(this, &_languages), st::boxLayerScroll);
refresh();
subscribe(Lang::CurrentCloudManager().languageListChanged(), [this] {
refresh();
});
}
void LanguageBox::refreshLangItems() {
clearButtons();
addButton(lang(lng_box_ok), [this] { closeBox(); }); addButton(lang(lng_box_ok), [this] { closeBox(); });
setTitle(lang(lng_languages)); setTitle(lang(lng_languages));
request(MTPlangpack_GetLanguages()).done([this](const MTPVector<MTPLangPackLanguage> &result) { update();
auto currentId = Lang::Current().id();
auto currentFound = false;
std::vector<QString> languageIds = { qsl("en") };
std::vector<QString> languageNames = { qsl("English") };
for (auto &language : result.v) {
t_assert(language.type() == mtpc_langPackLanguage);
auto &data = language.c_langPackLanguage();
auto languageId = qs(data.vlang_code);
auto languageName = qs(data.vname);
if (languageId != qstr("en")) {
languageIds.push_back(languageId);
languageNames.push_back(languageName);
}
}
if (currentId == qstr("custom")) {
languageIds.insert(languageIds.begin(), currentId);
languageNames.insert(languageNames.begin(), qsl("Custom LangPack"));
currentFound = true;
}
auto languageCount = languageIds.size();
_langGroup = std::make_shared<Ui::RadiobuttonGroup>(cLang());
auto y = st::boxOptionListPadding.top();
_langs.reserve(languageCount);
for (auto i = 0; i != languageCount; ++i) {
if (!currentFound && languageIds[i] == currentId) {
currentFound = true;
}
_langs.emplace_back(this, _langGroup, i, languageNames[i], st::langsButton);
auto button = _langs.back().data();
button->moveToLeft(st::boxPadding.left() + st::boxOptionListPadding.left(), y + st::langsButton.margin.top());
button->show();
y += button->heightNoMargins() + st::boxOptionListSkip;
}
_langGroup->setChangedCallback([this](int value) { languageChanged(value); });
setDimensions(st::langsWidth, st::boxOptionListPadding.top() + languageCount * st::langsButton.height + languageCount * st::boxOptionListSkip + st::boxOptionListPadding.bottom() + st::boxPadding.bottom());
}).fail([this](const RPCError &error) {
closeBox();
}).send();
setDimensions(st::langsWidth, st::langsWidth);
} }
void LanguageBox::languageChanged(int languageId) { void LanguageBox::refresh() {
//Expects(languageId == languageTest || (languageId >= 0 && languageId < base::array_size(LanguageCodes))); refreshLanguages();
//if (languageId == cLang()) { _inner->refresh();
// return; auto maxHeight = st::boxOptionListPadding.top() + _languages.size() * (st::langsButton.height + st::boxOptionListSkip) + st::boxOptionListPadding.bottom() + st::boxPadding.bottom();
//} setDimensions(st::langsWidth, qMin(maxHeight, st::boxMaxListHeight));
}
//Lang::FileParser::Result result;
//if (languageId > 0) { void LanguageBox::refreshLanguages() {
// Lang::FileParser loader(qsl(":/langs/lang_") + LanguageCodes[languageId].c_str() + qsl(".strings"), { lng_sure_save_language, lng_cancel, lng_box_ok }); _languages = Languages();
// result = loader.found(); auto list = Lang::CurrentCloudManager().languageList();
//} else if (languageId == languageTest) { _languages.reserve(list.size() + 1);
// Lang::FileParser loader(cLangFile(), { lng_sure_save_language, lng_cancel, lng_box_ok }); auto currentId = Lang::Current().id();
// result = loader.found(); auto currentIndex = -1;
//} _languages.push_back({ qsl("en"), qsl("English") });
//auto text = result.value(lng_sure_save_language, Lang::GetOriginalValue(lng_sure_save_language)), for (auto &language : list) {
// save = result.value(lng_box_ok, Lang::GetOriginalValue(lng_box_ok)), auto isCurrent = (language.id == currentId);
// cancel = result.value(lng_cancel, Lang::GetOriginalValue(lng_cancel)); if (language.id != qstr("en")) {
//Ui::show(Box<ConfirmBox>(text, save, cancel, base::lambda_guarded(this, [this, languageId] { if (isCurrent) {
// cSetLang(languageId); currentIndex = _languages.size();
// Local::writeSettings(); }
// App::restart(); _languages.push_back(language);
//}), base::lambda_guarded(this, [this] { } else if (isCurrent) {
// _langGroup->setValue(cLang()); currentIndex = 0;
//})), KeepOtherLayers); }
}
if (currentId == qstr("custom")) {
_languages.insert(_languages.begin(), { currentId, qsl("Custom LangPack") });
currentIndex = 0;
} else if (currentIndex < 0) {
currentIndex = _languages.size();
_languages.push_back({ currentId, lang(lng_language_name) });
}
_inner->setSelected(currentIndex);
} }

View File

@ -20,6 +20,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
*/ */
#pragma once #pragma once
#include "lang/lang_cloud_manager.h"
#include "boxes/abstract_box.h" #include "boxes/abstract_box.h"
#include "mtproto/sender.h" #include "mtproto/sender.h"
@ -29,8 +30,6 @@ class Radiobutton;
} // namespace Ui } // namespace Ui
class LanguageBox : public BoxContent, private MTP::Sender { class LanguageBox : public BoxContent, private MTP::Sender {
Q_OBJECT
public: public:
LanguageBox(QWidget*) { LanguageBox(QWidget*) {
} }
@ -39,9 +38,15 @@ protected:
void prepare() override; void prepare() override;
private: private:
void languageChanged(int languageId); using Languages = Lang::CloudManager::Languages;
std::shared_ptr<Ui::RadiobuttonGroup> _langGroup; void refresh();
std::vector<object_ptr<Ui::Radiobutton>> _langs; void refreshLanguages();
void refreshLangItems();
Languages _languages;
class Inner;
QPointer<Inner> _inner;
}; };

View File

@ -0,0 +1,123 @@
/*
This file is part of Telegram Desktop,
the official desktop version of Telegram messaging app, see https://telegram.org
Telegram Desktop is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
It is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
In addition, as a special exception, the copyright holders give permission
to link the code of portions of this program with the OpenSSL library.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
*/
#include "lang/lang_cloud_manager.h"
#include "lang/lang_instance.h"
#include "mtproto/mtp_instance.h"
#include "storage/localstorage.h"
#include "messenger.h"
#include "apiwrap.h"
#include "auth_session.h"
namespace Lang {
CloudManager::CloudManager(Instance &langpack, gsl::not_null<MTP::Instance*> mtproto) : MTP::Sender(mtproto)
, _langpack(langpack) {
requestLangPackDifference();
}
void CloudManager::requestLangPackDifference() {
auto &langpack = Lang::Current();
if (langpack.isCustom() || _langPackRequestId) {
return;
}
auto version = langpack.version();
if (version > 0) {
_langPackRequestId = request(MTPlangpack_GetDifference(MTP_int(version))).done([this](const MTPLangPackDifference &result) {
_langPackRequestId = 0;
applyLangPackDifference(result);
}).fail([this](const RPCError &error) {
_langPackRequestId = 0;
}).send();
} else {
_langPackRequestId = request(MTPlangpack_GetLangPack()).done([this](const MTPLangPackDifference &result) {
_langPackRequestId = 0;
applyLangPackDifference(result);
}).fail([this](const RPCError &error) {
_langPackRequestId = 0;
}).send();
}
}
void CloudManager::applyLangPackDifference(const MTPLangPackDifference &difference) {
Expects(difference.type() == mtpc_langPackDifference);
auto &current = Lang::Current();
if (current.isCustom()) {
return;
}
auto &langpack = difference.c_langPackDifference();
switchLangPackId(qs(langpack.vlang_code));
if (current.version() < langpack.vfrom_version.v) {
requestLangPackDifference();
} else if (!langpack.vstrings.v.isEmpty()) {
current.applyDifference(langpack);
Local::writeLangPack();
auto fullLangPackUpdated = (langpack.vfrom_version.v == 0);
if (fullLangPackUpdated) {
Lang::Current().updated().notify();
}
} else {
LOG(("Lang Info: Up to date."));
}
}
void CloudManager::requestLanguageList() {
_languagesRequestId = request(MTPlangpack_GetLanguages()).done([this](const MTPVector<MTPLangPackLanguage> &result) {
auto languages = Languages();
for_const (auto &langData, result.v) {
t_assert(langData.type() == mtpc_langPackLanguage);
auto &language = langData.c_langPackLanguage();
languages.push_back({ qs(language.vlang_code), qs(language.vname) });
}
if (_languages != languages) {
_languages = languages;
_languagesChanged.notify();
}
_languagesRequestId = 0;
}).fail([this](const RPCError &error) {
_languagesRequestId = 0;
}).send();
}
void CloudManager::switchToLanguage(const QString &id) {
switchLangPackId(id);
requestLangPackDifference();
}
void CloudManager::switchLangPackId(const QString &id) {
auto &current = Lang::Current();
if (current.id() != id) {
current.switchToId(id);
auto mtproto = requestMTP();
mtproto->reInitConnection(mtproto->mainDcId());
}
}
CloudManager &CurrentCloudManager() {
auto result = Messenger::Instance().langCloudManager();
t_assert(result != nullptr);
return *result;
}
} // namespace Lang

View File

@ -0,0 +1,77 @@
/*
This file is part of Telegram Desktop,
the official desktop version of Telegram messaging app, see https://telegram.org
Telegram Desktop is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
It is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
In addition, as a special exception, the copyright holders give permission
to link the code of portions of this program with the OpenSSL library.
Full license: https://github.com/telegramdesktop/tdesktop/blob/master/LICENSE
Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
*/
#pragma once
#include "mtproto/sender.h"
namespace MTP {
class Instance;
} // namespace MTP
namespace Lang {
class Instance;
class CloudManager : private MTP::Sender {
public:
CloudManager(Instance &langpack, gsl::not_null<MTP::Instance*> mtproto);
struct Language {
QString id;
QString name;
};
using Languages = QVector<Language>;
void requestLanguageList();
Languages languageList() const {
return _languages;
}
base::Observable<void> &languageListChanged() {
return _languagesChanged;
}
void requestLangPackDifference();
void applyLangPackDifference(const MTPLangPackDifference &difference);
void switchToLanguage(const QString &id);
private:
void applyLangPack(const MTPUpdates &updates);
void switchLangPackId(const QString &id);
Instance &_langpack;
Languages _languages;
base::Observable<void> _languagesChanged;
mtpRequestId _langPackRequestId = 0;
mtpRequestId _languagesRequestId = 0;
};
inline bool operator==(const CloudManager::Language &a, const CloudManager::Language &b) {
return (a.id == b.id) && (a.name == b.name);
}
inline bool operator!=(const CloudManager::Language &a, const CloudManager::Language &b) {
return !(a == b);
}
CloudManager &CurrentCloudManager();
} // namespace Lang

View File

@ -22,9 +22,9 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "messenger.h" #include "messenger.h"
#include "lang/lang_file_parser.h" #include "lang/lang_file_parser.h"
#include "platform/platform_specific.h"
#include "storage/serialize_common.h" #include "storage/serialize_common.h"
#include "storage/localstorage.h" #include "storage/localstorage.h"
#include "platform/platform_specific.h"
namespace Lang { namespace Lang {
namespace { namespace {
@ -32,6 +32,16 @@ namespace {
constexpr auto kDefaultLanguage = str_const("en"); constexpr auto kDefaultLanguage = str_const("en");
constexpr auto kLangValuesLimit = 20000; constexpr auto kLangValuesLimit = 20000;
constexpr str_const kLegacyLanguages[] = {
"en",
"it",
"es",
"de",
"nl",
"pt_BR",
"ko",
};
class ValueParser { class ValueParser {
public: public:
ValueParser(const QByteArray &key, LangKey keyIndex, const QByteArray &value); ValueParser(const QByteArray &key, LangKey keyIndex, const QByteArray &value);
@ -220,14 +230,38 @@ bool ValueParser::parse() {
} // namespace } // namespace
void Instance::fillDefaults() { void Instance::switchToId(const QString &id) {
reset();
_id = id;
}
void Instance::switchToCustomFile(const QString &filePath) {
reset();
fillFromCustomFile(filePath);
}
void Instance::reset() {
_values.clear(); _values.clear();
_nonDefaultValues.clear();
_legacyId = kLegacyLanguageNone;
_customFilePathAbsolute = QString();
_customFilePathRelative = QString();
_customFileContent = QByteArray();
_version = 0;
fillDefaults();
}
void Instance::fillDefaults() {
Expects(_values.empty());
_values.reserve(kLangKeysCount); _values.reserve(kLangKeysCount);
for (auto i = 0; i != kLangKeysCount; ++i) { for (auto i = 0; i != kLangKeysCount; ++i) {
_values.emplace_back(GetOriginalValue(LangKey(i))); _values.emplace_back(GetOriginalValue(LangKey(i)));
} }
_id = str_const_toString(kDefaultLanguage); }
_legacyId = kLegacyDefaultLanguage;
QString Instance::DefaultLanguageId() {
return str_const_toString(kDefaultLanguage);
} }
QString Instance::cloudLangCode() const { QString Instance::cloudLangCode() const {
@ -235,7 +269,7 @@ QString Instance::cloudLangCode() const {
if (_systemLanguage.isEmpty()) { if (_systemLanguage.isEmpty()) {
_systemLanguage = Platform::SystemLanguage(); _systemLanguage = Platform::SystemLanguage();
if (_systemLanguage.isEmpty()) { if (_systemLanguage.isEmpty()) {
_systemLanguage = str_const_toString(kDefaultLanguage); _systemLanguage = Instance::DefaultLanguageId();
} }
} }
return _systemLanguage; return _systemLanguage;

View File

@ -27,36 +27,23 @@ namespace Lang {
constexpr auto kLegacyLanguageNone = -2; constexpr auto kLegacyLanguageNone = -2;
constexpr auto kLegacyCustomLanguage = -1; constexpr auto kLegacyCustomLanguage = -1;
constexpr auto kLegacyDefaultLanguage = 0; constexpr auto kLegacyDefaultLanguage = 0;
constexpr str_const kLegacyLanguages[] = {
"en",
"it",
"es",
"de",
"nl",
"pt_BR",
"ko",
};
class Instance { class Instance {
public: public:
Instance() { Instance() {
fillDefaults(); fillDefaults();
} }
struct CreateFromIdTag {}; void switchToId(const QString &id);
Instance(const QString &id, CreateFromIdTag) { void switchToCustomFile(const QString &filePath);
fillDefaults();
_id = id;
}
struct CreateFromCustomFileTag {};
Instance(const QString &filePath, CreateFromCustomFileTag) {
fillDefaults();
fillFromCustomFile(filePath);
}
Instance(const Instance &other) = delete; Instance(const Instance &other) = delete;
Instance &operator=(const Instance &other) = delete; Instance &operator=(const Instance &other) = delete;
Instance(Instance &&other) = default; Instance(Instance &&other) = default;
Instance &operator=(Instance &&other) = default; Instance &operator=(Instance &&other) = default;
static QString DefaultLanguageId();
QString cloudLangCode() const;
QString id() const { QString id() const {
return _id; return _id;
} }
@ -66,13 +53,15 @@ public:
int version() const { int version() const {
return _version; return _version;
} }
QString cloudLangCode() const;
QByteArray serialize() const; QByteArray serialize() const;
void fillFromSerialized(const QByteArray &data); void fillFromSerialized(const QByteArray &data);
void fillFromLegacy(int legacyId, const QString &legacyPath); void fillFromLegacy(int legacyId, const QString &legacyPath);
void applyDifference(const MTPDlangPackDifference &difference); void applyDifference(const MTPDlangPackDifference &difference);
base::Observable<void> &updated() {
return _updated;
}
QString getValue(LangKey key) { QString getValue(LangKey key) {
Expects(key >= 0 && key < kLangKeysCount); Expects(key >= 0 && key < kLangKeysCount);
@ -83,6 +72,7 @@ public:
private: private:
void applyValue(const QByteArray &key, const QByteArray &value); void applyValue(const QByteArray &key, const QByteArray &value);
void resetValue(const QByteArray &key); void resetValue(const QByteArray &key);
void reset();
void fillDefaults(); void fillDefaults();
void fillFromCustomFile(const QString &filePath); void fillFromCustomFile(const QString &filePath);
void loadFromContent(const QByteArray &content); void loadFromContent(const QByteArray &content);
@ -94,6 +84,8 @@ private:
QString _customFilePathRelative; QString _customFilePathRelative;
QByteArray _customFileContent; QByteArray _customFileContent;
int _version = 0; int _version = 0;
base::Observable<void> _updated;
mutable QString _systemLanguage; mutable QString _systemLanguage;
std::vector<QString> _values; std::vector<QString> _values;

View File

@ -38,6 +38,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "history/history_service_layout.h" #include "history/history_service_layout.h"
#include "overviewwidget.h" #include "overviewwidget.h"
#include "lang/lang_keys.h" #include "lang/lang_keys.h"
#include "lang/lang_cloud_manager.h"
#include "boxes/add_contact_box.h" #include "boxes/add_contact_box.h"
#include "storage/file_upload.h" #include "storage/file_upload.h"
#include "messenger.h" #include "messenger.h"
@ -5689,11 +5690,11 @@ void MainWidget::feedUpdate(const MTPUpdate &update) {
////// Cloud langpacks ////// Cloud langpacks
case mtpc_updateLangPack: { case mtpc_updateLangPack: {
auto &langpack = update.c_updateLangPack(); auto &langpack = update.c_updateLangPack();
Messenger::Instance().mtp()->applyLangPackDifference(langpack.vdifference); Messenger::Instance().langCloudManager()->applyLangPackDifference(langpack.vdifference);
} break; } break;
case mtpc_updateLangPackTooLong: { case mtpc_updateLangPackTooLong: {
Messenger::Instance().mtp()->requestLangPackDifference(); Messenger::Instance().langCloudManager()->requestLangPackDifference();
} break; } break;
} }

View File

@ -31,6 +31,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "calls/calls_instance.h" #include "calls/calls_instance.h"
#include "lang/lang_file_parser.h" #include "lang/lang_file_parser.h"
#include "lang/lang_translator.h" #include "lang/lang_translator.h"
#include "lang/lang_cloud_manager.h"
#include "observer_peer.h" #include "observer_peer.h"
#include "storage/file_upload.h" #include "storage/file_upload.h"
#include "mainwidget.h" #include "mainwidget.h"
@ -318,6 +319,8 @@ void Messenger::startMtp() {
} }
_private->storedAuthSession.reset(); _private->storedAuthSession.reset();
} }
_langCloudManager = std::make_unique<Lang::CloudManager>(langpack(), mtp());
} }
void Messenger::destroyMtpKeys(MTP::AuthKeysList &&keys) { void Messenger::destroyMtpKeys(MTP::AuthKeysList &&keys) {

View File

@ -50,6 +50,7 @@ class Instance;
namespace Lang { namespace Lang {
class Instance; class Instance;
class Translator; class Translator;
class CloudManager;
} // namespace Lang } // namespace Lang
class Messenger final : public QObject, public RPCSender, private base::Subscriber { class Messenger final : public QObject, public RPCSender, private base::Subscriber {
@ -109,6 +110,9 @@ public:
Lang::Instance &langpack() { Lang::Instance &langpack() {
return *_langpack; return *_langpack;
} }
Lang::CloudManager *langCloudManager() {
return _langCloudManager.get();
}
void authSessionCreate(UserId userId); void authSessionCreate(UserId userId);
void authSessionDestroy(); void authSessionDestroy();
base::Observable<void> &authSessionChanged() { base::Observable<void> &authSessionChanged() {
@ -197,6 +201,7 @@ private:
FileUploader *_uploader = nullptr; FileUploader *_uploader = nullptr;
std::unique_ptr<Lang::Instance> _langpack; std::unique_ptr<Lang::Instance> _langpack;
std::unique_ptr<Lang::CloudManager> _langCloudManager;
std::unique_ptr<Lang::Translator> _translator; std::unique_ptr<Lang::Translator> _translator;
std::unique_ptr<MTP::DcOptions> _dcOptions; std::unique_ptr<MTP::DcOptions> _dcOptions;
std::unique_ptr<MTP::Instance> _mtproto; std::unique_ptr<MTP::Instance> _mtproto;

View File

@ -33,7 +33,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
namespace MTP { namespace MTP {
class Instance::Private : public Sender { class Instance::Private : private Sender {
public: public:
Private(Instance *instance, DcOptions *options, Instance::Mode mode); Private(Instance *instance, DcOptions *options, Instance::Mode mode);
@ -51,8 +51,6 @@ public:
void requestConfig(); void requestConfig();
void requestCDNConfig(); void requestCDNConfig();
void requestLangPackDifference();
void applyLangPackDifference(const MTPLangPackDifference &difference);
void restart(); void restart();
void restart(ShiftedDcId shiftedDcId); void restart(ShiftedDcId shiftedDcId);
@ -64,6 +62,7 @@ public:
void killSession(ShiftedDcId shiftedDcId); void killSession(ShiftedDcId shiftedDcId);
void killSession(std::unique_ptr<internal::Session> session); void killSession(std::unique_ptr<internal::Session> session);
void stopSession(ShiftedDcId shiftedDcId); void stopSession(ShiftedDcId shiftedDcId);
void reInitConnection(DcId dcId);
void logout(RPCDoneHandlerPtr onDone, RPCFailHandlerPtr onFail); void logout(RPCDoneHandlerPtr onDone, RPCFailHandlerPtr onFail);
internal::DcenterPtr getDcById(ShiftedDcId shiftedDcId); internal::DcenterPtr getDcById(ShiftedDcId shiftedDcId);
@ -129,8 +128,6 @@ private:
void checkDelayedRequests(); void checkDelayedRequests();
void switchLangPackId(const QString &id);
Instance *_instance = nullptr; Instance *_instance = nullptr;
DcOptions *_dcOptions = nullptr; DcOptions *_dcOptions = nullptr;
Instance::Mode _mode = Instance::Mode::Normal; Instance::Mode _mode = Instance::Mode::Normal;
@ -183,8 +180,6 @@ private:
base::Timer _checkDelayedTimer; base::Timer _checkDelayedTimer;
mtpRequestId _langPackRequestId = 0;
// Debug flag to find out how we end up crashing. // Debug flag to find out how we end up crashing.
bool MustNotCreateSessions = false; bool MustNotCreateSessions = false;
@ -246,7 +241,6 @@ void Instance::Private::start(Config &&config) {
t_assert((_mainDcId == Config::kNoneMainDc) == isKeysDestroyer()); t_assert((_mainDcId == Config::kNoneMainDc) == isKeysDestroyer());
if (!isKeysDestroyer()) { if (!isKeysDestroyer()) {
requestConfig(); requestConfig();
requestLangPackDifference();
} }
} }
@ -303,58 +297,6 @@ void Instance::Private::requestCDNConfig() {
}).send(); }).send();
} }
void Instance::Private::requestLangPackDifference() {
auto &langpack = Lang::Current();
if (langpack.isCustom() || _langPackRequestId) {
return;
}
auto version = langpack.version();
if (version > 0) {
_langPackRequestId = request(MTPlangpack_GetDifference(MTP_int(version))).done([this](const MTPLangPackDifference &result) {
_langPackRequestId = 0;
applyLangPackDifference(result);
}).fail([this](const RPCError &error) {
_langPackRequestId = 0;
}).send();
} else {
_langPackRequestId = request(MTPlangpack_GetLangPack()).done([this](const MTPLangPackDifference &result) {
_langPackRequestId = 0;
applyLangPackDifference(result);
}).fail([this](const RPCError &error) {
_langPackRequestId = 0;
}).send();
}
}
void Instance::Private::applyLangPackDifference(const MTPLangPackDifference &difference) {
Expects(difference.type() == mtpc_langPackDifference);
auto &current = Lang::Current();
if (current.isCustom()) {
return;
}
auto &langpack = difference.c_langPackDifference();
switchLangPackId(qs(langpack.vlang_code));
if (current.version() < langpack.vfrom_version.v) {
requestLangPackDifference();
} else if (!langpack.vstrings.v.isEmpty()) {
current.applyDifference(langpack);
Local::writeLangPack();
} else {
LOG(("Lang Info: Up to date."));
}
}
void Instance::Private::switchLangPackId(const QString &id) {
auto &current = Lang::Current();
if (current.id() != id) {
current = Lang::Instance(id, Lang::Instance::CreateFromIdTag());
restart(maindc());
}
}
void Instance::Private::restart() { void Instance::Private::restart() {
for (auto &session : _sessions) { for (auto &session : _sessions) {
session.second->restart(); session.second->restart();
@ -477,7 +419,9 @@ void Instance::Private::killSession(ShiftedDcId shiftedDcId) {
_sessions.emplace(_mainDcId, std::move(main)); _sessions.emplace(_mainDcId, std::move(main));
_mainSession->start(); _mainSession->start();
} }
QMetaObject::invokeMethod(_instance, "onClearKilledSessions", Qt::QueuedConnection); InvokeQueued(_instance, [this] {
clearKilledSessions();
});
} }
void Instance::Private::clearKilledSessions() { void Instance::Private::clearKilledSessions() {
@ -493,6 +437,11 @@ void Instance::Private::stopSession(ShiftedDcId shiftedDcId) {
} }
} }
void Instance::Private::reInitConnection(DcId dcId) {
killSession(dcId);
getSession(dcId)->notifyLayerInited(false);
}
void Instance::Private::logout(RPCDoneHandlerPtr onDone, RPCFailHandlerPtr onFail) { void Instance::Private::logout(RPCDoneHandlerPtr onDone, RPCFailHandlerPtr onFail) {
_instance->send(MTPauth_LogOut(), onDone, onFail); _instance->send(MTPauth_LogOut(), onDone, onFail);
@ -1333,14 +1282,6 @@ void Instance::requestCDNConfig() {
_private->requestCDNConfig(); _private->requestCDNConfig();
} }
void Instance::requestLangPackDifference() {
_private->requestLangPackDifference();
}
void Instance::applyLangPackDifference(const MTPLangPackDifference &difference) {
_private->applyLangPackDifference(difference);
}
void Instance::connectionFinished(internal::Connection *connection) { void Instance::connectionFinished(internal::Connection *connection) {
_private->connectionFinished(connection); _private->connectionFinished(connection);
} }
@ -1381,6 +1322,10 @@ void Instance::stopSession(ShiftedDcId shiftedDcId) {
_private->stopSession(shiftedDcId); _private->stopSession(shiftedDcId);
} }
void Instance::reInitConnection(DcId dcId) {
_private->reInitConnection(dcId);
}
void Instance::logout(RPCDoneHandlerPtr onDone, RPCFailHandlerPtr onFail) { void Instance::logout(RPCDoneHandlerPtr onDone, RPCFailHandlerPtr onFail) {
_private->logout(onDone, onFail); _private->logout(onDone, onFail);
} }
@ -1489,10 +1434,6 @@ void Instance::onKeyDestroyed(qint32 shiftedDcId) {
_private->completedKeyDestroy(shiftedDcId); _private->completedKeyDestroy(shiftedDcId);
} }
void Instance::onClearKilledSessions() {
_private->clearKilledSessions();
}
Instance::~Instance() { Instance::~Instance() {
_private->prepareToDestroy(); _private->prepareToDestroy();
} }

View File

@ -89,6 +89,7 @@ public:
int32 state(mtpRequestId requestId); // < 0 means waiting for such count of ms int32 state(mtpRequestId requestId); // < 0 means waiting for such count of ms
void killSession(ShiftedDcId shiftedDcId); void killSession(ShiftedDcId shiftedDcId);
void stopSession(ShiftedDcId shiftedDcId); void stopSession(ShiftedDcId shiftedDcId);
void reInitConnection(DcId dcId);
void logout(RPCDoneHandlerPtr onDone, RPCFailHandlerPtr onFail); void logout(RPCDoneHandlerPtr onDone, RPCFailHandlerPtr onFail);
internal::DcenterPtr getDcById(ShiftedDcId shiftedDcId); internal::DcenterPtr getDcById(ShiftedDcId shiftedDcId);
@ -122,8 +123,6 @@ public:
void requestConfig(); void requestConfig();
void requestCDNConfig(); void requestCDNConfig();
void requestLangPackDifference();
void applyLangPackDifference(const MTPLangPackDifference &difference);
~Instance(); ~Instance();
@ -138,7 +137,6 @@ signals:
private slots: private slots:
void onKeyDestroyed(qint32 shiftedDcId); void onKeyDestroyed(qint32 shiftedDcId);
void onClearKilledSessions();
private: private:
internal::Session *getSession(ShiftedDcId shiftedDcId); internal::Session *getSession(ShiftedDcId shiftedDcId);

View File

@ -288,13 +288,16 @@ public:
SentRequestWrap request(mtpRequestId requestId) noexcept WARN_UNUSED_RESULT; SentRequestWrap request(mtpRequestId requestId) noexcept WARN_UNUSED_RESULT;
void requestSendDelayed() { void requestSendDelayed() {
MTP::sendAnything(); _instance->sendAnything();
} }
void requestCancellingDiscard() { void requestCancellingDiscard() {
for (auto &request : _requests) { for (auto &request : _requests) {
request.handled(); request.handled();
} }
} }
gsl::not_null<Instance*> requestMTP() const {
return _instance;
}
private: private:
class RequestWrap { class RequestWrap {

View File

@ -34,6 +34,8 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "boxes/about_box.h" #include "boxes/about_box.h"
#include "core/file_utilities.h" #include "core/file_utilities.h"
#include "lang/lang_file_parser.h" #include "lang/lang_file_parser.h"
#include "lang/lang_cloud_manager.h"
#include "messenger.h"
#include "autoupdater.h" #include "autoupdater.h"
namespace Settings { namespace Settings {
@ -220,7 +222,7 @@ void GeneralWidget::chooseCustomLang() {
save = result.value(lng_box_ok, Lang::GetOriginalValue(lng_box_ok)), save = result.value(lng_box_ok, Lang::GetOriginalValue(lng_box_ok)),
cancel = result.value(lng_cancel, Lang::GetOriginalValue(lng_cancel)); cancel = result.value(lng_cancel, Lang::GetOriginalValue(lng_cancel));
Ui::show(Box<ConfirmBox>(text, save, cancel, base::lambda_guarded(this, [this, filePath] { Ui::show(Box<ConfirmBox>(text, save, cancel, base::lambda_guarded(this, [this, filePath] {
Lang::Current() = Lang::Instance(filePath, Lang::Instance::CreateFromCustomFileTag()); Lang::Current().switchToCustomFile(filePath);
Local::writeLangPack(); Local::writeLangPack();
onRestart(); onRestart();
}))); })));
@ -233,9 +235,19 @@ void GeneralWidget::chooseCustomLang() {
void GeneralWidget::onChangeLanguage() { void GeneralWidget::onChangeLanguage() {
if ((_changeLanguage->clickModifiers() & Qt::ShiftModifier) && (_changeLanguage->clickModifiers() & Qt::AltModifier)) { if ((_changeLanguage->clickModifiers() & Qt::ShiftModifier) && (_changeLanguage->clickModifiers() & Qt::AltModifier)) {
chooseCustomLang(); chooseCustomLang();
return;
}
auto manager = Messenger::Instance().langCloudManager();
if (manager->languageList().isEmpty()) {
_languagesLoadedSubscription = subscribe(manager->languageListChanged(), [this] {
unsubscribe(base::take(_languagesLoadedSubscription));
Ui::show(Box<LanguageBox>());
});
} else { } else {
unsubscribe(base::take(_languagesLoadedSubscription));
Ui::show(Box<LanguageBox>()); Ui::show(Box<LanguageBox>());
} }
manager->requestLanguageList();
} }
void GeneralWidget::onRestart() { void GeneralWidget::onRestart() {

View File

@ -117,6 +117,8 @@ private:
object_ptr<Ui::WidgetSlideWrap<Ui::Checkbox>> _startMinimized = { nullptr }; object_ptr<Ui::WidgetSlideWrap<Ui::Checkbox>> _startMinimized = { nullptr };
object_ptr<Ui::Checkbox> _addInSendTo = { nullptr }; object_ptr<Ui::Checkbox> _addInSendTo = { nullptr };
int _languagesLoadedSubscription = 0;
}; };
} // namespace Settings } // namespace Settings

View File

@ -177,6 +177,8 @@
<(src_loc)/intro/introsignup.h <(src_loc)/intro/introsignup.h
<(src_loc)/intro/introstart.cpp <(src_loc)/intro/introstart.cpp
<(src_loc)/intro/introstart.h <(src_loc)/intro/introstart.h
<(src_loc)/lang/lang_cloud_manager.cpp
<(src_loc)/lang/lang_cloud_manager.h
<(src_loc)/lang/lang_file_parser.cpp <(src_loc)/lang/lang_file_parser.cpp
<(src_loc)/lang/lang_file_parser.h <(src_loc)/lang/lang_file_parser.h
<(src_loc)/lang/lang_instance.cpp <(src_loc)/lang/lang_instance.cpp