Use most-filled document from OneOf list.

This commit is contained in:
John Preston 2018-08-17 17:59:07 +03:00
parent e25ecce887
commit 82a3cd9bdb
9 changed files with 163 additions and 113 deletions

View File

@ -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";

View File

@ -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() {

View File

@ -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<File> &files(FileType type);
const std::vector<File> &files(FileType type) const;
QString &fileMissingError(FileType type);

View File

@ -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<const Value*> 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) {

View File

@ -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<FileType, ScanInfo> 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<const Value*> 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<BoxContent> value)
: _value(value) {
}
@ -912,41 +928,34 @@ void PanelController::ensurePanelCreated() {
}
}
int PanelController::findNonEmptyDocumentIndex(const Scope &scope) const {
base::optional<int> 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<const Value*> 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<const Value*> 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<int> 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<int> 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<int> 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) {

View File

@ -32,11 +32,20 @@ QString AdjustKeyName(not_null<const Value*> value, const QString &key);
bool SkipFieldCheck(not_null<const Value*> 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<int> 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<int> documentIndex) const;
void startScopeEdit(int index, base::optional<int> documentIndex);
base::optional<int> findBestDocumentIndex(const Scope &scope) const;
void requestScopeFilesType(int index);
void cancelValueEdit();
std::map<FileType, ScanInfo> valueSpecialFiles(
const Value &value) const;
void processValueSaveFinished(not_null<const Value*> value);
void processVerificationNeeded(not_null<const Value*> value);

View File

@ -330,6 +330,7 @@ not_null<Ui::RpWidget*> PanelEditDocument::setupContent(
object_ptr<EditScans>(
inner,
_controller,
_scheme.scansHeader,
*scansError,
std::move(specialFiles),
std::move(translations)));

View File

@ -439,6 +439,7 @@ EditScans::EditScans(
EditScans::EditScans(
QWidget *parent,
not_null<PanelController*> controller,
const QString &header,
const QString &error,
std::map<FileType, ScanInfo> &&specialFiles,
base::optional<ScanListData> &&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<int> EditScans::validateGetErrorTop() {
@ -481,7 +482,7 @@ base::optional<int> 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<FileType, ScanInfo> &&files) {
void EditScans::setupSpecialScans(
const QString &header,
std::map<FileType, ScanInfo> &&files) {
const auto requiresBothSides = files.find(FileType::ReverseSide)
!= end(files);
const auto title = [&](FileType type) {
@ -665,16 +668,18 @@ void EditScans::setupSpecialScans(std::map<FileType, ScanInfo> &&files) {
SpecialScan(std::move(info))).first;
auto &scan = i->second;
scan.header = inner->add(
object_ptr<Ui::SlideWrap<Ui::FlatLabel>>(
inner,
object_ptr<Ui::FlatLabel>(
if (_specialScans.size() == 1) {
scan.header = inner->add(
object_ptr<Ui::SlideWrap<Ui::FlatLabel>>(
inner,
title(type),
Ui::FlatLabel::InitType::Simple,
st::passportFormHeader),
st::passportUploadHeaderPadding));
scan.header->toggle(scan.file.key.id != 0, anim::type::instant);
object_ptr<Ui::FlatLabel>(
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<Ui::VerticalLayout>(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.");
}();

View File

@ -55,6 +55,7 @@ public:
EditScans(
QWidget *parent,
not_null<PanelController*> controller,
const QString &header,
const QString &error,
std::map<FileType, ScanInfo> &&specialFiles,
base::optional<ScanListData> &&translations);
@ -112,7 +113,9 @@ private:
not_null<Ui::VerticalLayout*> container,
FileType type,
const QString &header);
void setupSpecialScans(std::map<FileType, ScanInfo> &&files);
void setupSpecialScans(
const QString &header,
std::map<FileType, ScanInfo> &&files);
void init();
void chooseScan(FileType type);