mirror of
https://github.com/vale981/tdesktop
synced 2025-03-06 02:01:40 -05:00
Update scheme, special scans for identity type.
This commit is contained in:
parent
72b29dd90d
commit
6aecb81c23
13 changed files with 655 additions and 281 deletions
|
@ -1534,6 +1534,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
"lng_passport_identity_card_upload" = "Upload a scan of your identity card";
|
||||
"lng_passport_identity_license" = "Driver's license";
|
||||
"lng_passport_identity_license_upload" = "Upload a scan of your driver's license";
|
||||
"lng_passport_identity_internal" = "Internal passport";
|
||||
"lng_passport_identity_internal_upload" = "Upload a scan of your internal passport";
|
||||
"lng_passport_identity_about" = "Your document must contain your photograph, name and surname, date of birth, citizenship, document issue date and document number.";
|
||||
"lng_passport_address_title" = "Residential address";
|
||||
"lng_passport_address_description" = "Upload a proof of your address";
|
||||
|
@ -1543,6 +1545,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
"lng_passport_address_statement_upload" = "Upload a scan of your bank statement";
|
||||
"lng_passport_address_agreement" = "Tenancy agreement";
|
||||
"lng_passport_address_agreement_upload" = "Upload a scan of your tenancy agreement";
|
||||
"lng_passport_address_registration" = "Passport registration";
|
||||
"lng_passport_address_registration_upload" = "Upload a scan of your passport registration";
|
||||
"lng_passport_address_temporary" = "Temporary registration";
|
||||
"lng_passport_address_temporary_upload" = "Upload a scan of your temporary registration";
|
||||
"lng_passport_address_about" = "To confirm your address please upload a scan or photo of the selected document (all pages).";
|
||||
"lng_passport_document_type" = "Please choose the type of your document:";
|
||||
"lng_passport_upload_document" = "Upload document";
|
||||
|
@ -1563,8 +1569,16 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
"lng_passport_upload_more" = "Upload additional scans";
|
||||
"lng_passport_selfie_title" = "Selfie";
|
||||
"lng_passport_selfie_name" = "Photo";
|
||||
"lng_passport_selfie_description" = "Take a picture of yourself holding hour document.";
|
||||
"lng_passport_selfie_description" = "Take a picture of yourself holding your document.";
|
||||
"lng_passport_upload_selfie" = "Upload selfie";
|
||||
"lng_passport_front_side_title" = "Front side";
|
||||
"lng_passport_front_side_name" = "Scan";
|
||||
"lng_passport_front_side_description" = "Upload front side of your document.";
|
||||
"lng_passport_upload_front_side" = "Upload front side scan";
|
||||
"lng_passport_reverse_side_title" = "Reverse side";
|
||||
"lng_passport_reverse_side_name" = "Scan";
|
||||
"lng_passport_reverse_side_description" = "Upload reverse side of your document.";
|
||||
"lng_passport_upload_reverse_side" = "Upload reverse side scan";
|
||||
"lng_passport_personal_details" = "Personal details";
|
||||
"lng_passport_personal_details_enter" = "Enter your personal details";
|
||||
"lng_passport_choose_image" = "Choose scan image";
|
||||
|
|
|
@ -982,23 +982,28 @@ secureValueTypePersonalDetails#9d2a81e3 = SecureValueType;
|
|||
secureValueTypePassport#3dac6a00 = SecureValueType;
|
||||
secureValueTypeDriverLicense#6e425c4 = SecureValueType;
|
||||
secureValueTypeIdentityCard#a0d0744b = SecureValueType;
|
||||
secureValueTypeInternalPassport#99a48f23 = SecureValueType;
|
||||
secureValueTypeAddress#cbe31e26 = SecureValueType;
|
||||
secureValueTypeUtilityBill#fc36954e = SecureValueType;
|
||||
secureValueTypeBankStatement#89137c0d = SecureValueType;
|
||||
secureValueTypeRentalAgreement#8b883488 = SecureValueType;
|
||||
secureValueTypePassportRegistration#99e3806a = SecureValueType;
|
||||
secureValueTypeTemporaryRegistration#ea02ec33 = SecureValueType;
|
||||
secureValueTypePhone#b320aadb = SecureValueType;
|
||||
secureValueTypeEmail#8e3ca7ee = SecureValueType;
|
||||
|
||||
secureValue#ec4134c8 flags:# type:SecureValueType data:flags.0?SecureData files:flags.1?Vector<SecureFile> plain_data:flags.2?SecurePlainData selfie:flags.3?SecureFile hash:bytes = SecureValue;
|
||||
secureValue#b4b4b699 flags:# type:SecureValueType data:flags.0?SecureData front_side:flags.1?SecureFile reverse_side:flags.2?SecureFile selfie:flags.3?SecureFile files:flags.4?Vector<SecureFile> plain_data:flags.5?SecurePlainData hash:bytes = SecureValue;
|
||||
|
||||
inputSecureValue#c0da30f0 flags:# type:SecureValueType data:flags.0?SecureData files:flags.1?Vector<InputSecureFile> plain_data:flags.2?SecurePlainData selfie:flags.3?InputSecureFile = InputSecureValue;
|
||||
inputSecureValue#67872e8 flags:# type:SecureValueType data:flags.0?SecureData front_side:flags.1?InputSecureFile reverse_side:flags.2?InputSecureFile selfie:flags.3?InputSecureFile files:flags.4?Vector<InputSecureFile> plain_data:flags.5?SecurePlainData = InputSecureValue;
|
||||
|
||||
secureValueHash#ed1ecdb0 type:SecureValueType hash:bytes = SecureValueHash;
|
||||
|
||||
secureValueErrorData#e8a40bd9 type:SecureValueType data_hash:bytes field:string text:string = SecureValueError;
|
||||
secureValueErrorFrontSide#be3dfa type:SecureValueType file_hash:bytes text:string = SecureValueError;
|
||||
secureValueErrorReverseSide#868a2aa5 type:SecureValueType file_hash:bytes text:string = SecureValueError;
|
||||
secureValueErrorSelfie#e537ced6 type:SecureValueType file_hash:bytes text:string = SecureValueError;
|
||||
secureValueErrorFile#7a700873 type:SecureValueType file_hash:bytes text:string = SecureValueError;
|
||||
secureValueErrorFiles#666220e9 type:SecureValueType file_hash:Vector<bytes> text:string = SecureValueError;
|
||||
secureValueErrorSelfie#e537ced6 type:SecureValueType file_hash:bytes text:string = SecureValueError;
|
||||
|
||||
secureCredentialsEncrypted#33f0ea47 data:bytes hash:bytes secret:bytes = SecureCredentialsEncrypted;
|
||||
|
||||
|
@ -1287,4 +1292,4 @@ langpack.getStrings#2e1ee318 lang_code:string keys:Vector<string> = Vector<LangP
|
|||
langpack.getDifference#b2e4d7d from_version:int = LangPackDifference;
|
||||
langpack.getLanguages#800fd57d = Vector<LangPackLanguage>;
|
||||
|
||||
// LAYER 80
|
||||
// LAYER 81
|
||||
|
|
|
@ -55,7 +55,7 @@ addChildParentFlags('MTPDchannelForbidden', 'MTPDchannel');
|
|||
parentFlagsCheck = {};
|
||||
|
||||
countedTypeIdExceptions = {};
|
||||
for i in range(77,81):
|
||||
for i in range(77, 82):
|
||||
countedTypeIdExceptions[i] = {}
|
||||
countedTypeIdExceptions[i]['channel'] = True
|
||||
countedTypeIdExceptions['ipPortSecret'] = True
|
||||
|
|
|
@ -177,12 +177,15 @@ void HistoryService::setMessageByAction(const MTPmessageAction &action) {
|
|||
case mtpc_secureValueTypePassport:
|
||||
case mtpc_secureValueTypeDriverLicense:
|
||||
case mtpc_secureValueTypeIdentityCard:
|
||||
case mtpc_secureValueTypeInternalPassport:
|
||||
return lang(lng_action_secure_proof_of_identity);
|
||||
case mtpc_secureValueTypeAddress:
|
||||
return lang(lng_action_secure_address);
|
||||
case mtpc_secureValueTypeUtilityBill:
|
||||
case mtpc_secureValueTypeBankStatement:
|
||||
case mtpc_secureValueTypeRentalAgreement:
|
||||
case mtpc_secureValueTypePassportRegistration:
|
||||
case mtpc_secureValueTypeTemporaryRegistration:
|
||||
return lang(lng_action_secure_proof_of_address);
|
||||
case mtpc_secureValueTypePhone:
|
||||
return lang(lng_action_secure_phone);
|
||||
|
|
|
@ -69,41 +69,64 @@ QImage ReadImage(bytes::const_span buffer) {
|
|||
Value::Type ConvertType(const MTPSecureValueType &type) {
|
||||
using Type = Value::Type;
|
||||
switch (type.type()) {
|
||||
case mtpc_secureValueTypePersonalDetails: return Type::PersonalDetails;
|
||||
case mtpc_secureValueTypePassport: return Type::Passport;
|
||||
case mtpc_secureValueTypeDriverLicense: return Type::DriverLicense;
|
||||
case mtpc_secureValueTypeIdentityCard: return Type::IdentityCard;
|
||||
case mtpc_secureValueTypeAddress: return Type::Address;
|
||||
case mtpc_secureValueTypeUtilityBill: return Type::UtilityBill;
|
||||
case mtpc_secureValueTypeBankStatement: return Type::BankStatement;
|
||||
case mtpc_secureValueTypeRentalAgreement: return Type::RentalAgreement;
|
||||
case mtpc_secureValueTypePhone: return Type::Phone;
|
||||
case mtpc_secureValueTypeEmail: return Type::Email;
|
||||
case mtpc_secureValueTypePersonalDetails:
|
||||
return Type::PersonalDetails;
|
||||
case mtpc_secureValueTypePassport:
|
||||
return Type::Passport;
|
||||
case mtpc_secureValueTypeDriverLicense:
|
||||
return Type::DriverLicense;
|
||||
case mtpc_secureValueTypeIdentityCard:
|
||||
return Type::IdentityCard;
|
||||
case mtpc_secureValueTypeInternalPassport:
|
||||
return Type::InternalPassport;
|
||||
case mtpc_secureValueTypeAddress:
|
||||
return Type::Address;
|
||||
case mtpc_secureValueTypeUtilityBill:
|
||||
return Type::UtilityBill;
|
||||
case mtpc_secureValueTypeBankStatement:
|
||||
return Type::BankStatement;
|
||||
case mtpc_secureValueTypeRentalAgreement:
|
||||
return Type::RentalAgreement;
|
||||
case mtpc_secureValueTypePassportRegistration:
|
||||
return Type::PassportRegistration;
|
||||
case mtpc_secureValueTypeTemporaryRegistration:
|
||||
return Type::TemporaryRegistration;
|
||||
case mtpc_secureValueTypePhone:
|
||||
return Type::Phone;
|
||||
case mtpc_secureValueTypeEmail:
|
||||
return Type::Email;
|
||||
}
|
||||
Unexpected("Type in secureValueType type.");
|
||||
};
|
||||
|
||||
MTPSecureValueType ConvertType(Value::Type type) {
|
||||
using Type = Value::Type;
|
||||
switch (type) {
|
||||
case Value::Type::PersonalDetails:
|
||||
case Type::PersonalDetails:
|
||||
return MTP_secureValueTypePersonalDetails();
|
||||
case Value::Type::Passport:
|
||||
case Type::Passport:
|
||||
return MTP_secureValueTypePassport();
|
||||
case Value::Type::DriverLicense:
|
||||
case Type::DriverLicense:
|
||||
return MTP_secureValueTypeDriverLicense();
|
||||
case Value::Type::IdentityCard:
|
||||
case Type::IdentityCard:
|
||||
return MTP_secureValueTypeIdentityCard();
|
||||
case Value::Type::Address:
|
||||
case Type::InternalPassport:
|
||||
return MTP_secureValueTypeInternalPassport();
|
||||
case Type::Address:
|
||||
return MTP_secureValueTypeAddress();
|
||||
case Value::Type::UtilityBill:
|
||||
case Type::UtilityBill:
|
||||
return MTP_secureValueTypeUtilityBill();
|
||||
case Value::Type::BankStatement:
|
||||
case Type::BankStatement:
|
||||
return MTP_secureValueTypeBankStatement();
|
||||
case Value::Type::RentalAgreement:
|
||||
case Type::RentalAgreement:
|
||||
return MTP_secureValueTypeRentalAgreement();
|
||||
case Value::Type::Phone:
|
||||
case Type::PassportRegistration:
|
||||
return MTP_secureValueTypePassportRegistration();
|
||||
case Type::TemporaryRegistration:
|
||||
return MTP_secureValueTypeTemporaryRegistration();
|
||||
case Type::Phone:
|
||||
return MTP_secureValueTypePhone();
|
||||
case Value::Type::Email:
|
||||
case Type::Email:
|
||||
return MTP_secureValueTypeEmail();
|
||||
}
|
||||
Unexpected("Type in FormController::submit.");
|
||||
|
@ -135,21 +158,34 @@ FormRequest PreprocessRequest(const FormRequest &request) {
|
|||
}
|
||||
|
||||
QString ValueCredentialsKey(Value::Type type) {
|
||||
using Type = Value::Type;
|
||||
switch (type) {
|
||||
case Value::Type::PersonalDetails: return "personal_details";
|
||||
case Value::Type::Passport: return "passport";
|
||||
case Value::Type::DriverLicense: return "driver_license";
|
||||
case Value::Type::IdentityCard: return "identity_card";
|
||||
case Value::Type::Address: return "address";
|
||||
case Value::Type::UtilityBill: return "utility_bill";
|
||||
case Value::Type::BankStatement: return "bank_statement";
|
||||
case Value::Type::RentalAgreement: return "rental_agreement";
|
||||
case Value::Type::Phone:
|
||||
case Value::Type::Email: return QString();
|
||||
case Type::PersonalDetails: return "personal_details";
|
||||
case Type::Passport: return "passport";
|
||||
case Type::DriverLicense: return "driver_license";
|
||||
case Type::IdentityCard: return "identity_card";
|
||||
case Type::InternalPassport: return "internal_passport";
|
||||
case Type::Address: return "address";
|
||||
case Type::UtilityBill: return "utility_bill";
|
||||
case Type::BankStatement: return "bank_statement";
|
||||
case Type::RentalAgreement: return "rental_agreement";
|
||||
case Type::PassportRegistration: return "passport_registration";
|
||||
case Type::TemporaryRegistration: return "temporary_registration";
|
||||
case Type::Phone:
|
||||
case Type::Email: return QString();
|
||||
}
|
||||
Unexpected("Type in ValueCredentialsKey.");
|
||||
}
|
||||
|
||||
QString SpecialScanCredentialsKey(SpecialFile type) {
|
||||
switch (type) {
|
||||
case SpecialFile::FrontSide: return "front_side";
|
||||
case SpecialFile::ReverseSide: return "reverse_side";
|
||||
case SpecialFile::Selfie: return "selfie";
|
||||
}
|
||||
Unexpected("Type in SpecialScanCredentialsKey.");
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
FormRequest::FormRequest(
|
||||
|
@ -215,6 +251,43 @@ UploadScanData *UploadScanDataPointer::operator->() const {
|
|||
Value::Value(Type type) : type(type) {
|
||||
}
|
||||
|
||||
bool Value::requiresSpecialScan(
|
||||
SpecialFile type,
|
||||
bool selfieRequired) const {
|
||||
switch (type) {
|
||||
case SpecialFile::FrontSide:
|
||||
return (this->type == Type::Passport)
|
||||
|| (this->type == Type::DriverLicense)
|
||||
|| (this->type == Type::IdentityCard)
|
||||
|| (this->type == Type::InternalPassport);
|
||||
case SpecialFile::ReverseSide:
|
||||
return (this->type == Type::DriverLicense)
|
||||
|| (this->type == Type::IdentityCard);
|
||||
case SpecialFile::Selfie:
|
||||
return selfieRequired;
|
||||
}
|
||||
Unexpected("Special scan type in requiresSpecialScan.");
|
||||
}
|
||||
|
||||
bool Value::scansAreFilled(bool selfieRequired) const {
|
||||
if (!requiresSpecialScan(SpecialFile::FrontSide, selfieRequired)) {
|
||||
return !scans.empty();
|
||||
}
|
||||
const auto types = {
|
||||
SpecialFile::FrontSide,
|
||||
SpecialFile::ReverseSide,
|
||||
SpecialFile::Selfie
|
||||
};
|
||||
for (const auto type : types) {
|
||||
if (requiresSpecialScan(type, selfieRequired)
|
||||
&& (specialScans.find(type) == end(specialScans))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
|
||||
FormController::FormController(
|
||||
not_null<Window::Controller*> controller,
|
||||
const FormRequest &request)
|
||||
|
@ -266,8 +339,13 @@ auto FormController::prepareFinalData() -> FinalData {
|
|||
}
|
||||
object.insert("files", files);
|
||||
}
|
||||
if (_form.identitySelfieRequired && value->selfie) {
|
||||
object.insert("selfie", GetJSONFromFile(*value->selfie));
|
||||
for (const auto &[type, scan] : value->specialScans) {
|
||||
const auto selfieRequired = _form.identitySelfieRequired;
|
||||
if (value->requiresSpecialScan(type, selfieRequired)) {
|
||||
object.insert(
|
||||
SpecialScanCredentialsKey(type),
|
||||
GetJSONFromFile(scan));
|
||||
}
|
||||
}
|
||||
secureData.insert(key, object);
|
||||
};
|
||||
|
@ -290,7 +368,7 @@ auto FormController::prepareFinalData() -> FinalData {
|
|||
addValue(scope.fields);
|
||||
if (!scope.documents.empty()) {
|
||||
for (const auto &document : scope.documents) {
|
||||
if (!document->scans.empty()) {
|
||||
if (document->scansAreFilled(scope.selfieRequired)) {
|
||||
addValue(document);
|
||||
break;
|
||||
}
|
||||
|
@ -549,6 +627,18 @@ void FormController::fillErrors() {
|
|||
LOG(("API Error: File not found for error value."));
|
||||
return nullptr;
|
||||
};
|
||||
const auto setSpecialScanError = [&](SpecialFile type, auto &&data) {
|
||||
if (const auto value = find(data.vtype)) {
|
||||
const auto i = value->specialScans.find(type);
|
||||
if (i != value->specialScans.end()) {
|
||||
i->second.error = qs(data.vtext);
|
||||
} else {
|
||||
LOG(("API Error: "
|
||||
"Special scan %1 not found for error value."
|
||||
).arg(int(type)));
|
||||
}
|
||||
}
|
||||
};
|
||||
for (const auto &error : _form.pendingErrors) {
|
||||
switch (error.type()) {
|
||||
case mtpc_secureValueErrorData: {
|
||||
|
@ -573,16 +663,19 @@ void FormController::fillErrors() {
|
|||
value->scanMissingError = qs(data.vtext);
|
||||
}
|
||||
} break;
|
||||
case mtpc_secureValueErrorFrontSide: {
|
||||
const auto &data = error.c_secureValueErrorFrontSide();
|
||||
setSpecialScanError(SpecialFile::FrontSide, data);
|
||||
} break;
|
||||
case mtpc_secureValueErrorReverseSide: {
|
||||
const auto &data = error.c_secureValueErrorReverseSide();
|
||||
setSpecialScanError(SpecialFile::ReverseSide, data);
|
||||
} break;
|
||||
case mtpc_secureValueErrorSelfie: {
|
||||
const auto &data = error.c_secureValueErrorSelfie();
|
||||
if (const auto value = find(data.vtype)) {
|
||||
if (value->selfie) {
|
||||
value->selfie->error = qs(data.vtext);
|
||||
} else {
|
||||
LOG(("API Error: Selfie not found for error value."));
|
||||
}
|
||||
}
|
||||
setSpecialScanError(SpecialFile::Selfie, data);
|
||||
} break;
|
||||
default: Unexpected("Error type in FormController::fillErrors.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -639,8 +732,10 @@ bool FormController::validateValueSecrets(Value &value) {
|
|||
return false;
|
||||
}
|
||||
}
|
||||
if (value.selfie && !validateFileSecret(*value.selfie)) {
|
||||
return false;
|
||||
for (auto &[type, file] : value.specialScans) {
|
||||
if (!validateFileSecret(file)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -692,25 +787,40 @@ void FormController::restoreScan(
|
|||
scanDeleteRestore(value, scanIndex, false);
|
||||
}
|
||||
|
||||
void FormController::uploadSelfie(
|
||||
void FormController::uploadSpecialScan(
|
||||
not_null<const Value*> value,
|
||||
SpecialFile type,
|
||||
QByteArray &&content) {
|
||||
const auto nonconst = findValue(value);
|
||||
nonconst->selfieInEdit = EditFile{ nonconst, File(), nullptr };
|
||||
auto &file = *nonconst->selfieInEdit;
|
||||
auto scanInEdit = EditFile{ nonconst, File(), nullptr };
|
||||
auto i = nonconst->specialScansInEdit.find(type);
|
||||
if (i != nonconst->specialScansInEdit.end()) {
|
||||
i->second = std::move(scanInEdit);
|
||||
} else {
|
||||
i = nonconst->specialScansInEdit.emplace(
|
||||
type,
|
||||
std::move(scanInEdit)).first;
|
||||
}
|
||||
auto &file = i->second;
|
||||
encryptFile(file, std::move(content), [=](UploadScanData &&result) {
|
||||
const auto i = nonconst->specialScansInEdit.find(type);
|
||||
Assert(i != nonconst->specialScansInEdit.end());
|
||||
uploadEncryptedFile(
|
||||
*nonconst->selfieInEdit,
|
||||
i->second,
|
||||
std::move(result));
|
||||
});
|
||||
}
|
||||
|
||||
void FormController::deleteSelfie(not_null<const Value*> value) {
|
||||
selfieDeleteRestore(value, true);
|
||||
void FormController::deleteSpecialScan(
|
||||
not_null<const Value*> value,
|
||||
SpecialFile type) {
|
||||
specialScanDeleteRestore(value, type, true);
|
||||
}
|
||||
|
||||
void FormController::restoreSelfie(not_null<const Value*> value) {
|
||||
selfieDeleteRestore(value, false);
|
||||
void FormController::restoreSpecialScan(
|
||||
not_null<const Value*> value,
|
||||
SpecialFile type) {
|
||||
specialScanDeleteRestore(value, type, false);
|
||||
}
|
||||
|
||||
void FormController::prepareFile(
|
||||
|
@ -779,13 +889,14 @@ void FormController::scanDeleteRestore(
|
|||
_scanUpdated.fire(&scan);
|
||||
}
|
||||
|
||||
void FormController::selfieDeleteRestore(
|
||||
void FormController::specialScanDeleteRestore(
|
||||
not_null<const Value*> value,
|
||||
SpecialFile type,
|
||||
bool deleted) {
|
||||
Expects(value->selfieInEdit.has_value());
|
||||
|
||||
const auto nonconst = findValue(value);
|
||||
auto &scan = *nonconst->selfieInEdit;
|
||||
const auto i = nonconst->specialScansInEdit.find(type);
|
||||
Assert(i != nonconst->specialScansInEdit.end());
|
||||
auto &scan = i->second;
|
||||
scan.deleted = deleted;
|
||||
_scanUpdated.fire(&scan);
|
||||
}
|
||||
|
@ -1006,8 +1117,11 @@ void FormController::startValueEdit(not_null<const Value*> value) {
|
|||
for (auto &scan : nonconst->scans) {
|
||||
loadFile(scan);
|
||||
}
|
||||
if (nonconst->selfie && _form.identitySelfieRequired) {
|
||||
loadFile(*nonconst->selfie);
|
||||
for (auto &[type, scan] : nonconst->specialScans) {
|
||||
const auto selfieRequired = _form.identitySelfieRequired;
|
||||
if (nonconst->requiresSpecialScan(type, selfieRequired)) {
|
||||
loadFile(scan);
|
||||
}
|
||||
}
|
||||
nonconst->scansInEdit = ranges::view::all(
|
||||
nonconst->scans
|
||||
|
@ -1015,13 +1129,12 @@ void FormController::startValueEdit(not_null<const Value*> value) {
|
|||
return EditFile(nonconst, file, nullptr);
|
||||
}) | ranges::to_vector;
|
||||
|
||||
if (nonconst->selfie) {
|
||||
nonconst->selfieInEdit = EditFile(
|
||||
nonconst->specialScansInEdit.clear();
|
||||
for (const auto &[type, scan] : nonconst->specialScans) {
|
||||
nonconst->specialScansInEdit.emplace(type, EditFile(
|
||||
nonconst,
|
||||
*nonconst->selfie,
|
||||
nullptr);
|
||||
} else {
|
||||
nonconst->selfieInEdit = base::none;
|
||||
scan,
|
||||
nullptr));
|
||||
}
|
||||
|
||||
nonconst->data.parsedInEdit = nonconst->data.parsed;
|
||||
|
@ -1116,18 +1229,27 @@ bool FormController::savingValue(not_null<const Value*> value) const {
|
|||
}
|
||||
|
||||
bool FormController::uploadingScan(not_null<const Value*> value) const {
|
||||
const auto uploading = [](const EditFile &file) {
|
||||
return file.uploadData
|
||||
&& file.uploadData->fullId
|
||||
&& !file.deleted;
|
||||
};
|
||||
if (ranges::find_if(value->scansInEdit, uploading)
|
||||
!= end(value->scansInEdit)) {
|
||||
return true;
|
||||
}
|
||||
if (ranges::find_if(value->specialScansInEdit, [&](const auto &pair) {
|
||||
return uploading(pair.second);
|
||||
}) != end(value->specialScansInEdit)) {
|
||||
return true;
|
||||
}
|
||||
for (const auto &scan : value->scansInEdit) {
|
||||
if (scan.uploadData
|
||||
&& scan.uploadData->fullId
|
||||
&& !scan.deleted) {
|
||||
if (uploading(scan)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (value->selfieInEdit) {
|
||||
const auto &selfie = *value->selfieInEdit;
|
||||
if (selfie.uploadData
|
||||
&& selfie.uploadData->fullId
|
||||
&& !selfie.deleted) {
|
||||
for (const auto &[type, scan] : value->specialScansInEdit) {
|
||||
if (uploading(scan)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -1155,7 +1277,7 @@ void FormController::clearValueEdit(not_null<Value*> value) {
|
|||
return;
|
||||
}
|
||||
value->scansInEdit.clear();
|
||||
value->selfieInEdit = base::none;
|
||||
value->specialScansInEdit.clear();
|
||||
value->data.encryptedSecretInEdit.clear();
|
||||
value->data.hashInEdit.clear();
|
||||
value->data.parsedInEdit = ValueMap();
|
||||
|
@ -1200,8 +1322,10 @@ bool FormController::editValueChanged(
|
|||
return true;
|
||||
}
|
||||
}
|
||||
if (value->selfieInEdit && editFileChanged(*value->selfieInEdit)) {
|
||||
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) {
|
||||
|
@ -1230,7 +1354,7 @@ void FormController::saveValueEdit(
|
|||
nonconst->saveRequestId = -1;
|
||||
crl::on_main(this, [=] {
|
||||
base::take(nonconst->scansInEdit);
|
||||
base::take(nonconst->selfieInEdit);
|
||||
base::take(nonconst->specialScansInEdit);
|
||||
base::take(nonconst->data.encryptedSecretInEdit);
|
||||
base::take(nonconst->data.hashInEdit);
|
||||
base::take(nonconst->data.parsedInEdit);
|
||||
|
@ -1313,41 +1437,36 @@ void FormController::saveEncryptedValue(not_null<Value*> value) {
|
|||
_secret,
|
||||
value->data.hashInEdit);
|
||||
|
||||
const auto selfie = (value->selfieInEdit
|
||||
&& !value->selfieInEdit->deleted)
|
||||
? inputFile(*value->selfieInEdit)
|
||||
: MTPInputSecureFile();
|
||||
const auto hasSpecialFile = [&](SpecialFile type) {
|
||||
const auto i = value->specialScansInEdit.find(type);
|
||||
return (i != end(value->specialScansInEdit) && !i->second.deleted);
|
||||
};
|
||||
const auto specialFile = [&](SpecialFile type) {
|
||||
const auto i = value->specialScansInEdit.find(type);
|
||||
return (i != end(value->specialScansInEdit) && !i->second.deleted)
|
||||
? inputFile(i->second)
|
||||
: MTPInputSecureFile();
|
||||
};
|
||||
const auto frontSide = specialFile(SpecialFile::FrontSide);
|
||||
const auto reverseSide = specialFile(SpecialFile::ReverseSide);
|
||||
const auto selfie = specialFile(SpecialFile::Selfie);
|
||||
|
||||
const auto type = [&] {
|
||||
switch (value->type) {
|
||||
case Value::Type::PersonalDetails:
|
||||
return MTP_secureValueTypePersonalDetails();
|
||||
case Value::Type::Passport:
|
||||
return MTP_secureValueTypePassport();
|
||||
case Value::Type::DriverLicense:
|
||||
return MTP_secureValueTypeDriverLicense();
|
||||
case Value::Type::IdentityCard:
|
||||
return MTP_secureValueTypeIdentityCard();
|
||||
case Value::Type::Address:
|
||||
return MTP_secureValueTypeAddress();
|
||||
case Value::Type::UtilityBill:
|
||||
return MTP_secureValueTypeUtilityBill();
|
||||
case Value::Type::BankStatement:
|
||||
return MTP_secureValueTypeBankStatement();
|
||||
case Value::Type::RentalAgreement:
|
||||
return MTP_secureValueTypeRentalAgreement();
|
||||
}
|
||||
Unexpected("Value type in saveEncryptedValue().");
|
||||
}();
|
||||
const auto type = ConvertType(value->type);
|
||||
const auto flags = (value->data.parsedInEdit.fields.empty()
|
||||
? MTPDinputSecureValue::Flag(0)
|
||||
: MTPDinputSecureValue::Flag::f_data)
|
||||
| (hasSpecialFile(SpecialFile::FrontSide)
|
||||
? MTPDinputSecureValue::Flag::f_front_side
|
||||
: MTPDinputSecureValue::Flag(0))
|
||||
| (hasSpecialFile(SpecialFile::ReverseSide)
|
||||
? MTPDinputSecureValue::Flag::f_reverse_side
|
||||
: MTPDinputSecureValue::Flag(0))
|
||||
| (hasSpecialFile(SpecialFile::Selfie)
|
||||
? MTPDinputSecureValue::Flag::f_selfie
|
||||
: MTPDinputSecureValue::Flag(0))
|
||||
| (value->scansInEdit.empty()
|
||||
? MTPDinputSecureValue::Flag(0)
|
||||
: MTPDinputSecureValue::Flag::f_files)
|
||||
| ((value->selfieInEdit && !value->selfieInEdit->deleted)
|
||||
? MTPDinputSecureValue::Flag::f_selfie
|
||||
: MTPDinputSecureValue::Flag(0));
|
||||
: MTPDinputSecureValue::Flag::f_files);
|
||||
Assert(flags != MTPDinputSecureValue::Flags(0));
|
||||
|
||||
sendSaveRequest(value, MTP_inputSecureValue(
|
||||
|
@ -1357,9 +1476,11 @@ void FormController::saveEncryptedValue(not_null<Value*> value) {
|
|||
MTP_bytes(encryptedData.bytes),
|
||||
MTP_bytes(value->data.hashInEdit),
|
||||
MTP_bytes(value->data.encryptedSecretInEdit)),
|
||||
frontSide,
|
||||
reverseSide,
|
||||
selfie,
|
||||
MTP_vector<MTPInputSecureFile>(inputFiles),
|
||||
MTPSecurePlainData(),
|
||||
selfie));
|
||||
MTPSecurePlainData()));
|
||||
}
|
||||
|
||||
void FormController::savePlainTextValue(not_null<Value*> value) {
|
||||
|
@ -1384,9 +1505,11 @@ void FormController::savePlainTextValue(not_null<Value*> value) {
|
|||
MTP_flags(MTPDinputSecureValue::Flag::f_plain_data),
|
||||
type,
|
||||
MTPSecureData(),
|
||||
MTPInputSecureFile(),
|
||||
MTPInputSecureFile(),
|
||||
MTPInputSecureFile(),
|
||||
MTPVector<MTPInputSecureFile>(),
|
||||
plain(MTP_string(text)),
|
||||
MTPInputSecureFile()));
|
||||
plain(MTP_string(text))));
|
||||
}
|
||||
|
||||
void FormController::sendSaveRequest(
|
||||
|
@ -1398,13 +1521,13 @@ void FormController::sendSaveRequest(
|
|||
data,
|
||||
MTP_long(_secretId)
|
||||
)).done([=](const MTPSecureValue &result) {
|
||||
auto filesInEdit = base::take(value->scansInEdit);
|
||||
if (auto selfie = base::take(value->selfieInEdit)) {
|
||||
filesInEdit.push_back(std::move(*selfie));
|
||||
auto scansInEdit = base::take(value->scansInEdit);
|
||||
for (auto &[type, scan] : base::take(value->specialScansInEdit)) {
|
||||
scansInEdit.push_back(std::move(scan));
|
||||
}
|
||||
|
||||
const auto editScreens = value->editScreens;
|
||||
*value = parseValue(result, filesInEdit);
|
||||
*value = parseValue(result, scansInEdit);
|
||||
decryptValue(*value);
|
||||
value->editScreens = editScreens;
|
||||
|
||||
|
@ -1737,8 +1860,21 @@ auto FormController::parseValue(
|
|||
if (data.has_files()) {
|
||||
result.scans = parseFiles(data.vfiles.v, editData);
|
||||
}
|
||||
const auto parseSpecialScan = [&](
|
||||
SpecialFile type,
|
||||
const MTPSecureFile &file) {
|
||||
if (auto parsed = parseFile(file, editData)) {
|
||||
result.specialScans.emplace(type, std::move(*parsed));
|
||||
}
|
||||
};
|
||||
if (data.has_front_side()) {
|
||||
parseSpecialScan(SpecialFile::FrontSide, data.vfront_side);
|
||||
}
|
||||
if (data.has_reverse_side()) {
|
||||
parseSpecialScan(SpecialFile::ReverseSide, data.vreverse_side);
|
||||
}
|
||||
if (data.has_selfie()) {
|
||||
result.selfie = parseFile(data.vselfie, editData);
|
||||
parseSpecialScan(SpecialFile::Selfie, data.vselfie);
|
||||
}
|
||||
if (data.has_plain_data()) {
|
||||
switch (data.vplain_data.type()) {
|
||||
|
@ -1765,8 +1901,10 @@ auto FormController::findEditFile(const FullMsgId &fullId) -> EditFile* {
|
|||
return &scan;
|
||||
}
|
||||
}
|
||||
if (value.selfieInEdit && found(*value.selfieInEdit)) {
|
||||
return &*value.selfieInEdit;
|
||||
for (auto &[special, scan] : value.specialScansInEdit) {
|
||||
if (found(scan)) {
|
||||
return &scan;
|
||||
}
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
|
@ -1782,8 +1920,10 @@ auto FormController::findEditFile(const FileKey &key) -> EditFile* {
|
|||
return &scan;
|
||||
}
|
||||
}
|
||||
if (value.selfieInEdit && found(*value.selfieInEdit)) {
|
||||
return &*value.selfieInEdit;
|
||||
for (auto &[special, scan] : value.specialScansInEdit) {
|
||||
if (found(scan)) {
|
||||
return &scan;
|
||||
}
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
|
@ -1800,8 +1940,10 @@ auto FormController::findFile(const FileKey &key)
|
|||
return { &value, &scan };
|
||||
}
|
||||
}
|
||||
if (value.selfie && found(*value.selfie)) {
|
||||
return { &value, &*value.selfie };
|
||||
for (auto &[special, scan] : value.specialScans) {
|
||||
if (found(scan)) {
|
||||
return { &value, &scan };
|
||||
}
|
||||
}
|
||||
}
|
||||
return { nullptr, nullptr };
|
||||
|
|
|
@ -132,16 +132,27 @@ struct Verification {
|
|||
|
||||
};
|
||||
|
||||
struct Form;
|
||||
|
||||
enum class SpecialFile {
|
||||
FrontSide,
|
||||
ReverseSide,
|
||||
Selfie,
|
||||
};
|
||||
|
||||
struct Value {
|
||||
enum class Type {
|
||||
PersonalDetails,
|
||||
Passport,
|
||||
DriverLicense,
|
||||
IdentityCard,
|
||||
InternalPassport,
|
||||
Address,
|
||||
UtilityBill,
|
||||
BankStatement,
|
||||
RentalAgreement,
|
||||
PassportRegistration,
|
||||
TemporaryRegistration,
|
||||
Phone,
|
||||
Email,
|
||||
};
|
||||
|
@ -150,13 +161,16 @@ struct Value {
|
|||
Value(Value &&other) = default;
|
||||
Value &operator=(Value &&other) = default;
|
||||
|
||||
bool requiresSpecialScan(SpecialFile type, bool selfieRequired) const;
|
||||
bool scansAreFilled(bool selfieRequired) const;
|
||||
|
||||
Type type;
|
||||
ValueData data;
|
||||
std::vector<File> scans;
|
||||
std::vector<EditFile> scansInEdit;
|
||||
std::map<SpecialFile, File> specialScans;
|
||||
QString scanMissingError;
|
||||
base::optional<File> selfie;
|
||||
base::optional<EditFile> selfieInEdit;
|
||||
std::vector<EditFile> scansInEdit;
|
||||
std::map<SpecialFile, EditFile> specialScansInEdit;
|
||||
Verification verification;
|
||||
bytes::vector submitHash;
|
||||
|
||||
|
@ -244,9 +258,16 @@ public:
|
|||
void uploadScan(not_null<const Value*> value, QByteArray &&content);
|
||||
void deleteScan(not_null<const Value*> value, int fileIndex);
|
||||
void restoreScan(not_null<const Value*> value, int fileIndex);
|
||||
void uploadSelfie(not_null<const Value*> value, QByteArray &&content);
|
||||
void deleteSelfie(not_null<const Value*> value);
|
||||
void restoreSelfie(not_null<const Value*> value);
|
||||
void uploadSpecialScan(
|
||||
not_null<const Value*> value,
|
||||
SpecialFile type,
|
||||
QByteArray &&content);
|
||||
void deleteSpecialScan(
|
||||
not_null<const Value*> value,
|
||||
SpecialFile type);
|
||||
void restoreSpecialScan(
|
||||
not_null<const Value*> value,
|
||||
SpecialFile type);
|
||||
|
||||
rpl::producer<> secretReadyEvents() const;
|
||||
|
||||
|
@ -349,8 +370,9 @@ private:
|
|||
not_null<const Value*> value,
|
||||
int fileIndex,
|
||||
bool deleted);
|
||||
void selfieDeleteRestore(
|
||||
void specialScanDeleteRestore(
|
||||
not_null<const Value*> value,
|
||||
SpecialFile type,
|
||||
bool deleted);
|
||||
|
||||
QString getPhoneFromValue(not_null<const Value*> value) const;
|
||||
|
|
|
@ -22,10 +22,13 @@ std::map<Value::Type, Scope::Type> ScopeTypesMap() {
|
|||
{ Value::Type::Passport, Scope::Type::Identity },
|
||||
{ Value::Type::DriverLicense, Scope::Type::Identity },
|
||||
{ Value::Type::IdentityCard, Scope::Type::Identity },
|
||||
{ Value::Type::InternalPassport, Scope::Type::Identity },
|
||||
{ Value::Type::Address, Scope::Type::Address },
|
||||
{ Value::Type::UtilityBill, Scope::Type::Address },
|
||||
{ Value::Type::BankStatement, Scope::Type::Address },
|
||||
{ Value::Type::RentalAgreement, Scope::Type::Address },
|
||||
{ Value::Type::PassportRegistration, Scope::Type::Address },
|
||||
{ Value::Type::TemporaryRegistration, Scope::Type::Address },
|
||||
{ Value::Type::Phone, Scope::Type::Phone },
|
||||
{ Value::Type::Email, Scope::Type::Email },
|
||||
};
|
||||
|
@ -111,7 +114,7 @@ QString ComputeScopeRowReadyString(const Scope &scope) {
|
|||
const auto &fields = scope.fields->data.parsed.fields;
|
||||
const auto document = [&]() -> const Value* {
|
||||
for (const auto &document : scope.documents) {
|
||||
if (!document->scans.empty()) {
|
||||
if (document->scansAreFilled(scope.selfieRequired)) {
|
||||
return document;
|
||||
}
|
||||
}
|
||||
|
@ -119,25 +122,31 @@ QString ComputeScopeRowReadyString(const Scope &scope) {
|
|||
}();
|
||||
if (document && scope.documents.size() > 1) {
|
||||
pushListValue([&] {
|
||||
using Type = Value::Type;
|
||||
switch (document->type) {
|
||||
case Value::Type::Passport:
|
||||
case Type::Passport:
|
||||
return lang(lng_passport_identity_passport);
|
||||
case Value::Type::DriverLicense:
|
||||
case Type::DriverLicense:
|
||||
return lang(lng_passport_identity_license);
|
||||
case Value::Type::IdentityCard:
|
||||
case Type::IdentityCard:
|
||||
return lang(lng_passport_identity_card);
|
||||
case Value::Type::BankStatement:
|
||||
case Type::InternalPassport:
|
||||
return lang(lng_passport_identity_internal);
|
||||
case Type::BankStatement:
|
||||
return lang(lng_passport_address_statement);
|
||||
case Value::Type::UtilityBill:
|
||||
case Type::UtilityBill:
|
||||
return lang(lng_passport_address_bill);
|
||||
case Value::Type::RentalAgreement:
|
||||
case Type::RentalAgreement:
|
||||
return lang(lng_passport_address_agreement);
|
||||
case Type::PassportRegistration:
|
||||
return lang(lng_passport_address_registration);
|
||||
case Type::TemporaryRegistration:
|
||||
return lang(lng_passport_address_temporary);
|
||||
default: Unexpected("Files type in ComputeScopeRowReadyString.");
|
||||
}
|
||||
}());
|
||||
}
|
||||
if (!scope.documents.empty()
|
||||
&& (!document || (scope.selfieRequired && !document->selfie))) {
|
||||
if (!scope.documents.empty() && !document) {
|
||||
return QString();
|
||||
}
|
||||
const auto scheme = GetDocumentScheme(scope.type);
|
||||
|
@ -155,8 +164,6 @@ QString ComputeScopeRowReadyString(const Scope &scope) {
|
|||
pushListValue(format ? format(text) : text);
|
||||
} else if (scope.documents.empty()) {
|
||||
continue;
|
||||
} else if (!document) {
|
||||
return QString();
|
||||
} else {
|
||||
const auto i = document->data.parsed.fields.find(row.key);
|
||||
if (i == end(document->data.parsed.fields)) {
|
||||
|
@ -195,8 +202,10 @@ ScopeRow ComputeScopeRow(const Scope &scope) {
|
|||
errors.push_back(scan.error);
|
||||
}
|
||||
}
|
||||
if (value->selfie && !value->selfie->error.isEmpty()) {
|
||||
errors.push_back(value->selfie->error);
|
||||
for (const auto &[type, scan] : value->specialScans) {
|
||||
if (!scan.error.isEmpty()) {
|
||||
errors.push_back(scan.error);
|
||||
}
|
||||
}
|
||||
if (!value->scanMissingError.isEmpty()) {
|
||||
errors.push_back(value->scanMissingError);
|
||||
|
@ -236,6 +245,11 @@ ScopeRow ComputeScopeRow(const Scope &scope) {
|
|||
lang(lng_passport_identity_license),
|
||||
lang(lng_passport_identity_license_upload),
|
||||
});
|
||||
case Value::Type::InternalPassport:
|
||||
return addReadyError({
|
||||
lang(lng_passport_identity_internal),
|
||||
lang(lng_passport_identity_internal_upload),
|
||||
});
|
||||
default: Unexpected("Identity type in ComputeScopeRow.");
|
||||
}
|
||||
}
|
||||
|
@ -266,6 +280,16 @@ ScopeRow ComputeScopeRow(const Scope &scope) {
|
|||
lang(lng_passport_address_agreement),
|
||||
lang(lng_passport_address_agreement_upload),
|
||||
});
|
||||
case Value::Type::PassportRegistration:
|
||||
return addReadyError({
|
||||
lang(lng_passport_address_registration),
|
||||
lang(lng_passport_address_registration_upload),
|
||||
});
|
||||
case Value::Type::TemporaryRegistration:
|
||||
return addReadyError({
|
||||
lang(lng_passport_address_temporary),
|
||||
lang(lng_passport_address_temporary_upload),
|
||||
});
|
||||
default: Unexpected("Address type in ComputeScopeRow.");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -92,6 +92,9 @@ EditDocumentScheme GetDocumentScheme(
|
|||
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.");
|
||||
}
|
||||
|
@ -174,6 +177,12 @@ EditDocumentScheme GetDocumentScheme(
|
|||
case Value::Type::RentalAgreement:
|
||||
result.scansHeader = lang(lng_passport_address_agreement);
|
||||
break;
|
||||
case Value::Type::PassportRegistration:
|
||||
result.scansHeader = lang(lng_passport_address_registration);
|
||||
break;
|
||||
case Value::Type::TemporaryRegistration:
|
||||
result.scansHeader = lang(lng_passport_address_temporary);
|
||||
break;
|
||||
default:
|
||||
Unexpected("scansType in GetDocumentScheme:Address.");
|
||||
}
|
||||
|
@ -496,28 +505,30 @@ void PanelController::restoreScan(int fileIndex) {
|
|||
_form->restoreScan(_editDocument, fileIndex);
|
||||
}
|
||||
|
||||
void PanelController::uploadSelfie(QByteArray &&content) {
|
||||
void PanelController::uploadSpecialScan(
|
||||
SpecialFile type,
|
||||
QByteArray &&content) {
|
||||
Expects(_editScope != nullptr);
|
||||
Expects(_editDocument != nullptr);
|
||||
Expects(_editScope->selfieRequired);
|
||||
|
||||
_form->uploadSelfie(_editDocument, std::move(content));
|
||||
_form->uploadSpecialScan(_editDocument, type, std::move(content));
|
||||
}
|
||||
|
||||
void PanelController::deleteSelfie() {
|
||||
void PanelController::deleteSpecialScan(SpecialFile type) {
|
||||
Expects(_editScope != nullptr);
|
||||
Expects(_editDocument != nullptr);
|
||||
Expects(_editScope->selfieRequired);
|
||||
|
||||
_form->deleteSelfie(_editDocument);
|
||||
_form->deleteSpecialScan(_editDocument, type);
|
||||
}
|
||||
|
||||
void PanelController::restoreSelfie() {
|
||||
void PanelController::restoreSpecialScan(SpecialFile type) {
|
||||
Expects(_editScope != nullptr);
|
||||
Expects(_editDocument != nullptr);
|
||||
Expects(_editScope->selfieRequired);
|
||||
|
||||
_form->restoreSelfie(_editDocument);
|
||||
_form->restoreSpecialScan(_editDocument, type);
|
||||
}
|
||||
|
||||
rpl::producer<ScanInfo> PanelController::scanUpdated() const {
|
||||
|
@ -566,15 +577,23 @@ ScanInfo PanelController::collectScanInfo(const EditFile &file) const {
|
|||
return formatDownloadText(0, file.fields.size);
|
||||
}
|
||||
}();
|
||||
auto isSelfie = (file.value == _editDocument)
|
||||
&& (_editDocument->selfieInEdit.has_value())
|
||||
&& (&file == &*_editDocument->selfieInEdit);
|
||||
const auto specialType = [&]() -> base::optional<SpecialFile> {
|
||||
if (file.value != _editDocument) {
|
||||
return base::none;
|
||||
}
|
||||
for (const auto &[type, scan] : _editDocument->specialScansInEdit) {
|
||||
if (&file == &scan) {
|
||||
return type;
|
||||
}
|
||||
}
|
||||
return base::none;
|
||||
}();
|
||||
return {
|
||||
FileKey{ file.fields.id, file.fields.dcId },
|
||||
!file.fields.error.isEmpty() ? file.fields.error : status,
|
||||
file.fields.image,
|
||||
file.deleted,
|
||||
isSelfie,
|
||||
specialType,
|
||||
file.fields.error };
|
||||
}
|
||||
|
||||
|
@ -593,8 +612,8 @@ std::vector<ScopeError> PanelController::collectErrors(
|
|||
for (const auto &scan : value->scansInEdit) {
|
||||
addFileError(scan);
|
||||
}
|
||||
if (value->selfieInEdit) {
|
||||
addFileError(*value->selfieInEdit);
|
||||
for (const auto &[type, scan] : value->specialScansInEdit) {
|
||||
addFileError(scan);
|
||||
}
|
||||
for (const auto &[key, value] : value->data.parsedInEdit.fields) {
|
||||
if (!value.error.isEmpty()) {
|
||||
|
@ -635,7 +654,7 @@ bool PanelController::hasValueDocument() const {
|
|||
}
|
||||
return !_editDocument->data.parsed.fields.empty()
|
||||
|| !_editDocument->scans.empty()
|
||||
|| _editDocument->selfie.has_value();
|
||||
|| !_editDocument->specialScans.empty();
|
||||
}
|
||||
|
||||
bool PanelController::hasValueFields() const {
|
||||
|
@ -748,13 +767,15 @@ void PanelController::ensurePanelCreated() {
|
|||
}
|
||||
}
|
||||
|
||||
int PanelController::findNonEmptyIndex(
|
||||
const std::vector<not_null<const Value*>> &files) const {
|
||||
const auto i = ranges::find_if(files, [](not_null<const Value*> file) {
|
||||
return !file->scans.empty();
|
||||
});
|
||||
if (i != end(files)) {
|
||||
return (i - begin(files));
|
||||
int PanelController::findNonEmptyDocumentIndex(const Scope &scope) const {
|
||||
const auto &documents = scope.documents;
|
||||
const auto i = ranges::find_if(
|
||||
documents,
|
||||
[&](not_null<const Value*> document) {
|
||||
return document->scansAreFilled(scope.selfieRequired);
|
||||
});
|
||||
if (i != end(documents)) {
|
||||
return (i - begin(documents));
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
@ -764,14 +785,14 @@ void PanelController::editScope(int index) {
|
|||
Expects(_panel != nullptr);
|
||||
Expects(index >= 0 && index < _scopes.size());
|
||||
|
||||
if (_scopes[index].documents.empty()) {
|
||||
const auto &scope = _scopes[index];
|
||||
if (scope.documents.empty()) {
|
||||
editScope(index, -1);
|
||||
} else {
|
||||
const auto documentIndex = findNonEmptyIndex(
|
||||
_scopes[index].documents);
|
||||
const auto documentIndex = findNonEmptyDocumentIndex(scope);
|
||||
if (documentIndex >= 0) {
|
||||
editScope(index, documentIndex);
|
||||
} else if (_scopes[index].documents.size() > 1) {
|
||||
} else if (scope.documents.size() > 1) {
|
||||
requestScopeFilesType(index);
|
||||
} else {
|
||||
editWithUpload(index, 0);
|
||||
|
@ -802,6 +823,8 @@ void PanelController::requestScopeFilesType(int index) {
|
|||
return lang(lng_passport_identity_card);
|
||||
case Value::Type::DriverLicense:
|
||||
return lang(lng_passport_identity_license);
|
||||
case Value::Type::InternalPassport:
|
||||
return lang(lng_passport_identity_internal);
|
||||
default:
|
||||
Unexpected("IdentityType in requestScopeFilesType");
|
||||
}
|
||||
|
@ -823,6 +846,10 @@ void PanelController::requestScopeFilesType(int index) {
|
|||
return lang(lng_passport_address_statement);
|
||||
case Value::Type::RentalAgreement:
|
||||
return lang(lng_passport_address_agreement);
|
||||
case Value::Type::PassportRegistration:
|
||||
return lang(lng_passport_address_registration);
|
||||
case Value::Type::TemporaryRegistration:
|
||||
return lang(lng_passport_address_temporary);
|
||||
default:
|
||||
Unexpected("AddressType in requestScopeFilesType");
|
||||
}
|
||||
|
@ -842,7 +869,13 @@ void PanelController::editWithUpload(int index, int documentIndex) {
|
|||
EditScans::ChooseScan(_panel.get(), [=](QByteArray &&content) {
|
||||
base::take(_scopeDocumentTypeBox);
|
||||
editScope(index, documentIndex);
|
||||
uploadScan(std::move(content));
|
||||
if (_scopes[index].documents[documentIndex]->requiresSpecialScan(
|
||||
SpecialFile::FrontSide,
|
||||
false)) {
|
||||
uploadSpecialScan(SpecialFile::FrontSide, std::move(content));
|
||||
} else {
|
||||
uploadScan(std::move(content));
|
||||
}
|
||||
}, [=](ReadScanError error) {
|
||||
readScanError(error);
|
||||
});
|
||||
|
@ -897,9 +930,7 @@ void PanelController::editScope(int index, int documentIndex) {
|
|||
_editDocument->data.parsedInEdit,
|
||||
_editDocument->scanMissingError,
|
||||
valueFiles(*_editDocument),
|
||||
(_editScope->selfieRequired
|
||||
? valueSelfie(*_editDocument)
|
||||
: nullptr))
|
||||
valueSpecialFiles(*_editDocument))
|
||||
: object_ptr<PanelEditDocument>(
|
||||
_panel.get(),
|
||||
this,
|
||||
|
@ -1050,13 +1081,26 @@ std::vector<ScanInfo> PanelController::valueFiles(
|
|||
return result;
|
||||
}
|
||||
|
||||
std::unique_ptr<ScanInfo> PanelController::valueSelfie(
|
||||
std::map<SpecialFile, ScanInfo> PanelController::valueSpecialFiles(
|
||||
const Value &value) const {
|
||||
if (value.selfieInEdit) {
|
||||
return std::make_unique<ScanInfo>(
|
||||
collectScanInfo(*value.selfieInEdit));
|
||||
auto result = std::map<SpecialFile, ScanInfo>();
|
||||
const auto types = {
|
||||
SpecialFile::FrontSide,
|
||||
SpecialFile::ReverseSide,
|
||||
SpecialFile::Selfie
|
||||
};
|
||||
for (const auto type : types) {
|
||||
if (value.requiresSpecialScan(type, _editScope->selfieRequired)) {
|
||||
const auto i = value.specialScansInEdit.find(type);
|
||||
const auto j = result.emplace(
|
||||
type,
|
||||
(i != end(value.specialScansInEdit)
|
||||
? collectScanInfo(i->second)
|
||||
: ScanInfo())).first;
|
||||
j->second.special = type;
|
||||
}
|
||||
}
|
||||
return std::make_unique<ScanInfo>();
|
||||
return result;
|
||||
}
|
||||
|
||||
void PanelController::cancelValueEdit() {
|
||||
|
|
|
@ -30,7 +30,7 @@ struct ScanInfo {
|
|||
QString status;
|
||||
QImage thumb;
|
||||
bool deleted = false;
|
||||
bool selfie = false;
|
||||
base::optional<SpecialFile> special;
|
||||
QString error;
|
||||
|
||||
};
|
||||
|
@ -81,9 +81,9 @@ public:
|
|||
void uploadScan(QByteArray &&content);
|
||||
void deleteScan(int fileIndex);
|
||||
void restoreScan(int fileIndex);
|
||||
void uploadSelfie(QByteArray &&content);
|
||||
void deleteSelfie();
|
||||
void restoreSelfie();
|
||||
void uploadSpecialScan(SpecialFile type, QByteArray &&content);
|
||||
void deleteSpecialScan(SpecialFile type);
|
||||
void restoreSpecialScan(SpecialFile type);
|
||||
rpl::producer<ScanInfo> scanUpdated() const;
|
||||
rpl::producer<ScopeError> saveErrors() const;
|
||||
void readScanError(ReadScanError error);
|
||||
|
@ -133,12 +133,12 @@ private:
|
|||
|
||||
void editScope(int index, int documentIndex);
|
||||
void editWithUpload(int index, int documentIndex);
|
||||
int findNonEmptyIndex(
|
||||
const std::vector<not_null<const Value*>> &files) const;
|
||||
int findNonEmptyDocumentIndex(const Scope &scope) const;
|
||||
void requestScopeFilesType(int index);
|
||||
void cancelValueEdit();
|
||||
std::vector<ScanInfo> valueFiles(const Value &value) const;
|
||||
std::unique_ptr<ScanInfo> valueSelfie(const Value &value) const;
|
||||
std::map<SpecialFile, ScanInfo> valueSpecialFiles(
|
||||
const Value &value) const;
|
||||
void processValueSaveFinished(not_null<const Value*> value);
|
||||
void processVerificationNeeded(not_null<const Value*> value);
|
||||
|
||||
|
|
|
@ -213,7 +213,7 @@ PanelEditDocument::PanelEditDocument(
|
|||
const ValueMap &scanData,
|
||||
const QString &missingScansError,
|
||||
std::vector<ScanInfo> &&files,
|
||||
std::unique_ptr<ScanInfo> &&selfie)
|
||||
std::map<SpecialFile, ScanInfo> &&specialFiles)
|
||||
: _controller(controller)
|
||||
, _scheme(std::move(scheme))
|
||||
, _scroll(this, st::passportPanelScroll)
|
||||
|
@ -228,7 +228,7 @@ PanelEditDocument::PanelEditDocument(
|
|||
&scanData,
|
||||
missingScansError,
|
||||
std::move(files),
|
||||
std::move(selfie));
|
||||
std::move(specialFiles));
|
||||
}
|
||||
|
||||
PanelEditDocument::PanelEditDocument(
|
||||
|
@ -245,7 +245,7 @@ PanelEditDocument::PanelEditDocument(
|
|||
this,
|
||||
langFactory(lng_passport_save_value),
|
||||
st::passportPanelSaveValue) {
|
||||
setupControls(data, nullptr, QString(), {}, nullptr);
|
||||
setupControls(data, nullptr, QString(), {}, {});
|
||||
}
|
||||
|
||||
void PanelEditDocument::setupControls(
|
||||
|
@ -253,13 +253,13 @@ void PanelEditDocument::setupControls(
|
|||
const ValueMap *scanData,
|
||||
const QString &missingScansError,
|
||||
std::vector<ScanInfo> &&files,
|
||||
std::unique_ptr<ScanInfo> &&selfie) {
|
||||
std::map<SpecialFile, ScanInfo> &&specialFiles) {
|
||||
const auto inner = setupContent(
|
||||
data,
|
||||
scanData,
|
||||
missingScansError,
|
||||
std::move(files),
|
||||
std::move(selfie));
|
||||
std::move(specialFiles));
|
||||
|
||||
using namespace rpl::mappers;
|
||||
|
||||
|
@ -277,7 +277,7 @@ not_null<Ui::RpWidget*> PanelEditDocument::setupContent(
|
|||
const ValueMap *scanData,
|
||||
const QString &missingScansError,
|
||||
std::vector<ScanInfo> &&files,
|
||||
std::unique_ptr<ScanInfo> &&selfie) {
|
||||
std::map<SpecialFile, ScanInfo> &&specialFiles) {
|
||||
const auto inner = _scroll->setOwnedWidget(
|
||||
object_ptr<Ui::VerticalLayout>(this));
|
||||
_scroll->widthValue(
|
||||
|
@ -285,15 +285,23 @@ not_null<Ui::RpWidget*> PanelEditDocument::setupContent(
|
|||
inner->resizeToWidth(width);
|
||||
}, inner->lifetime());
|
||||
|
||||
if (scanData) {
|
||||
if (!specialFiles.empty()) {
|
||||
_editScans = inner->add(
|
||||
object_ptr<EditScans>(
|
||||
inner,
|
||||
_controller,
|
||||
// _scheme.scansHeader,
|
||||
// missingScansError,
|
||||
// std::move(files),
|
||||
std::move(specialFiles)));
|
||||
} else if (scanData) {
|
||||
_editScans = inner->add(
|
||||
object_ptr<EditScans>(
|
||||
inner,
|
||||
_controller,
|
||||
_scheme.scansHeader,
|
||||
missingScansError,
|
||||
std::move(files),
|
||||
std::move(selfie)));
|
||||
std::move(files)));
|
||||
} else {
|
||||
inner->add(object_ptr<BoxContentDivider>(
|
||||
inner,
|
||||
|
|
|
@ -30,6 +30,7 @@ struct ValueMap;
|
|||
struct ScanInfo;
|
||||
class EditScans;
|
||||
class PanelDetailsRow;
|
||||
enum class SpecialFile;
|
||||
enum class PanelDetailsType;
|
||||
|
||||
struct EditDocumentScheme {
|
||||
|
@ -64,7 +65,7 @@ public:
|
|||
const ValueMap &scanData,
|
||||
const QString &missingScansError,
|
||||
std::vector<ScanInfo> &&files,
|
||||
std::unique_ptr<ScanInfo> &&selfie);
|
||||
std::map<SpecialFile, ScanInfo> &&specialFiles);
|
||||
PanelEditDocument(
|
||||
QWidget *parent,
|
||||
not_null<PanelController*> controller,
|
||||
|
@ -84,13 +85,13 @@ private:
|
|||
const ValueMap *scanData,
|
||||
const QString &missingScansError,
|
||||
std::vector<ScanInfo> &&files,
|
||||
std::unique_ptr<ScanInfo> &&selfie);
|
||||
std::map<SpecialFile, ScanInfo> &&specialFiles);
|
||||
not_null<Ui::RpWidget*> setupContent(
|
||||
const ValueMap &data,
|
||||
const ValueMap *scanData,
|
||||
const QString &missingScansError,
|
||||
std::vector<ScanInfo> &&files,
|
||||
std::unique_ptr<ScanInfo> &&selfie);
|
||||
std::map<SpecialFile, ScanInfo> &&specialFiles);
|
||||
void updateControlsGeometry();
|
||||
|
||||
Result collect() const;
|
||||
|
|
|
@ -106,6 +106,19 @@ private:
|
|||
|
||||
};
|
||||
|
||||
struct EditScans::SpecialScan {
|
||||
SpecialScan(ScanInfo &&file);
|
||||
|
||||
ScanInfo file;
|
||||
QPointer<Ui::SlideWrap<Ui::FlatLabel>> header;
|
||||
QPointer<Ui::VerticalLayout> wrap;
|
||||
base::unique_qptr<Ui::SlideWrap<ScanButton>> row;
|
||||
QPointer<Info::Profile::Button> upload;
|
||||
bool errorShown = false;
|
||||
Animation errorAnimation;
|
||||
|
||||
};
|
||||
|
||||
ScanButton::ScanButton(
|
||||
QWidget *parent,
|
||||
const style::PassportScanRow &st,
|
||||
|
@ -235,21 +248,34 @@ void ScanButton::paintEvent(QPaintEvent *e) {
|
|||
width());
|
||||
}
|
||||
|
||||
EditScans::SpecialScan::SpecialScan(ScanInfo &&file)
|
||||
: file(std::move(file)) {
|
||||
}
|
||||
|
||||
EditScans::EditScans(
|
||||
QWidget *parent,
|
||||
not_null<PanelController*> controller,
|
||||
const QString &header,
|
||||
const QString &errorMissing,
|
||||
std::vector<ScanInfo> &&files,
|
||||
std::unique_ptr<ScanInfo> &&selfie)
|
||||
std::vector<ScanInfo> &&files)
|
||||
: RpWidget(parent)
|
||||
, _controller(controller)
|
||||
, _files(std::move(files))
|
||||
, _selfie(std::move(selfie))
|
||||
, _initialCount(_files.size())
|
||||
, _errorMissing(errorMissing)
|
||||
, _content(this) {
|
||||
setupContent(header);
|
||||
setupScans(header);
|
||||
}
|
||||
|
||||
EditScans::EditScans(
|
||||
QWidget *parent,
|
||||
not_null<PanelController*> controller,
|
||||
std::map<SpecialFile, ScanInfo> &&specialFiles)
|
||||
: RpWidget(parent)
|
||||
, _controller(controller)
|
||||
, _initialCount(-1)
|
||||
, _content(this) {
|
||||
setupSpecialScans(std::move(specialFiles));
|
||||
}
|
||||
|
||||
bool EditScans::uploadedSomeMore() const {
|
||||
|
@ -261,6 +287,13 @@ bool EditScans::uploadedSomeMore() const {
|
|||
}
|
||||
|
||||
base::optional<int> EditScans::validateGetErrorTop() {
|
||||
auto result = base::optional<int>();
|
||||
const auto suggestResult = [&](int value) {
|
||||
if (!result || *result > value) {
|
||||
result = value;
|
||||
}
|
||||
};
|
||||
|
||||
const auto exists = ranges::find_if(
|
||||
_files,
|
||||
[](const ScanInfo &file) { return !file.deleted; }) != end(_files);
|
||||
|
@ -269,11 +302,10 @@ base::optional<int> EditScans::validateGetErrorTop() {
|
|||
[](const ScanInfo &file) { return !file.error.isEmpty(); }
|
||||
) != end(_files);
|
||||
|
||||
auto result = base::optional<int>();
|
||||
if (!exists
|
||||
|| ((errorExists || _uploadMoreError) && !uploadedSomeMore())) {
|
||||
if (_upload && (!exists
|
||||
|| ((errorExists || _uploadMoreError) && !uploadedSomeMore()))) {
|
||||
toggleError(true);
|
||||
result = (_files.size() > 5) ? _upload->y() : _header->y();
|
||||
suggestResult((_files.size() > 5) ? _upload->y() : _header->y());
|
||||
}
|
||||
|
||||
const auto nonDeletedErrorIt = ranges::find_if(
|
||||
|
@ -283,24 +315,21 @@ base::optional<int> EditScans::validateGetErrorTop() {
|
|||
});
|
||||
if (nonDeletedErrorIt != end(_files)) {
|
||||
const auto index = (nonDeletedErrorIt - begin(_files));
|
||||
toggleError(true);
|
||||
if (!result) {
|
||||
result = _rows[index]->y();
|
||||
}
|
||||
// toggleError(true);
|
||||
suggestResult(_rows[index]->y());
|
||||
}
|
||||
if (_selfie
|
||||
&& (!_selfie->key.id
|
||||
|| _selfie->deleted
|
||||
|| !_selfie->error.isEmpty())) {
|
||||
toggleSelfieError(true);
|
||||
if (!result) {
|
||||
result = _selfieHeader->y();
|
||||
for (const auto &[type, scan] : _specialScans) {
|
||||
if (!scan.file.key.id
|
||||
|| scan.file.deleted
|
||||
|| !scan.file.error.isEmpty()) {
|
||||
toggleSpecialScanError(type, true);
|
||||
suggestResult(scan.header->y());
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void EditScans::setupContent(const QString &header) {
|
||||
void EditScans::setupScans(const QString &header) {
|
||||
const auto inner = _content.data();
|
||||
inner->move(0, 0);
|
||||
|
||||
|
@ -356,43 +385,92 @@ void EditScans::setupContent(const QString &header) {
|
|||
inner,
|
||||
st::passportFormDividerHeight));
|
||||
|
||||
if (_selfie) {
|
||||
_selfieHeader = inner->add(
|
||||
init();
|
||||
}
|
||||
|
||||
void EditScans::setupSpecialScans(std::map<SpecialFile, ScanInfo> &&files) {
|
||||
const auto title = [](SpecialFile type) {
|
||||
switch (type) {
|
||||
case SpecialFile::FrontSide:
|
||||
return lang(lng_passport_front_side_title);
|
||||
case SpecialFile::ReverseSide:
|
||||
return lang(lng_passport_reverse_side_title);
|
||||
case SpecialFile::Selfie:
|
||||
return lang(lng_passport_selfie_title);
|
||||
}
|
||||
Unexpected("Type in special row title.");
|
||||
};
|
||||
const auto uploadKey = [](SpecialFile type) {
|
||||
switch (type) {
|
||||
case SpecialFile::FrontSide:
|
||||
return lng_passport_upload_front_side;
|
||||
case SpecialFile::ReverseSide:
|
||||
return lng_passport_upload_reverse_side;
|
||||
case SpecialFile::Selfie:
|
||||
return lng_passport_upload_selfie;
|
||||
}
|
||||
Unexpected("Type in special row upload key.");
|
||||
};
|
||||
const auto description = [](SpecialFile type) {
|
||||
switch (type) {
|
||||
case SpecialFile::FrontSide:
|
||||
return lang(lng_passport_front_side_description);
|
||||
case SpecialFile::ReverseSide:
|
||||
return lang(lng_passport_reverse_side_description);
|
||||
case SpecialFile::Selfie:
|
||||
return lang(lng_passport_selfie_description);
|
||||
}
|
||||
Unexpected("Type in special row upload key.");
|
||||
};
|
||||
|
||||
const auto inner = _content.data();
|
||||
inner->move(0, 0);
|
||||
for (auto &[type, info] : files) {
|
||||
const auto i = _specialScans.emplace(
|
||||
type,
|
||||
SpecialScan(std::move(info))).first;
|
||||
auto &scan = i->second;
|
||||
|
||||
scan.header = inner->add(
|
||||
object_ptr<Ui::SlideWrap<Ui::FlatLabel>>(
|
||||
inner,
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
inner,
|
||||
lang(lng_passport_selfie_title),
|
||||
title(type),
|
||||
Ui::FlatLabel::InitType::Simple,
|
||||
st::passportFormHeader),
|
||||
st::passportUploadHeaderPadding));
|
||||
_selfieHeader->toggle(_selfie->key.id != 0, anim::type::instant);
|
||||
_selfieWrap = inner->add(object_ptr<Ui::VerticalLayout>(inner));
|
||||
if (_selfie->key.id) {
|
||||
createSelfieRow(*_selfie);
|
||||
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);
|
||||
}
|
||||
_selfieUpload = inner->add(
|
||||
scan.upload = inner->add(
|
||||
object_ptr<Info::Profile::Button>(
|
||||
inner,
|
||||
Lang::Viewer(
|
||||
lng_passport_upload_selfie
|
||||
uploadKey(type)
|
||||
) | Info::Profile::ToUpperValue(),
|
||||
st::passportUploadButton),
|
||||
st::passportUploadButtonPadding);
|
||||
_selfieUpload->addClickHandler([=] {
|
||||
chooseSelfie();
|
||||
scan.upload->addClickHandler([=] {
|
||||
chooseSpecialScan(type);
|
||||
});
|
||||
|
||||
inner->add(object_ptr<PanelLabel>(
|
||||
inner,
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
_content,
|
||||
lang(lng_passport_selfie_description),
|
||||
description(type),
|
||||
Ui::FlatLabel::InitType::Simple,
|
||||
st::passportFormLabel),
|
||||
st::passportFormLabelPadding));
|
||||
}
|
||||
|
||||
init();
|
||||
}
|
||||
|
||||
void EditScans::init() {
|
||||
_controller->scanUpdated(
|
||||
) | rpl::start_with_next([=](ScanInfo &&info) {
|
||||
updateScan(std::move(info));
|
||||
|
@ -410,8 +488,8 @@ void EditScans::setupContent(const QString &header) {
|
|||
}
|
||||
|
||||
void EditScans::updateScan(ScanInfo &&info) {
|
||||
if (info.selfie) {
|
||||
updateSelfie(std::move(info));
|
||||
if (info.special) {
|
||||
updateSpecialScan(*info.special, std::move(info));
|
||||
return;
|
||||
}
|
||||
const auto i = ranges::find(_files, info.key, [](const ScanInfo &file) {
|
||||
|
@ -438,24 +516,26 @@ void EditScans::updateScan(ScanInfo &&info) {
|
|||
}
|
||||
}
|
||||
|
||||
void EditScans::updateSelfie(ScanInfo &&info) {
|
||||
void EditScans::updateSpecialScan(SpecialFile type, ScanInfo &&info) {
|
||||
Expects(info.key.id != 0);
|
||||
|
||||
if (!_selfie) {
|
||||
const auto i = _specialScans.find(type);
|
||||
if (i == end(_specialScans)) {
|
||||
return;
|
||||
}
|
||||
if (_selfie->key.id) {
|
||||
updateFileRow(_selfieRow->entity(), info);
|
||||
auto &scan = i->second;
|
||||
if (scan.file.key.id) {
|
||||
updateFileRow(scan.row->entity(), info);
|
||||
if (!info.deleted) {
|
||||
hideSelfieError();
|
||||
hideSpecialScanError(type);
|
||||
}
|
||||
} else {
|
||||
createSelfieRow(info);
|
||||
_selfieWrap->resizeToWidth(width());
|
||||
_selfieRow->show(anim::type::normal);
|
||||
_selfieHeader->show(anim::type::normal);
|
||||
createSpecialScanRow(scan, info);
|
||||
scan.wrap->resizeToWidth(width());
|
||||
scan.row->show(anim::type::normal);
|
||||
scan.header->show(anim::type::normal);
|
||||
}
|
||||
*_selfie = std::move(info);
|
||||
scan.file = std::move(info);
|
||||
}
|
||||
|
||||
void EditScans::updateFileRow(
|
||||
|
@ -468,24 +548,37 @@ void EditScans::updateFileRow(
|
|||
};
|
||||
|
||||
|
||||
void EditScans::createSelfieRow(const ScanInfo &info) {
|
||||
_selfieRow = createScan(
|
||||
_selfieWrap,
|
||||
info,
|
||||
lang(lng_passport_selfie_name));
|
||||
const auto row = _selfieRow->entity();
|
||||
void EditScans::createSpecialScanRow(
|
||||
SpecialScan &scan,
|
||||
const ScanInfo &info) {
|
||||
Expects(scan.file.special.has_value());
|
||||
|
||||
const auto type = *scan.file.special;
|
||||
const auto name = [&] {
|
||||
switch (type) {
|
||||
case SpecialFile::FrontSide:
|
||||
return lang(lng_passport_front_side_name);
|
||||
case SpecialFile::ReverseSide:
|
||||
return lang(lng_passport_reverse_side_name);
|
||||
case SpecialFile::Selfie:
|
||||
return lang(lng_passport_selfie_name);
|
||||
}
|
||||
Unexpected("Type in special file name.");
|
||||
}();
|
||||
scan.row = createScan(scan.wrap, info, name);
|
||||
const auto row = scan.row->entity();
|
||||
|
||||
row->deleteClicks(
|
||||
) | rpl::start_with_next([=] {
|
||||
_controller->deleteSelfie();
|
||||
_controller->deleteSpecialScan(type);
|
||||
}, row->lifetime());
|
||||
|
||||
row->restoreClicks(
|
||||
) | rpl::start_with_next([=] {
|
||||
_controller->restoreSelfie();
|
||||
_controller->restoreSpecialScan(type);
|
||||
}, row->lifetime());
|
||||
|
||||
hideSelfieError();
|
||||
hideSpecialScanError(type);
|
||||
}
|
||||
|
||||
void EditScans::pushScan(const ScanInfo &info) {
|
||||
|
@ -541,9 +634,9 @@ void EditScans::chooseScan() {
|
|||
});
|
||||
}
|
||||
|
||||
void EditScans::chooseSelfie() {
|
||||
void EditScans::chooseSpecialScan(SpecialFile type) {
|
||||
ChooseScan(this, [=](QByteArray &&content) {
|
||||
_controller->uploadSelfie(std::move(content));
|
||||
_controller->uploadSpecialScan(type, std::move(content));
|
||||
}, [=](ReadScanError error) {
|
||||
_controller->readScanError(error);
|
||||
});
|
||||
|
@ -643,32 +736,42 @@ void EditScans::errorAnimationCallback() {
|
|||
}
|
||||
}
|
||||
|
||||
void EditScans::hideSelfieError() {
|
||||
toggleSelfieError(false);
|
||||
void EditScans::hideSpecialScanError(SpecialFile type) {
|
||||
toggleSpecialScanError(type, false);
|
||||
}
|
||||
|
||||
void EditScans::toggleSelfieError(bool shown) {
|
||||
if (_selfieErrorShown != shown) {
|
||||
_selfieErrorShown = shown;
|
||||
_selfieErrorAnimation.start(
|
||||
[=] { selfieErrorAnimationCallback(); },
|
||||
_selfieErrorShown ? 0. : 1.,
|
||||
_selfieErrorShown ? 1. : 0.,
|
||||
auto EditScans::findSpecialScan(SpecialFile type) -> SpecialScan& {
|
||||
const auto i = _specialScans.find(type);
|
||||
Assert(i != end(_specialScans));
|
||||
return i->second;
|
||||
}
|
||||
|
||||
void EditScans::toggleSpecialScanError(SpecialFile type, bool shown) {
|
||||
auto &scan = findSpecialScan(type);
|
||||
if (scan.errorShown != shown) {
|
||||
scan.errorShown = shown;
|
||||
scan.errorAnimation.start(
|
||||
[=] { specialScanErrorAnimationCallback(type); },
|
||||
scan.errorShown ? 0. : 1.,
|
||||
scan.errorShown ? 1. : 0.,
|
||||
st::passportDetailsField.duration);
|
||||
}
|
||||
}
|
||||
|
||||
void EditScans::selfieErrorAnimationCallback() {
|
||||
const auto error = _selfieErrorAnimation.current(
|
||||
_selfieErrorShown ? 1. : 0.);
|
||||
void EditScans::specialScanErrorAnimationCallback(SpecialFile type) {
|
||||
auto &scan = findSpecialScan(type);
|
||||
const auto error = scan.errorAnimation.current(
|
||||
scan.errorShown ? 1. : 0.);
|
||||
if (error == 0.) {
|
||||
_selfieUpload->setColorOverride(base::none);
|
||||
scan.upload->setColorOverride(base::none);
|
||||
} else {
|
||||
_selfieUpload->setColorOverride(anim::color(
|
||||
scan.upload->setColorOverride(anim::color(
|
||||
st::passportUploadButton.textFg,
|
||||
st::boxTextFgError,
|
||||
error));
|
||||
}
|
||||
}
|
||||
|
||||
EditScans::~EditScans() = default;
|
||||
|
||||
} // namespace Passport
|
||||
|
|
|
@ -26,6 +26,7 @@ class Button;
|
|||
|
||||
namespace Passport {
|
||||
|
||||
enum class SpecialFile;
|
||||
class PanelController;
|
||||
class ScanButton;
|
||||
struct ScanInfo;
|
||||
|
@ -44,8 +45,11 @@ public:
|
|||
not_null<PanelController*> controller,
|
||||
const QString &header,
|
||||
const QString &errorMissing,
|
||||
std::vector<ScanInfo> &&files,
|
||||
std::unique_ptr<ScanInfo> &&selfie);
|
||||
std::vector<ScanInfo> &&files);
|
||||
EditScans(
|
||||
QWidget *parent,
|
||||
not_null<PanelController*> controller,
|
||||
std::map<SpecialFile, ScanInfo> &&specialFiles);
|
||||
|
||||
base::optional<int> validateGetErrorTop();
|
||||
|
||||
|
@ -54,21 +58,31 @@ public:
|
|||
base::lambda<void(QByteArray&&)> doneCallback,
|
||||
base::lambda<void(ReadScanError)> errorCallback);
|
||||
|
||||
~EditScans();
|
||||
|
||||
private:
|
||||
void setupContent(const QString &header);
|
||||
struct SpecialScan;
|
||||
|
||||
void setupScans(const QString &header);
|
||||
void setupSpecialScans(std::map<SpecialFile, ScanInfo> &&files);
|
||||
void init();
|
||||
|
||||
void chooseScan();
|
||||
void chooseSelfie();
|
||||
void chooseSpecialScan(SpecialFile type);
|
||||
void updateScan(ScanInfo &&info);
|
||||
void updateSelfie(ScanInfo &&info);
|
||||
void updateSpecialScan(SpecialFile type, ScanInfo &&info);
|
||||
void updateFileRow(
|
||||
not_null<ScanButton*> button,
|
||||
const ScanInfo &info);
|
||||
void pushScan(const ScanInfo &info);
|
||||
void createSelfieRow(const ScanInfo &info);
|
||||
void createSpecialScanRow(
|
||||
SpecialScan &scan,
|
||||
const ScanInfo &info);
|
||||
base::unique_qptr<Ui::SlideWrap<ScanButton>> createScan(
|
||||
not_null<Ui::VerticalLayout*> parent,
|
||||
const ScanInfo &info,
|
||||
const QString &name);
|
||||
SpecialScan &findSpecialScan(SpecialFile type);
|
||||
|
||||
rpl::producer<QString> uploadButtonText() const;
|
||||
|
||||
|
@ -77,13 +91,12 @@ private:
|
|||
void errorAnimationCallback();
|
||||
bool uploadedSomeMore() const;
|
||||
|
||||
void toggleSelfieError(bool shown);
|
||||
void hideSelfieError();
|
||||
void selfieErrorAnimationCallback();
|
||||
void toggleSpecialScanError(SpecialFile type, bool shown);
|
||||
void hideSpecialScanError(SpecialFile type);
|
||||
void specialScanErrorAnimationCallback(SpecialFile type);
|
||||
|
||||
not_null<PanelController*> _controller;
|
||||
std::vector<ScanInfo> _files;
|
||||
std::unique_ptr<ScanInfo> _selfie;
|
||||
int _initialCount = 0;
|
||||
QString _errorMissing;
|
||||
|
||||
|
@ -98,12 +111,7 @@ private:
|
|||
bool _errorShown = false;
|
||||
Animation _errorAnimation;
|
||||
|
||||
QPointer<Ui::SlideWrap<Ui::FlatLabel>> _selfieHeader;
|
||||
QPointer<Ui::VerticalLayout> _selfieWrap;
|
||||
base::unique_qptr<Ui::SlideWrap<ScanButton>> _selfieRow;
|
||||
QPointer<Info::Profile::Button> _selfieUpload;
|
||||
bool _selfieErrorShown = false;
|
||||
Animation _selfieErrorAnimation;
|
||||
std::map<SpecialFile, SpecialScan> _specialScans;
|
||||
|
||||
};
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue