diff --git a/Telegram/SourceFiles/passport/passport_form_controller.cpp b/Telegram/SourceFiles/passport/passport_form_controller.cpp index d874632a9..37ed0835b 100644 --- a/Telegram/SourceFiles/passport/passport_form_controller.cpp +++ b/Telegram/SourceFiles/passport/passport_form_controller.cpp @@ -151,6 +151,13 @@ void CollectToRequestedRow( }); } +void ApplyDataChanges(ValueData &data, ValueMap &&changes) { + data.parsedInEdit = data.parsed; + for (auto &[key, value] : changes.fields) { + data.parsedInEdit.fields[key] = std::move(value); + } +} + RequestedRow CollectRequestedRow(const MTPSecureRequiredType &data) { auto result = RequestedRow(); CollectToRequestedRow(result, data); @@ -220,6 +227,39 @@ QString ValidateUrl(const QString &url) { } // namespace +bool ValueChanged(not_null value, const ValueMap &data) { + const auto FileChanged = [](const EditFile &file) { + if (file.uploadData) { + return !file.deleted; + } + return file.deleted; + }; + + auto filesCount = 0; + for (const auto &scan : value->scansInEdit) { + if (FileChanged(scan)) { + return true; + } + } + for (const auto &[type, scan] : value->specialScansInEdit) { + if (FileChanged(scan)) { + return true; + } + } + const auto &existing = value->data.parsed.fields; + for (const auto &[key, value] : data.fields) { + const auto i = existing.find(key); + if (i != existing.end()) { + if (i->second.text != value.text) { + return true; + } + } else if (!value.text.isEmpty()) { + return true; + } + } + return false; +} + FormRequest::FormRequest( UserId botId, const QString &scope, @@ -1576,42 +1616,6 @@ bool FormController::isEncryptedValue(Value::Type type) const { return (type != Value::Type::Phone && type != Value::Type::Email); } -bool FormController::editFileChanged(const EditFile &file) const { - if (file.uploadData) { - return !file.deleted; - } - return file.deleted; -} - -bool FormController::editValueChanged( - not_null value, - const ValueMap &data) const { - auto filesCount = 0; - for (const auto &scan : value->scansInEdit) { - if (editFileChanged(scan)) { - return true; - } - } - for (const auto &[type, scan] : value->specialScansInEdit) { - if (editFileChanged(scan)) { - return true; - } - } - auto existing = value->data.parsed.fields; - for (const auto &[key, value] : data.fields) { - const auto i = existing.find(key); - if (i != existing.end()) { - if (i->second.text != value.text) { - return true; - } - existing.erase(i); - } else if (!value.text.isEmpty()) { - return true; - } - } - return !existing.empty(); -} - void FormController::saveValueEdit( not_null value, ValueMap &&data) { @@ -1623,7 +1627,7 @@ void FormController::saveValueEdit( // and we don't reset value->error/[scan|translation]MissingError. // Otherwise we reset them after save by re-parsing the value. const auto nonconst = findValue(value); - if (!editValueChanged(nonconst, data)) { + if (!ValueChanged(nonconst, data)) { nonconst->saveRequestId = -1; crl::on_main(this, [=] { base::take(nonconst->scansInEdit); @@ -1636,7 +1640,7 @@ void FormController::saveValueEdit( }); return; } - nonconst->data.parsedInEdit = std::move(data); + ApplyDataChanges(nonconst->data, std::move(data)); if (isEncryptedValue(nonconst->type)) { saveEncryptedValue(nonconst); diff --git a/Telegram/SourceFiles/passport/passport_form_controller.h b/Telegram/SourceFiles/passport/passport_form_controller.h index e73097092..cb63b8184 100644 --- a/Telegram/SourceFiles/passport/passport_form_controller.h +++ b/Telegram/SourceFiles/passport/passport_form_controller.h @@ -201,6 +201,8 @@ private: }; +bool ValueChanged(not_null value, const ValueMap &data); + struct RequestedValue { explicit RequestedValue(Value::Type type); @@ -327,9 +329,6 @@ public: void startValueEdit(not_null value); void cancelValueEdit(not_null value); void cancelValueVerification(not_null value); - bool editValueChanged( - not_null value, - const ValueMap &data) const; void saveValueEdit(not_null value, ValueMap &&data); void deleteValueEdit(not_null value); bool savingValue(not_null value) const; @@ -454,7 +453,6 @@ private: void valueEditFailed(not_null value); void clearValueEdit(not_null value); void clearValueVerification(not_null value); - bool editFileChanged(const EditFile &file) const; bool isEncryptedValue(Value::Type type) const; void saveEncryptedValue(not_null value); diff --git a/Telegram/SourceFiles/passport/passport_form_view_controller.cpp b/Telegram/SourceFiles/passport/passport_form_view_controller.cpp index c315c8c3d..40c2839e1 100644 --- a/Telegram/SourceFiles/passport/passport_form_view_controller.cpp +++ b/Telegram/SourceFiles/passport/passport_form_view_controller.cpp @@ -278,7 +278,10 @@ QString ComputeScopeRowReadyString(const Scope &scope) { } return nullptr; }(); - if (document && scope.documents.size() > 1) { + if ((document && scope.documents.size() > 1) + || (!scope.details + && (ScopeTypeForValueType(document->type) + == Scope::Type::Address))) { pushListValue("_type", [&] { using Type = Value::Type; switch (document->type) { @@ -307,7 +310,10 @@ QString ComputeScopeRowReadyString(const Scope &scope) { if (!scope.documents.empty() && !document) { return QString(); } - const auto scheme = GetDocumentScheme(scope.type); + const auto scheme = GetDocumentScheme( + scope.type, + document ? base::make_optional(document->type) : base::none, + scope.details ? scope.details->nativeNames : false); for (const auto &row : scheme.rows) { const auto format = row.format; if (row.valueClass == EditDocumentScheme::ValueClass::Fields) { @@ -357,9 +363,10 @@ QString ComputeScopeRowReadyString(const Scope &scope) { } ScopeRow ComputeScopeRow(const Scope &scope) { - const auto addReadyError = [&](ScopeRow &&row) { - const auto ready = ComputeScopeRowReadyString(scope); - row.ready = ready; + const auto addReadyError = [&]( + ScopeRow &&row, + QString titleFallback = QString()) { + row.ready = ComputeScopeRowReadyString(scope); auto errors = QStringList(); const auto addValueErrors = [&](not_null value) { if (!value->error.isEmpty()) { @@ -408,7 +415,10 @@ ScopeRow ComputeScopeRow(const Scope &scope) { } if (!errors.isEmpty()) { row.error = errors[0];// errors.join('\n'); + } else if (row.title == row.ready && !titleFallback.isEmpty()) { + row.title = titleFallback; } + // #TODO passport half-full value //if (row.error.isEmpty() // && row.ready.isEmpty() @@ -464,7 +474,7 @@ ScopeRow ComputeScopeRow(const Scope &scope) { return addReadyError({ lang(lng_passport_address), lang(lng_passport_address_enter), - }); + }); case Scope::Type::Address: Assert(!scope.documents.empty()); if (scope.documents.size() == 1) { @@ -473,27 +483,27 @@ ScopeRow ComputeScopeRow(const Scope &scope) { return addReadyError({ lang(lng_passport_address_statement), lang(lng_passport_address_statement_upload), - }); + }, lang(lng_passport_address_title)); case Value::Type::UtilityBill: return addReadyError({ lang(lng_passport_address_bill), lang(lng_passport_address_bill_upload), - }); + }, lang(lng_passport_address_title)); case Value::Type::RentalAgreement: return addReadyError({ lang(lng_passport_address_agreement), lang(lng_passport_address_agreement_upload), - }); + }, lang(lng_passport_address_title)); case Value::Type::PassportRegistration: return addReadyError({ lang(lng_passport_address_registration), lang(lng_passport_address_registration_upload), - }); + }, lang(lng_passport_address_title)); case Value::Type::TemporaryRegistration: return addReadyError({ lang(lng_passport_address_temporary), lang(lng_passport_address_temporary_upload), - }); + }, lang(lng_passport_address_title)); default: Unexpected("Address type in ComputeScopeRow."); } } diff --git a/Telegram/SourceFiles/passport/passport_panel_controller.cpp b/Telegram/SourceFiles/passport/passport_panel_controller.cpp index 7ec6e508a..093f6d50d 100644 --- a/Telegram/SourceFiles/passport/passport_panel_controller.cpp +++ b/Telegram/SourceFiles/passport/passport_panel_controller.cpp @@ -34,7 +34,8 @@ constexpr auto kMaxPostcodeSize = 10; EditDocumentScheme GetDocumentScheme( Scope::Type type, - base::optional scansType) { + base::optional scansType, + bool nativeNames) { using Scheme = EditDocumentScheme; using ValueClass = Scheme::ValueClass; const auto DontFormat = nullptr; @@ -74,7 +75,8 @@ EditDocumentScheme GetDocumentScheme( } return base::none; }; - + const auto NativeNameValidate = LimitedValidate(kMaxNameSize); + const auto NativeNameOrEmptyValidate = LimitedValidate(kMaxNameSize, 0); const auto DocumentValidate = LimitedValidate(kMaxDocumentSize); const auto StreetValidate = LimitedValidate(kMaxStreetSize); const auto CityValidate = LimitedValidate(kMaxCitySize, kMinCitySize); @@ -129,35 +131,40 @@ EditDocumentScheme GetDocumentScheme( Unexpected("scansType in GetDocumentScheme:Identity."); } } + using Validator = EditDocumentScheme::Row::Validator; result.rows = { { ValueClass::Fields, PanelDetailsType::Text, - qsl("first_name"), + nativeNames ? qsl("first_name_native") : qsl("first_name"), lang(lng_passport_first_name), - NameValidate, + nativeNames ? Validator(NativeNameValidate) : NameValidate, DontFormat, kMaxNameSize, }, { ValueClass::Fields, PanelDetailsType::Text, - qsl("middle_name"), + (nativeNames + ? qsl("middle_name_native") + : qsl("middle_name")), lang(lng_passport_middle_name), - NameOrEmptyValidate, + (nativeNames + ? Validator(NativeNameOrEmptyValidate) + : NameOrEmptyValidate), DontFormat, kMaxNameSize, - qsl("first_name") + nativeNames ? qsl("first_name_native") : qsl("first_name"), }, { ValueClass::Fields, PanelDetailsType::Text, - qsl("last_name"), + nativeNames ? qsl("last_name_native") : qsl("last_name"), lang(lng_passport_last_name), - NameValidate, + nativeNames ? Validator(NativeNameValidate) : NameValidate, DontFormat, kMaxNameSize, - qsl("first_name") + nativeNames ? qsl("first_name_native") : qsl("first_name"), }, { ValueClass::Fields, @@ -360,6 +367,15 @@ const std::map &NativeToLatinMap() { return result; } +QString AdjustKeyName(not_null value, const QString &key) { + if (!value->nativeNames) { + return key; + } + const auto &map = LatinToNativeMap(); + const auto i = map.find(key); + return (i == end(map)) ? key : i->second; +} + bool SkipFieldCheck(not_null value, const QString &key) { if (value->type != Value::Type::PersonalDetails) { return false; @@ -1058,7 +1074,8 @@ void PanelController::startScopeEdit(int index, int documentIndex) { this, GetDocumentScheme( _editScope->type, - _editDocument->type), + _editDocument->type, + _editValue->nativeNames), _editValue->error, _editValue->data.parsedInEdit, _editDocument->error, @@ -1071,7 +1088,8 @@ void PanelController::startScopeEdit(int index, int documentIndex) { this, GetDocumentScheme( _editScope->type, - _editDocument->type), + _editDocument->type, + false), _editDocument->error, _editDocument->data.parsedInEdit, _editDocument->scanMissingError, @@ -1089,7 +1107,10 @@ void PanelController::startScopeEdit(int index, int documentIndex) { auto result = object_ptr( _panel->widget(), this, - GetDocumentScheme(_editScope->type), + GetDocumentScheme( + _editScope->type, + base::none, + _editValue->nativeNames), _editValue->error, _editValue->data.parsedInEdit); const auto weak = make_weak(result.data()); @@ -1288,6 +1309,8 @@ void PanelController::saveScope(ValueMap &&data, ValueMap &&filesData) { if (_editValue) { _form->saveValueEdit(_editValue, std::move(data)); + } else { + Assert(data.fields.empty()); } if (_editDocument) { _form->saveValueEdit(_editDocument, std::move(filesData)); @@ -1299,10 +1322,9 @@ void PanelController::saveScope(ValueMap &&data, ValueMap &&filesData) { bool PanelController::editScopeChanged( const ValueMap &data, const ValueMap &filesData) const { - if (_editValue && _form->editValueChanged(_editValue, data)) { + if (_editValue && ValueChanged(_editValue, data)) { return true; - } else if (_editDocument - && _form->editValueChanged(_editDocument, filesData)) { + } else if (_editDocument && ValueChanged(_editDocument, filesData)) { return true; } return false; diff --git a/Telegram/SourceFiles/passport/passport_panel_controller.h b/Telegram/SourceFiles/passport/passport_panel_controller.h index 6d389f495..329813d20 100644 --- a/Telegram/SourceFiles/passport/passport_panel_controller.h +++ b/Telegram/SourceFiles/passport/passport_panel_controller.h @@ -22,11 +22,13 @@ enum class ReadScanError; EditDocumentScheme GetDocumentScheme( Scope::Type type, - base::optional scansType = base::none); + base::optional scansType, + bool nativeNames); EditContactScheme GetContactScheme(Scope::Type type); const std::map &LatinToNativeMap(); const std::map &NativeToLatinMap(); +QString AdjustKeyName(not_null value, const QString &key); bool SkipFieldCheck(not_null value, const QString &key); struct ScanInfo { diff --git a/Telegram/SourceFiles/passport/passport_panel_edit_document.h b/Telegram/SourceFiles/passport/passport_panel_edit_document.h index 8d196dac7..22d31fb68 100644 --- a/Telegram/SourceFiles/passport/passport_panel_edit_document.h +++ b/Telegram/SourceFiles/passport/passport_panel_edit_document.h @@ -42,14 +42,16 @@ struct EditDocumentScheme { Scans, }; struct Row { + using Validator = Fn(const QString &value)>; + using Formatter = Fn; ValueClass valueClass = ValueClass::Fields; PanelDetailsType inputType = PanelDetailsType(); QString key; QString label; - Fn(const QString &value)> error; - Fn format; + Validator error; + Formatter format; int lengthLimit = 0; - QString keyForAttachmentTo; // attach last_name to first_name + QString keyForAttachmentTo; // attach [last|middle]_name to first_* }; std::vector rows; QString fieldsHeader;