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