diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 47f2802e3..00053b008 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -1563,7 +1563,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_passport_identity_internal" = "Internal passport"; "lng_passport_identity_internal_upload" = "Upload a scan of your internal passport"; "lng_passport_identity_about" = "The document must contain your photograph, first and last name, date of birth, document number, country of issue, and expiry date."; -"lng_passport_identity_selfie" = "Take a selfie with your document"; "lng_passport_address_title" = "Residential address"; "lng_passport_address_description" = "Upload a proof of your address"; "lng_passport_address_bill" = "Utility bill"; @@ -1584,6 +1583,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_passport_phone_description" = "Enter your phone number"; "lng_passport_email_title" = "Email"; "lng_passport_email_description" = "Enter your email address"; +"lng_passport_identity_selfie" = "Take a selfie with your document"; +"lng_passport_translation_needed" = "Upload a translation of your document"; "lng_passport_accept_allow" = "You accept the {policy} and allow their {bot} to send you messages."; "lng_passport_allow" = "You allow {bot} to send you messages."; "lng_passport_policy" = "{bot} privacy policy"; @@ -1596,27 +1597,23 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_passport_upload_scans" = "Upload scans"; "lng_passport_upload_more" = "Upload additional scans"; "lng_passport_selfie_title" = "Selfie"; -"lng_passport_selfie_name" = "Photo"; "lng_passport_selfie_description" = "Upload a photo of yourself holding your document. Make sure the ID and your face are clearly visible."; "lng_passport_upload_selfie" = "Upload selfie"; "lng_passport_reupload_selfie" = "Reupload selfie"; "lng_passport_front_side_title" = "Front side"; -"lng_passport_front_side_name" = "Scan"; "lng_passport_front_side_description" = "Upload the front side of your document."; "lng_passport_upload_front_side" = "Upload a scan of the front side"; "lng_passport_reupload_front_side" = "Reupload a scan of the front side"; "lng_passport_reverse_side_title" = "Reverse side"; -"lng_passport_reverse_side_name" = "Scan"; "lng_passport_reverse_side_description" = "Upload the reverse side of your document."; "lng_passport_upload_reverse_side" = "Upload a scan of the reverse side"; "lng_passport_reupload_reverse_side" = "Reupload a scan of the reverse side"; "lng_passport_main_page_title" = "Main page"; -"lng_passport_main_page_name" = "Scan"; "lng_passport_main_page_description" = "Upload the main page of your document."; "lng_passport_upload_main_page" = "Upload a scan of the main page"; "lng_passport_reupload_main_page" = "Reupload a scan of the main page"; "lng_passport_personal_details" = "Personal details"; -"lng_passport_personal_details_enter" = "Enter your personal details"; +"lng_passport_personal_details_enter" = "Fill in your personal details"; "lng_passport_document_details" = "Document details"; "lng_passport_choose_image" = "Choose scan image"; "lng_passport_delete_scan_undo" = "Undo"; @@ -1636,7 +1633,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_passport_native_name_title" = "Name in document language"; "lng_passport_native_name_about" = "Your name in the language of the country ({country}) that issued the document."; "lng_passport_address" = "Address"; -"lng_passport_address_enter" = "Enter your address"; +"lng_passport_address_enter" = "Please provide your address"; "lng_passport_street" = "Street"; "lng_passport_city" = "City"; "lng_passport_state" = "State"; diff --git a/Telegram/SourceFiles/passport/passport_form_controller.cpp b/Telegram/SourceFiles/passport/passport_form_controller.cpp index 1d5cba123..114132d21 100644 --- a/Telegram/SourceFiles/passport/passport_form_controller.cpp +++ b/Telegram/SourceFiles/passport/passport_form_controller.cpp @@ -386,23 +386,28 @@ void Value::fillDataFrom(Value &&other) { } bool Value::scansAreFilled() const { - if (requiresScan(FileType::Translation) && _translations.empty()) { - return false; - } else if (requiresScan(FileType::Scan) && _scans.empty()) { - return false; - } - const auto types = { - FileType::FrontSide, - FileType::ReverseSide, - FileType::Selfie + return (whatNotFilled() == 0); +} + +int Value::whatNotFilled() const { + const auto noRequiredSpecialScan = [&](FileType type) { + return requiresSpecialScan(type) + && (specialScans.find(type) == end(specialScans)); }; - for (const auto type : types) { - if (requiresSpecialScan(type) - && (specialScans.find(type) == end(specialScans))) { - return false; - } + if (requiresScan(FileType::Scan) && _scans.empty()) { + return kNothingFilled; + } else if (noRequiredSpecialScan(FileType::FrontSide)) { + return kNothingFilled; } - return true; + auto result = 0; + if (requiresScan(FileType::Translation) && _translations.empty()) { + result |= kNoTranslationFilled; + } + if (noRequiredSpecialScan(FileType::ReverseSide) + || noRequiredSpecialScan(FileType::Selfie)) { + result |= kNoSelfieFilled; + } + return result; } void Value::saveInEdit() { diff --git a/Telegram/SourceFiles/passport/passport_form_controller.h b/Telegram/SourceFiles/passport/passport_form_controller.h index 657458598..f84b6e5c3 100644 --- a/Telegram/SourceFiles/passport/passport_form_controller.h +++ b/Telegram/SourceFiles/passport/passport_form_controller.h @@ -186,6 +186,11 @@ struct Value { bool uploadingScan() const; bool saving() const; + static constexpr auto kNothingFilled = 0x100; + static constexpr auto kNoTranslationFilled = 0x10; + static constexpr auto kNoSelfieFilled = 0x001; + int whatNotFilled() const; + std::vector &files(FileType type); const std::vector &files(FileType type) const; QString &fileMissingError(FileType type); diff --git a/Telegram/SourceFiles/passport/passport_form_view_controller.cpp b/Telegram/SourceFiles/passport/passport_form_view_controller.cpp index 4e75cf3bf..a8c029bf3 100644 --- a/Telegram/SourceFiles/passport/passport_form_view_controller.cpp +++ b/Telegram/SourceFiles/passport/passport_form_view_controller.cpp @@ -479,18 +479,28 @@ ScopeRow ComputeScopeRow(const Scope &scope) { row.title = titleFallback; } - // #TODO passport half-full value - //if (row.error.isEmpty() - // && row.ready.isEmpty() - // && scope.type == Scope::Type::Identity - // && scope.selfieRequired) { - // auto noSelfieScope = scope; - // noSelfieScope.selfieRequired = false; - // if (!ComputeScopeRowReadyString(noSelfieScope).isEmpty()) { - // // Only selfie is missing. - // row.description = lang(lng_passport_identity_selfie); - // } - //} + if (row.error.isEmpty() + && row.ready.isEmpty() + && !scope.documents.empty()) { + if (document) { + row.description = (scope.type == Scope::Type::Identity) + ? lang(lng_passport_personal_details_enter) + : lang(lng_passport_address_enter); + } else { + const auto best = ranges::min( + scope.documents, + std::less<>(), + [](not_null document) { + return document->whatNotFilled(); + }); + const auto notFilled = best->whatNotFilled(); + if (notFilled & Value::kNoTranslationFilled) { + row.description = lang(lng_passport_translation_needed); + } else if (notFilled & Value::kNoSelfieFilled) { + row.description = lang(lng_passport_identity_selfie); + } + } + } return row; }; switch (scope.type) { diff --git a/Telegram/SourceFiles/passport/passport_panel_controller.cpp b/Telegram/SourceFiles/passport/passport_panel_controller.cpp index 6fa15c866..7cc528249 100644 --- a/Telegram/SourceFiles/passport/passport_panel_controller.cpp +++ b/Telegram/SourceFiles/passport/passport_panel_controller.cpp @@ -64,11 +64,11 @@ ScanInfo CollectScanInfo(const EditFile &file) { } }(); return { + file.type, FileKey{ file.fields.id, file.fields.dcId }, !file.fields.error.isEmpty() ? file.fields.error : status, file.fields.image, file.deleted, - file.type, file.fields.error }; } @@ -95,7 +95,7 @@ std::map PrepareSpecialFiles(const Value &value) { type, (i != end(value.specialScansInEdit) ? CollectScanInfo(i->second) - : ScanInfo())).first; + : ScanInfo(type))).first; } } return result; @@ -185,22 +185,20 @@ EditDocumentScheme GetDocumentScheme( result.detailsHeader = lang(lng_passport_personal_details); result.fieldsHeader = lang(lng_passport_document_details); if (scansType) { - switch (*scansType) { - case Value::Type::Passport: - result.scansHeader = lang(lng_passport_identity_passport); - break; - case Value::Type::DriverLicense: - result.scansHeader = lang(lng_passport_identity_license); - break; - case Value::Type::IdentityCard: - result.scansHeader = lang(lng_passport_identity_card); - break; - case Value::Type::InternalPassport: - result.scansHeader = lang(lng_passport_identity_internal); - break; - default: - Unexpected("scansType in GetDocumentScheme:Identity."); - } + result.scansHeader = [&] { + switch (*scansType) { + case Value::Type::Passport: + return lang(lng_passport_identity_passport); + case Value::Type::DriverLicense: + return lang(lng_passport_identity_license); + case Value::Type::IdentityCard: + return lang(lng_passport_identity_card); + case Value::Type::InternalPassport: + return lang(lng_passport_identity_internal); + default: + Unexpected("scansType in GetDocumentScheme:Identity."); + } + }(); } result.rows = { { @@ -501,6 +499,24 @@ bool SkipFieldCheck(not_null value, const QString &key) { return dontCheckNames.find(key) != end(dontCheckNames); } +ScanInfo::ScanInfo(FileType type) : type(type) { +} + +ScanInfo::ScanInfo( + FileType type, + const FileKey &key, + const QString &status, + const QImage &thumb, + bool deleted, + const QString &error) +: type(type) +, key(key) +, status(status) +, thumb(thumb) +, deleted(deleted) +, error(error) { +} + BoxPointer::BoxPointer(QPointer value) : _value(value) { } @@ -912,41 +928,34 @@ void PanelController::ensurePanelCreated() { } } -int PanelController::findNonEmptyDocumentIndex(const Scope &scope) const { +base::optional PanelController::findBestDocumentIndex( + const Scope &scope) const { + Expects(!scope.documents.empty()); + const auto &documents = scope.documents; - const auto i = ranges::find_if( + const auto i = ranges::min_element( documents, + std::less<>(), [](not_null document) { - return document->scansAreFilled(); + return document->whatNotFilled(); }); - if (i != end(documents)) { - return (i - begin(documents)); - } - // If we have a document where only selfie is not filled - return it. - // #TODO passport half-full value - //const auto j = ranges::find_if( - // documents, - // [&](not_null document) { - // return document->scansAreFilled(false); - // }); - //if (j != end(documents)) { - // return (j - begin(documents)); - //} + return ((*i)->whatNotFilled() == Value::kNothingFilled) + ? base::none + : base::make_optional(int(i - begin(documents))); return -1; } - void PanelController::editScope(int index) { Expects(_panel != nullptr); Expects(index >= 0 && index < _scopes.size()); const auto &scope = _scopes[index]; if (scope.documents.empty()) { - editScope(index, -1); + editScope(index, base::none); } else { - const auto documentIndex = findNonEmptyDocumentIndex(scope); - if (documentIndex >= 0 || scope.documents.size() == 1) { - editScope(index, (documentIndex >= 0) ? documentIndex : 0); + const auto documentIndex = findBestDocumentIndex(scope); + if (documentIndex || scope.documents.size() == 1) { + editScope(index, documentIndex ? *documentIndex : 0); } else { requestScopeFilesType(index); } @@ -1056,16 +1065,16 @@ void PanelController::readScanError(ReadScanError error) { bool PanelController::editRequiresScanUpload( int index, - int documentIndex) const { + base::optional documentIndex) const { Expects(index >= 0 && index < _scopes.size()); - Expects((documentIndex < 0) - || (documentIndex >= 0 - && documentIndex < _scopes[index].documents.size())); + Expects(!documentIndex + || (*documentIndex >= 0 + && *documentIndex < _scopes[index].documents.size())); - if (documentIndex < 0) { + if (!documentIndex) { return false; } - const auto document = _scopes[index].documents[documentIndex]; + const auto document = _scopes[index].documents[*documentIndex]; if (document->requiresSpecialScan(FileType::FrontSide)) { const auto &scans = document->specialScans; return (scans.find(FileType::FrontSide) == end(scans)); @@ -1073,26 +1082,30 @@ bool PanelController::editRequiresScanUpload( return document->files(FileType::Scan).empty(); } -void PanelController::editScope(int index, int documentIndex) { +void PanelController::editScope( + int index, + base::optional documentIndex) { if (editRequiresScanUpload(index, documentIndex)) { - editWithUpload(index, documentIndex); + editWithUpload(index, *documentIndex); } else { startScopeEdit(index, documentIndex); } } -void PanelController::startScopeEdit(int index, int documentIndex) { +void PanelController::startScopeEdit( + int index, + base::optional documentIndex) { Expects(_panel != nullptr); Expects(index >= 0 && index < _scopes.size()); - Expects(_scopes[index].details != 0 || documentIndex >= 0); - Expects((documentIndex < 0) - || (documentIndex >= 0 - && documentIndex < _scopes[index].documents.size())); + Expects(_scopes[index].details != 0 || documentIndex.has_value()); + Expects(!documentIndex.has_value() + || (*documentIndex >= 0 + && *documentIndex < _scopes[index].documents.size())); _editScope = &_scopes[index]; _editValue = _editScope->details; - _editDocument = (documentIndex >= 0) - ? _scopes[index].documents[documentIndex].get() + _editDocument = documentIndex + ? _scopes[index].documents[*documentIndex].get() : nullptr; if (_editValue) { diff --git a/Telegram/SourceFiles/passport/passport_panel_controller.h b/Telegram/SourceFiles/passport/passport_panel_controller.h index 074176149..78c39e27e 100644 --- a/Telegram/SourceFiles/passport/passport_panel_controller.h +++ b/Telegram/SourceFiles/passport/passport_panel_controller.h @@ -32,11 +32,20 @@ QString AdjustKeyName(not_null value, const QString &key); bool SkipFieldCheck(not_null value, const QString &key); struct ScanInfo { + explicit ScanInfo(FileType type); + ScanInfo( + FileType type, + const FileKey &key, + const QString &status, + const QImage &thumb, + bool deleted, + const QString &error); + + FileType type; FileKey key; QString status; QImage thumb; bool deleted = false; - FileType type = FileType(); QString error; }; @@ -140,15 +149,15 @@ public: private: void ensurePanelCreated(); - void editScope(int index, int documentIndex); + void editScope(int index, base::optional documentIndex); void editWithUpload(int index, int documentIndex); - bool editRequiresScanUpload(int index, int documentIndex) const; - void startScopeEdit(int index, int documentIndex); - int findNonEmptyDocumentIndex(const Scope &scope) const; + bool editRequiresScanUpload( + int index, + base::optional documentIndex) const; + void startScopeEdit(int index, base::optional documentIndex); + base::optional findBestDocumentIndex(const Scope &scope) const; void requestScopeFilesType(int index); void cancelValueEdit(); - std::map valueSpecialFiles( - const Value &value) const; void processValueSaveFinished(not_null value); void processVerificationNeeded(not_null value); diff --git a/Telegram/SourceFiles/passport/passport_panel_edit_document.cpp b/Telegram/SourceFiles/passport/passport_panel_edit_document.cpp index 9bfdf7b56..2f0ece3fb 100644 --- a/Telegram/SourceFiles/passport/passport_panel_edit_document.cpp +++ b/Telegram/SourceFiles/passport/passport_panel_edit_document.cpp @@ -330,6 +330,7 @@ not_null PanelEditDocument::setupContent( object_ptr( inner, _controller, + _scheme.scansHeader, *scansError, std::move(specialFiles), std::move(translations))); diff --git a/Telegram/SourceFiles/passport/passport_panel_edit_scans.cpp b/Telegram/SourceFiles/passport/passport_panel_edit_scans.cpp index 5441076d5..921366581 100644 --- a/Telegram/SourceFiles/passport/passport_panel_edit_scans.cpp +++ b/Telegram/SourceFiles/passport/passport_panel_edit_scans.cpp @@ -439,6 +439,7 @@ EditScans::EditScans( EditScans::EditScans( QWidget *parent, not_null controller, + const QString &header, const QString &error, std::map &&specialFiles, base::optional &&translations) @@ -448,7 +449,7 @@ EditScans::EditScans( , _content(this) , _scansList(_controller) , _translationsList(_controller, std::move(translations)) { - setupSpecialScans(std::move(specialFiles)); + setupSpecialScans(header, std::move(specialFiles)); } base::optional EditScans::validateGetErrorTop() { @@ -481,7 +482,7 @@ base::optional EditScans::validateGetErrorTop() { || scan.file.deleted || !scan.file.error.isEmpty()) { toggleSpecialScanError(type, true); - suggestResult(scan.header->y()); + suggestResult(scan.header ? scan.header->y() : scan.wrap->y()); } } suggestList(FileType::Translation); @@ -592,7 +593,9 @@ void EditScans::setupList( st::passportFormDividerHeight)); } -void EditScans::setupSpecialScans(std::map &&files) { +void EditScans::setupSpecialScans( + const QString &header, + std::map &&files) { const auto requiresBothSides = files.find(FileType::ReverseSide) != end(files); const auto title = [&](FileType type) { @@ -665,16 +668,18 @@ void EditScans::setupSpecialScans(std::map &&files) { SpecialScan(std::move(info))).first; auto &scan = i->second; - scan.header = inner->add( - object_ptr>( - inner, - object_ptr( + if (_specialScans.size() == 1) { + scan.header = inner->add( + object_ptr>( inner, - title(type), - Ui::FlatLabel::InitType::Simple, - st::passportFormHeader), - st::passportUploadHeaderPadding)); - scan.header->toggle(scan.file.key.id != 0, anim::type::instant); + object_ptr( + inner, + header, + Ui::FlatLabel::InitType::Simple, + st::passportFormHeader), + st::passportUploadHeaderPadding)); + scan.header->toggle(scan.file.key.id != 0, anim::type::instant); + } scan.wrap = inner->add(object_ptr(inner)); if (scan.file.key.id) { createSpecialScanRow(scan, scan.file, requiresBothSides); @@ -787,7 +792,9 @@ void EditScans::updateSpecialScan(ScanInfo &&info) { createSpecialScanRow(scan, info, requiresBothSides); scan.wrap->resizeToWidth(width()); scan.row->show(anim::type::normal); - scan.header->show(anim::type::normal); + if (scan.header) { + scan.header->show(anim::type::normal); + } specialScanChanged(type, true); } scan.file = std::move(info); @@ -805,12 +812,12 @@ void EditScans::createSpecialScanRow( switch (type) { case FileType::FrontSide: return lang(requiresBothSides - ? lng_passport_front_side_name - : lng_passport_main_page_name); + ? lng_passport_front_side_title + : lng_passport_main_page_title); case FileType::ReverseSide: - return lang(lng_passport_reverse_side_name); + return lang(lng_passport_reverse_side_title); case FileType::Selfie: - return lang(lng_passport_selfie_name); + return lang(lng_passport_selfie_title); } Unexpected("Type in special file name."); }(); diff --git a/Telegram/SourceFiles/passport/passport_panel_edit_scans.h b/Telegram/SourceFiles/passport/passport_panel_edit_scans.h index fb0e3c37a..c5c0a0faf 100644 --- a/Telegram/SourceFiles/passport/passport_panel_edit_scans.h +++ b/Telegram/SourceFiles/passport/passport_panel_edit_scans.h @@ -55,6 +55,7 @@ public: EditScans( QWidget *parent, not_null controller, + const QString &header, const QString &error, std::map &&specialFiles, base::optional &&translations); @@ -112,7 +113,9 @@ private: not_null container, FileType type, const QString &header); - void setupSpecialScans(std::map &&files); + void setupSpecialScans( + const QString &header, + std::map &&files); void init(); void chooseScan(FileType type);