2018-04-03 18:24:31 +00:00
|
|
|
/*
|
|
|
|
This file is part of Telegram Desktop,
|
|
|
|
the official desktop application for the Telegram messaging service.
|
|
|
|
|
|
|
|
For license and copyright information please follow this link:
|
|
|
|
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|
|
|
*/
|
|
|
|
#include "passport/passport_form_view_controller.h"
|
|
|
|
|
|
|
|
#include "passport/passport_form_controller.h"
|
2018-04-12 15:45:04 +00:00
|
|
|
#include "passport/passport_panel_edit_document.h"
|
|
|
|
#include "passport/passport_panel_edit_contact.h"
|
|
|
|
#include "passport/passport_panel_controller.h"
|
|
|
|
#include "lang/lang_keys.h"
|
2018-04-03 18:24:31 +00:00
|
|
|
|
|
|
|
namespace Passport {
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
std::map<Value::Type, Scope::Type> ScopeTypesMap() {
|
|
|
|
return {
|
|
|
|
{ Value::Type::PersonalDetails, Scope::Type::Identity },
|
|
|
|
{ Value::Type::Passport, Scope::Type::Identity },
|
|
|
|
{ Value::Type::DriverLicense, Scope::Type::Identity },
|
|
|
|
{ Value::Type::IdentityCard, Scope::Type::Identity },
|
2018-05-11 22:55:56 +00:00
|
|
|
{ Value::Type::InternalPassport, Scope::Type::Identity },
|
2018-04-03 18:24:31 +00:00
|
|
|
{ Value::Type::Address, Scope::Type::Address },
|
|
|
|
{ Value::Type::UtilityBill, Scope::Type::Address },
|
|
|
|
{ Value::Type::BankStatement, Scope::Type::Address },
|
|
|
|
{ Value::Type::RentalAgreement, Scope::Type::Address },
|
2018-05-11 22:55:56 +00:00
|
|
|
{ Value::Type::PassportRegistration, Scope::Type::Address },
|
|
|
|
{ Value::Type::TemporaryRegistration, Scope::Type::Address },
|
2018-04-03 18:24:31 +00:00
|
|
|
{ Value::Type::Phone, Scope::Type::Phone },
|
|
|
|
{ Value::Type::Email, Scope::Type::Email },
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
Scope::Type ScopeTypeForValueType(Value::Type type) {
|
|
|
|
static const auto map = ScopeTypesMap();
|
|
|
|
const auto i = map.find(type);
|
|
|
|
Assert(i != map.end());
|
|
|
|
return i->second;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::map<Scope::Type, Value::Type> ScopeFieldsMap() {
|
|
|
|
return {
|
|
|
|
{ Scope::Type::Identity, Value::Type::PersonalDetails },
|
|
|
|
{ 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();
|
|
|
|
const auto i = map.find(type);
|
|
|
|
Assert(i != map.end());
|
|
|
|
return i->second;
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
Scope::Scope(Type type, not_null<const Value*> fields)
|
|
|
|
: type(type)
|
|
|
|
, fields(fields) {
|
|
|
|
}
|
|
|
|
|
2018-04-12 15:45:04 +00:00
|
|
|
std::vector<Scope> ComputeScopes(
|
|
|
|
not_null<const FormController*> controller) {
|
2018-04-03 18:24:31 +00:00
|
|
|
auto scopes = std::map<Scope::Type, Scope>();
|
|
|
|
const auto &form = controller->form();
|
|
|
|
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(
|
2018-04-12 15:45:04 +00:00
|
|
|
i->second.documents,
|
2018-04-03 18:24:31 +00:00
|
|
|
type,
|
|
|
|
[](not_null<const Value*> value) { return value->type; });
|
2018-04-12 15:45:04 +00:00
|
|
|
if (alreadyIt != end(i->second.documents)) {
|
2018-04-03 18:24:31 +00:00
|
|
|
LOG(("API Error: Value type %1 multiple times in request."
|
|
|
|
).arg(int(type)));
|
|
|
|
continue;
|
|
|
|
} else if (type != fieldsType) {
|
2018-04-12 15:45:04 +00:00
|
|
|
i->second.documents.push_back(findValue(type));
|
2018-04-03 18:24:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
auto result = std::vector<Scope>();
|
|
|
|
result.reserve(scopes.size());
|
|
|
|
for (auto &[type, scope] : scopes) {
|
|
|
|
result.push_back(std::move(scope));
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2018-04-12 15:45:04 +00:00
|
|
|
QString ComputeScopeRowReadyString(const Scope &scope) {
|
|
|
|
switch (scope.type) {
|
|
|
|
case Scope::Type::Identity:
|
|
|
|
case Scope::Type::Address: {
|
|
|
|
auto list = QStringList();
|
2018-04-13 18:14:14 +00:00
|
|
|
const auto pushListValue = [&](const QString &value) {
|
|
|
|
if (const auto trimmed = value.trimmed(); !trimmed.isEmpty()) {
|
|
|
|
list.push_back(trimmed);
|
|
|
|
}
|
|
|
|
};
|
2018-04-12 15:45:04 +00:00
|
|
|
const auto &fields = scope.fields->data.parsed.fields;
|
|
|
|
const auto document = [&]() -> const Value* {
|
|
|
|
for (const auto &document : scope.documents) {
|
2018-05-11 22:55:56 +00:00
|
|
|
if (document->scansAreFilled(scope.selfieRequired)) {
|
2018-04-12 15:45:04 +00:00
|
|
|
return document;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nullptr;
|
|
|
|
}();
|
|
|
|
if (document && scope.documents.size() > 1) {
|
2018-04-13 18:14:14 +00:00
|
|
|
pushListValue([&] {
|
2018-05-11 22:55:56 +00:00
|
|
|
using Type = Value::Type;
|
2018-04-12 15:45:04 +00:00
|
|
|
switch (document->type) {
|
2018-05-11 22:55:56 +00:00
|
|
|
case Type::Passport:
|
2018-04-12 15:45:04 +00:00
|
|
|
return lang(lng_passport_identity_passport);
|
2018-05-11 22:55:56 +00:00
|
|
|
case Type::DriverLicense:
|
2018-04-12 15:45:04 +00:00
|
|
|
return lang(lng_passport_identity_license);
|
2018-05-11 22:55:56 +00:00
|
|
|
case Type::IdentityCard:
|
2018-04-12 15:45:04 +00:00
|
|
|
return lang(lng_passport_identity_card);
|
2018-05-11 22:55:56 +00:00
|
|
|
case Type::InternalPassport:
|
|
|
|
return lang(lng_passport_identity_internal);
|
|
|
|
case Type::BankStatement:
|
2018-04-12 15:45:04 +00:00
|
|
|
return lang(lng_passport_address_statement);
|
2018-05-11 22:55:56 +00:00
|
|
|
case Type::UtilityBill:
|
2018-04-12 15:45:04 +00:00
|
|
|
return lang(lng_passport_address_bill);
|
2018-05-11 22:55:56 +00:00
|
|
|
case Type::RentalAgreement:
|
2018-04-12 15:45:04 +00:00
|
|
|
return lang(lng_passport_address_agreement);
|
2018-05-11 22:55:56 +00:00
|
|
|
case Type::PassportRegistration:
|
|
|
|
return lang(lng_passport_address_registration);
|
|
|
|
case Type::TemporaryRegistration:
|
|
|
|
return lang(lng_passport_address_temporary);
|
2018-04-12 15:45:04 +00:00
|
|
|
default: Unexpected("Files type in ComputeScopeRowReadyString.");
|
|
|
|
}
|
|
|
|
}());
|
|
|
|
}
|
2018-05-11 22:55:56 +00:00
|
|
|
if (!scope.documents.empty() && !document) {
|
2018-04-12 15:45:04 +00:00
|
|
|
return QString();
|
|
|
|
}
|
|
|
|
const auto scheme = GetDocumentScheme(scope.type);
|
|
|
|
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)) {
|
|
|
|
return QString();
|
2018-04-15 15:06:53 +00:00
|
|
|
}
|
|
|
|
const auto text = i->second.text;
|
2018-05-15 13:35:59 +00:00
|
|
|
if (row.error && row.error(text).has_value()) {
|
2018-04-12 15:45:04 +00:00
|
|
|
return QString();
|
|
|
|
}
|
2018-04-15 15:06:53 +00:00
|
|
|
pushListValue(format ? format(text) : text);
|
|
|
|
} else if (scope.documents.empty()) {
|
|
|
|
continue;
|
2018-04-12 15:45:04 +00:00
|
|
|
} else {
|
|
|
|
const auto i = document->data.parsed.fields.find(row.key);
|
|
|
|
if (i == end(document->data.parsed.fields)) {
|
|
|
|
return QString();
|
2018-04-15 15:06:53 +00:00
|
|
|
}
|
|
|
|
const auto text = i->second.text;
|
2018-05-15 13:35:59 +00:00
|
|
|
if (row.error && row.error(text).has_value()) {
|
2018-04-12 15:45:04 +00:00
|
|
|
return QString();
|
|
|
|
}
|
2018-04-15 15:06:53 +00:00
|
|
|
pushListValue(text);
|
2018-04-12 15:45:04 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return list.join(", ");
|
|
|
|
} break;
|
|
|
|
case Scope::Type::Phone:
|
|
|
|
case Scope::Type::Email: {
|
|
|
|
const auto format = GetContactScheme(scope.type).format;
|
|
|
|
const auto &fields = scope.fields->data.parsed.fields;
|
|
|
|
const auto i = fields.find("value");
|
|
|
|
return (i != end(fields))
|
2018-04-15 15:06:53 +00:00
|
|
|
? (format ? format(i->second.text) : i->second.text)
|
2018-04-12 15:45:04 +00:00
|
|
|
: QString();
|
|
|
|
} break;
|
|
|
|
}
|
|
|
|
Unexpected("Scope type in ComputeScopeRowReadyString.");
|
|
|
|
}
|
|
|
|
|
|
|
|
ScopeRow ComputeScopeRow(const Scope &scope) {
|
2018-04-13 17:42:28 +00:00
|
|
|
const auto addReadyError = [&](ScopeRow &&row) {
|
|
|
|
const auto ready = ComputeScopeRowReadyString(scope);
|
|
|
|
row.ready = ready;
|
2018-04-17 17:54:52 +00:00
|
|
|
auto errors = QStringList();
|
|
|
|
const auto addValueErrors = [&](not_null<const Value*> value) {
|
|
|
|
for (const auto &scan : value->scans) {
|
|
|
|
if (!scan.error.isEmpty()) {
|
|
|
|
errors.push_back(scan.error);
|
|
|
|
}
|
|
|
|
}
|
2018-05-11 22:55:56 +00:00
|
|
|
for (const auto &[type, scan] : value->specialScans) {
|
|
|
|
if (!scan.error.isEmpty()) {
|
|
|
|
errors.push_back(scan.error);
|
|
|
|
}
|
2018-04-17 17:54:52 +00:00
|
|
|
}
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
ranges::for_each(scope.documents, addValueErrors);
|
|
|
|
addValueErrors(scope.fields);
|
|
|
|
row.error = errors.join('\n');
|
2018-04-13 17:42:28 +00:00
|
|
|
return row;
|
|
|
|
};
|
2018-04-12 15:45:04 +00:00
|
|
|
switch (scope.type) {
|
|
|
|
case Scope::Type::Identity:
|
|
|
|
if (scope.documents.empty()) {
|
2018-04-13 17:42:28 +00:00
|
|
|
return addReadyError({
|
2018-04-12 15:45:04 +00:00
|
|
|
lang(lng_passport_personal_details),
|
|
|
|
lang(lng_passport_personal_details_enter),
|
2018-04-13 17:42:28 +00:00
|
|
|
});
|
2018-04-12 15:45:04 +00:00
|
|
|
} else if (scope.documents.size() == 1) {
|
|
|
|
switch (scope.documents.front()->type) {
|
|
|
|
case Value::Type::Passport:
|
2018-04-13 17:42:28 +00:00
|
|
|
return addReadyError({
|
2018-04-12 15:45:04 +00:00
|
|
|
lang(lng_passport_identity_passport),
|
|
|
|
lang(lng_passport_identity_passport_upload),
|
2018-04-13 17:42:28 +00:00
|
|
|
});
|
2018-04-12 15:45:04 +00:00
|
|
|
case Value::Type::IdentityCard:
|
2018-04-13 17:42:28 +00:00
|
|
|
return addReadyError({
|
2018-04-12 15:45:04 +00:00
|
|
|
lang(lng_passport_identity_card),
|
|
|
|
lang(lng_passport_identity_card_upload),
|
2018-04-13 17:42:28 +00:00
|
|
|
});
|
2018-04-12 15:45:04 +00:00
|
|
|
case Value::Type::DriverLicense:
|
2018-04-13 17:42:28 +00:00
|
|
|
return addReadyError({
|
2018-04-12 15:45:04 +00:00
|
|
|
lang(lng_passport_identity_license),
|
|
|
|
lang(lng_passport_identity_license_upload),
|
2018-04-13 17:42:28 +00:00
|
|
|
});
|
2018-05-11 22:55:56 +00:00
|
|
|
case Value::Type::InternalPassport:
|
|
|
|
return addReadyError({
|
|
|
|
lang(lng_passport_identity_internal),
|
|
|
|
lang(lng_passport_identity_internal_upload),
|
|
|
|
});
|
2018-04-12 15:45:04 +00:00
|
|
|
default: Unexpected("Identity type in ComputeScopeRow.");
|
|
|
|
}
|
|
|
|
}
|
2018-04-13 17:42:28 +00:00
|
|
|
return addReadyError({
|
2018-04-12 15:45:04 +00:00
|
|
|
lang(lng_passport_identity_title),
|
|
|
|
lang(lng_passport_identity_description),
|
2018-04-13 17:42:28 +00:00
|
|
|
});
|
2018-04-12 15:45:04 +00:00
|
|
|
case Scope::Type::Address:
|
|
|
|
if (scope.documents.empty()) {
|
2018-04-13 17:42:28 +00:00
|
|
|
return addReadyError({
|
2018-04-12 15:45:04 +00:00
|
|
|
lang(lng_passport_address),
|
|
|
|
lang(lng_passport_address_enter),
|
2018-04-13 17:42:28 +00:00
|
|
|
});
|
2018-04-12 15:45:04 +00:00
|
|
|
} else if (scope.documents.size() == 1) {
|
|
|
|
switch (scope.documents.front()->type) {
|
|
|
|
case Value::Type::BankStatement:
|
2018-04-13 17:42:28 +00:00
|
|
|
return addReadyError({
|
2018-04-12 15:45:04 +00:00
|
|
|
lang(lng_passport_address_statement),
|
|
|
|
lang(lng_passport_address_statement_upload),
|
2018-04-13 17:42:28 +00:00
|
|
|
});
|
2018-04-12 15:45:04 +00:00
|
|
|
case Value::Type::UtilityBill:
|
2018-04-13 17:42:28 +00:00
|
|
|
return addReadyError({
|
2018-04-12 15:45:04 +00:00
|
|
|
lang(lng_passport_address_bill),
|
|
|
|
lang(lng_passport_address_bill_upload),
|
2018-04-13 17:42:28 +00:00
|
|
|
});
|
2018-04-12 15:45:04 +00:00
|
|
|
case Value::Type::RentalAgreement:
|
2018-04-13 17:42:28 +00:00
|
|
|
return addReadyError({
|
2018-04-12 15:45:04 +00:00
|
|
|
lang(lng_passport_address_agreement),
|
|
|
|
lang(lng_passport_address_agreement_upload),
|
2018-04-13 17:42:28 +00:00
|
|
|
});
|
2018-05-11 22:55:56 +00:00
|
|
|
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),
|
|
|
|
});
|
2018-04-12 15:45:04 +00:00
|
|
|
default: Unexpected("Address type in ComputeScopeRow.");
|
|
|
|
}
|
|
|
|
}
|
2018-04-13 17:42:28 +00:00
|
|
|
return addReadyError({
|
2018-04-12 15:45:04 +00:00
|
|
|
lang(lng_passport_address_title),
|
|
|
|
lang(lng_passport_address_description),
|
2018-04-13 17:42:28 +00:00
|
|
|
});
|
2018-04-12 15:45:04 +00:00
|
|
|
case Scope::Type::Phone:
|
2018-04-13 17:42:28 +00:00
|
|
|
return addReadyError({
|
2018-04-12 15:45:04 +00:00
|
|
|
lang(lng_passport_phone_title),
|
|
|
|
lang(lng_passport_phone_description),
|
2018-04-13 17:42:28 +00:00
|
|
|
});
|
2018-04-12 15:45:04 +00:00
|
|
|
case Scope::Type::Email:
|
2018-04-13 17:42:28 +00:00
|
|
|
return addReadyError({
|
2018-04-12 15:45:04 +00:00
|
|
|
lang(lng_passport_email_title),
|
|
|
|
lang(lng_passport_email_description),
|
2018-04-13 17:42:28 +00:00
|
|
|
});
|
2018-04-12 15:45:04 +00:00
|
|
|
default: Unexpected("Scope type in ComputeScopeRow.");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-04-03 18:24:31 +00:00
|
|
|
} // namespace Passport
|