Show change language link in intro.

This commit is contained in:
John Preston 2017-05-30 12:31:40 +03:00
parent f5353080e7
commit d47a38dfcf
8 changed files with 168 additions and 129 deletions

View File

@ -44,6 +44,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "styles/style_intro.h"
#include "styles/style_window.h"
#include "window/themes/window_theme.h"
#include "lang/lang_cloud_manager.h"
#include "auth_session.h"
namespace Intro {
@ -70,22 +71,10 @@ Widget::Widget(QWidget *parent) : TWidget(parent)
_settings->entity()->setClickedCallback([] { App::wnd()->showSettings(); });
//if (cLang() == languageDefault) {
// auto systemLangId = Sandbox::LangSystem();
// if (systemLangId != languageDefault) {
// Lang::FileParser loader(qsl(":/langs/lang_") + LanguageCodes[systemLangId].c_str() + qsl(".strings"), { lng_switch_to_this });
// auto text = loader.found().value(lng_switch_to_this);
// if (!text.isEmpty()) {
// _changeLanguage.create(this, object_ptr<Ui::LinkButton>(this, text), st::introCoverDuration);
// _changeLanguage->entity()->setClickedCallback([this, systemLangId] { changeLanguage(systemLangId); });
// }
// }
//} else {
// _changeLanguage.create(this, object_ptr<Ui::LinkButton>(this, Lang::GetOriginalValue(lng_switch_to_this)), st::introCoverDuration);
// _changeLanguage->entity()->setClickedCallback([this] { changeLanguage(languageDefault); });
//}
subscribe(Lang::CurrentCloudManager().firstLanguageSuggestion(), [this] { createLanguageLink(); });
createLanguageLink();
MTP::send(MTPhelp_GetNearestDc(), rpcDone(&Widget::gotNearestDC));
getNearestDC();
appendStep(new StartWidget(this, getData()));
fixOrder();
@ -105,6 +94,32 @@ Widget::Widget(QWidget *parent) : TWidget(parent)
#endif // !TDESKTOP_DISABLE_AUTOUPDATE
}
void Widget::createLanguageLink() {
if (_changeLanguage) return;
auto createLink = [this](const QString &text, const QString &languageId) {
_changeLanguage.create(this, object_ptr<Ui::LinkButton>(this, text), st::introCoverDuration);
_changeLanguage->entity()->setClickedCallback([this, languageId] {
Lang::CurrentCloudManager().switchToLanguage(languageId);
});
};
auto currentId = Lang::Current().id();
auto defaultId = Lang::DefaultLanguageId();
auto suggestedId = Lang::CurrentCloudManager().suggestedLanguage();
if (!suggestedId.isEmpty() && suggestedId != currentId) {
request(MTPlangpack_GetStrings(MTP_string(suggestedId), MTP_vector<MTPstring>(1, MTP_string("lng_switch_to_this")))).done([this, suggestedId, createLink](const MTPVector<MTPLangPackString> &result) {
auto strings = Lang::Instance::ParseStrings(result);
auto it = strings.find(lng_switch_to_this);
if (it != strings.end()) {
createLink(it->second, suggestedId);
}
}).send();
} else if (!currentId.isEmpty() && currentId != defaultId) {
createLink(Lang::GetOriginalValue(lng_switch_to_this), defaultId);
}
}
#ifndef TDESKTOP_DISABLE_AUTOUPDATE
void Widget::onCheckUpdateStatus() {
if (Sandbox::updatingState() == Application::UpdatingReady) {
@ -123,12 +138,6 @@ void Widget::onCheckUpdateStatus() {
}
#endif // TDESKTOP_DISABLE_AUTOUPDATE
void Widget::changeLanguage(int32 languageId) {
cSetLang(languageId);
Local::writeSettings();
App::restart();
}
void Widget::setInnerFocus() {
if (getStep()->animating()) {
setFocus();
@ -223,53 +232,50 @@ void Widget::resetAccount() {
Ui::show(Box<ConfirmBox>(lang(lng_signin_sure_reset), lang(lng_signin_reset), st::attentionBoxButton, base::lambda_guarded(this, [this] {
if (_resetRequest) return;
_resetRequest = MTP::send(MTPaccount_DeleteAccount(MTP_string("Forgot password")), rpcDone(&Widget::resetDone), rpcFail(&Widget::resetFail));
_resetRequest = request(MTPaccount_DeleteAccount(MTP_string("Forgot password"))).done([this](const MTPBool &result) {
_resetRequest = 0;
Ui::hideLayer();
moveToStep(new SignupWidget(this, getData()), Direction::Replace);
}).fail([this](const RPCError &error) {
_resetRequest = 0;
auto type = error.type();
if (type.startsWith(qstr("2FA_CONFIRM_WAIT_"))) {
int seconds = type.mid(qstr("2FA_CONFIRM_WAIT_").size()).toInt();
int days = (seconds + 59) / 86400;
int hours = ((seconds + 59) % 86400) / 3600;
int minutes = ((seconds + 59) % 3600) / 60;
QString when;
if (days > 0) {
when = lng_signin_reset_in_days(lt_count_days, days, lt_count_hours, hours, lt_count_minutes, minutes);
} else if (hours > 0) {
when = lng_signin_reset_in_hours(lt_count_hours, hours, lt_count_minutes, minutes);
} else {
when = lng_signin_reset_in_minutes(lt_count_minutes, minutes);
}
Ui::show(Box<InformBox>(lng_signin_reset_wait(lt_phone_number, App::formatPhone(getData()->phone), lt_when, when)));
} else if (type == qstr("2FA_RECENT_CONFIRM")) {
Ui::show(Box<InformBox>(lang(lng_signin_reset_cancelled)));
} else {
Ui::hideLayer();
getStep()->showError(lang(lng_server_error));
}
}).send();
})));
}
void Widget::resetDone(const MTPBool &result) {
Ui::hideLayer();
moveToStep(new SignupWidget(this, getData()), Direction::Replace);
}
bool Widget::resetFail(const RPCError &error) {
if (MTP::isDefaultHandledError(error)) return false;
_resetRequest = 0;
auto type = error.type();
if (type.startsWith(qstr("2FA_CONFIRM_WAIT_"))) {
int seconds = type.mid(qstr("2FA_CONFIRM_WAIT_").size()).toInt();
int days = (seconds + 59) / 86400;
int hours = ((seconds + 59) % 86400) / 3600;
int minutes = ((seconds + 59) % 3600) / 60;
QString when;
if (days > 0) {
when = lng_signin_reset_in_days(lt_count_days, days, lt_count_hours, hours, lt_count_minutes, minutes);
} else if (hours > 0) {
when = lng_signin_reset_in_hours(lt_count_hours, hours, lt_count_minutes, minutes);
} else {
when = lng_signin_reset_in_minutes(lt_count_minutes, minutes);
void Widget::getNearestDC() {
request(MTPhelp_GetNearestDc()).done([this](const MTPNearestDc &result) {
auto &nearest = result.c_nearestDc();
DEBUG_LOG(("Got nearest dc, country: %1, nearest: %2, this: %3").arg(qs(nearest.vcountry)).arg(nearest.vnearest_dc.v).arg(nearest.vthis_dc.v));
Messenger::Instance().suggestMainDcId(nearest.vnearest_dc.v);
auto nearestCountry = qs(nearest.vcountry);
if (getData()->country != nearestCountry) {
getData()->country = nearestCountry;
getData()->updated.notify();
}
Ui::show(Box<InformBox>(lng_signin_reset_wait(lt_phone_number, App::formatPhone(getData()->phone), lt_when, when)));
} else if (type == qstr("2FA_RECENT_CONFIRM")) {
Ui::show(Box<InformBox>(lang(lng_signin_reset_cancelled)));
} else {
Ui::hideLayer();
getStep()->showError(lang(lng_server_error));
}
return true;
}
void Widget::gotNearestDC(const MTPNearestDc &result) {
auto &nearest = result.c_nearestDc();
DEBUG_LOG(("Got nearest dc, country: %1, nearest: %2, this: %3").arg(qs(nearest.vcountry)).arg(nearest.vnearest_dc.v).arg(nearest.vthis_dc.v));
Messenger::Instance().suggestMainDcId(nearest.vnearest_dc.v);
auto nearestCountry = qs(nearest.vcountry);
if (getData()->country != nearestCountry) {
getData()->country = nearestCountry;
getData()->updated.notify();
}
}).send();
}
void Widget::showControls() {

View File

@ -20,6 +20,8 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
*/
#pragma once
#include "mtproto/sender.h"
namespace Ui {
class IconButton;
class RoundButton;
@ -33,7 +35,7 @@ class WidgetFadeWrap;
namespace Intro {
class Widget : public TWidget, public RPCSender {
class Widget : public TWidget, private MTP::Sender, private base::Subscriber {
Q_OBJECT
public:
@ -207,8 +209,8 @@ public:
private:
void animationCallback();
void createLanguageLink();
void changeLanguage(int32 languageId);
void updateControlsGeometry();
Data *getData() {
return &_data;
@ -222,14 +224,7 @@ private:
void showResetButton();
void resetAccount();
void resetDone(const MTPBool &result);
bool resetFail(const RPCError &error);
Animation _a_show;
bool _showBack = false;
QPixmap _cacheUnder, _cacheOver;
QVector<Step*> _stepHistory;
Step *getStep(int skip = 0) {
t_assert(_stepHistory.size() + skip > 0);
return _stepHistory.at(_stepHistory.size() - skip - 1);
@ -238,7 +233,13 @@ private:
void moveToStep(Step *step, Direction direction);
void appendStep(Step *step);
void gotNearestDC(const MTPNearestDc &dc);
void getNearestDC();
Animation _a_show;
bool _showBack = false;
QPixmap _cacheUnder, _cacheOver;
QVector<Step*> _stepHistory;
Data _data;

View File

@ -298,7 +298,7 @@ QString Instance::systemLangCode() const {
_systemLanguage = DefaultLanguageId();
}
}
// _systemLanguage = "de"; // TESTING
_systemLanguage = "de"; // TESTING
}
return _systemLanguage;
}
@ -442,6 +442,38 @@ void Instance::fillFromLegacy(int legacyId, const QString &legacyPath) {
}
}
template <typename SetCallback, typename ResetCallback>
void Instance::HandleString(const MTPLangPackString &mtpString, SetCallback setCallback, ResetCallback resetCallback) {
switch (mtpString.type()) {
case mtpc_langPackString: {
auto &string = mtpString.c_langPackString();
setCallback(qba(string.vkey), qba(string.vvalue));
} break;
case mtpc_langPackStringPluralized: {
auto &string = mtpString.c_langPackStringPluralized();
auto key = qba(string.vkey);
setCallback(key + "#zero", string.has_zero_value() ? qba(string.vzero_value) : QByteArray());
setCallback(key + "#one", string.has_one_value() ? qba(string.vone_value) : QByteArray());
setCallback(key + "#two", string.has_two_value() ? qba(string.vtwo_value) : QByteArray());
setCallback(key + "#few", string.has_few_value() ? qba(string.vfew_value) : QByteArray());
setCallback(key + "#many", string.has_many_value() ? qba(string.vmany_value) : QByteArray());
setCallback(key + "#other", qba(string.vother_value));
} break;
case mtpc_langPackStringDeleted: {
auto &string = mtpString.c_langPackStringDeleted();
auto key = qba(string.vkey);
resetCallback(key);
for (auto plural : { "#zero", "#one", "#two", "#few", "#many", "#other" }) {
resetCallback(key + plural);
}
} break;
default: Unexpected("LangPack string type in applyUpdate().");
}
}
void Instance::applyDifference(const MTPDlangPackDifference &difference) {
auto updateLanguageId = qs(difference.vlang_code);
auto isValidUpdate = (updateLanguageId == _id) || (_id.isEmpty() && updateLanguageId == DefaultLanguageId());
@ -450,37 +482,51 @@ void Instance::applyDifference(const MTPDlangPackDifference &difference) {
_version = difference.vversion.v;
for_const (auto &mtpString, difference.vstrings.v) {
switch (mtpString.type()) {
case mtpc_langPackString: {
auto &string = mtpString.c_langPackString();
applyValue(qba(string.vkey), qba(string.vvalue));
} break;
case mtpc_langPackStringPluralized: {
auto &string = mtpString.c_langPackStringPluralized();
auto key = qba(string.vkey);
applyValue(key + "#zero", string.has_zero_value() ? qba(string.vzero_value) : QByteArray());
applyValue(key + "#one", string.has_one_value() ? qba(string.vone_value) : QByteArray());
applyValue(key + "#two", string.has_two_value() ? qba(string.vtwo_value) : QByteArray());
applyValue(key + "#few", string.has_few_value() ? qba(string.vfew_value) : QByteArray());
applyValue(key + "#many", string.has_many_value() ? qba(string.vmany_value) : QByteArray());
applyValue(key + "#other", qba(string.vother_value));
} break;
case mtpc_langPackStringDeleted: {
auto &string = mtpString.c_langPackStringDeleted();
auto key = qba(string.vkey);
HandleString(mtpString, [this](auto &&key, auto &&value) {
applyValue(key, value);
}, [this](auto &&key) {
resetValue(key);
for (auto plural : { "#zero", "#one", "#two", "#few", "#many", "#other" }) {
resetValue(key + plural);
}
} break;
});
}
}
default: Unexpected("LangPack string type in applyUpdate().");
std::map<LangKey, QString> Instance::ParseStrings(const MTPVector<MTPLangPackString> &strings) {
auto result = std::map<LangKey, QString>();
for (auto &mtpString : strings.v) {
HandleString(mtpString, [&result](auto &&key, auto &&value) {
ParseKeyValue(key, value, result);
}, [&result](auto &&key) {
auto keyIndex = GetKeyIndex(QLatin1String(key));
if (keyIndex != kLangKeysCount) {
result.erase(keyIndex);
}
});
}
return result;
}
template <typename Result>
void Instance::ParseKeyValue(const QByteArray &key, const QByteArray &value, Result &result) {
auto keyIndex = GetKeyIndex(QLatin1String(key));
if (keyIndex == kLangKeysCount) {
LOG(("Lang Error: Unknown key '%1'").arg(QString::fromLatin1(key)));
return;
}
ValueParser parser(key, keyIndex, value);
if (parser.parse()) {
result[keyIndex] = parser.takeResult();
for (auto &plural : parser.takePluralValues()) {
result[plural.first] = plural.second;
}
}
}
void Instance::applyValue(const QByteArray &key, const QByteArray &value) {
_nonDefaultValues[key] = value;
ParseKeyValue(key, value, _values);
}
void Instance::resetValue(const QByteArray &key) {
_nonDefaultValues.erase(key);
@ -490,27 +536,6 @@ void Instance::resetValue(const QByteArray &key) {
}
}
void Instance::applyValue(const QByteArray &key, const QByteArray &value) {
_nonDefaultValues[key] = value;
auto pluralValues = std::map<ushort, QString>();
auto keyIndex = GetKeyIndex(QLatin1String(key));
if (keyIndex == kLangKeysCount) {
LOG(("Lang Error: Unknown key '%1'").arg(QString::fromLatin1(key)));
return;
}
ValueParser parser(key, keyIndex, value);
if (!parser.parse()) {
return;
}
_values[keyIndex] = parser.takeResult();
for (auto &plural : parser.takePluralValues()) {
_values[plural.first] = plural.second;
}
}
Instance &Current() {
return Messenger::Instance().langpack();
}

View File

@ -66,6 +66,7 @@ public:
void fillFromLegacy(int legacyId, const QString &legacyPath);
void applyDifference(const MTPDlangPackDifference &difference);
static std::map<LangKey, QString> ParseStrings(const MTPVector<MTPLangPackString> &strings);
base::Observable<void> &updated() {
return _updated;
}
@ -77,6 +78,16 @@ public:
}
private:
// SetCallback takes two QByteArrays: key, value.
// It is called for all key-value pairs in string.
// ResetCallback takes one QByteArray: key.
template <typename SetCallback, typename ResetCallback>
static void HandleString(const MTPLangPackString &mtpString, SetCallback setCallback, ResetCallback resetCallback);
// Writes each key-value pair in the result container.
template <typename Result>
static void ParseKeyValue(const QByteArray &key, const QByteArray &value, Result &result);
void applyValue(const QByteArray &key, const QByteArray &value);
void resetValue(const QByteArray &key);
void reset();

View File

@ -734,6 +734,10 @@ Messenger::~Messenger() {
App::clearHistories();
authSessionDestroy();
// The langpack manager should be destroyed before MTProto instance,
// because it is MTP::Sender and it may have pending requests.
_langCloudManager.reset();
_mtproto.reset();
_mtprotoForKeysDestroy.reset();

View File

@ -21,7 +21,6 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
#include "settings.h"
#include "platform/platform_specific.h"
#include "lang/lang_keys.h"
bool gRtl = false;
Qt::LayoutDirection gLangDir = gRtl ? Qt::RightToLeft : Qt::LeftToRight;
@ -89,9 +88,6 @@ bool gPasswordRecovered = false;
int32 gPasscodeBadTries = 0;
TimeMs gPasscodeLastTry = 0;
int32 gLang = -2; // auto
QString gLangFile;
bool gRetina = false;
float64 gRetinaFactor = 1.;
int32 gIntRetinaFactor = 1;

View File

@ -234,9 +234,6 @@ inline void incrementRecentHashtag(RecentHashtagPack &recent, const QString &tag
}
}
DeclareSetting(int32, Lang);
DeclareSetting(QString, LangFile);
DeclareSetting(QStringList, SendPaths);
DeclareSetting(QString, StartUrl);

View File

@ -2309,7 +2309,6 @@ void writeSettings() {
quint32 size = 12 * (sizeof(quint32) + sizeof(qint32));
size += sizeof(quint32) + Serialize::bytearraySize(dcOptionsSerialized);
size += sizeof(quint32) + Serialize::stringSize(cLangFile());
size += sizeof(quint32) + sizeof(qint32);
if (Global::ConnectionType() == dbictHttpProxy || Global::ConnectionType() == dbictTcpProxy) {