Removed static storing of passport config.

This commit is contained in:
23rd 2021-10-14 00:36:48 +03:00
parent adb0a9b6f0
commit 2efd735243
7 changed files with 198 additions and 150 deletions

View file

@ -42,8 +42,6 @@ constexpr auto kTranslationScansLimit = 20;
constexpr auto kShortPollTimeout = crl::time(3000); constexpr auto kShortPollTimeout = crl::time(3000);
constexpr auto kRememberCredentialsDelay = crl::time(1800 * 1000); constexpr auto kRememberCredentialsDelay = crl::time(1800 * 1000);
Config GlobalConfig;
bool ForwardServiceErrorRequired(const QString &error) { bool ForwardServiceErrorRequired(const QString &error) {
return (error == qstr("BOT_INVALID")) return (error == qstr("BOT_INVALID"))
|| (error == qstr("PUBLIC_KEY_REQUIRED")) || (error == qstr("PUBLIC_KEY_REQUIRED"))
@ -239,46 +237,35 @@ QString ValidateUrl(const QString &url) {
: result; : result;
} }
auto ParseConfig(const QByteArray &json) {
auto languagesByCountryCode = std::map<QString, QString>();
auto error = QJsonParseError{ 0, QJsonParseError::NoError };
const auto document = QJsonDocument::fromJson(json, &error);
if (error.error != QJsonParseError::NoError) {
LOG(("API Error: Failed to parse passport config, error: %1."
).arg(error.errorString()));
return languagesByCountryCode;
} else if (!document.isObject()) {
LOG(("API Error: Not an object received in passport config."));
return languagesByCountryCode;
}
const auto object = document.object();
for (auto i = object.constBegin(); i != object.constEnd(); ++i) {
const auto countryCode = i.key();
const auto language = i.value();
if (!language.isString()) {
LOG(("API Error: Not a string in passport config item."));
continue;
}
languagesByCountryCode.emplace(
countryCode,
language.toString());
}
return languagesByCountryCode;
}
} // namespace } // namespace
Config &ConfigInstance() {
return GlobalConfig;
}
Config ParseConfig(const MTPhelp_PassportConfig &data) {
return data.match([](const MTPDhelp_passportConfig &data) {
auto result = Config();
result.hash = data.vhash().v;
auto error = QJsonParseError{ 0, QJsonParseError::NoError };
const auto document = QJsonDocument::fromJson(
data.vcountries_langs().c_dataJSON().vdata().v,
&error);
if (error.error != QJsonParseError::NoError) {
LOG(("API Error: Failed to parse passport config, error: %1."
).arg(error.errorString()));
return result;
} else if (!document.isObject()) {
LOG(("API Error: Not an object received in passport config."));
return result;
}
const auto object = document.object();
for (auto i = object.constBegin(); i != object.constEnd(); ++i) {
const auto countryCode = i.key();
const auto language = i.value();
if (!language.isString()) {
LOG(("API Error: Not a string in passport config item."));
continue;
}
result.languagesByCountryCode.emplace(
countryCode,
language.toString());
}
return result;
}, [](const MTPDhelp_passportConfigNotModified &data) {
return ConfigInstance();
});
}
QString NonceNameByScope(const QString &scope) { QString NonceNameByScope(const QString &scope) {
if (scope.startsWith('{') && scope.endsWith('}')) { if (scope.startsWith('{') && scope.endsWith('}')) {
return qsl("nonce"); return qsl("nonce");
@ -638,7 +625,6 @@ Main::Session &FormController::session() const {
void FormController::show() { void FormController::show() {
requestForm(); requestForm();
requestPassword(); requestPassword();
requestConfig();
} }
UserData *FormController::bot() const { UserData *FormController::bot() const {
@ -1242,6 +1228,44 @@ void FormController::fillErrors() {
} }
} }
rpl::producer<EditDocumentCountry> FormController::preferredLanguage(
const QString &countryCode) {
const auto findLang = [=] {
if (countryCode.isEmpty()) {
return QString();
}
auto &langs = _passportConfig.languagesByCountryCode;
const auto i = langs.find(countryCode);
return (i == end(langs)) ? QString() : i->second;
};
return [=](auto consumer) {
const auto hash = _passportConfig.hash;
if (hash) {
consumer.put_next({ countryCode, findLang() });
consumer.put_done();
return rpl::lifetime() ;
}
_api.request(MTPhelp_GetPassportConfig(
MTP_int(hash)
)).done([=](const MTPhelp_PassportConfig &result) {
result.match([&](const MTPDhelp_passportConfig &data) {
_passportConfig.hash = data.vhash().v;
_passportConfig.languagesByCountryCode = ParseConfig(
data.vcountries_langs().c_dataJSON().vdata().v);
}, [](const MTPDhelp_passportConfigNotModified &data) {
});
consumer.put_next({ countryCode, findLang() });
consumer.put_done();
}).fail([=](const MTP::Error &error) {
consumer.put_next({ countryCode, QString() });
consumer.put_done();
}).send();
return rpl::lifetime();
};
}
void FormController::fillNativeFromFallback() { void FormController::fillNativeFromFallback() {
// Check if additional values (*_name_native) were requested. // Check if additional values (*_name_native) were requested.
const auto i = _form.values.find(Value::Type::PersonalDetails); const auto i = _form.values.find(Value::Type::PersonalDetails);
@ -1254,48 +1278,58 @@ void FormController::fillNativeFromFallback() {
const auto scheme = GetDocumentScheme( const auto scheme = GetDocumentScheme(
Scope::Type::PersonalDetails, Scope::Type::PersonalDetails,
std::nullopt, std::nullopt,
true); true,
[=](const QString &code) { return preferredLanguage(code); });
const auto dependencyIt = values.fields.find( const auto dependencyIt = values.fields.find(
scheme.additionalDependencyKey); scheme.additionalDependencyKey);
const auto dependency = (dependencyIt == end(values.fields)) const auto dependency = (dependencyIt == end(values.fields))
? QString() ? QString()
: dependencyIt->second.text; : dependencyIt->second.text;
if (scheme.additionalShown(dependency)
!= EditDocumentScheme::AdditionalVisibility::OnlyIfError) {
return;
}
// Copy additional values from fallback if they're not filled yet. // Copy additional values from fallback if they're not filled yet.
auto changed = false;
using Scheme = EditDocumentScheme; using Scheme = EditDocumentScheme;
for (const auto &row : scheme.rows) { scheme.preferredLanguage(
if (row.valueClass == Scheme::ValueClass::Additional) { dependency
const auto nativeIt = values.fields.find(row.key); ) | rpl::map(
const auto native = (nativeIt == end(values.fields)) scheme.additionalShown
? QString() ) | rpl::take(
: nativeIt->second.text; 1
if (!native.isEmpty() ) | rpl::start_with_next([=](Scheme::AdditionalVisibility v) {
|| (nativeIt != end(values.fields) if (v != Scheme::AdditionalVisibility::OnlyIfError) {
&& !nativeIt->second.error.isEmpty())) { return;
return; }
} auto values = i->second.data.parsed;
const auto latinIt = values.fields.find( auto changed = false;
row.additionalFallbackKey);
const auto latin = (latinIt == end(values.fields)) for (const auto &row : scheme.rows) {
? QString() if (row.valueClass == Scheme::ValueClass::Additional) {
: latinIt->second.text; const auto nativeIt = values.fields.find(row.key);
if (row.error(latin).has_value()) { const auto native = (nativeIt == end(values.fields))
return; ? QString()
} else if (native != latin) { : nativeIt->second.text;
values.fields[row.key].text = latin; if (!native.isEmpty()
changed = true; || (nativeIt != end(values.fields)
&& !nativeIt->second.error.isEmpty())) {
return;
}
const auto latinIt = values.fields.find(
row.additionalFallbackKey);
const auto latin = (latinIt == end(values.fields))
? QString()
: latinIt->second.text;
if (row.error(latin).has_value()) {
return;
} else if (native != latin) {
values.fields[row.key].text = latin;
changed = true;
}
} }
} }
} if (changed) {
if (changed) { startValueEdit(&i->second);
startValueEdit(&i->second); saveValueEdit(&i->second, std::move(values));
saveValueEdit(&i->second, std::move(values)); }
} }, _lifetime);
} }
void FormController::decryptValue(Value &value) const { void FormController::decryptValue(Value &value) const {
@ -2507,20 +2541,6 @@ void FormController::formDone(const MTPaccount_AuthorizationForm &result) {
} }
} }
void FormController::requestConfig() {
const auto hash = ConfigInstance().hash;
_configRequestId = _api.request(MTPhelp_GetPassportConfig(
MTP_int(hash)
)).done([=](const MTPhelp_PassportConfig &result) {
_configRequestId = 0;
ConfigInstance() = ParseConfig(result);
showForm();
}).fail([=](const MTP::Error &error) {
_configRequestId = 0;
showForm();
}).send();
}
bool FormController::parseForm(const MTPaccount_AuthorizationForm &result) { bool FormController::parseForm(const MTPaccount_AuthorizationForm &result) {
Expects(result.type() == mtpc_account_authorizationForm); Expects(result.type() == mtpc_account_authorizationForm);
@ -2614,7 +2634,7 @@ void FormController::shortPollEmailConfirmation() {
} }
void FormController::showForm() { void FormController::showForm() {
if (_formRequestId || _passwordRequestId || _configRequestId) { if (_formRequestId || _passwordRequestId) {
return; return;
} else if (!_bot) { } else if (!_bot) {
formFail(Lang::Hard::NoAuthorizationBot()); formFail(Lang::Hard::NoAuthorizationBot());

View file

@ -29,12 +29,7 @@ class Session;
namespace Passport { namespace Passport {
struct Config { struct EditDocumentCountry;
int32 hash = 0;
std::map<QString, QString> languagesByCountryCode;
};
Config &ConfigInstance();
Config ParseConfig(const MTPhelp_PassportConfig &data);
struct SavedCredentials { struct SavedCredentials {
bytes::vector hashForAuth; bytes::vector hashForAuth;
@ -416,6 +411,9 @@ public:
void cancel(); void cancel();
void cancelSure(); void cancelSure();
[[nodiscard]] rpl::producer<EditDocumentCountry> preferredLanguage(
const QString &countryCode);
rpl::lifetime &lifetime(); rpl::lifetime &lifetime();
~FormController(); ~FormController();
@ -439,7 +437,6 @@ private:
void requestForm(); void requestForm();
void requestPassword(); void requestPassword();
void requestConfig();
void formDone(const MTPaccount_AuthorizationForm &result); void formDone(const MTPaccount_AuthorizationForm &result);
void formFail(const QString &error); void formFail(const QString &error);
@ -560,7 +557,6 @@ private:
mtpRequestId _formRequestId = 0; mtpRequestId _formRequestId = 0;
mtpRequestId _passwordRequestId = 0; mtpRequestId _passwordRequestId = 0;
mtpRequestId _passwordCheckRequestId = 0; mtpRequestId _passwordCheckRequestId = 0;
mtpRequestId _configRequestId = 0;
PasswordSettings _password; PasswordSettings _password;
crl::time _lastSrpIdInvalidTime = 0; crl::time _lastSrpIdInvalidTime = 0;
@ -572,6 +568,11 @@ private:
mtpRequestId _recoverRequestId = 0; mtpRequestId _recoverRequestId = 0;
base::flat_map<FileKey, std::unique_ptr<mtpFileLoader>> _fileLoaders; base::flat_map<FileKey, std::unique_ptr<mtpFileLoader>> _fileLoaders;
struct {
int32 hash = 0;
std::map<QString, QString> languagesByCountryCode;
} _passportConfig;
rpl::event_stream<not_null<const EditFile*>> _scanUpdated; rpl::event_stream<not_null<const EditFile*>> _scanUpdated;
rpl::event_stream<not_null<const Value*>> _valueSaveFinished; rpl::event_stream<not_null<const Value*>> _valueSaveFinished;
rpl::event_stream<not_null<const Value*>> _verificationNeeded; rpl::event_stream<not_null<const Value*>> _verificationNeeded;

View file

@ -377,7 +377,8 @@ QString ComputeScopeRowReadyString(const Scope &scope) {
const auto scheme = GetDocumentScheme( const auto scheme = GetDocumentScheme(
scope.type, scope.type,
document ? base::make_optional(document->type) : std::nullopt, document ? base::make_optional(document->type) : std::nullopt,
scope.details ? scope.details->nativeNames : false); scope.details ? scope.details->nativeNames : false,
nullptr);
using ValueClass = EditDocumentScheme::ValueClass; using ValueClass = EditDocumentScheme::ValueClass;
const auto skipAdditional = [&] { const auto skipAdditional = [&] {
if (!fields) { if (!fields) {

View file

@ -117,7 +117,8 @@ std::map<FileType, ScanInfo> PrepareSpecialFiles(const Value &value) {
EditDocumentScheme GetDocumentScheme( EditDocumentScheme GetDocumentScheme(
Scope::Type type, Scope::Type type,
std::optional<Value::Type> scansType, std::optional<Value::Type> scansType,
bool nativeNames) { bool nativeNames,
preferredLangCallback &&preferredLanguage) {
using Scheme = EditDocumentScheme; using Scheme = EditDocumentScheme;
using ValueClass = Scheme::ValueClass; using ValueClass = Scheme::ValueClass;
const auto DontFormat = nullptr; const auto DontFormat = nullptr;
@ -294,21 +295,17 @@ EditDocumentScheme GetDocumentScheme(
if (nativeNames) { if (nativeNames) {
result.additionalDependencyKey = qsl("residence_country_code"); result.additionalDependencyKey = qsl("residence_country_code");
const auto languageValue = [](const QString &countryCode) { result.preferredLanguage = preferredLanguage
if (countryCode.isEmpty()) { ? std::move(preferredLanguage)
return QString(); : [](const QString &) {
} return rpl::single(EditDocumentCountry());
const auto &config = ConfigInstance(); };
const auto i = config.languagesByCountryCode.find( const auto languageValue = [](const QString &langCode) {
countryCode); return Lang::GetNonDefaultValue(kLanguageNamePrefix
if (i == end(config.languagesByCountryCode)) { + langCode.toUtf8());
return QString();
}
return Lang::GetNonDefaultValue(
kLanguageNamePrefix + i->second.toUtf8());
}; };
result.additionalHeader = [=](const QString &countryCode) { result.additionalHeader = [=](const EditDocumentCountry &info) {
const auto language = languageValue(countryCode); const auto language = languageValue(info.languageCode);
return language.isEmpty() return language.isEmpty()
? tr::lng_passport_native_name_title(tr::now) ? tr::lng_passport_native_name_title(tr::now)
: tr::lng_passport_native_name_language( : tr::lng_passport_native_name_language(
@ -316,32 +313,28 @@ EditDocumentScheme GetDocumentScheme(
lt_language, lt_language,
language); language);
}; };
result.additionalDescription = [=](const QString &countryCode) { result.additionalDescription = [=](
const auto language = languageValue(countryCode); const EditDocumentCountry &info) {
const auto language = languageValue(info.languageCode);
if (!language.isEmpty()) { if (!language.isEmpty()) {
return tr::lng_passport_native_name_language_about(tr::now); return tr::lng_passport_native_name_language_about(
tr::now);
} }
const auto name = Countries::Instance().countryNameByISO2( const auto name = Countries::Instance().countryNameByISO2(
countryCode); info.countryCode);
Assert(!name.isEmpty()); Assert(!name.isEmpty());
return tr::lng_passport_native_name_about( return tr::lng_passport_native_name_about(
tr::now, tr::now,
lt_country, lt_country,
name); name);
}; };
result.additionalShown = [](const QString &countryCode) { result.additionalShown = [](const EditDocumentCountry &info) {
using Result = EditDocumentScheme::AdditionalVisibility; using Result = EditDocumentScheme::AdditionalVisibility;
if (countryCode.isEmpty()) { return (info.countryCode.isEmpty())
return Result::Hidden; ? Result::Hidden
} : (info.languageCode == "en")
const auto &config = ConfigInstance(); ? Result::OnlyIfError
const auto i = config.languagesByCountryCode.find( : Result::Shown;
countryCode);
if (i != end(config.languagesByCountryCode)
&& i->second == "en") {
return Result::OnlyIfError;
}
return Result::Shown;
}; };
using Row = EditDocumentScheme::Row; using Row = EditDocumentScheme::Row;
auto additional = std::initializer_list<Row>{ auto additional = std::initializer_list<Row>{
@ -1149,6 +1142,10 @@ void PanelController::startScopeEdit(
_form->startValueEdit(_editDocument); _form->startValueEdit(_editDocument);
} }
auto preferredLanguage = [=](const QString &countryCode) {
return _form->preferredLanguage(countryCode);
};
auto content = [&]() -> object_ptr<Ui::RpWidget> { auto content = [&]() -> object_ptr<Ui::RpWidget> {
switch (_editScope->type) { switch (_editScope->type) {
case Scope::Type::Identity: case Scope::Type::Identity:
@ -1169,7 +1166,8 @@ void PanelController::startScopeEdit(
GetDocumentScheme( GetDocumentScheme(
_editScope->type, _editScope->type,
_editDocument->type, _editDocument->type,
_editValue->nativeNames), _editValue->nativeNames,
std::move(preferredLanguage)),
_editValue->error, _editValue->error,
_editValue->data.parsedInEdit, _editValue->data.parsedInEdit,
_editDocument->error, _editDocument->error,
@ -1183,7 +1181,8 @@ void PanelController::startScopeEdit(
GetDocumentScheme( GetDocumentScheme(
_editScope->type, _editScope->type,
_editDocument->type, _editDocument->type,
false), false,
std::move(preferredLanguage)),
_editDocument->error, _editDocument->error,
_editDocument->data.parsedInEdit, _editDocument->data.parsedInEdit,
std::move(scans), std::move(scans),
@ -1204,7 +1203,8 @@ void PanelController::startScopeEdit(
GetDocumentScheme( GetDocumentScheme(
_editScope->type, _editScope->type,
std::nullopt, std::nullopt,
_editValue->nativeNames), _editValue->nativeNames,
std::move(preferredLanguage)),
_editValue->error, _editValue->error,
_editValue->data.parsedInEdit); _editValue->data.parsedInEdit);
const auto weak = Ui::MakeWeak(result.data()); const auto weak = Ui::MakeWeak(result.data());

View file

@ -20,15 +20,19 @@ namespace Passport {
class FormController; class FormController;
class Panel; class Panel;
struct EditDocumentCountry;
struct EditDocumentScheme; struct EditDocumentScheme;
struct EditContactScheme; struct EditContactScheme;
enum class ReadScanError; enum class ReadScanError;
using preferredLangCallback =
Fn<rpl::producer<EditDocumentCountry>(const QString &)>;
EditDocumentScheme GetDocumentScheme( EditDocumentScheme GetDocumentScheme(
Scope::Type type, Scope::Type type,
std::optional<Value::Type> scansType, std::optional<Value::Type> scansType,
bool nativeNames); bool nativeNames,
preferredLangCallback &&preferredLanguage);
EditContactScheme GetContactScheme(Scope::Type type); EditContactScheme GetContactScheme(Scope::Type type);
const std::map<QString, QString> &LatinToNativeMap(); const std::map<QString, QString> &LatinToNativeMap();

View file

@ -425,25 +425,42 @@ not_null<Ui::RpWidget*> PanelEditDocument::setupContent(
showIfError = true; showIfError = true;
} }
}); });
const auto shown = [=](const QString &code) { const auto shown = [=](const Scheme::CountryInfo &info) {
using Result = Scheme::AdditionalVisibility; using Result = Scheme::AdditionalVisibility;
const auto value = _scheme.additionalShown(code); const auto value = _scheme.additionalShown(info);
return (value == Result::Shown) return (value == Result::Shown)
|| (value == Result::OnlyIfError && showIfError); || (value == Result::OnlyIfError && showIfError);
}; };
auto title = row->value( auto langValue = row->value(
) | rpl::filter( ) | rpl::map(
_scheme.preferredLanguage
) | rpl::flatten_latest();
auto title = rpl::duplicate(langValue) | rpl::filter(
shown shown
) | rpl::map([=](const QString &code) { ) | rpl::map([=](const Scheme::CountryInfo &info) {
return _scheme.additionalHeader(code); return _scheme.additionalHeader(info);
}); });
added->add( const auto headerLabel = added->add(
object_ptr<Ui::FlatLabel>( object_ptr<Ui::FlatLabel>(
added, added,
std::move(title), rpl::duplicate(title),
st::passportFormHeader), st::passportFormHeader),
st::passportNativeNameHeaderPadding); st::passportNativeNameHeaderPadding);
std::move(
title
) | rpl::start_with_next([=] {
const auto &padding = st::passportNativeNameHeaderPadding;
const auto available = added->width()
- padding.left()
- padding.right();
headerLabel->resizeToNaturalWidth(available);
headerLabel->moveToLeft(
padding.left(),
padding.top(),
available);
}, headerLabel->lifetime());
enumerateRows([&]( enumerateRows([&](
int i, int i,
@ -454,11 +471,10 @@ not_null<Ui::RpWidget*> PanelEditDocument::setupContent(
} }
}); });
auto description = row->value( auto description = rpl::duplicate(langValue) | rpl::filter(
) | rpl::filter(
shown shown
) | rpl::map([=](const QString &code) { ) | rpl::map([=](const Scheme::CountryInfo &info) {
return _scheme.additionalDescription(code); return _scheme.additionalDescription(info);
}); });
added->add( added->add(
object_ptr<Ui::DividerLabel>( object_ptr<Ui::DividerLabel>(
@ -470,11 +486,10 @@ not_null<Ui::RpWidget*> PanelEditDocument::setupContent(
st::passportFormLabelPadding), st::passportFormLabelPadding),
st::passportNativeNameAboutMargin); st::passportNativeNameAboutMargin);
wrap->toggleOn(row->value() | rpl::map(shown)); wrap->toggleOn(rpl::duplicate(langValue) | rpl::map(shown));
wrap->finishAnimating(); wrap->finishAnimating();
row->value( std::move(langValue) | rpl::map(
) | rpl::map(
shown shown
) | rpl::start_with_next([=](bool visible) { ) | rpl::start_with_next([=](bool visible) {
_additionalShown = visible; _additionalShown = visible;

View file

@ -39,6 +39,11 @@ class EditScans;
enum class FileType; enum class FileType;
struct ScanListData; struct ScanListData;
struct EditDocumentCountry {
QString countryCode;
QString languageCode;
};
struct EditDocumentScheme { struct EditDocumentScheme {
enum class ValueClass { enum class ValueClass {
Fields, Fields,
@ -50,6 +55,7 @@ struct EditDocumentScheme {
OnlyIfError, OnlyIfError,
Shown, Shown,
}; };
using CountryInfo = EditDocumentCountry;
struct Row { struct Row {
using Validator = Fn<std::optional<QString>(const QString &value)>; using Validator = Fn<std::optional<QString>(const QString &value)>;
using Formatter = Fn<QString(const QString &value)>; using Formatter = Fn<QString(const QString &value)>;
@ -69,9 +75,10 @@ struct EditDocumentScheme {
QString scansHeader; QString scansHeader;
QString additionalDependencyKey; QString additionalDependencyKey;
Fn<AdditionalVisibility(const QString &dependency)> additionalShown; Fn<AdditionalVisibility(const CountryInfo &dependency)> additionalShown;
Fn<QString(const QString &dependency)> additionalHeader; Fn<QString(const CountryInfo &dependency)> additionalHeader;
Fn<QString(const QString &dependency)> additionalDescription; Fn<QString(const CountryInfo &dependency)> additionalDescription;
Fn<rpl::producer<CountryInfo>(const QString &)> preferredLanguage;
}; };