mirror of
https://github.com/telegramdesktop/tdesktop
synced 2025-02-19 06:26:55 +00:00
Start improved passport support.
This commit is contained in:
parent
bdab477040
commit
3c43f621ce
@ -1668,7 +1668,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||
"lng_passport_error_cant_read" = "Can't read this file. Please choose an image.";
|
||||
"lng_passport_bad_name" = "Please use latin characters only.";
|
||||
"lng_passport_wait_upload" = "Please wait while upload is finished.";
|
||||
"lng_passport_fix_errors" = "Please correct errors.";
|
||||
"lng_passport_app_out_of_date" = "Sorry, your Telegram app is out of date and can't handle this request. Please update Telegram.";
|
||||
|
||||
"lng_export_title" = "Export Personal Data";
|
||||
|
@ -995,9 +995,9 @@ secureValueTypeTemporaryRegistration#ea02ec33 = SecureValueType;
|
||||
secureValueTypePhone#b320aadb = SecureValueType;
|
||||
secureValueTypeEmail#8e3ca7ee = SecureValueType;
|
||||
|
||||
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;
|
||||
secureValue#187fa0ca flags:# type:SecureValueType data:flags.0?SecureData front_side:flags.1?SecureFile reverse_side:flags.2?SecureFile selfie:flags.3?SecureFile translation:flags.6?Vector<SecureFile> files:flags.4?Vector<SecureFile> plain_data:flags.5?SecurePlainData hash:bytes = SecureValue;
|
||||
|
||||
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;
|
||||
inputSecureValue#db21d0a7 flags:# type:SecureValueType data:flags.0?SecureData front_side:flags.1?InputSecureFile reverse_side:flags.2?InputSecureFile selfie:flags.3?InputSecureFile translation:flags.6?Vector<InputSecureFile> files:flags.4?Vector<InputSecureFile> plain_data:flags.5?SecurePlainData = InputSecureValue;
|
||||
|
||||
secureValueHash#ed1ecdb0 type:SecureValueType hash:bytes = SecureValueHash;
|
||||
|
||||
@ -1007,10 +1007,13 @@ secureValueErrorReverseSide#868a2aa5 type:SecureValueType file_hash:bytes text:s
|
||||
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;
|
||||
secureValueError#869d758f type:SecureValueType hash:bytes text:string = SecureValueError;
|
||||
secureValueErrorTranslationFile#a1144770 type:SecureValueType file_hash:bytes text:string = SecureValueError;
|
||||
secureValueErrorTranslationFiles#34636dd8 type:SecureValueType file_hash:Vector<bytes> text:string = SecureValueError;
|
||||
|
||||
secureCredentialsEncrypted#33f0ea47 data:bytes hash:bytes secret:bytes = SecureCredentialsEncrypted;
|
||||
|
||||
account.authorizationForm#cb976d53 flags:# selfie_required:flags.1?true required_types:Vector<SecureValueType> values:Vector<SecureValue> errors:Vector<SecureValueError> users:Vector<User> privacy_policy_url:flags.0?string = account.AuthorizationForm;
|
||||
account.authorizationForm#ad2e1cd8 flags:# required_types:Vector<SecureRequiredType> values:Vector<SecureValue> errors:Vector<SecureValueError> users:Vector<User> privacy_policy_url:flags.0?string = account.AuthorizationForm;
|
||||
|
||||
account.sentEmailCode#811f854f email_pattern:string length:int = account.SentEmailCode;
|
||||
|
||||
@ -1033,6 +1036,9 @@ secureSecretSettings#1527bcac secure_algo:SecurePasswordKdfAlgo secure_secret:by
|
||||
inputCheckPasswordEmpty#9880f658 = InputCheckPasswordSRP;
|
||||
inputCheckPasswordSRP#d27ff082 srp_id:long A:bytes M1:bytes = InputCheckPasswordSRP;
|
||||
|
||||
secureRequiredType#829d99da flags:# native_names:flags.0?true selfie_required:flags.1?true translation_required:flags.2?true type:SecureValueType = SecureRequiredType;
|
||||
secureRequiredTypeOneOf#27477b4 types:Vector<SecureRequiredType> = SecureRequiredType;
|
||||
|
||||
---functions---
|
||||
|
||||
invokeAfterMsg#cb9f372d {X:Type} msg_id:long query:!X = X;
|
||||
@ -1321,4 +1327,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 84
|
||||
// LAYER 85
|
||||
|
@ -132,7 +132,30 @@ MTPSecureValueType ConvertType(Value::Type type) {
|
||||
return MTP_secureValueTypeEmail();
|
||||
}
|
||||
Unexpected("Type in FormController::submit.");
|
||||
};
|
||||
}
|
||||
|
||||
void CollectToRequestedRow(
|
||||
RequestedRow &row,
|
||||
const MTPSecureRequiredType &data) {
|
||||
data.match([&](const MTPDsecureRequiredType &data) {
|
||||
row.values.emplace_back(ConvertType(data.vtype));
|
||||
auto &value = row.values.back();
|
||||
value.selfieRequired = data.is_selfie_required();
|
||||
value.translationRequired = data.is_translation_required();
|
||||
value.nativeNames = data.is_native_names();
|
||||
}, [&](const MTPDsecureRequiredTypeOneOf &data) {
|
||||
row.values.reserve(row.values.size() + data.vtypes.v.size());
|
||||
for (const auto &one : data.vtypes.v) {
|
||||
CollectToRequestedRow(row, one);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
RequestedRow CollectRequestedRow(const MTPSecureRequiredType &data) {
|
||||
auto result = RequestedRow();
|
||||
CollectToRequestedRow(result, data);
|
||||
return result;
|
||||
}
|
||||
|
||||
QJsonObject GetJSONFromMap(
|
||||
const std::map<QString, bytes::const_span> &map) {
|
||||
@ -257,12 +280,13 @@ UploadScanData *UploadScanDataPointer::operator->() const {
|
||||
return _value.get();
|
||||
}
|
||||
|
||||
RequestedValue::RequestedValue(Value::Type type) : type(type) {
|
||||
}
|
||||
|
||||
Value::Value(Type type) : type(type) {
|
||||
}
|
||||
|
||||
bool Value::requiresSpecialScan(
|
||||
SpecialFile type,
|
||||
bool selfieRequired) const {
|
||||
bool Value::requiresSpecialScan(SpecialFile type) const {
|
||||
switch (type) {
|
||||
case SpecialFile::FrontSide:
|
||||
return (this->type == Type::Passport)
|
||||
@ -278,9 +302,11 @@ bool Value::requiresSpecialScan(
|
||||
Unexpected("Special scan type in requiresSpecialScan.");
|
||||
}
|
||||
|
||||
bool Value::scansAreFilled(bool selfieRequired) const {
|
||||
if (!requiresSpecialScan(SpecialFile::FrontSide, selfieRequired)) {
|
||||
return !scans.empty();
|
||||
bool Value::scansAreFilled() const {
|
||||
if (!requiresSpecialScan(SpecialFile::FrontSide) && scans.empty()) {
|
||||
return false;
|
||||
} else if (translationRequired && translations.empty()) {
|
||||
return false;
|
||||
}
|
||||
const auto types = {
|
||||
SpecialFile::FrontSide,
|
||||
@ -288,14 +314,13 @@ bool Value::scansAreFilled(bool selfieRequired) const {
|
||||
SpecialFile::Selfie
|
||||
};
|
||||
for (const auto type : types) {
|
||||
if (requiresSpecialScan(type, selfieRequired)
|
||||
if (requiresSpecialScan(type)
|
||||
&& (specialScans.find(type) == end(specialScans))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
FormController::FormController(
|
||||
not_null<Window::Controller*> controller,
|
||||
@ -346,8 +371,7 @@ auto FormController::prepareFinalData() -> FinalData {
|
||||
object.insert("files", files);
|
||||
}
|
||||
for (const auto &[type, scan] : value->specialScans) {
|
||||
const auto selfieRequired = _form.identitySelfieRequired;
|
||||
if (value->requiresSpecialScan(type, selfieRequired)) {
|
||||
if (value->requiresSpecialScan(type)) {
|
||||
object.insert(
|
||||
SpecialScanCredentialsKey(type),
|
||||
GetJSONFromFile(scan));
|
||||
@ -364,17 +388,21 @@ auto FormController::prepareFinalData() -> FinalData {
|
||||
addValueToJSON(key, value);
|
||||
}
|
||||
};
|
||||
const auto scopes = ComputeScopes(this);
|
||||
const auto scopes = ComputeScopes(_form);
|
||||
for (const auto &scope : scopes) {
|
||||
const auto row = ComputeScopeRow(scope);
|
||||
if (row.ready.isEmpty() || !row.error.isEmpty()) {
|
||||
errors.push_back(scope.fields);
|
||||
errors.push_back(scope.details
|
||||
? scope.details
|
||||
: scope.documents[0].get());
|
||||
continue;
|
||||
}
|
||||
addValue(scope.fields);
|
||||
if (scope.details) {
|
||||
addValue(scope.details);
|
||||
}
|
||||
if (!scope.documents.empty()) {
|
||||
for (const auto &document : scope.documents) {
|
||||
if (document->scansAreFilled(scope.selfieRequired)) {
|
||||
if (document->scansAreFilled()) {
|
||||
addValue(document);
|
||||
break;
|
||||
}
|
||||
@ -803,6 +831,7 @@ void FormController::decryptValues() {
|
||||
}
|
||||
|
||||
void FormController::fillErrors() {
|
||||
// #TODO passport filter by flags
|
||||
const auto find = [&](const MTPSecureValueType &type) -> Value* {
|
||||
const auto converted = ConvertType(type);
|
||||
const auto i = _form.values.find(ConvertType(type));
|
||||
@ -835,43 +864,44 @@ void FormController::fillErrors() {
|
||||
}
|
||||
};
|
||||
for (const auto &error : _form.pendingErrors) {
|
||||
switch (error.type()) {
|
||||
case mtpc_secureValueErrorData: {
|
||||
const auto &data = error.c_secureValueErrorData();
|
||||
error.match([&](const MTPDsecureValueError &data) {
|
||||
if (const auto value = find(data.vtype)) {
|
||||
value->error = qs(data.vtext);
|
||||
}
|
||||
}, [&](const MTPDsecureValueErrorData &data) {
|
||||
if (const auto value = find(data.vtype)) {
|
||||
const auto key = qs(data.vfield);
|
||||
value->data.parsed.fields[key].error = qs(data.vtext);
|
||||
}
|
||||
} break;
|
||||
case mtpc_secureValueErrorFile: {
|
||||
const auto &data = error.c_secureValueErrorFile();
|
||||
}, [&](const MTPDsecureValueErrorFile &data) {
|
||||
const auto hash = bytes::make_span(data.vfile_hash.v);
|
||||
if (const auto value = find(data.vtype)) {
|
||||
if (const auto file = scan(*value, hash)) {
|
||||
file->error = qs(data.vtext);
|
||||
}
|
||||
}
|
||||
} break;
|
||||
case mtpc_secureValueErrorFiles: {
|
||||
const auto &data = error.c_secureValueErrorFiles();
|
||||
}, [&](const MTPDsecureValueErrorFiles &data) {
|
||||
if (const auto value = find(data.vtype)) {
|
||||
value->scanMissingError = qs(data.vtext);
|
||||
}
|
||||
} break;
|
||||
case mtpc_secureValueErrorFrontSide: {
|
||||
const auto &data = error.c_secureValueErrorFrontSide();
|
||||
}, [&](const MTPDsecureValueErrorTranslationFile &data) {
|
||||
const auto hash = bytes::make_span(data.vfile_hash.v);
|
||||
if (const auto value = find(data.vtype)) {
|
||||
if (const auto file = scan(*value, hash)) { // #TODO passport
|
||||
file->error = qs(data.vtext);
|
||||
}
|
||||
}
|
||||
}, [&](const MTPDsecureValueErrorTranslationFiles &data) {
|
||||
if (const auto value = find(data.vtype)) {
|
||||
value->translationMissingError = qs(data.vtext);
|
||||
}
|
||||
}, [&](const MTPDsecureValueErrorFrontSide &data) {
|
||||
setSpecialScanError(SpecialFile::FrontSide, data);
|
||||
} break;
|
||||
case mtpc_secureValueErrorReverseSide: {
|
||||
const auto &data = error.c_secureValueErrorReverseSide();
|
||||
}, [&](const MTPDsecureValueErrorReverseSide &data) {
|
||||
setSpecialScanError(SpecialFile::ReverseSide, data);
|
||||
} break;
|
||||
case mtpc_secureValueErrorSelfie: {
|
||||
const auto &data = error.c_secureValueErrorSelfie();
|
||||
}, [&](const MTPDsecureValueErrorSelfie &data) {
|
||||
setSpecialScanError(SpecialFile::Selfie, data);
|
||||
} break;
|
||||
default: Unexpected("Error type in FormController::fillErrors.");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@ -1312,9 +1342,13 @@ void FormController::startValueEdit(not_null<const Value*> value) {
|
||||
for (auto &scan : nonconst->scans) {
|
||||
loadFile(scan);
|
||||
}
|
||||
if (nonconst->translationRequired) {
|
||||
for (auto &scan : nonconst->translations) {
|
||||
loadFile(scan);
|
||||
}
|
||||
}
|
||||
for (auto &[type, scan] : nonconst->specialScans) {
|
||||
const auto selfieRequired = _form.identitySelfieRequired;
|
||||
if (nonconst->requiresSpecialScan(type, selfieRequired)) {
|
||||
if (nonconst->requiresSpecialScan(type)) {
|
||||
loadFile(scan);
|
||||
}
|
||||
}
|
||||
@ -1324,6 +1358,12 @@ void FormController::startValueEdit(not_null<const Value*> value) {
|
||||
return EditFile(nonconst, file, nullptr);
|
||||
}) | ranges::to_vector;
|
||||
|
||||
nonconst->translationsInEdit = ranges::view::all(
|
||||
nonconst->translations
|
||||
) | ranges::view::transform([=](const File &file) {
|
||||
return EditFile(nonconst, file, nullptr);
|
||||
}) | ranges::to_vector;
|
||||
|
||||
nonconst->specialScansInEdit.clear();
|
||||
for (const auto &[type, scan] : nonconst->specialScans) {
|
||||
nonconst->specialScansInEdit.emplace(type, EditFile(
|
||||
@ -1597,7 +1637,7 @@ void FormController::saveEncryptedValue(not_null<Value*> value) {
|
||||
return;
|
||||
}
|
||||
|
||||
const auto inputFile = [](const EditFile &file) {
|
||||
const auto wrapFile = [](const EditFile &file) {
|
||||
if (const auto uploadData = file.uploadData.get()) {
|
||||
return MTP_inputSecureFileUploaded(
|
||||
MTP_long(file.fields.id),
|
||||
@ -1611,13 +1651,22 @@ void FormController::saveEncryptedValue(not_null<Value*> value) {
|
||||
MTP_long(file.fields.accessHash));
|
||||
};
|
||||
|
||||
auto inputFiles = QVector<MTPInputSecureFile>();
|
||||
inputFiles.reserve(value->scansInEdit.size());
|
||||
auto files = QVector<MTPInputSecureFile>();
|
||||
files.reserve(value->scansInEdit.size());
|
||||
for (const auto &scan : value->scansInEdit) {
|
||||
if (scan.deleted) {
|
||||
continue;
|
||||
}
|
||||
inputFiles.push_back(inputFile(scan));
|
||||
files.push_back(wrapFile(scan));
|
||||
}
|
||||
|
||||
auto translations = QVector<MTPInputSecureFile>();
|
||||
translations.reserve(value->translationsInEdit.size());
|
||||
for (const auto &scan : value->translationsInEdit) {
|
||||
if (scan.deleted) {
|
||||
continue;
|
||||
}
|
||||
translations.push_back(wrapFile(scan));
|
||||
}
|
||||
|
||||
if (value->data.secret.empty()) {
|
||||
@ -1639,7 +1688,7 @@ void FormController::saveEncryptedValue(not_null<Value*> value) {
|
||||
const auto specialFile = [&](SpecialFile type) {
|
||||
const auto i = value->specialScansInEdit.find(type);
|
||||
return (i != end(value->specialScansInEdit) && !i->second.deleted)
|
||||
? inputFile(i->second)
|
||||
? wrapFile(i->second)
|
||||
: MTPInputSecureFile();
|
||||
};
|
||||
const auto frontSide = specialFile(SpecialFile::FrontSide);
|
||||
@ -1659,6 +1708,9 @@ void FormController::saveEncryptedValue(not_null<Value*> value) {
|
||||
| (hasSpecialFile(SpecialFile::Selfie)
|
||||
? MTPDinputSecureValue::Flag::f_selfie
|
||||
: MTPDinputSecureValue::Flag(0))
|
||||
| (value->translationsInEdit.empty()
|
||||
? MTPDinputSecureValue::Flag(0)
|
||||
: MTPDinputSecureValue::Flag::f_translation)
|
||||
| (value->scansInEdit.empty()
|
||||
? MTPDinputSecureValue::Flag(0)
|
||||
: MTPDinputSecureValue::Flag::f_files);
|
||||
@ -1674,7 +1726,8 @@ void FormController::saveEncryptedValue(not_null<Value*> value) {
|
||||
frontSide,
|
||||
reverseSide,
|
||||
selfie,
|
||||
MTP_vector<MTPInputSecureFile>(inputFiles),
|
||||
MTP_vector<MTPInputSecureFile>(translations),
|
||||
MTP_vector<MTPInputSecureFile>(files),
|
||||
MTPSecurePlainData()));
|
||||
}
|
||||
|
||||
@ -1704,6 +1757,7 @@ void FormController::savePlainTextValue(not_null<Value*> value) {
|
||||
MTPInputSecureFile(),
|
||||
MTPInputSecureFile(),
|
||||
MTPVector<MTPInputSecureFile>(),
|
||||
MTPVector<MTPInputSecureFile>(),
|
||||
plain(MTP_string(text))));
|
||||
}
|
||||
|
||||
@ -2157,13 +2211,14 @@ auto FormController::findFile(const FileKey &key)
|
||||
}
|
||||
|
||||
void FormController::formDone(const MTPaccount_AuthorizationForm &result) {
|
||||
parseForm(result);
|
||||
if (!_passwordRequestId) {
|
||||
if (!parseForm(result)) {
|
||||
_view->showCriticalError(lang(lng_passport_form_error));
|
||||
} else if (!_passwordRequestId) {
|
||||
showForm();
|
||||
}
|
||||
}
|
||||
|
||||
void FormController::parseForm(const MTPaccount_AuthorizationForm &result) {
|
||||
bool FormController::parseForm(const MTPaccount_AuthorizationForm &result) {
|
||||
Expects(result.type() == mtpc_account_authorizationForm);
|
||||
|
||||
const auto &data = result.c_account_authorizationForm();
|
||||
@ -2177,21 +2232,34 @@ void FormController::parseForm(const MTPaccount_AuthorizationForm &result) {
|
||||
if (alreadyIt != _form.values.end()) {
|
||||
LOG(("API Error: Two values for type %1 in authorization form"
|
||||
"%1").arg(int(type)));
|
||||
continue;
|
||||
return false;
|
||||
}
|
||||
_form.values.emplace(type, std::move(parsed));
|
||||
}
|
||||
_form.identitySelfieRequired = data.is_selfie_required();
|
||||
if (data.has_privacy_policy_url()) {
|
||||
_form.privacyPolicyUrl = qs(data.vprivacy_policy_url);
|
||||
}
|
||||
for (const auto &required : data.vrequired_types.v) {
|
||||
const auto type = ConvertType(required);
|
||||
_form.request.push_back(type);
|
||||
_form.values.emplace(type, Value(type));
|
||||
const auto row = CollectRequestedRow(required);
|
||||
for (const auto value : row.values) {
|
||||
const auto [i, ok] = _form.values.emplace(
|
||||
value.type,
|
||||
Value(value.type));
|
||||
i->second.selfieRequired = value.selfieRequired;
|
||||
i->second.translationRequired = value.translationRequired;
|
||||
i->second.nativeNames = value.nativeNames;
|
||||
}
|
||||
_form.request.push_back(row.values
|
||||
| ranges::view::transform([](const RequestedValue &value) {
|
||||
return value.type;
|
||||
}) | ranges::to_vector);
|
||||
}
|
||||
if (!ValidateForm(_form)) {
|
||||
return false;
|
||||
}
|
||||
_bot = App::userLoaded(_request.botId);
|
||||
_form.pendingErrors = data.verrors.v;
|
||||
return true;
|
||||
}
|
||||
|
||||
void FormController::formFail(const QString &error) {
|
||||
|
@ -168,31 +168,52 @@ struct Value {
|
||||
Value(Value &&other) = default;
|
||||
Value &operator=(Value &&other) = default;
|
||||
|
||||
bool requiresSpecialScan(SpecialFile type, bool selfieRequired) const;
|
||||
bool scansAreFilled(bool selfieRequired) const;
|
||||
bool requiresSpecialScan(SpecialFile type) const;
|
||||
bool scansAreFilled() const;
|
||||
|
||||
Type type;
|
||||
ValueData data;
|
||||
std::vector<File> scans;
|
||||
std::vector<File> translations;
|
||||
std::map<SpecialFile, File> specialScans;
|
||||
QString error;
|
||||
QString scanMissingError;
|
||||
QString translationMissingError;
|
||||
std::vector<EditFile> scansInEdit;
|
||||
std::vector<EditFile> translationsInEdit;
|
||||
std::map<SpecialFile, EditFile> specialScansInEdit;
|
||||
Verification verification;
|
||||
bytes::vector submitHash;
|
||||
|
||||
bool selfieRequired = false;
|
||||
bool translationRequired = false;
|
||||
bool nativeNames = false;
|
||||
|
||||
int editScreens = 0;
|
||||
mtpRequestId saveRequestId = 0;
|
||||
|
||||
};
|
||||
|
||||
struct RequestedValue {
|
||||
explicit RequestedValue(Value::Type type);
|
||||
|
||||
Value::Type type;
|
||||
bool selfieRequired = false;
|
||||
bool translationRequired = false;
|
||||
bool nativeNames = false;
|
||||
};
|
||||
|
||||
struct RequestedRow {
|
||||
std::vector<RequestedValue> values;
|
||||
};
|
||||
|
||||
struct Form {
|
||||
using Request = std::vector<std::vector<Value::Type>>;
|
||||
|
||||
std::map<Value::Type, Value> values;
|
||||
std::vector<Value::Type> request;
|
||||
bool identitySelfieRequired = false;
|
||||
Request request;
|
||||
QString privacyPolicyUrl;
|
||||
QVector<MTPSecureValueError> pendingErrors;
|
||||
|
||||
};
|
||||
|
||||
struct PasswordSettings {
|
||||
@ -333,7 +354,7 @@ private:
|
||||
|
||||
void formDone(const MTPaccount_AuthorizationForm &result);
|
||||
void formFail(const QString &error);
|
||||
void parseForm(const MTPaccount_AuthorizationForm &result);
|
||||
bool parseForm(const MTPaccount_AuthorizationForm &result);
|
||||
void showForm();
|
||||
Value parseValue(
|
||||
const MTPSecureValue &value,
|
||||
|
@ -18,12 +18,12 @@ namespace {
|
||||
|
||||
std::map<Value::Type, Scope::Type> ScopeTypesMap() {
|
||||
return {
|
||||
{ Value::Type::PersonalDetails, Scope::Type::Identity },
|
||||
{ Value::Type::PersonalDetails, Scope::Type::PersonalDetails },
|
||||
{ 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::Address, Scope::Type::AddressDetails },
|
||||
{ Value::Type::UtilityBill, Scope::Type::Address },
|
||||
{ Value::Type::BankStatement, Scope::Type::Address },
|
||||
{ Value::Type::RentalAgreement, Scope::Type::Address },
|
||||
@ -41,62 +41,141 @@ Scope::Type ScopeTypeForValueType(Value::Type type) {
|
||||
return i->second;
|
||||
}
|
||||
|
||||
std::map<Scope::Type, Value::Type> ScopeFieldsMap() {
|
||||
std::map<Scope::Type, Value::Type> ScopeDetailsMap() {
|
||||
return {
|
||||
{ Scope::Type::PersonalDetails, Value::Type::PersonalDetails },
|
||||
{ Scope::Type::Identity, Value::Type::PersonalDetails },
|
||||
{ Scope::Type::AddressDetails, Value::Type::Address },
|
||||
{ Scope::Type::Address, Value::Type::Address },
|
||||
{ Scope::Type::Phone, Value::Type::Phone },
|
||||
{ Scope::Type::Email, Value::Type::Email },
|
||||
};
|
||||
}
|
||||
|
||||
Value::Type FieldsTypeForScopeType(Scope::Type type) {
|
||||
static const auto map = ScopeFieldsMap();
|
||||
Value::Type DetailsTypeForScopeType(Scope::Type type) {
|
||||
static const auto map = ScopeDetailsMap();
|
||||
const auto i = map.find(type);
|
||||
Assert(i != map.end());
|
||||
return i->second;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
bool InlineDetails(
|
||||
const Form::Request &request,
|
||||
Scope::Type into,
|
||||
Value::Type details) {
|
||||
const auto count = ranges::count_if(
|
||||
request,
|
||||
[&](const std::vector<Value::Type> &types) {
|
||||
Expects(!types.empty());
|
||||
|
||||
Scope::Scope(Type type, not_null<const Value*> fields)
|
||||
: type(type)
|
||||
, fields(fields) {
|
||||
return ScopeTypeForValueType(types[0]) == into;
|
||||
});
|
||||
if (count != 1) {
|
||||
return false;
|
||||
}
|
||||
const auto has = ranges::find_if(
|
||||
request,
|
||||
[&](const std::vector<Value::Type> &types) {
|
||||
Expects(!types.empty());
|
||||
|
||||
return (types[0] == details);
|
||||
}
|
||||
) != end(request);
|
||||
return has;
|
||||
}
|
||||
|
||||
std::vector<Scope> ComputeScopes(
|
||||
not_null<const FormController*> controller) {
|
||||
auto scopes = std::map<Scope::Type, Scope>();
|
||||
const auto &form = controller->form();
|
||||
bool InlineDetails(const Form::Request &request, Value::Type details) {
|
||||
if (details == Value::Type::PersonalDetails) {
|
||||
return InlineDetails(request, Scope::Type::Identity, details);
|
||||
} else if (details == Value::Type::Address) {
|
||||
return InlineDetails(request, Scope::Type::Address, details);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
Scope::Scope(Type type) : type(type) {
|
||||
}
|
||||
|
||||
bool ValidateForm(const Form &form) {
|
||||
base::flat_set<Value::Type> values;
|
||||
for (const auto &requested : form.request) {
|
||||
if (requested.empty()) {
|
||||
LOG(("API Error: Empty types list in authorization form row."));
|
||||
return false;
|
||||
}
|
||||
const auto scopeType = ScopeTypeForValueType(requested[0]);
|
||||
const auto ownsDetails = (scopeType != Scope::Type::Identity
|
||||
&& scopeType != Scope::Type::Address);
|
||||
if (ownsDetails && requested.size() != 1) {
|
||||
LOG(("API Error: Large types list in authorization form row."));
|
||||
return false;
|
||||
}
|
||||
for (const auto type : requested) {
|
||||
if (values.contains(type)) {
|
||||
LOG(("API Error: Value twice in authorization form row."));
|
||||
return false;
|
||||
}
|
||||
values.emplace(type);
|
||||
}
|
||||
}
|
||||
for (const auto &[type, value] : form.values) {
|
||||
if (!value.translationRequired) {
|
||||
for (const auto &scan : value.translations) {
|
||||
if (!scan.error.isEmpty()) {
|
||||
LOG(("API Error: "
|
||||
"Translation error in authorization form value."));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (!value.translationMissingError.isEmpty()) {
|
||||
LOG(("API Error: "
|
||||
"Translations error in authorization form value."));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
for (const auto &[type, specialScan] : value.specialScans) {
|
||||
if (!value.requiresSpecialScan(type)
|
||||
&& !specialScan.error.isEmpty()) {
|
||||
LOG(("API Error: "
|
||||
"Special scan error in authorization form value."));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<Scope> ComputeScopes(const Form &form) {
|
||||
auto result = std::vector<Scope>();
|
||||
const auto findValue = [&](const Value::Type type) {
|
||||
const auto i = form.values.find(type);
|
||||
Assert(i != form.values.end());
|
||||
return &i->second;
|
||||
};
|
||||
for (const auto type : form.request) {
|
||||
const auto scopeType = ScopeTypeForValueType(type);
|
||||
const auto fieldsType = FieldsTypeForScopeType(scopeType);
|
||||
const auto [i, ok] = scopes.emplace(
|
||||
scopeType,
|
||||
Scope(scopeType, findValue(fieldsType)));
|
||||
i->second.selfieRequired = (scopeType == Scope::Type::Identity)
|
||||
&& form.identitySelfieRequired;
|
||||
const auto alreadyIt = ranges::find(
|
||||
i->second.documents,
|
||||
type,
|
||||
[](not_null<const Value*> value) { return value->type; });
|
||||
if (alreadyIt != end(i->second.documents)) {
|
||||
LOG(("API Error: Value type %1 multiple times in request."
|
||||
).arg(int(type)));
|
||||
for (const auto &requested : form.request) {
|
||||
Assert(!requested.empty());
|
||||
const auto scopeType = ScopeTypeForValueType(requested[0]);
|
||||
const auto detailsType = DetailsTypeForScopeType(scopeType);
|
||||
const auto ownsDetails = (scopeType != Scope::Type::Identity
|
||||
&& scopeType != Scope::Type::Address);
|
||||
const auto inlineDetails = InlineDetails(form.request, detailsType);
|
||||
if (ownsDetails && inlineDetails) {
|
||||
continue;
|
||||
} else if (type != fieldsType) {
|
||||
i->second.documents.push_back(findValue(type));
|
||||
}
|
||||
}
|
||||
auto result = std::vector<Scope>();
|
||||
result.reserve(scopes.size());
|
||||
for (auto &[type, scope] : scopes) {
|
||||
result.push_back(std::move(scope));
|
||||
result.push_back(Scope(scopeType));
|
||||
auto &scope = result.back();
|
||||
scope.details = (ownsDetails || inlineDetails)
|
||||
? findValue(detailsType)
|
||||
: nullptr;
|
||||
if (ownsDetails) {
|
||||
Assert(requested.size() == 1);
|
||||
} else {
|
||||
for (const auto type : requested) {
|
||||
scope.documents.push_back(findValue(type));
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@ -129,7 +208,9 @@ QString JoinScopeRowReadyString(
|
||||
|
||||
QString ComputeScopeRowReadyString(const Scope &scope) {
|
||||
switch (scope.type) {
|
||||
case Scope::Type::PersonalDetails:
|
||||
case Scope::Type::Identity:
|
||||
case Scope::Type::AddressDetails:
|
||||
case Scope::Type::Address: {
|
||||
auto list = std::vector<std::pair<QString, QString>>();
|
||||
const auto pushListValue = [&](
|
||||
@ -153,10 +234,12 @@ QString ComputeScopeRowReadyString(const Scope &scope) {
|
||||
}
|
||||
}
|
||||
};
|
||||
const auto &fields = scope.fields->data.parsed.fields;
|
||||
const auto fields = scope.details
|
||||
? &scope.details->data.parsed.fields
|
||||
: nullptr;
|
||||
const auto document = [&]() -> const Value* {
|
||||
for (const auto &document : scope.documents) {
|
||||
if (document->scansAreFilled(scope.selfieRequired)) {
|
||||
if (document->scansAreFilled()) {
|
||||
return document;
|
||||
}
|
||||
}
|
||||
@ -195,8 +278,11 @@ QString ComputeScopeRowReadyString(const Scope &scope) {
|
||||
for (const auto &row : scheme.rows) {
|
||||
const auto format = row.format;
|
||||
if (row.valueClass == EditDocumentScheme::ValueClass::Fields) {
|
||||
const auto i = fields.find(row.key);
|
||||
if (i == end(fields)) {
|
||||
if (!fields) {
|
||||
continue;
|
||||
}
|
||||
const auto i = fields->find(row.key);
|
||||
if (i == end(*fields)) {
|
||||
return QString();
|
||||
}
|
||||
const auto text = i->second.text;
|
||||
@ -225,8 +311,9 @@ QString ComputeScopeRowReadyString(const Scope &scope) {
|
||||
} break;
|
||||
case Scope::Type::Phone:
|
||||
case Scope::Type::Email: {
|
||||
Assert(scope.details != nullptr);
|
||||
const auto format = GetContactScheme(scope.type).format;
|
||||
const auto &fields = scope.fields->data.parsed.fields;
|
||||
const auto &fields = scope.details->data.parsed.fields;
|
||||
const auto i = fields.find("value");
|
||||
return (i != end(fields))
|
||||
? (format ? format(i->second.text) : i->second.text)
|
||||
@ -242,19 +329,30 @@ ScopeRow ComputeScopeRow(const Scope &scope) {
|
||||
row.ready = ready;
|
||||
auto errors = QStringList();
|
||||
const auto addValueErrors = [&](not_null<const Value*> value) {
|
||||
if (!value->error.isEmpty()) {
|
||||
errors.push_back(value->error);
|
||||
}
|
||||
if (!value->scanMissingError.isEmpty()) {
|
||||
errors.push_back(value->scanMissingError);
|
||||
}
|
||||
if (!value->translationMissingError.isEmpty()) {
|
||||
errors.push_back(value->translationMissingError);
|
||||
}
|
||||
for (const auto &scan : value->scans) {
|
||||
if (!scan.error.isEmpty()) {
|
||||
errors.push_back(scan.error);
|
||||
}
|
||||
}
|
||||
for (const auto &scan : value->translations) {
|
||||
if (!scan.error.isEmpty()) {
|
||||
errors.push_back(scan.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);
|
||||
}
|
||||
for (const auto &[key, value] : value->data.parsed.fields) {
|
||||
if (!value.error.isEmpty()) {
|
||||
errors.push_back(value.error);
|
||||
@ -263,7 +361,7 @@ ScopeRow ComputeScopeRow(const Scope &scope) {
|
||||
};
|
||||
const auto document = [&]() -> const Value* {
|
||||
for (const auto &document : scope.documents) {
|
||||
if (document->scansAreFilled(scope.selfieRequired)) {
|
||||
if (document->scansAreFilled()) {
|
||||
return document;
|
||||
}
|
||||
}
|
||||
@ -272,31 +370,35 @@ ScopeRow ComputeScopeRow(const Scope &scope) {
|
||||
if (document) {
|
||||
addValueErrors(document);
|
||||
}
|
||||
addValueErrors(scope.fields);
|
||||
if (scope.details) {
|
||||
addValueErrors(scope.details);
|
||||
}
|
||||
if (!errors.isEmpty()) {
|
||||
row.error = lang(lng_passport_fix_errors);// errors.join('\n');
|
||||
}
|
||||
if (row.error.isEmpty()
|
||||
&& row.ready.isEmpty()
|
||||
&& scope.type == Scope::Type::Identity
|
||||
&& scope.selfieRequired) {
|
||||
auto noSelfieScope = scope;
|
||||
noSelfieScope.selfieRequired = false;
|
||||
if (!ComputeScopeRowReadyString(noSelfieScope).isEmpty()) {
|
||||
// Only selfie is missing.
|
||||
row.description = lang(lng_passport_identity_selfie);
|
||||
}
|
||||
row.error = errors[0];// errors.join('\n');
|
||||
}
|
||||
// #TODO passport half-full value
|
||||
//if (row.error.isEmpty()
|
||||
// && row.ready.isEmpty()
|
||||
// && scope.type == Scope::Type::Identity
|
||||
// && scope.selfieRequired) {
|
||||
// auto noSelfieScope = scope;
|
||||
// noSelfieScope.selfieRequired = false;
|
||||
// if (!ComputeScopeRowReadyString(noSelfieScope).isEmpty()) {
|
||||
// // Only selfie is missing.
|
||||
// row.description = lang(lng_passport_identity_selfie);
|
||||
// }
|
||||
//}
|
||||
return row;
|
||||
};
|
||||
switch (scope.type) {
|
||||
case Scope::Type::PersonalDetails:
|
||||
return addReadyError({
|
||||
lang(lng_passport_personal_details),
|
||||
lang(lng_passport_personal_details_enter),
|
||||
});
|
||||
case Scope::Type::Identity:
|
||||
if (scope.documents.empty()) {
|
||||
return addReadyError({
|
||||
lang(lng_passport_personal_details),
|
||||
lang(lng_passport_personal_details_enter),
|
||||
});
|
||||
} else if (scope.documents.size() == 1) {
|
||||
Assert(!scope.documents.empty());
|
||||
if (scope.documents.size() == 1) {
|
||||
switch (scope.documents.front()->type) {
|
||||
case Value::Type::Passport:
|
||||
return addReadyError({
|
||||
@ -325,13 +427,14 @@ ScopeRow ComputeScopeRow(const Scope &scope) {
|
||||
lang(lng_passport_identity_title),
|
||||
lang(lng_passport_identity_description),
|
||||
});
|
||||
case Scope::Type::Address:
|
||||
if (scope.documents.empty()) {
|
||||
return addReadyError({
|
||||
lang(lng_passport_address),
|
||||
lang(lng_passport_address_enter),
|
||||
case Scope::Type::AddressDetails:
|
||||
return addReadyError({
|
||||
lang(lng_passport_address),
|
||||
lang(lng_passport_address_enter),
|
||||
});
|
||||
} else if (scope.documents.size() == 1) {
|
||||
case Scope::Type::Address:
|
||||
Assert(!scope.documents.empty());
|
||||
if (scope.documents.size() == 1) {
|
||||
switch (scope.documents.front()->type) {
|
||||
case Value::Type::BankStatement:
|
||||
return addReadyError({
|
||||
|
@ -13,17 +13,18 @@ namespace Passport {
|
||||
|
||||
struct Scope {
|
||||
enum class Type {
|
||||
PersonalDetails,
|
||||
Identity,
|
||||
AddressDetails,
|
||||
Address,
|
||||
Phone,
|
||||
Email,
|
||||
};
|
||||
Scope(Type type, not_null<const Value*> fields);
|
||||
explicit Scope(Type type);
|
||||
|
||||
Type type;
|
||||
not_null<const Value*> fields;
|
||||
const Value *details = nullptr;
|
||||
std::vector<not_null<const Value*>> documents;
|
||||
bool selfieRequired = false;
|
||||
};
|
||||
|
||||
struct ScopeRow {
|
||||
@ -33,8 +34,8 @@ struct ScopeRow {
|
||||
QString error;
|
||||
};
|
||||
|
||||
std::vector<Scope> ComputeScopes(
|
||||
not_null<const FormController*> controller);
|
||||
bool ValidateForm(const Form &form);
|
||||
std::vector<Scope> ComputeScopes(const Form &form);
|
||||
QString ComputeScopeRowReadyString(const Scope &scope);
|
||||
ScopeRow ComputeScopeRow(const Scope &scope);
|
||||
|
||||
|
@ -99,6 +99,7 @@ EditDocumentScheme GetDocumentScheme(
|
||||
return !CountryFormat(value).isEmpty();
|
||||
});
|
||||
|
||||
// #TODO passport scheme
|
||||
switch (type) {
|
||||
case Scope::Type::Identity: {
|
||||
auto result = Scheme();
|
||||
@ -360,7 +361,7 @@ BoxContent *BoxPointer::operator->() const {
|
||||
|
||||
PanelController::PanelController(not_null<FormController*> form)
|
||||
: _form(form)
|
||||
, _scopes(ComputeScopes(_form)) {
|
||||
, _scopes(ComputeScopes(_form->form())) {
|
||||
_form->secretReadyEvents(
|
||||
) | rpl::start_with_next([=] {
|
||||
ensurePanelCreated();
|
||||
@ -378,8 +379,6 @@ PanelController::PanelController(not_null<FormController*> form)
|
||||
}) | rpl::start_with_next([=](not_null<const Value*> field) {
|
||||
_verificationBoxes.erase(field);
|
||||
}, lifetime());
|
||||
|
||||
_scopes = ComputeScopes(_form);
|
||||
}
|
||||
|
||||
not_null<UserData*> PanelController::bot() const {
|
||||
@ -397,12 +396,14 @@ void PanelController::fillRows(
|
||||
bool ready,
|
||||
bool error)> callback) {
|
||||
if (_scopes.empty()) {
|
||||
_scopes = ComputeScopes(_form);
|
||||
_scopes = ComputeScopes(_form->form());
|
||||
}
|
||||
for (const auto &scope : _scopes) {
|
||||
const auto row = ComputeScopeRow(scope);
|
||||
const auto main = scope.fields;
|
||||
if (!row.ready.isEmpty()) {
|
||||
const auto main = scope.details
|
||||
? not_null<const Value*>(scope.details)
|
||||
: scope.documents[0];
|
||||
if (main && !row.ready.isEmpty()) {
|
||||
_submitErrors.erase(
|
||||
ranges::remove(_submitErrors, main),
|
||||
_submitErrors.end());
|
||||
@ -546,9 +547,7 @@ void PanelController::uploadSpecialScan(
|
||||
QByteArray &&content) {
|
||||
Expects(_editScope != nullptr);
|
||||
Expects(_editDocument != nullptr);
|
||||
Expects(_editDocument->requiresSpecialScan(
|
||||
type,
|
||||
_editScope->selfieRequired));
|
||||
Expects(_editDocument->requiresSpecialScan(type));
|
||||
|
||||
_form->uploadSpecialScan(_editDocument, type, std::move(content));
|
||||
}
|
||||
@ -556,9 +555,7 @@ void PanelController::uploadSpecialScan(
|
||||
void PanelController::deleteSpecialScan(SpecialFile type) {
|
||||
Expects(_editScope != nullptr);
|
||||
Expects(_editDocument != nullptr);
|
||||
Expects(_editDocument->requiresSpecialScan(
|
||||
type,
|
||||
_editScope->selfieRequired));
|
||||
Expects(_editDocument->requiresSpecialScan(type));
|
||||
|
||||
_form->deleteSpecialScan(_editDocument, type);
|
||||
}
|
||||
@ -566,9 +563,7 @@ void PanelController::deleteSpecialScan(SpecialFile type) {
|
||||
void PanelController::restoreSpecialScan(SpecialFile type) {
|
||||
Expects(_editScope != nullptr);
|
||||
Expects(_editDocument != nullptr);
|
||||
Expects(_editDocument->requiresSpecialScan(
|
||||
type,
|
||||
_editScope->selfieRequired));
|
||||
Expects(_editDocument->requiresSpecialScan(type));
|
||||
|
||||
_form->restoreSpecialScan(_editDocument, type);
|
||||
}
|
||||
@ -642,9 +637,15 @@ ScanInfo PanelController::collectScanInfo(const EditFile &file) const {
|
||||
std::vector<ScopeError> PanelController::collectErrors(
|
||||
not_null<const Value*> value) const {
|
||||
auto result = std::vector<ScopeError>();
|
||||
if (!value->error.isEmpty()) {
|
||||
result.push_back({ FileKey(), value->error });
|
||||
}
|
||||
if (!value->scanMissingError.isEmpty()) {
|
||||
result.push_back({ FileKey(), value->scanMissingError });
|
||||
}
|
||||
if (!value->translationMissingError.isEmpty()) {
|
||||
result.push_back({ FileKey(), value->translationMissingError });
|
||||
}
|
||||
const auto addFileError = [&](const EditFile &file) {
|
||||
if (!file.fields.error.isEmpty()) {
|
||||
const auto key = FileKey{ file.fields.id, file.fields.dcId };
|
||||
@ -654,6 +655,9 @@ std::vector<ScopeError> PanelController::collectErrors(
|
||||
for (const auto &scan : value->scansInEdit) {
|
||||
addFileError(scan);
|
||||
}
|
||||
for (const auto &scan : value->translationsInEdit) {
|
||||
addFileError(scan);
|
||||
}
|
||||
for (const auto &[type, scan] : value->specialScansInEdit) {
|
||||
addFileError(scan);
|
||||
}
|
||||
@ -671,13 +675,14 @@ auto PanelController::deleteValueLabel() const
|
||||
|
||||
if (hasValueDocument()) {
|
||||
return Lang::Viewer(lng_passport_delete_document);
|
||||
}
|
||||
if (!hasValueFields()) {
|
||||
} else if (!hasValueFields()) {
|
||||
return base::none;
|
||||
}
|
||||
switch (_editScope->type) {
|
||||
case Scope::Type::PersonalDetails:
|
||||
case Scope::Type::Identity:
|
||||
return Lang::Viewer(lng_passport_delete_details);
|
||||
case Scope::Type::AddressDetails:
|
||||
case Scope::Type::Address:
|
||||
return Lang::Viewer(lng_passport_delete_address);
|
||||
case Scope::Type::Email:
|
||||
@ -700,27 +705,26 @@ bool PanelController::hasValueDocument() const {
|
||||
}
|
||||
|
||||
bool PanelController::hasValueFields() const {
|
||||
Expects(_editValue != nullptr);
|
||||
|
||||
return !_editValue->data.parsed.fields.empty();
|
||||
return _editValue && !_editValue->data.parsed.fields.empty();
|
||||
}
|
||||
|
||||
void PanelController::deleteValue() {
|
||||
Expects(_editScope != nullptr);
|
||||
Expects(hasValueDocument() || hasValueFields());
|
||||
|
||||
if (savingScope()) {
|
||||
return;
|
||||
}
|
||||
const auto text = [&] {
|
||||
switch (_editScope->type) {
|
||||
case Scope::Type::PersonalDetails:
|
||||
return lang(lng_passport_delete_details_sure);
|
||||
case Scope::Type::Identity:
|
||||
return lang(hasValueDocument()
|
||||
? lng_passport_delete_document_sure
|
||||
: lng_passport_delete_details_sure);
|
||||
return lang(lng_passport_delete_document_sure);
|
||||
case Scope::Type::AddressDetails:
|
||||
return lang(lng_passport_delete_address_sure);
|
||||
case Scope::Type::Address:
|
||||
return lang(hasValueDocument()
|
||||
? lng_passport_delete_document_sure
|
||||
: lng_passport_delete_address_sure);
|
||||
return lang(lng_passport_delete_document_sure);
|
||||
case Scope::Type::Phone:
|
||||
return lang(lng_passport_delete_phone_sure);
|
||||
case Scope::Type::Email:
|
||||
@ -745,7 +749,7 @@ void PanelController::deleteValue() {
|
||||
}
|
||||
|
||||
void PanelController::deleteValueSure(bool withDetails) {
|
||||
Expects(_editValue != nullptr);
|
||||
Expects(!withDetails || _editValue != nullptr);
|
||||
|
||||
if (hasValueDocument()) {
|
||||
_form->deleteValueEdit(_editDocument);
|
||||
@ -830,21 +834,22 @@ 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);
|
||||
[](not_null<const Value*> document) {
|
||||
return document->scansAreFilled();
|
||||
});
|
||||
if (i != end(documents)) {
|
||||
return (i - begin(documents));
|
||||
}
|
||||
// If we have a document where only selfie is not filled - return it.
|
||||
const auto j = ranges::find_if(
|
||||
documents,
|
||||
[&](not_null<const Value*> document) {
|
||||
return document->scansAreFilled(false);
|
||||
});
|
||||
if (j != end(documents)) {
|
||||
return (j - begin(documents));
|
||||
}
|
||||
// #TODO passport half-full value
|
||||
//const auto j = ranges::find_if(
|
||||
// documents,
|
||||
// [&](not_null<const Value*> document) {
|
||||
// return document->scansAreFilled(false);
|
||||
// });
|
||||
//if (j != end(documents)) {
|
||||
// return (j - begin(documents));
|
||||
//}
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -936,8 +941,7 @@ void PanelController::editWithUpload(int index, int documentIndex) {
|
||||
|
||||
const auto &document = _scopes[index].documents[documentIndex];
|
||||
const auto requiresSpecialScan = document->requiresSpecialScan(
|
||||
SpecialFile::FrontSide,
|
||||
false);
|
||||
SpecialFile::FrontSide);
|
||||
const auto allowMany = !requiresSpecialScan;
|
||||
const auto widget = _panel->widget();
|
||||
EditScans::ChooseScan(widget.get(), [=](QByteArray &&content) {
|
||||
@ -981,21 +985,26 @@ void PanelController::editScope(int index, int documentIndex) {
|
||||
&& documentIndex < _scopes[index].documents.size()));
|
||||
|
||||
_editScope = &_scopes[index];
|
||||
_editValue = _editScope->fields;
|
||||
_editValue = _editScope->details;
|
||||
_editDocument = (documentIndex >= 0)
|
||||
? _scopes[index].documents[documentIndex].get()
|
||||
: nullptr;
|
||||
Assert(_editValue || _editDocument);
|
||||
|
||||
_form->startValueEdit(_editValue);
|
||||
if (_editValue) {
|
||||
_form->startValueEdit(_editValue);
|
||||
}
|
||||
if (_editDocument) {
|
||||
_form->startValueEdit(_editDocument);
|
||||
}
|
||||
|
||||
auto content = [&]() -> object_ptr<Ui::RpWidget> {
|
||||
// #TODO passport pass and display value->error
|
||||
switch (_editScope->type) {
|
||||
case Scope::Type::Identity:
|
||||
case Scope::Type::Address: {
|
||||
auto result = _editDocument
|
||||
Assert(_editDocument != nullptr);
|
||||
auto result = _editValue
|
||||
? object_ptr<PanelEditDocument>(
|
||||
_panel->widget(),
|
||||
this,
|
||||
@ -1007,6 +1016,7 @@ void PanelController::editScope(int index, int documentIndex) {
|
||||
_editDocument->scanMissingError,
|
||||
valueFiles(*_editDocument),
|
||||
valueSpecialFiles(*_editDocument))
|
||||
// #TODO passport document without details
|
||||
: object_ptr<PanelEditDocument>(
|
||||
_panel->widget(),
|
||||
this,
|
||||
@ -1018,8 +1028,23 @@ void PanelController::editScope(int index, int documentIndex) {
|
||||
};
|
||||
return std::move(result);
|
||||
} break;
|
||||
case Scope::Type::PersonalDetails:
|
||||
case Scope::Type::AddressDetails: {
|
||||
Assert(_editValue != nullptr);
|
||||
auto result = object_ptr<PanelEditDocument>(
|
||||
_panel->widget(),
|
||||
this,
|
||||
GetDocumentScheme(_editScope->type),
|
||||
_editValue->data.parsedInEdit);
|
||||
const auto weak = make_weak(result.data());
|
||||
_panelHasUnsavedChanges = [=] {
|
||||
return weak ? weak->hasUnsavedChanges() : false;
|
||||
};
|
||||
return std::move(result);
|
||||
} break;
|
||||
case Scope::Type::Phone:
|
||||
case Scope::Type::Email: {
|
||||
Assert(_editValue != nullptr);
|
||||
const auto &parsed = _editValue->data.parsedInEdit;
|
||||
const auto valueIt = parsed.fields.find("value");
|
||||
const auto value = (valueIt == end(parsed.fields)
|
||||
@ -1081,16 +1106,12 @@ void PanelController::processValueSaveFinished(
|
||||
}
|
||||
|
||||
bool PanelController::uploadingScopeScan() const {
|
||||
Expects(_editValue != nullptr);
|
||||
|
||||
return _form->uploadingScan(_editValue)
|
||||
return (_editValue && _form->uploadingScan(_editValue))
|
||||
|| (_editDocument && _form->uploadingScan(_editDocument));
|
||||
}
|
||||
|
||||
bool PanelController::savingScope() const {
|
||||
Expects(_editValue != nullptr);
|
||||
|
||||
return _form->savingValue(_editValue)
|
||||
return (_editValue && _form->savingValue(_editValue))
|
||||
|| (_editDocument && _form->savingValue(_editDocument));
|
||||
}
|
||||
|
||||
@ -1173,7 +1194,7 @@ std::map<SpecialFile, ScanInfo> PanelController::valueSpecialFiles(
|
||||
SpecialFile::Selfie
|
||||
};
|
||||
for (const auto type : types) {
|
||||
if (value.requiresSpecialScan(type, _editScope->selfieRequired)) {
|
||||
if (value.requiresSpecialScan(type)) {
|
||||
const auto i = value.specialScansInEdit.find(type);
|
||||
const auto j = result.emplace(
|
||||
type,
|
||||
@ -1190,7 +1211,9 @@ void PanelController::cancelValueEdit() {
|
||||
Expects(_editScope != nullptr);
|
||||
|
||||
_editScopeBoxes.clear();
|
||||
_form->cancelValueEdit(base::take(_editValue));
|
||||
if (const auto value = base::take(_editValue)) {
|
||||
_form->cancelValueEdit(value);
|
||||
}
|
||||
if (const auto document = base::take(_editDocument)) {
|
||||
_form->cancelValueEdit(document);
|
||||
}
|
||||
@ -1199,7 +1222,6 @@ void PanelController::cancelValueEdit() {
|
||||
|
||||
void PanelController::saveScope(ValueMap &&data, ValueMap &&filesData) {
|
||||
Expects(_panel != nullptr);
|
||||
Expects(_editValue != nullptr);
|
||||
|
||||
if (uploadingScopeScan()) {
|
||||
showToast(lang(lng_passport_wait_upload));
|
||||
@ -1208,7 +1230,9 @@ void PanelController::saveScope(ValueMap &&data, ValueMap &&filesData) {
|
||||
return;
|
||||
}
|
||||
|
||||
_form->saveValueEdit(_editValue, std::move(data));
|
||||
if (_editValue) {
|
||||
_form->saveValueEdit(_editValue, std::move(data));
|
||||
}
|
||||
if (_editDocument) {
|
||||
_form->saveValueEdit(_editDocument, std::move(filesData));
|
||||
} else {
|
||||
@ -1219,12 +1243,11 @@ void PanelController::saveScope(ValueMap &&data, ValueMap &&filesData) {
|
||||
bool PanelController::editScopeChanged(
|
||||
const ValueMap &data,
|
||||
const ValueMap &filesData) const {
|
||||
Expects(_editValue != nullptr);
|
||||
|
||||
if (_form->editValueChanged(_editValue, data)) {
|
||||
if (_editValue && _form->editValueChanged(_editValue, data)) {
|
||||
return true;
|
||||
} else if (_editDocument
|
||||
&& _form->editValueChanged(_editDocument, filesData)) {
|
||||
return true;
|
||||
} else if (_editDocument) {
|
||||
return _form->editValueChanged(_editDocument, filesData);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user