mirror of
https://github.com/vale981/tdesktop
synced 2025-03-06 10:11:41 -05:00
Submit passport results.
This commit is contained in:
parent
c20cf243db
commit
ead31757d7
18 changed files with 664 additions and 403 deletions
|
@ -44,4 +44,16 @@ bool is_ipv6(const QString &ip) {
|
||||||
return ip.indexOf('.') < 0 && ip.indexOf(':') >= 0;
|
return ip.indexOf('.') < 0 && ip.indexOf(':') >= 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString url_append_query(const QString &url, const QString &add) {
|
||||||
|
const auto query = ranges::find(url, '?');
|
||||||
|
const auto hash = ranges::find(url, '#');
|
||||||
|
const auto base = url.mid(0, hash - url.begin());
|
||||||
|
const auto added = base
|
||||||
|
+ (hash <= query ? '?' : '&')
|
||||||
|
+ add;
|
||||||
|
const auto result = added
|
||||||
|
+ (hash < url.end() ? url.mid(hash - url.begin()) : QString());
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace qthelp
|
} // namespace qthelp
|
||||||
|
|
|
@ -22,7 +22,11 @@ enum class UrlParamNameTransform {
|
||||||
ToLower,
|
ToLower,
|
||||||
};
|
};
|
||||||
// Parses a string like "p1=v1&p2=v2&..&pn=vn" to a map.
|
// Parses a string like "p1=v1&p2=v2&..&pn=vn" to a map.
|
||||||
QMap<QString, QString> url_parse_params(const QString ¶ms, UrlParamNameTransform transform = UrlParamNameTransform::NoTransform);
|
QMap<QString, QString> url_parse_params(
|
||||||
|
const QString ¶ms,
|
||||||
|
UrlParamNameTransform transform = UrlParamNameTransform::NoTransform);
|
||||||
|
|
||||||
|
QString url_append_query(const QString &url, const QString &add);
|
||||||
|
|
||||||
bool is_ipv6(const QString &ip);
|
bool is_ipv6(const QString &ip);
|
||||||
|
|
||||||
|
|
|
@ -841,13 +841,15 @@ bool Messenger::openLocalUrl(const QString &url) {
|
||||||
const auto scope = params.value("scope", QString());
|
const auto scope = params.value("scope", QString());
|
||||||
const auto callback = params.value("callback_url", QString());
|
const auto callback = params.value("callback_url", QString());
|
||||||
const auto publicKey = params.value("public_key", QString());
|
const auto publicKey = params.value("public_key", QString());
|
||||||
|
const auto payload = params.value("payload", QString());
|
||||||
if (const auto window = App::wnd()) {
|
if (const auto window = App::wnd()) {
|
||||||
if (const auto controller = window->controller()) {
|
if (const auto controller = window->controller()) {
|
||||||
controller->showPassportForm(Passport::FormRequest(
|
controller->showPassportForm(Passport::FormRequest(
|
||||||
botId,
|
botId,
|
||||||
scope,
|
scope,
|
||||||
callback,
|
callback,
|
||||||
publicKey));
|
publicKey,
|
||||||
|
payload));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -164,6 +164,29 @@ public:
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
bytes::vector encryptOAEPpadding(bytes::const_span data) const {
|
||||||
|
Expects(isValid());
|
||||||
|
|
||||||
|
const auto resultSize = RSA_size(_rsa);
|
||||||
|
auto result = bytes::vector(resultSize, gsl::byte{});
|
||||||
|
const auto encryptedSize = RSA_public_encrypt(
|
||||||
|
data.size(),
|
||||||
|
reinterpret_cast<const unsigned char*>(data.data()),
|
||||||
|
reinterpret_cast<unsigned char*>(result.data()),
|
||||||
|
_rsa,
|
||||||
|
RSA_PKCS1_OAEP_PADDING);
|
||||||
|
if (encryptedSize != resultSize) {
|
||||||
|
ERR_load_crypto_strings();
|
||||||
|
LOG(("RSA Error: RSA_public_encrypt failed, "
|
||||||
|
"key fp: %1, result: %2, error: %3"
|
||||||
|
).arg(getFingerPrint()
|
||||||
|
).arg(encryptedSize
|
||||||
|
).arg(ERR_error_string(ERR_get_error(), 0)
|
||||||
|
));
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
~Private() {
|
~Private() {
|
||||||
RSA_free(_rsa);
|
RSA_free(_rsa);
|
||||||
}
|
}
|
||||||
|
@ -236,5 +259,10 @@ bytes::vector RSAPublicKey::decrypt(bytes::const_span data) const {
|
||||||
return _private->decrypt(data);
|
return _private->decrypt(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bytes::vector RSAPublicKey::encryptOAEPpadding(
|
||||||
|
bytes::const_span data) const {
|
||||||
|
return _private->encryptOAEPpadding(data);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace internal
|
} // namespace internal
|
||||||
} // namespace MTP
|
} // namespace MTP
|
||||||
|
|
|
@ -37,6 +37,9 @@ public:
|
||||||
// data has exactly 256 chars to be decrypted
|
// data has exactly 256 chars to be decrypted
|
||||||
bytes::vector decrypt(bytes::const_span data) const;
|
bytes::vector decrypt(bytes::const_span data) const;
|
||||||
|
|
||||||
|
// data has lequal than 215 chars to be decrypted
|
||||||
|
bytes::vector encryptOAEPpadding(bytes::const_span data) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
class Private;
|
class Private;
|
||||||
std::shared_ptr<Private> _private;
|
std::shared_ptr<Private> _private;
|
||||||
|
|
|
@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "passport/passport_encryption.h"
|
#include "passport/passport_encryption.h"
|
||||||
|
|
||||||
#include "base/openssl_help.h"
|
#include "base/openssl_help.h"
|
||||||
|
#include "mtproto/rsa_public_key.h"
|
||||||
|
|
||||||
namespace Passport {
|
namespace Passport {
|
||||||
namespace {
|
namespace {
|
||||||
|
@ -314,14 +315,6 @@ bytes::vector PrepareValueHash(
|
||||||
return openssl::Sha256(bytes::concatenate(dataHash, valueSecret));
|
return openssl::Sha256(bytes::concatenate(dataHash, valueSecret));
|
||||||
}
|
}
|
||||||
|
|
||||||
bytes::vector PrepareFilesHash(
|
|
||||||
gsl::span<bytes::const_span> fileHashes,
|
|
||||||
bytes::const_span valueSecret) {
|
|
||||||
return openssl::Sha256(bytes::concatenate(
|
|
||||||
bytes::concatenate(fileHashes),
|
|
||||||
valueSecret));
|
|
||||||
}
|
|
||||||
|
|
||||||
bytes::vector EncryptValueSecret(
|
bytes::vector EncryptValueSecret(
|
||||||
bytes::const_span valueSecret,
|
bytes::const_span valueSecret,
|
||||||
bytes::const_span secret,
|
bytes::const_span secret,
|
||||||
|
@ -350,4 +343,11 @@ uint64 CountSecureSecretHash(bytes::const_span secret) {
|
||||||
return *reinterpret_cast<const uint64*>(part.data());
|
return *reinterpret_cast<const uint64*>(part.data());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bytes::vector EncryptCredentialsSecret(
|
||||||
|
bytes::const_span secret,
|
||||||
|
bytes::const_span publicKey) {
|
||||||
|
const auto key = MTP::internal::RSAPublicKey(publicKey);
|
||||||
|
return key.encryptOAEPpadding(secret);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Passport
|
} // namespace Passport
|
||||||
|
|
|
@ -54,10 +54,10 @@ bytes::vector DecryptValueSecret(
|
||||||
bytes::const_span secret,
|
bytes::const_span secret,
|
||||||
bytes::const_span valueHash);
|
bytes::const_span valueHash);
|
||||||
|
|
||||||
bytes::vector PrepareFilesHash(
|
|
||||||
gsl::span<bytes::const_span> fileHashes,
|
|
||||||
bytes::const_span valueSecret);
|
|
||||||
|
|
||||||
uint64 CountSecureSecretHash(bytes::const_span secret);
|
uint64 CountSecureSecretHash(bytes::const_span secret);
|
||||||
|
|
||||||
|
bytes::vector EncryptCredentialsSecret(
|
||||||
|
bytes::const_span secret,
|
||||||
|
bytes::const_span publicKey);
|
||||||
|
|
||||||
} // namespace Passport
|
} // namespace Passport
|
||||||
|
|
|
@ -12,8 +12,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "boxes/confirm_box.h"
|
#include "boxes/confirm_box.h"
|
||||||
#include "lang/lang_keys.h"
|
#include "lang/lang_keys.h"
|
||||||
#include "base/openssl_help.h"
|
#include "base/openssl_help.h"
|
||||||
|
#include "base/qthelp_url.h"
|
||||||
#include "mainwindow.h"
|
#include "mainwindow.h"
|
||||||
#include "window/window_controller.h"
|
#include "window/window_controller.h"
|
||||||
|
#include "core/click_handler_types.h"
|
||||||
#include "auth_session.h"
|
#include "auth_session.h"
|
||||||
#include "storage/localimageloader.h"
|
#include "storage/localimageloader.h"
|
||||||
#include "storage/localstorage.h"
|
#include "storage/localstorage.h"
|
||||||
|
@ -46,17 +48,86 @@ Value::Type ConvertType(const MTPSecureValueType &type) {
|
||||||
Unexpected("Type in secureValueType type.");
|
Unexpected("Type in secureValueType type.");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
MTPSecureValueType ConvertType(Value::Type type) {
|
||||||
|
switch (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();
|
||||||
|
case Value::Type::Phone:
|
||||||
|
return MTP_secureValueTypePhone();
|
||||||
|
case Value::Type::Email:
|
||||||
|
return MTP_secureValueTypeEmail();
|
||||||
|
}
|
||||||
|
Unexpected("Type in FormController::submit.");
|
||||||
|
};
|
||||||
|
|
||||||
|
QJsonObject GetJSONFromMap(
|
||||||
|
const std::map<QString, bytes::const_span> &map) {
|
||||||
|
auto result = QJsonObject();
|
||||||
|
for (const auto &[key, value] : map) {
|
||||||
|
const auto raw = QByteArray::fromRawData(
|
||||||
|
reinterpret_cast<const char*>(value.data()),
|
||||||
|
value.size());
|
||||||
|
result.insert(key, QString::fromUtf8(raw.toBase64()));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
QJsonObject GetJSONFromFile(const File &file) {
|
||||||
|
return GetJSONFromMap({
|
||||||
|
{ "file_hash", file.hash },
|
||||||
|
{ "secret", file.secret }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
FormRequest PreprocessRequest(const FormRequest &request) {
|
||||||
|
auto result = request;
|
||||||
|
result.publicKey.replace("\r\n", "\n");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString ValueCredentialsKey(Value::Type type) {
|
||||||
|
switch (type) {
|
||||||
|
case Value::Type::PersonalDetails: return "personal_details";
|
||||||
|
case Value::Type::Passport: return "passport";
|
||||||
|
case Value::Type::DriverLicense: return "driver_license";
|
||||||
|
case Value::Type::IdentityCard: return "identity_card";
|
||||||
|
case Value::Type::Address: return "address";
|
||||||
|
case Value::Type::UtilityBill: return "utility_bill";
|
||||||
|
case Value::Type::BankStatement: return "bank_statement";
|
||||||
|
case Value::Type::RentalAgreement: return "rental_agreement";
|
||||||
|
case Value::Type::Phone:
|
||||||
|
case Value::Type::Email: return QString();
|
||||||
|
}
|
||||||
|
Unexpected("Type in ValueCredentialsKey.");
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
FormRequest::FormRequest(
|
FormRequest::FormRequest(
|
||||||
UserId botId,
|
UserId botId,
|
||||||
const QString &scope,
|
const QString &scope,
|
||||||
const QString &callbackUrl,
|
const QString &callbackUrl,
|
||||||
const QString &publicKey)
|
const QString &publicKey,
|
||||||
|
const QString &payload)
|
||||||
: botId(botId)
|
: botId(botId)
|
||||||
, scope(scope)
|
, scope(scope)
|
||||||
, callbackUrl(callbackUrl)
|
, callbackUrl(callbackUrl)
|
||||||
, publicKey(publicKey) {
|
, publicKey(publicKey)
|
||||||
|
, payload(payload) {
|
||||||
}
|
}
|
||||||
|
|
||||||
EditFile::EditFile(
|
EditFile::EditFile(
|
||||||
|
@ -111,7 +182,7 @@ FormController::FormController(
|
||||||
not_null<Window::Controller*> controller,
|
not_null<Window::Controller*> controller,
|
||||||
const FormRequest &request)
|
const FormRequest &request)
|
||||||
: _controller(controller)
|
: _controller(controller)
|
||||||
, _request(request)
|
, _request(PreprocessRequest(request))
|
||||||
, _view(std::make_unique<PanelController>(this)) {
|
, _view(std::make_unique<PanelController>(this)) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -136,6 +207,99 @@ bytes::vector FormController::passwordHashForAuth(
|
||||||
_password.salt));
|
_password.salt));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto FormController::prepareFinalData() const -> FinalData {
|
||||||
|
auto hashes = QVector<MTPSecureValueHash>();
|
||||||
|
auto secureData = QJsonObject();
|
||||||
|
const auto addValueToJSON = [&](
|
||||||
|
const QString &key,
|
||||||
|
not_null<const Value*> value) {
|
||||||
|
auto object = QJsonObject();
|
||||||
|
if (!value->data.parsed.fields.empty()) {
|
||||||
|
object.insert("data", GetJSONFromMap({
|
||||||
|
{ "data_hash", value->data.hash },
|
||||||
|
{ "secret", value->data.secret }
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
if (!value->scans.empty()) {
|
||||||
|
auto files = QJsonArray();
|
||||||
|
for (const auto &scan : value->scans) {
|
||||||
|
files.append(GetJSONFromFile(scan));
|
||||||
|
}
|
||||||
|
object.insert("files", files);
|
||||||
|
}
|
||||||
|
if (_form.identitySelfieRequired && value->selfie) {
|
||||||
|
object.insert("selfie", GetJSONFromFile(*value->selfie));
|
||||||
|
}
|
||||||
|
secureData.insert(key, object);
|
||||||
|
};
|
||||||
|
const auto addValue = [&](not_null<const Value*> value) {
|
||||||
|
hashes.push_back(MTP_secureValueHash(
|
||||||
|
ConvertType(value->type),
|
||||||
|
MTP_bytes(value->submitHash)));
|
||||||
|
const auto key = ValueCredentialsKey(value->type);
|
||||||
|
if (!key.isEmpty()) {
|
||||||
|
addValueToJSON(key, value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const auto scopes = ComputeScopes(this);
|
||||||
|
for (const auto &scope : scopes) {
|
||||||
|
const auto ready = ComputeScopeRowReadyString(scope);
|
||||||
|
if (ready.isEmpty()) {
|
||||||
|
_valueError.fire_copy(scope.fields);
|
||||||
|
}
|
||||||
|
addValue(scope.fields);
|
||||||
|
if (!scope.documents.empty()) {
|
||||||
|
for (const auto &document : scope.documents) {
|
||||||
|
if (!document->scans.empty()) {
|
||||||
|
addValue(document);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto json = QJsonObject();
|
||||||
|
json.insert("secure_data", secureData);
|
||||||
|
json.insert("payload", _request.payload);
|
||||||
|
|
||||||
|
return {
|
||||||
|
hashes,
|
||||||
|
QJsonDocument(json).toJson(QJsonDocument::Compact)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
void FormController::submit() {
|
||||||
|
if (_submitRequestId) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto prepared = prepareFinalData();
|
||||||
|
const auto credentialsEncryptedData = EncryptData(
|
||||||
|
bytes::make_span(prepared.credentials));
|
||||||
|
const auto credentialsEncryptedSecret = EncryptCredentialsSecret(
|
||||||
|
credentialsEncryptedData.secret,
|
||||||
|
bytes::make_span(_request.publicKey.toUtf8()));
|
||||||
|
|
||||||
|
_submitRequestId = request(MTPaccount_AcceptAuthorization(
|
||||||
|
MTP_int(_request.botId),
|
||||||
|
MTP_string(_request.scope),
|
||||||
|
MTP_string(_request.publicKey),
|
||||||
|
MTP_vector<MTPSecureValueHash>(prepared.hashes),
|
||||||
|
MTP_secureCredentialsEncrypted(
|
||||||
|
MTP_bytes(credentialsEncryptedData.bytes),
|
||||||
|
MTP_bytes(credentialsEncryptedData.hash),
|
||||||
|
MTP_bytes(credentialsEncryptedSecret))
|
||||||
|
)).done([=](const MTPBool &result) {
|
||||||
|
const auto url = qthelp::url_append_query(
|
||||||
|
_request.callbackUrl,
|
||||||
|
"tg_passport=success");
|
||||||
|
UrlClickHandler::doOpen(url);
|
||||||
|
}).fail([=](const RPCError &error) {
|
||||||
|
_view->show(Box<InformBox>(
|
||||||
|
"Failed sending data :(\n" + error.type()));
|
||||||
|
}).send();
|
||||||
|
}
|
||||||
|
|
||||||
void FormController::submitPassword(const QString &password) {
|
void FormController::submitPassword(const QString &password) {
|
||||||
Expects(!_password.salt.empty());
|
Expects(!_password.salt.empty());
|
||||||
|
|
||||||
|
@ -231,19 +395,7 @@ bool FormController::validateValueSecrets(Value &value) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (auto &file : value.files) {
|
const auto validateFileSecret = [&](File &file) {
|
||||||
file.secret = DecryptValueSecret(
|
|
||||||
file.encryptedSecret,
|
|
||||||
_secret,
|
|
||||||
file.hash);
|
|
||||||
if (file.secret.empty()) {
|
|
||||||
LOG(("API Error: Could not decrypt file secret. "
|
|
||||||
"Forgetting files and data :("));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (value.selfie) {
|
|
||||||
auto &file = *value.selfie;
|
|
||||||
file.secret = DecryptValueSecret(
|
file.secret = DecryptValueSecret(
|
||||||
file.encryptedSecret,
|
file.encryptedSecret,
|
||||||
_secret,
|
_secret,
|
||||||
|
@ -253,6 +405,15 @@ bool FormController::validateValueSecrets(Value &value) {
|
||||||
"Forgetting files and data :("));
|
"Forgetting files and data :("));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
for (auto &scan : value.scans) {
|
||||||
|
if (!validateFileSecret(scan)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (value.selfie && !validateFileSecret(*value.selfie)) {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -273,31 +434,31 @@ void FormController::uploadScan(
|
||||||
not_null<const Value*> value,
|
not_null<const Value*> value,
|
||||||
QByteArray &&content) {
|
QByteArray &&content) {
|
||||||
const auto nonconst = findValue(value);
|
const auto nonconst = findValue(value);
|
||||||
auto fileIndex = int(nonconst->filesInEdit.size());
|
auto scanIndex = int(nonconst->scansInEdit.size());
|
||||||
nonconst->filesInEdit.emplace_back(
|
nonconst->scansInEdit.emplace_back(
|
||||||
nonconst,
|
nonconst,
|
||||||
File(),
|
File(),
|
||||||
nullptr);
|
nullptr);
|
||||||
auto &file = nonconst->filesInEdit.back();
|
auto &scan = nonconst->scansInEdit.back();
|
||||||
encryptFile(file, std::move(content), [=](UploadScanData &&result) {
|
encryptFile(scan, std::move(content), [=](UploadScanData &&result) {
|
||||||
Expects(fileIndex >= 0 && fileIndex < nonconst->filesInEdit.size());
|
Expects(scanIndex >= 0 && scanIndex < nonconst->scansInEdit.size());
|
||||||
|
|
||||||
uploadEncryptedFile(
|
uploadEncryptedFile(
|
||||||
nonconst->filesInEdit[fileIndex],
|
nonconst->scansInEdit[scanIndex],
|
||||||
std::move(result));
|
std::move(result));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void FormController::deleteScan(
|
void FormController::deleteScan(
|
||||||
not_null<const Value*> value,
|
not_null<const Value*> value,
|
||||||
int fileIndex) {
|
int scanIndex) {
|
||||||
scanDeleteRestore(value, fileIndex, true);
|
scanDeleteRestore(value, scanIndex, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FormController::restoreScan(
|
void FormController::restoreScan(
|
||||||
not_null<const Value*> value,
|
not_null<const Value*> value,
|
||||||
int fileIndex) {
|
int scanIndex) {
|
||||||
scanDeleteRestore(value, fileIndex, false);
|
scanDeleteRestore(value, scanIndex, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FormController::uploadSelfie(
|
void FormController::uploadSelfie(
|
||||||
|
@ -371,14 +532,14 @@ void FormController::encryptFile(
|
||||||
|
|
||||||
void FormController::scanDeleteRestore(
|
void FormController::scanDeleteRestore(
|
||||||
not_null<const Value*> value,
|
not_null<const Value*> value,
|
||||||
int fileIndex,
|
int scanIndex,
|
||||||
bool deleted) {
|
bool deleted) {
|
||||||
Expects(fileIndex >= 0 && fileIndex < value->filesInEdit.size());
|
Expects(scanIndex >= 0 && scanIndex < value->scansInEdit.size());
|
||||||
|
|
||||||
const auto nonconst = findValue(value);
|
const auto nonconst = findValue(value);
|
||||||
auto &file = nonconst->filesInEdit[fileIndex];
|
auto &scan = nonconst->scansInEdit[scanIndex];
|
||||||
file.deleted = deleted;
|
scan.deleted = deleted;
|
||||||
_scanUpdated.fire(&file);
|
_scanUpdated.fire(&scan);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FormController::selfieDeleteRestore(
|
void FormController::selfieDeleteRestore(
|
||||||
|
@ -387,9 +548,9 @@ void FormController::selfieDeleteRestore(
|
||||||
Expects(value->selfieInEdit.has_value());
|
Expects(value->selfieInEdit.has_value());
|
||||||
|
|
||||||
const auto nonconst = findValue(value);
|
const auto nonconst = findValue(value);
|
||||||
auto &file = *nonconst->selfieInEdit;
|
auto &scan = *nonconst->selfieInEdit;
|
||||||
file.deleted = deleted;
|
scan.deleted = deleted;
|
||||||
_scanUpdated.fire(&file);
|
_scanUpdated.fire(&scan);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FormController::subscribeToUploader() {
|
void FormController::subscribeToUploader() {
|
||||||
|
@ -502,6 +663,11 @@ auto FormController::valueSaveFinished() const
|
||||||
return _valueSaveFinished.events();
|
return _valueSaveFinished.events();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto FormController::valueError() const
|
||||||
|
-> rpl::producer<not_null<const Value*>> {
|
||||||
|
return _valueError.events();
|
||||||
|
}
|
||||||
|
|
||||||
auto FormController::verificationNeeded() const
|
auto FormController::verificationNeeded() const
|
||||||
-> rpl::producer<not_null<const Value*>> {
|
-> rpl::producer<not_null<const Value*>> {
|
||||||
return _verificationNeeded.events();
|
return _verificationNeeded.events();
|
||||||
|
@ -598,14 +764,14 @@ void FormController::startValueEdit(not_null<const Value*> value) {
|
||||||
if (savingValue(nonconst)) {
|
if (savingValue(nonconst)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
for (auto &file : nonconst->files) {
|
for (auto &scan : nonconst->scans) {
|
||||||
loadFile(file);
|
loadFile(scan);
|
||||||
}
|
}
|
||||||
if (nonconst->selfie) {
|
if (nonconst->selfie) {
|
||||||
loadFile(*nonconst->selfie);
|
loadFile(*nonconst->selfie);
|
||||||
}
|
}
|
||||||
nonconst->filesInEdit = ranges::view::all(
|
nonconst->scansInEdit = ranges::view::all(
|
||||||
nonconst->files
|
nonconst->scans
|
||||||
) | ranges::view::transform([=](const File &file) {
|
) | ranges::view::transform([=](const File &file) {
|
||||||
return EditFile(nonconst, file, nullptr);
|
return EditFile(nonconst, file, nullptr);
|
||||||
}) | ranges::to_vector;
|
}) | ranges::to_vector;
|
||||||
|
@ -729,7 +895,7 @@ void FormController::clearValueEdit(not_null<Value*> value) {
|
||||||
if (savingValue(value)) {
|
if (savingValue(value)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
value->filesInEdit.clear();
|
value->scansInEdit.clear();
|
||||||
value->selfieInEdit = base::none;
|
value->selfieInEdit = base::none;
|
||||||
value->data.encryptedSecretInEdit.clear();
|
value->data.encryptedSecretInEdit.clear();
|
||||||
value->data.hashInEdit.clear();
|
value->data.hashInEdit.clear();
|
||||||
|
@ -770,8 +936,8 @@ bool FormController::editValueChanged(
|
||||||
not_null<const Value*> value,
|
not_null<const Value*> value,
|
||||||
const ValueMap &data) const {
|
const ValueMap &data) const {
|
||||||
auto filesCount = 0;
|
auto filesCount = 0;
|
||||||
for (const auto &file : value->filesInEdit) {
|
for (const auto &scan : value->scansInEdit) {
|
||||||
if (editFileChanged(file)) {
|
if (editFileChanged(scan)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -804,7 +970,7 @@ void FormController::saveValueEdit(
|
||||||
if (!editValueChanged(nonconst, data)) {
|
if (!editValueChanged(nonconst, data)) {
|
||||||
nonconst->saveRequestId = -1;
|
nonconst->saveRequestId = -1;
|
||||||
crl::on_main(this, [=] {
|
crl::on_main(this, [=] {
|
||||||
base::take(nonconst->filesInEdit);
|
base::take(nonconst->scansInEdit);
|
||||||
base::take(nonconst->selfieInEdit);
|
base::take(nonconst->selfieInEdit);
|
||||||
base::take(nonconst->data.encryptedSecretInEdit);
|
base::take(nonconst->data.encryptedSecretInEdit);
|
||||||
base::take(nonconst->data.hashInEdit);
|
base::take(nonconst->data.hashInEdit);
|
||||||
|
@ -848,12 +1014,12 @@ void FormController::saveEncryptedValue(not_null<Value*> value) {
|
||||||
};
|
};
|
||||||
|
|
||||||
auto inputFiles = QVector<MTPInputSecureFile>();
|
auto inputFiles = QVector<MTPInputSecureFile>();
|
||||||
inputFiles.reserve(value->filesInEdit.size());
|
inputFiles.reserve(value->scansInEdit.size());
|
||||||
for (const auto &file : value->filesInEdit) {
|
for (const auto &scan : value->scansInEdit) {
|
||||||
if (file.deleted) {
|
if (scan.deleted) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
inputFiles.push_back(inputFile(file));
|
inputFiles.push_back(inputFile(scan));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (value->data.secret.empty()) {
|
if (value->data.secret.empty()) {
|
||||||
|
@ -894,11 +1060,11 @@ void FormController::saveEncryptedValue(not_null<Value*> value) {
|
||||||
}
|
}
|
||||||
Unexpected("Value type in saveEncryptedValue().");
|
Unexpected("Value type in saveEncryptedValue().");
|
||||||
}();
|
}();
|
||||||
const auto flags = ((value->filesInEdit.empty()
|
const auto flags = ((value->scansInEdit.empty()
|
||||||
&& value->data.parsedInEdit.fields.empty())
|
&& value->data.parsedInEdit.fields.empty())
|
||||||
? MTPDinputSecureValue::Flag(0)
|
? MTPDinputSecureValue::Flag(0)
|
||||||
: MTPDinputSecureValue::Flag::f_data)
|
: MTPDinputSecureValue::Flag::f_data)
|
||||||
| (value->filesInEdit.empty()
|
| (value->scansInEdit.empty()
|
||||||
? MTPDinputSecureValue::Flag(0)
|
? MTPDinputSecureValue::Flag(0)
|
||||||
: MTPDinputSecureValue::Flag::f_files)
|
: MTPDinputSecureValue::Flag::f_files)
|
||||||
| ((value->selfieInEdit && !value->selfieInEdit->deleted)
|
| ((value->selfieInEdit && !value->selfieInEdit->deleted)
|
||||||
|
@ -954,27 +1120,15 @@ void FormController::sendSaveRequest(
|
||||||
data,
|
data,
|
||||||
MTP_long(_secretId)
|
MTP_long(_secretId)
|
||||||
)).done([=](const MTPSecureValue &result) {
|
)).done([=](const MTPSecureValue &result) {
|
||||||
Expects(result.type() == mtpc_secureValue);
|
auto filesInEdit = base::take(value->scansInEdit);
|
||||||
|
|
||||||
value->saveRequestId = 0;
|
|
||||||
|
|
||||||
const auto filesInEdit = base::take(value->filesInEdit);
|
|
||||||
auto selfiesInEdit = std::vector<EditFile>();
|
|
||||||
if (auto selfie = base::take(value->selfieInEdit)) {
|
if (auto selfie = base::take(value->selfieInEdit)) {
|
||||||
selfiesInEdit.push_back(std::move(*selfie));
|
filesInEdit.push_back(std::move(*selfie));
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto &data = result.c_secureValue();
|
const auto editScreens = value->editScreens;
|
||||||
value->files = data.has_files()
|
*value = parseValue(result, filesInEdit);
|
||||||
? parseFiles(data.vfiles.v, filesInEdit)
|
decryptValue(*value);
|
||||||
: std::vector<File>();
|
value->editScreens = editScreens;
|
||||||
value->selfie = data.has_selfie()
|
|
||||||
? parseFile(data.vselfie, selfiesInEdit)
|
|
||||||
: base::none;
|
|
||||||
value->data.encryptedSecret = std::move(
|
|
||||||
value->data.encryptedSecretInEdit);
|
|
||||||
value->data.parsed = std::move(value->data.parsedInEdit);
|
|
||||||
value->data.hash = std::move(value->data.hashInEdit);
|
|
||||||
|
|
||||||
_valueSaveFinished.fire_copy(value);
|
_valueSaveFinished.fire_copy(value);
|
||||||
}).fail([=](const RPCError &error) {
|
}).fail([=](const RPCError &error) {
|
||||||
|
@ -1167,12 +1321,15 @@ void FormController::generateSecret(bytes::const_span password) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void FormController::requestForm() {
|
void FormController::requestForm() {
|
||||||
auto normalizedKey = _request.publicKey;
|
if (_request.payload.isEmpty()) {
|
||||||
normalizedKey.replace("\r\n", "\n");
|
_formRequestId = -1;
|
||||||
|
Ui::show(Box<InformBox>(lang(lng_passport_form_error)));
|
||||||
|
return;
|
||||||
|
}
|
||||||
_formRequestId = request(MTPaccount_GetAuthorizationForm(
|
_formRequestId = request(MTPaccount_GetAuthorizationForm(
|
||||||
MTP_int(_request.botId),
|
MTP_int(_request.botId),
|
||||||
MTP_string(_request.scope),
|
MTP_string(_request.scope),
|
||||||
MTP_bytes(normalizedKey.toUtf8())
|
MTP_string(_request.publicKey)
|
||||||
)).done([=](const MTPaccount_AuthorizationForm &result) {
|
)).done([=](const MTPaccount_AuthorizationForm &result) {
|
||||||
_formRequestId = 0;
|
_formRequestId = 0;
|
||||||
formDone(result);
|
formDone(result);
|
||||||
|
@ -1250,12 +1407,14 @@ void FormController::fillDownloadedFile(
|
||||||
}
|
}
|
||||||
|
|
||||||
auto FormController::parseValue(
|
auto FormController::parseValue(
|
||||||
const MTPSecureValue &value) const -> Value {
|
const MTPSecureValue &value,
|
||||||
|
const std::vector<EditFile> &editData) const -> Value {
|
||||||
Expects(value.type() == mtpc_secureValue);
|
Expects(value.type() == mtpc_secureValue);
|
||||||
|
|
||||||
const auto &data = value.c_secureValue();
|
const auto &data = value.c_secureValue();
|
||||||
const auto type = ConvertType(data.vtype);
|
const auto type = ConvertType(data.vtype);
|
||||||
auto result = Value(type);
|
auto result = Value(type);
|
||||||
|
result.submitHash = bytes::make_vector(data.vhash.v);
|
||||||
if (data.has_data()) {
|
if (data.has_data()) {
|
||||||
Assert(data.vdata.type() == mtpc_secureData);
|
Assert(data.vdata.type() == mtpc_secureData);
|
||||||
const auto &fields = data.vdata.c_secureData();
|
const auto &fields = data.vdata.c_secureData();
|
||||||
|
@ -1264,10 +1423,10 @@ auto FormController::parseValue(
|
||||||
result.data.encryptedSecret = bytes::make_vector(fields.vsecret.v);
|
result.data.encryptedSecret = bytes::make_vector(fields.vsecret.v);
|
||||||
}
|
}
|
||||||
if (data.has_files()) {
|
if (data.has_files()) {
|
||||||
result.files = parseFiles(data.vfiles.v);
|
result.scans = parseFiles(data.vfiles.v, editData);
|
||||||
}
|
}
|
||||||
if (data.has_selfie()) {
|
if (data.has_selfie()) {
|
||||||
result.selfie = parseFile(data.vselfie);
|
result.selfie = parseFile(data.vselfie, editData);
|
||||||
}
|
}
|
||||||
if (data.has_plain_data()) {
|
if (data.has_plain_data()) {
|
||||||
switch (data.vplain_data.type()) {
|
switch (data.vplain_data.type()) {
|
||||||
|
@ -1281,7 +1440,6 @@ auto FormController::parseValue(
|
||||||
} break;
|
} break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// #TODO passport selfie
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1290,9 +1448,9 @@ auto FormController::findEditFile(const FullMsgId &fullId) -> EditFile* {
|
||||||
return (file.uploadData && file.uploadData->fullId == fullId);
|
return (file.uploadData && file.uploadData->fullId == fullId);
|
||||||
};
|
};
|
||||||
for (auto &[type, value] : _form.values) {
|
for (auto &[type, value] : _form.values) {
|
||||||
for (auto &file : value.filesInEdit) {
|
for (auto &scan : value.scansInEdit) {
|
||||||
if (found(file)) {
|
if (found(scan)) {
|
||||||
return &file;
|
return &scan;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (value.selfieInEdit && found(*value.selfieInEdit)) {
|
if (value.selfieInEdit && found(*value.selfieInEdit)) {
|
||||||
|
@ -1307,9 +1465,9 @@ auto FormController::findEditFile(const FileKey &key) -> EditFile* {
|
||||||
return (file.fields.dcId == key.dcId && file.fields.id == key.id);
|
return (file.fields.dcId == key.dcId && file.fields.id == key.id);
|
||||||
};
|
};
|
||||||
for (auto &[type, value] : _form.values) {
|
for (auto &[type, value] : _form.values) {
|
||||||
for (auto &file : value.filesInEdit) {
|
for (auto &scan : value.scansInEdit) {
|
||||||
if (found(file)) {
|
if (found(scan)) {
|
||||||
return &file;
|
return &scan;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (value.selfieInEdit && found(*value.selfieInEdit)) {
|
if (value.selfieInEdit && found(*value.selfieInEdit)) {
|
||||||
|
@ -1325,9 +1483,9 @@ auto FormController::findFile(const FileKey &key)
|
||||||
return (file.dcId == key.dcId) && (file.id == key.id);
|
return (file.dcId == key.dcId) && (file.id == key.id);
|
||||||
};
|
};
|
||||||
for (auto &[type, value] : _form.values) {
|
for (auto &[type, value] : _form.values) {
|
||||||
for (auto &file : value.files) {
|
for (auto &scan : value.scans) {
|
||||||
if (found(file)) {
|
if (found(scan)) {
|
||||||
return { &value, &file };
|
return { &value, &scan };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (value.selfie && found(*value.selfie)) {
|
if (value.selfie && found(*value.selfie)) {
|
||||||
|
|
|
@ -31,12 +31,14 @@ struct FormRequest {
|
||||||
UserId botId,
|
UserId botId,
|
||||||
const QString &scope,
|
const QString &scope,
|
||||||
const QString &callbackUrl,
|
const QString &callbackUrl,
|
||||||
const QString &publicKey);
|
const QString &publicKey,
|
||||||
|
const QString &payload);
|
||||||
|
|
||||||
UserId botId;
|
UserId botId;
|
||||||
QString scope;
|
QString scope;
|
||||||
QString callbackUrl;
|
QString callbackUrl;
|
||||||
QString publicKey;
|
QString publicKey;
|
||||||
|
QString payload;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -142,11 +144,12 @@ struct Value {
|
||||||
|
|
||||||
Type type;
|
Type type;
|
||||||
ValueData data;
|
ValueData data;
|
||||||
std::vector<File> files;
|
std::vector<File> scans;
|
||||||
std::vector<EditFile> filesInEdit;
|
std::vector<EditFile> scansInEdit;
|
||||||
base::optional<File> selfie;
|
base::optional<File> selfie;
|
||||||
base::optional<EditFile> selfieInEdit;
|
base::optional<EditFile> selfieInEdit;
|
||||||
Verification verification;
|
Verification verification;
|
||||||
|
bytes::vector submitHash;
|
||||||
|
|
||||||
int editScreens = 0;
|
int editScreens = 0;
|
||||||
mtpRequestId saveRequestId = 0;
|
mtpRequestId saveRequestId = 0;
|
||||||
|
@ -204,7 +207,7 @@ public:
|
||||||
void show();
|
void show();
|
||||||
UserData *bot() const;
|
UserData *bot() const;
|
||||||
QString privacyPolicyUrl() const;
|
QString privacyPolicyUrl() const;
|
||||||
|
void submit();
|
||||||
void submitPassword(const QString &password);
|
void submitPassword(const QString &password);
|
||||||
rpl::producer<QString> passwordError() const;
|
rpl::producer<QString> passwordError() const;
|
||||||
QString passwordHint() const;
|
QString passwordHint() const;
|
||||||
|
@ -223,6 +226,7 @@ public:
|
||||||
|
|
||||||
rpl::producer<not_null<const EditFile*>> scanUpdated() const;
|
rpl::producer<not_null<const EditFile*>> scanUpdated() const;
|
||||||
rpl::producer<not_null<const Value*>> valueSaveFinished() const;
|
rpl::producer<not_null<const Value*>> valueSaveFinished() const;
|
||||||
|
rpl::producer<not_null<const Value*>> valueError() const;
|
||||||
rpl::producer<not_null<const Value*>> verificationNeeded() const;
|
rpl::producer<not_null<const Value*>> verificationNeeded() const;
|
||||||
rpl::producer<not_null<const Value*>> verificationUpdate() const;
|
rpl::producer<not_null<const Value*>> verificationUpdate() const;
|
||||||
void verify(not_null<const Value*> value, const QString &code);
|
void verify(not_null<const Value*> value, const QString &code);
|
||||||
|
@ -244,6 +248,10 @@ public:
|
||||||
~FormController();
|
~FormController();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
struct FinalData {
|
||||||
|
QVector<MTPSecureValueHash> hashes;
|
||||||
|
QByteArray credentials;
|
||||||
|
};
|
||||||
EditFile *findEditFile(const FullMsgId &fullId);
|
EditFile *findEditFile(const FullMsgId &fullId);
|
||||||
EditFile *findEditFile(const FileKey &key);
|
EditFile *findEditFile(const FileKey &key);
|
||||||
std::pair<Value*, File*> findFile(const FileKey &key);
|
std::pair<Value*, File*> findFile(const FileKey &key);
|
||||||
|
@ -256,13 +264,15 @@ private:
|
||||||
void formFail(const RPCError &error);
|
void formFail(const RPCError &error);
|
||||||
void parseForm(const MTPaccount_AuthorizationForm &result);
|
void parseForm(const MTPaccount_AuthorizationForm &result);
|
||||||
void showForm();
|
void showForm();
|
||||||
Value parseValue(const MTPSecureValue &value) const;
|
Value parseValue(
|
||||||
|
const MTPSecureValue &value,
|
||||||
|
const std::vector<EditFile> &editData = {}) const;
|
||||||
std::vector<File> parseFiles(
|
std::vector<File> parseFiles(
|
||||||
const QVector<MTPSecureFile> &data,
|
const QVector<MTPSecureFile> &data,
|
||||||
const std::vector<EditFile> &editData = {}) const;
|
const std::vector<EditFile> &editData) const;
|
||||||
base::optional<File> parseFile(
|
base::optional<File> parseFile(
|
||||||
const MTPSecureFile &data,
|
const MTPSecureFile &data,
|
||||||
const std::vector<EditFile> &editData = {}) const;
|
const std::vector<EditFile> &editData) const;
|
||||||
void fillDownloadedFile(
|
void fillDownloadedFile(
|
||||||
File &destination,
|
File &destination,
|
||||||
const std::vector<EditFile> &source) const;
|
const std::vector<EditFile> &source) const;
|
||||||
|
@ -330,6 +340,7 @@ private:
|
||||||
void sendSaveRequest(
|
void sendSaveRequest(
|
||||||
not_null<Value*> value,
|
not_null<Value*> value,
|
||||||
const MTPInputSecureValue &data);
|
const MTPInputSecureValue &data);
|
||||||
|
FinalData prepareFinalData() const;
|
||||||
|
|
||||||
not_null<Window::Controller*> _controller;
|
not_null<Window::Controller*> _controller;
|
||||||
FormRequest _request;
|
FormRequest _request;
|
||||||
|
@ -346,6 +357,7 @@ private:
|
||||||
|
|
||||||
rpl::event_stream<not_null<const EditFile*>> _scanUpdated;
|
rpl::event_stream<not_null<const EditFile*>> _scanUpdated;
|
||||||
rpl::event_stream<not_null<const Value*>> _valueSaveFinished;
|
rpl::event_stream<not_null<const Value*>> _valueSaveFinished;
|
||||||
|
rpl::event_stream<not_null<const Value*>> _valueError;
|
||||||
rpl::event_stream<not_null<const Value*>> _verificationNeeded;
|
rpl::event_stream<not_null<const Value*>> _verificationNeeded;
|
||||||
rpl::event_stream<not_null<const Value*>> _verificationUpdate;
|
rpl::event_stream<not_null<const Value*>> _verificationUpdate;
|
||||||
|
|
||||||
|
@ -355,6 +367,7 @@ private:
|
||||||
mtpRequestId _saveSecretRequestId = 0;
|
mtpRequestId _saveSecretRequestId = 0;
|
||||||
rpl::event_stream<> _secretReady;
|
rpl::event_stream<> _secretReady;
|
||||||
rpl::event_stream<QString> _passwordError;
|
rpl::event_stream<QString> _passwordError;
|
||||||
|
mtpRequestId _submitRequestId = 0;
|
||||||
|
|
||||||
rpl::lifetime _uploaderSubscriptions;
|
rpl::lifetime _uploaderSubscriptions;
|
||||||
rpl::lifetime _lifetime;
|
rpl::lifetime _lifetime;
|
||||||
|
|
|
@ -8,6 +8,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "passport/passport_form_view_controller.h"
|
#include "passport/passport_form_view_controller.h"
|
||||||
|
|
||||||
#include "passport/passport_form_controller.h"
|
#include "passport/passport_form_controller.h"
|
||||||
|
#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"
|
||||||
|
|
||||||
namespace Passport {
|
namespace Passport {
|
||||||
namespace {
|
namespace {
|
||||||
|
@ -57,7 +61,8 @@ Scope::Scope(Type type, not_null<const Value*> fields)
|
||||||
, fields(fields) {
|
, fields(fields) {
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<Scope> ComputeScopes(not_null<FormController*> controller) {
|
std::vector<Scope> ComputeScopes(
|
||||||
|
not_null<const FormController*> controller) {
|
||||||
auto scopes = std::map<Scope::Type, Scope>();
|
auto scopes = std::map<Scope::Type, Scope>();
|
||||||
const auto &form = controller->form();
|
const auto &form = controller->form();
|
||||||
const auto findValue = [&](const Value::Type type) {
|
const auto findValue = [&](const Value::Type type) {
|
||||||
|
@ -74,15 +79,15 @@ std::vector<Scope> ComputeScopes(not_null<FormController*> controller) {
|
||||||
i->second.selfieRequired = (scopeType == Scope::Type::Identity)
|
i->second.selfieRequired = (scopeType == Scope::Type::Identity)
|
||||||
&& form.identitySelfieRequired;
|
&& form.identitySelfieRequired;
|
||||||
const auto alreadyIt = ranges::find(
|
const auto alreadyIt = ranges::find(
|
||||||
i->second.files,
|
i->second.documents,
|
||||||
type,
|
type,
|
||||||
[](not_null<const Value*> value) { return value->type; });
|
[](not_null<const Value*> value) { return value->type; });
|
||||||
if (alreadyIt != end(i->second.files)) {
|
if (alreadyIt != end(i->second.documents)) {
|
||||||
LOG(("API Error: Value type %1 multiple times in request."
|
LOG(("API Error: Value type %1 multiple times in request."
|
||||||
).arg(int(type)));
|
).arg(int(type)));
|
||||||
continue;
|
continue;
|
||||||
} else if (type != fieldsType) {
|
} else if (type != fieldsType) {
|
||||||
i->second.files.push_back(findValue(type));
|
i->second.documents.push_back(findValue(type));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
auto result = std::vector<Scope>();
|
auto result = std::vector<Scope>();
|
||||||
|
@ -93,4 +98,168 @@ std::vector<Scope> ComputeScopes(not_null<FormController*> controller) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString ComputeScopeRowReadyString(const Scope &scope) {
|
||||||
|
switch (scope.type) {
|
||||||
|
case Scope::Type::Identity:
|
||||||
|
case Scope::Type::Address: {
|
||||||
|
auto list = QStringList();
|
||||||
|
const auto &fields = scope.fields->data.parsed.fields;
|
||||||
|
const auto document = [&]() -> const Value* {
|
||||||
|
for (const auto &document : scope.documents) {
|
||||||
|
if (!document->scans.empty()) {
|
||||||
|
return document;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}();
|
||||||
|
if (document && scope.documents.size() > 1) {
|
||||||
|
list.push_back([&] {
|
||||||
|
switch (document->type) {
|
||||||
|
case Value::Type::Passport:
|
||||||
|
return lang(lng_passport_identity_passport);
|
||||||
|
case Value::Type::DriverLicense:
|
||||||
|
return lang(lng_passport_identity_license);
|
||||||
|
case Value::Type::IdentityCard:
|
||||||
|
return lang(lng_passport_identity_card);
|
||||||
|
case Value::Type::BankStatement:
|
||||||
|
return lang(lng_passport_address_statement);
|
||||||
|
case Value::Type::UtilityBill:
|
||||||
|
return lang(lng_passport_address_bill);
|
||||||
|
case Value::Type::RentalAgreement:
|
||||||
|
return lang(lng_passport_address_agreement);
|
||||||
|
default: Unexpected("Files type in ComputeScopeRowReadyString.");
|
||||||
|
}
|
||||||
|
}());
|
||||||
|
}
|
||||||
|
if (document
|
||||||
|
&& (document->scans.empty()
|
||||||
|
|| (scope.selfieRequired && !document->selfie))) {
|
||||||
|
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();
|
||||||
|
} else if (row.validate && !row.validate(i->second)) {
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
list.push_back(format ? format(i->second) : i->second);
|
||||||
|
} else if (!document) {
|
||||||
|
return QString();
|
||||||
|
} else {
|
||||||
|
const auto i = document->data.parsed.fields.find(row.key);
|
||||||
|
if (i == end(document->data.parsed.fields)) {
|
||||||
|
return QString();
|
||||||
|
} else if (row.validate && !row.validate(i->second)) {
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
list.push_back(i->second);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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))
|
||||||
|
? (format ? format(i->second) : i->second)
|
||||||
|
: QString();
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
Unexpected("Scope type in ComputeScopeRowReadyString.");
|
||||||
|
}
|
||||||
|
|
||||||
|
ScopeRow ComputeScopeRow(const Scope &scope) {
|
||||||
|
switch (scope.type) {
|
||||||
|
case Scope::Type::Identity:
|
||||||
|
if (scope.documents.empty()) {
|
||||||
|
return {
|
||||||
|
lang(lng_passport_personal_details),
|
||||||
|
lang(lng_passport_personal_details_enter),
|
||||||
|
ComputeScopeRowReadyString(scope)
|
||||||
|
};
|
||||||
|
} else if (scope.documents.size() == 1) {
|
||||||
|
switch (scope.documents.front()->type) {
|
||||||
|
case Value::Type::Passport:
|
||||||
|
return {
|
||||||
|
lang(lng_passport_identity_passport),
|
||||||
|
lang(lng_passport_identity_passport_upload),
|
||||||
|
ComputeScopeRowReadyString(scope)
|
||||||
|
};
|
||||||
|
case Value::Type::IdentityCard:
|
||||||
|
return {
|
||||||
|
lang(lng_passport_identity_card),
|
||||||
|
lang(lng_passport_identity_card_upload),
|
||||||
|
ComputeScopeRowReadyString(scope)
|
||||||
|
};
|
||||||
|
case Value::Type::DriverLicense:
|
||||||
|
return {
|
||||||
|
lang(lng_passport_identity_license),
|
||||||
|
lang(lng_passport_identity_license_upload),
|
||||||
|
ComputeScopeRowReadyString(scope)
|
||||||
|
};
|
||||||
|
default: Unexpected("Identity type in ComputeScopeRow.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
lang(lng_passport_identity_title),
|
||||||
|
lang(lng_passport_identity_description),
|
||||||
|
ComputeScopeRowReadyString(scope)
|
||||||
|
};
|
||||||
|
case Scope::Type::Address:
|
||||||
|
if (scope.documents.empty()) {
|
||||||
|
return {
|
||||||
|
lang(lng_passport_address),
|
||||||
|
lang(lng_passport_address_enter),
|
||||||
|
ComputeScopeRowReadyString(scope)
|
||||||
|
};
|
||||||
|
} else if (scope.documents.size() == 1) {
|
||||||
|
switch (scope.documents.front()->type) {
|
||||||
|
case Value::Type::BankStatement:
|
||||||
|
return {
|
||||||
|
lang(lng_passport_address_statement),
|
||||||
|
lang(lng_passport_address_statement_upload),
|
||||||
|
ComputeScopeRowReadyString(scope)
|
||||||
|
};
|
||||||
|
case Value::Type::UtilityBill:
|
||||||
|
return {
|
||||||
|
lang(lng_passport_address_bill),
|
||||||
|
lang(lng_passport_address_bill_upload),
|
||||||
|
ComputeScopeRowReadyString(scope)
|
||||||
|
};
|
||||||
|
case Value::Type::RentalAgreement:
|
||||||
|
return {
|
||||||
|
lang(lng_passport_address_agreement),
|
||||||
|
lang(lng_passport_address_agreement_upload),
|
||||||
|
ComputeScopeRowReadyString(scope)
|
||||||
|
};
|
||||||
|
default: Unexpected("Address type in ComputeScopeRow.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
lang(lng_passport_address_title),
|
||||||
|
lang(lng_passport_address_description),
|
||||||
|
ComputeScopeRowReadyString(scope)
|
||||||
|
};
|
||||||
|
case Scope::Type::Phone:
|
||||||
|
return {
|
||||||
|
lang(lng_passport_phone_title),
|
||||||
|
lang(lng_passport_phone_description),
|
||||||
|
ComputeScopeRowReadyString(scope)
|
||||||
|
};
|
||||||
|
case Scope::Type::Email:
|
||||||
|
return {
|
||||||
|
lang(lng_passport_email_title),
|
||||||
|
lang(lng_passport_email_description),
|
||||||
|
ComputeScopeRowReadyString(scope)
|
||||||
|
};
|
||||||
|
default: Unexpected("Scope type in ComputeScopeRow.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Passport
|
} // namespace Passport
|
||||||
|
|
|
@ -22,11 +22,20 @@ struct Scope {
|
||||||
|
|
||||||
Type type;
|
Type type;
|
||||||
not_null<const Value*> fields;
|
not_null<const Value*> fields;
|
||||||
std::vector<not_null<const Value*>> files;
|
std::vector<not_null<const Value*>> documents;
|
||||||
bool selfieRequired = false;
|
bool selfieRequired = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
std::vector<Scope> ComputeScopes(not_null<FormController*> controller);
|
struct ScopeRow {
|
||||||
|
QString title;
|
||||||
|
QString description;
|
||||||
|
QString ready;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<Scope> ComputeScopes(
|
||||||
|
not_null<const FormController*> controller);
|
||||||
|
QString ComputeScopeRowReadyString(const Scope &scope);
|
||||||
|
ScopeRow ComputeScopeRow(const Scope &scope);
|
||||||
|
|
||||||
class ViewController {
|
class ViewController {
|
||||||
public:
|
public:
|
||||||
|
|
|
@ -18,13 +18,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "layout.h"
|
#include "layout.h"
|
||||||
|
|
||||||
namespace Passport {
|
namespace Passport {
|
||||||
namespace {
|
|
||||||
|
|
||||||
PanelEditDocument::Scheme GetDocumentScheme(
|
EditDocumentScheme GetDocumentScheme(
|
||||||
Scope::Type type,
|
Scope::Type type,
|
||||||
base::optional<Value::Type> scansType = base::none) {
|
base::optional<Value::Type> scansType) {
|
||||||
using Scheme = PanelEditDocument::Scheme;
|
using Scheme = EditDocumentScheme;
|
||||||
|
using ValueClass = Scheme::ValueClass;
|
||||||
const auto DontFormat = nullptr;
|
const auto DontFormat = nullptr;
|
||||||
const auto CountryFormat = [](const QString &value) {
|
const auto CountryFormat = [](const QString &value) {
|
||||||
const auto result = CountrySelectBox::NameByISO(value);
|
const auto result = CountrySelectBox::NameByISO(value);
|
||||||
|
@ -78,7 +77,7 @@ PanelEditDocument::Scheme GetDocumentScheme(
|
||||||
}
|
}
|
||||||
result.rows = {
|
result.rows = {
|
||||||
{
|
{
|
||||||
Scheme::ValueType::Fields,
|
ValueClass::Fields,
|
||||||
PanelDetailsType::Text,
|
PanelDetailsType::Text,
|
||||||
qsl("first_name"),
|
qsl("first_name"),
|
||||||
lang(lng_passport_first_name),
|
lang(lng_passport_first_name),
|
||||||
|
@ -86,7 +85,7 @@ PanelEditDocument::Scheme GetDocumentScheme(
|
||||||
DontFormat,
|
DontFormat,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Scheme::ValueType::Fields,
|
ValueClass::Fields,
|
||||||
PanelDetailsType::Text,
|
PanelDetailsType::Text,
|
||||||
qsl("last_name"),
|
qsl("last_name"),
|
||||||
lang(lng_passport_last_name),
|
lang(lng_passport_last_name),
|
||||||
|
@ -94,7 +93,7 @@ PanelEditDocument::Scheme GetDocumentScheme(
|
||||||
DontFormat,
|
DontFormat,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Scheme::ValueType::Fields,
|
ValueClass::Fields,
|
||||||
PanelDetailsType::Date,
|
PanelDetailsType::Date,
|
||||||
qsl("birth_date"),
|
qsl("birth_date"),
|
||||||
lang(lng_passport_birth_date),
|
lang(lng_passport_birth_date),
|
||||||
|
@ -102,7 +101,7 @@ PanelEditDocument::Scheme GetDocumentScheme(
|
||||||
DontFormat,
|
DontFormat,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Scheme::ValueType::Fields,
|
ValueClass::Fields,
|
||||||
PanelDetailsType::Gender,
|
PanelDetailsType::Gender,
|
||||||
qsl("gender"),
|
qsl("gender"),
|
||||||
lang(lng_passport_gender),
|
lang(lng_passport_gender),
|
||||||
|
@ -110,7 +109,7 @@ PanelEditDocument::Scheme GetDocumentScheme(
|
||||||
GenderFormat,
|
GenderFormat,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Scheme::ValueType::Fields,
|
ValueClass::Fields,
|
||||||
PanelDetailsType::Country,
|
PanelDetailsType::Country,
|
||||||
qsl("country_code"),
|
qsl("country_code"),
|
||||||
lang(lng_passport_country),
|
lang(lng_passport_country),
|
||||||
|
@ -118,7 +117,7 @@ PanelEditDocument::Scheme GetDocumentScheme(
|
||||||
CountryFormat,
|
CountryFormat,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Scheme::ValueType::Scans,
|
ValueClass::Scans,
|
||||||
PanelDetailsType::Text,
|
PanelDetailsType::Text,
|
||||||
qsl("document_no"),
|
qsl("document_no"),
|
||||||
lang(lng_passport_document_number),
|
lang(lng_passport_document_number),
|
||||||
|
@ -126,7 +125,7 @@ PanelEditDocument::Scheme GetDocumentScheme(
|
||||||
DontFormat,
|
DontFormat,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Scheme::ValueType::Scans,
|
ValueClass::Scans,
|
||||||
PanelDetailsType::Date,
|
PanelDetailsType::Date,
|
||||||
qsl("expiry_date"),
|
qsl("expiry_date"),
|
||||||
lang(lng_passport_expiry_date),
|
lang(lng_passport_expiry_date),
|
||||||
|
@ -157,7 +156,7 @@ PanelEditDocument::Scheme GetDocumentScheme(
|
||||||
}
|
}
|
||||||
result.rows = {
|
result.rows = {
|
||||||
{
|
{
|
||||||
Scheme::ValueType::Fields,
|
ValueClass::Fields,
|
||||||
PanelDetailsType::Text,
|
PanelDetailsType::Text,
|
||||||
qsl("street_line1"),
|
qsl("street_line1"),
|
||||||
lang(lng_passport_street),
|
lang(lng_passport_street),
|
||||||
|
@ -165,7 +164,7 @@ PanelEditDocument::Scheme GetDocumentScheme(
|
||||||
DontFormat,
|
DontFormat,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Scheme::ValueType::Fields,
|
ValueClass::Fields,
|
||||||
PanelDetailsType::Text,
|
PanelDetailsType::Text,
|
||||||
qsl("street_line2"),
|
qsl("street_line2"),
|
||||||
lang(lng_passport_street),
|
lang(lng_passport_street),
|
||||||
|
@ -173,7 +172,7 @@ PanelEditDocument::Scheme GetDocumentScheme(
|
||||||
DontFormat,
|
DontFormat,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Scheme::ValueType::Fields,
|
ValueClass::Fields,
|
||||||
PanelDetailsType::Text,
|
PanelDetailsType::Text,
|
||||||
qsl("city"),
|
qsl("city"),
|
||||||
lang(lng_passport_city),
|
lang(lng_passport_city),
|
||||||
|
@ -181,7 +180,7 @@ PanelEditDocument::Scheme GetDocumentScheme(
|
||||||
DontFormat,
|
DontFormat,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Scheme::ValueType::Fields,
|
ValueClass::Fields,
|
||||||
PanelDetailsType::Text,
|
PanelDetailsType::Text,
|
||||||
qsl("state"),
|
qsl("state"),
|
||||||
lang(lng_passport_state),
|
lang(lng_passport_state),
|
||||||
|
@ -189,7 +188,7 @@ PanelEditDocument::Scheme GetDocumentScheme(
|
||||||
DontFormat,
|
DontFormat,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Scheme::ValueType::Fields,
|
ValueClass::Fields,
|
||||||
PanelDetailsType::Country,
|
PanelDetailsType::Country,
|
||||||
qsl("country_code"),
|
qsl("country_code"),
|
||||||
lang(lng_passport_country),
|
lang(lng_passport_country),
|
||||||
|
@ -197,7 +196,7 @@ PanelEditDocument::Scheme GetDocumentScheme(
|
||||||
CountryFormat,
|
CountryFormat,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Scheme::ValueType::Fields,
|
ValueClass::Fields,
|
||||||
PanelDetailsType::Text,
|
PanelDetailsType::Text,
|
||||||
qsl("post_code"),
|
qsl("post_code"),
|
||||||
lang(lng_passport_postcode),
|
lang(lng_passport_postcode),
|
||||||
|
@ -211,11 +210,13 @@ PanelEditDocument::Scheme GetDocumentScheme(
|
||||||
Unexpected("Type in GetDocumentScheme().");
|
Unexpected("Type in GetDocumentScheme().");
|
||||||
}
|
}
|
||||||
|
|
||||||
PanelEditContact::Scheme GetContactScheme(Scope::Type type) {
|
EditContactScheme GetContactScheme(Scope::Type type) {
|
||||||
using Scheme = PanelEditContact::Scheme;
|
using Scheme = EditContactScheme;
|
||||||
|
using ValueType = Scheme::ValueType;
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case Scope::Type::Phone: {
|
case Scope::Type::Phone: {
|
||||||
auto result = Scheme(Scheme::ValueType::Phone);
|
auto result = Scheme(ValueType::Phone);
|
||||||
result.aboutExisting = lang(lng_passport_use_existing_phone);
|
result.aboutExisting = lang(lng_passport_use_existing_phone);
|
||||||
result.newHeader = lang(lng_passport_new_phone);
|
result.newHeader = lang(lng_passport_new_phone);
|
||||||
result.aboutNew = lang(lng_passport_new_phone_code);
|
result.aboutNew = lang(lng_passport_new_phone_code);
|
||||||
|
@ -234,7 +235,7 @@ PanelEditContact::Scheme GetContactScheme(Scope::Type type) {
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case Scope::Type::Email: {
|
case Scope::Type::Email: {
|
||||||
auto result = Scheme(Scheme::ValueType::Text);
|
auto result = Scheme(ValueType::Text);
|
||||||
result.aboutExisting = lang(lng_passport_use_existing_email);
|
result.aboutExisting = lang(lng_passport_use_existing_email);
|
||||||
result.newHeader = lang(lng_passport_new_email);
|
result.newHeader = lang(lng_passport_new_email);
|
||||||
result.newPlaceholder = langFactory(lng_passport_email_title);
|
result.newPlaceholder = langFactory(lng_passport_email_title);
|
||||||
|
@ -253,8 +254,6 @@ PanelEditContact::Scheme GetContactScheme(Scope::Type type) {
|
||||||
Unexpected("Type in GetContactScheme().");
|
Unexpected("Type in GetContactScheme().");
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
BoxPointer::BoxPointer(QPointer<BoxContent> value)
|
BoxPointer::BoxPointer(QPointer<BoxContent> value)
|
||||||
: _value(value) {
|
: _value(value) {
|
||||||
}
|
}
|
||||||
|
@ -323,158 +322,6 @@ QString PanelController::privacyPolicyUrl() const {
|
||||||
return _form->privacyPolicyUrl();
|
return _form->privacyPolicyUrl();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto PanelController::collectRowInfo(const Scope &scope) const -> Row {
|
|
||||||
switch (scope.type) {
|
|
||||||
case Scope::Type::Identity:
|
|
||||||
if (scope.files.empty()) {
|
|
||||||
return {
|
|
||||||
lang(lng_passport_personal_details),
|
|
||||||
lang(lng_passport_personal_details_enter)
|
|
||||||
};
|
|
||||||
} else if (scope.files.size() == 1) {
|
|
||||||
switch (scope.files.front()->type) {
|
|
||||||
case Value::Type::Passport:
|
|
||||||
return {
|
|
||||||
lang(lng_passport_identity_passport),
|
|
||||||
lang(lng_passport_identity_passport_upload)
|
|
||||||
};
|
|
||||||
case Value::Type::IdentityCard:
|
|
||||||
return {
|
|
||||||
lang(lng_passport_identity_card),
|
|
||||||
lang(lng_passport_identity_card_upload)
|
|
||||||
};
|
|
||||||
case Value::Type::DriverLicense:
|
|
||||||
return {
|
|
||||||
lang(lng_passport_identity_license),
|
|
||||||
lang(lng_passport_identity_license_upload)
|
|
||||||
};
|
|
||||||
default: Unexpected("Identity type in collectRowInfo.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
lang(lng_passport_identity_title),
|
|
||||||
lang(lng_passport_identity_description)
|
|
||||||
};
|
|
||||||
case Scope::Type::Address:
|
|
||||||
if (scope.files.empty()) {
|
|
||||||
return {
|
|
||||||
lang(lng_passport_address),
|
|
||||||
lang(lng_passport_address_enter)
|
|
||||||
};
|
|
||||||
} else if (scope.files.size() == 1) {
|
|
||||||
switch (scope.files.front()->type) {
|
|
||||||
case Value::Type::BankStatement:
|
|
||||||
return {
|
|
||||||
lang(lng_passport_address_statement),
|
|
||||||
lang(lng_passport_address_statement_upload)
|
|
||||||
};
|
|
||||||
case Value::Type::UtilityBill:
|
|
||||||
return {
|
|
||||||
lang(lng_passport_address_bill),
|
|
||||||
lang(lng_passport_address_bill_upload)
|
|
||||||
};
|
|
||||||
case Value::Type::RentalAgreement:
|
|
||||||
return {
|
|
||||||
lang(lng_passport_address_agreement),
|
|
||||||
lang(lng_passport_address_agreement_upload)
|
|
||||||
};
|
|
||||||
default: Unexpected("Address type in collectRowInfo.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
lang(lng_passport_address_title),
|
|
||||||
lang(lng_passport_address_description)
|
|
||||||
};
|
|
||||||
case Scope::Type::Phone:
|
|
||||||
return {
|
|
||||||
lang(lng_passport_phone_title),
|
|
||||||
lang(lng_passport_phone_description)
|
|
||||||
};
|
|
||||||
case Scope::Type::Email:
|
|
||||||
return {
|
|
||||||
lang(lng_passport_email_title),
|
|
||||||
lang(lng_passport_email_description)
|
|
||||||
};
|
|
||||||
default: Unexpected("Scope type in collectRowInfo.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QString PanelController::collectRowReadyString(const Scope &scope) const {
|
|
||||||
switch (scope.type) {
|
|
||||||
case Scope::Type::Identity:
|
|
||||||
case Scope::Type::Address: {
|
|
||||||
auto list = QStringList();
|
|
||||||
const auto &fields = scope.fields->data.parsed.fields;
|
|
||||||
const auto files = [&]() -> const Value* {
|
|
||||||
for (const auto &files : scope.files) {
|
|
||||||
if (!files->files.empty()) {
|
|
||||||
return files;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
}();
|
|
||||||
if (files && scope.files.size() > 1) {
|
|
||||||
list.push_back([&] {
|
|
||||||
switch (files->type) {
|
|
||||||
case Value::Type::Passport:
|
|
||||||
return lang(lng_passport_identity_passport);
|
|
||||||
case Value::Type::DriverLicense:
|
|
||||||
return lang(lng_passport_identity_license);
|
|
||||||
case Value::Type::IdentityCard:
|
|
||||||
return lang(lng_passport_identity_card);
|
|
||||||
case Value::Type::BankStatement:
|
|
||||||
return lang(lng_passport_address_statement);
|
|
||||||
case Value::Type::UtilityBill:
|
|
||||||
return lang(lng_passport_address_bill);
|
|
||||||
case Value::Type::RentalAgreement:
|
|
||||||
return lang(lng_passport_address_agreement);
|
|
||||||
default: Unexpected("Files type in collectRowReadyString.");
|
|
||||||
}
|
|
||||||
}());
|
|
||||||
}
|
|
||||||
if (files
|
|
||||||
&& (files->files.empty()
|
|
||||||
|| (scope.selfieRequired && !files->selfie))) {
|
|
||||||
return QString();
|
|
||||||
}
|
|
||||||
const auto scheme = GetDocumentScheme(scope.type);
|
|
||||||
for (const auto &row : scheme.rows) {
|
|
||||||
const auto format = row.format;
|
|
||||||
if (row.type == PanelEditDocument::Scheme::ValueType::Fields) {
|
|
||||||
const auto i = fields.find(row.key);
|
|
||||||
if (i == end(fields)) {
|
|
||||||
return QString();
|
|
||||||
} else if (row.validate && !row.validate(i->second)) {
|
|
||||||
return QString();
|
|
||||||
}
|
|
||||||
list.push_back(format ? format(i->second) : i->second);
|
|
||||||
} else if (!files) {
|
|
||||||
return QString();
|
|
||||||
} else {
|
|
||||||
const auto i = files->data.parsed.fields.find(row.key);
|
|
||||||
if (i == end(files->data.parsed.fields)) {
|
|
||||||
return QString();
|
|
||||||
} else if (row.validate && !row.validate(i->second)) {
|
|
||||||
return QString();
|
|
||||||
}
|
|
||||||
list.push_back(i->second);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
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))
|
|
||||||
? (format ? format(i->second) : i->second)
|
|
||||||
: QString();
|
|
||||||
} break;
|
|
||||||
}
|
|
||||||
Unexpected("Scope type in collectRowReadyString.");
|
|
||||||
}
|
|
||||||
|
|
||||||
void PanelController::fillRows(
|
void PanelController::fillRows(
|
||||||
base::lambda<void(
|
base::lambda<void(
|
||||||
QString title,
|
QString title,
|
||||||
|
@ -484,15 +331,18 @@ void PanelController::fillRows(
|
||||||
_scopes = ComputeScopes(_form);
|
_scopes = ComputeScopes(_form);
|
||||||
}
|
}
|
||||||
for (const auto &scope : _scopes) {
|
for (const auto &scope : _scopes) {
|
||||||
const auto row = collectRowInfo(scope);
|
const auto row = ComputeScopeRow(scope);
|
||||||
const auto ready = collectRowReadyString(scope);
|
|
||||||
callback(
|
callback(
|
||||||
row.title,
|
row.title,
|
||||||
ready.isEmpty() ? row.description : ready,
|
row.ready.isEmpty() ? row.description : row.ready,
|
||||||
!ready.isEmpty());
|
!row.ready.isEmpty());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PanelController::submitForm() {
|
||||||
|
_form->submit();
|
||||||
|
}
|
||||||
|
|
||||||
void PanelController::submitPassword(const QString &password) {
|
void PanelController::submitPassword(const QString &password) {
|
||||||
_form->submitPassword(password);
|
_form->submitPassword(password);
|
||||||
}
|
}
|
||||||
|
@ -515,71 +365,71 @@ QString PanelController::defaultPhoneNumber() const {
|
||||||
|
|
||||||
void PanelController::uploadScan(QByteArray &&content) {
|
void PanelController::uploadScan(QByteArray &&content) {
|
||||||
Expects(_editScope != nullptr);
|
Expects(_editScope != nullptr);
|
||||||
Expects(_editScopeFilesIndex >= 0
|
Expects(_editDocumentIndex >= 0
|
||||||
&& _editScopeFilesIndex < _editScope->files.size());
|
&& _editDocumentIndex < _editScope->documents.size());
|
||||||
|
|
||||||
_form->uploadScan(
|
_form->uploadScan(
|
||||||
_editScope->files[_editScopeFilesIndex],
|
_editScope->documents[_editDocumentIndex],
|
||||||
std::move(content));
|
std::move(content));
|
||||||
}
|
}
|
||||||
|
|
||||||
void PanelController::deleteScan(int fileIndex) {
|
void PanelController::deleteScan(int fileIndex) {
|
||||||
Expects(_editScope != nullptr);
|
Expects(_editScope != nullptr);
|
||||||
Expects(_editScopeFilesIndex >= 0
|
Expects(_editDocumentIndex >= 0
|
||||||
&& _editScopeFilesIndex < _editScope->files.size());
|
&& _editDocumentIndex < _editScope->documents.size());
|
||||||
|
|
||||||
_form->deleteScan(
|
_form->deleteScan(
|
||||||
_editScope->files[_editScopeFilesIndex],
|
_editScope->documents[_editDocumentIndex],
|
||||||
fileIndex);
|
fileIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PanelController::restoreScan(int fileIndex) {
|
void PanelController::restoreScan(int fileIndex) {
|
||||||
Expects(_editScope != nullptr);
|
Expects(_editScope != nullptr);
|
||||||
Expects(_editScopeFilesIndex >= 0
|
Expects(_editDocumentIndex >= 0
|
||||||
&& _editScopeFilesIndex < _editScope->files.size());
|
&& _editDocumentIndex < _editScope->documents.size());
|
||||||
|
|
||||||
_form->restoreScan(
|
_form->restoreScan(
|
||||||
_editScope->files[_editScopeFilesIndex],
|
_editScope->documents[_editDocumentIndex],
|
||||||
fileIndex);
|
fileIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PanelController::uploadSelfie(QByteArray &&content) {
|
void PanelController::uploadSelfie(QByteArray &&content) {
|
||||||
Expects(_editScope != nullptr);
|
Expects(_editScope != nullptr);
|
||||||
Expects(_editScopeFilesIndex >= 0
|
Expects(_editDocumentIndex >= 0
|
||||||
&& _editScopeFilesIndex < _editScope->files.size());
|
&& _editDocumentIndex < _editScope->documents.size());
|
||||||
Expects(_editScope->selfieRequired);
|
Expects(_editScope->selfieRequired);
|
||||||
|
|
||||||
_form->uploadSelfie(
|
_form->uploadSelfie(
|
||||||
_editScope->files[_editScopeFilesIndex],
|
_editScope->documents[_editDocumentIndex],
|
||||||
std::move(content));
|
std::move(content));
|
||||||
}
|
}
|
||||||
|
|
||||||
void PanelController::deleteSelfie() {
|
void PanelController::deleteSelfie() {
|
||||||
Expects(_editScope != nullptr);
|
Expects(_editScope != nullptr);
|
||||||
Expects(_editScopeFilesIndex >= 0
|
Expects(_editDocumentIndex >= 0
|
||||||
&& _editScopeFilesIndex < _editScope->files.size());
|
&& _editDocumentIndex < _editScope->documents.size());
|
||||||
Expects(_editScope->selfieRequired);
|
Expects(_editScope->selfieRequired);
|
||||||
|
|
||||||
_form->deleteSelfie(
|
_form->deleteSelfie(
|
||||||
_editScope->files[_editScopeFilesIndex]);
|
_editScope->documents[_editDocumentIndex]);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PanelController::restoreSelfie() {
|
void PanelController::restoreSelfie() {
|
||||||
Expects(_editScope != nullptr);
|
Expects(_editScope != nullptr);
|
||||||
Expects(_editScopeFilesIndex >= 0
|
Expects(_editDocumentIndex >= 0
|
||||||
&& _editScopeFilesIndex < _editScope->files.size());
|
&& _editDocumentIndex < _editScope->documents.size());
|
||||||
Expects(_editScope->selfieRequired);
|
Expects(_editScope->selfieRequired);
|
||||||
|
|
||||||
_form->restoreSelfie(
|
_form->restoreSelfie(
|
||||||
_editScope->files[_editScopeFilesIndex]);
|
_editScope->documents[_editDocumentIndex]);
|
||||||
}
|
}
|
||||||
|
|
||||||
rpl::producer<ScanInfo> PanelController::scanUpdated() const {
|
rpl::producer<ScanInfo> PanelController::scanUpdated() const {
|
||||||
return _form->scanUpdated(
|
return _form->scanUpdated(
|
||||||
) | rpl::filter([=](not_null<const EditFile*> file) {
|
) | rpl::filter([=](not_null<const EditFile*> file) {
|
||||||
return (_editScope != nullptr)
|
return (_editScope != nullptr)
|
||||||
&& (_editScopeFilesIndex >= 0)
|
&& (_editDocumentIndex >= 0)
|
||||||
&& (file->value == _editScope->files[_editScopeFilesIndex]);
|
&& (file->value == _editScope->documents[_editDocumentIndex]);
|
||||||
}) | rpl::map([=](not_null<const EditFile*> file) {
|
}) | rpl::map([=](not_null<const EditFile*> file) {
|
||||||
return collectScanInfo(*file);
|
return collectScanInfo(*file);
|
||||||
});
|
});
|
||||||
|
@ -587,7 +437,7 @@ rpl::producer<ScanInfo> PanelController::scanUpdated() const {
|
||||||
|
|
||||||
ScanInfo PanelController::collectScanInfo(const EditFile &file) const {
|
ScanInfo PanelController::collectScanInfo(const EditFile &file) const {
|
||||||
Expects(_editScope != nullptr);
|
Expects(_editScope != nullptr);
|
||||||
Expects(_editScopeFilesIndex >= 0);
|
Expects(_editDocumentIndex >= 0);
|
||||||
|
|
||||||
const auto status = [&] {
|
const auto status = [&] {
|
||||||
if (file.fields.accessHash) {
|
if (file.fields.accessHash) {
|
||||||
|
@ -618,10 +468,10 @@ ScanInfo PanelController::collectScanInfo(const EditFile &file) const {
|
||||||
return formatDownloadText(0, file.fields.size);
|
return formatDownloadText(0, file.fields.size);
|
||||||
}
|
}
|
||||||
}();
|
}();
|
||||||
const auto &files = _editScope->files;
|
const auto &documents = _editScope->documents;
|
||||||
auto isSelfie = (file.value == files[_editScopeFilesIndex])
|
auto isSelfie = (file.value == documents[_editDocumentIndex])
|
||||||
&& (files[_editScopeFilesIndex]->selfieInEdit.has_value())
|
&& (documents[_editDocumentIndex]->selfieInEdit.has_value())
|
||||||
&& (&file == &*files[_editScopeFilesIndex]->selfieInEdit);
|
&& (&file == &*documents[_editDocumentIndex]->selfieInEdit);
|
||||||
return {
|
return {
|
||||||
FileKey{ file.fields.id, file.fields.dcId },
|
FileKey{ file.fields.id, file.fields.dcId },
|
||||||
status,
|
status,
|
||||||
|
@ -664,7 +514,7 @@ void PanelController::ensurePanelCreated() {
|
||||||
int PanelController::findNonEmptyIndex(
|
int PanelController::findNonEmptyIndex(
|
||||||
const std::vector<not_null<const Value*>> &files) const {
|
const std::vector<not_null<const Value*>> &files) const {
|
||||||
const auto i = ranges::find_if(files, [](not_null<const Value*> file) {
|
const auto i = ranges::find_if(files, [](not_null<const Value*> file) {
|
||||||
return !file->files.empty();
|
return !file->scans.empty();
|
||||||
});
|
});
|
||||||
if (i != end(files)) {
|
if (i != end(files)) {
|
||||||
return (i - begin(files));
|
return (i - begin(files));
|
||||||
|
@ -677,13 +527,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].files.empty()) {
|
if (_scopes[index].documents.empty()) {
|
||||||
editScope(index, -1);
|
editScope(index, -1);
|
||||||
} else {
|
} else {
|
||||||
const auto filesIndex = findNonEmptyIndex(_scopes[index].files);
|
const auto documentIndex = findNonEmptyIndex(
|
||||||
if (filesIndex >= 0) {
|
_scopes[index].documents);
|
||||||
editScope(index, filesIndex);
|
if (documentIndex >= 0) {
|
||||||
} else if (_scopes[index].files.size() > 1) {
|
editScope(index, documentIndex);
|
||||||
|
} else if (_scopes[index].documents.size() > 1) {
|
||||||
requestScopeFilesType(index);
|
requestScopeFilesType(index);
|
||||||
} else {
|
} else {
|
||||||
editWithUpload(index, 0);
|
editWithUpload(index, 0);
|
||||||
|
@ -696,14 +547,14 @@ void PanelController::requestScopeFilesType(int index) {
|
||||||
Expects(index >= 0 && index < _scopes.size());
|
Expects(index >= 0 && index < _scopes.size());
|
||||||
|
|
||||||
const auto type = _scopes[index].type;
|
const auto type = _scopes[index].type;
|
||||||
_scopeFilesTypeBox = [&] {
|
_scopeDocumentTypeBox = [&] {
|
||||||
if (type == Scope::Type::Identity) {
|
if (type == Scope::Type::Identity) {
|
||||||
return show(RequestIdentityType(
|
return show(RequestIdentityType(
|
||||||
[=](int filesIndex) {
|
[=](int documentIndex) {
|
||||||
editWithUpload(index, filesIndex);
|
editWithUpload(index, documentIndex);
|
||||||
},
|
},
|
||||||
ranges::view::all(
|
ranges::view::all(
|
||||||
_scopes[index].files
|
_scopes[index].documents
|
||||||
) | ranges::view::transform([](auto value) {
|
) | ranges::view::transform([](auto value) {
|
||||||
return value->type;
|
return value->type;
|
||||||
}) | ranges::view::transform([](Value::Type type) {
|
}) | ranges::view::transform([](Value::Type type) {
|
||||||
|
@ -720,11 +571,11 @@ void PanelController::requestScopeFilesType(int index) {
|
||||||
}) | ranges::to_vector));
|
}) | ranges::to_vector));
|
||||||
} else if (type == Scope::Type::Address) {
|
} else if (type == Scope::Type::Address) {
|
||||||
return show(RequestAddressType(
|
return show(RequestAddressType(
|
||||||
[=](int filesIndex) {
|
[=](int documentIndex) {
|
||||||
editWithUpload(index, filesIndex);
|
editWithUpload(index, documentIndex);
|
||||||
},
|
},
|
||||||
ranges::view::all(
|
ranges::view::all(
|
||||||
_scopes[index].files
|
_scopes[index].documents
|
||||||
) | ranges::view::transform([](auto value) {
|
) | ranges::view::transform([](auto value) {
|
||||||
return value->type;
|
return value->type;
|
||||||
}) | ranges::view::transform([](Value::Type type) {
|
}) | ranges::view::transform([](Value::Type type) {
|
||||||
|
@ -745,51 +596,53 @@ void PanelController::requestScopeFilesType(int index) {
|
||||||
}();
|
}();
|
||||||
}
|
}
|
||||||
|
|
||||||
void PanelController::editWithUpload(int index, int filesIndex) {
|
void PanelController::editWithUpload(int index, int documentIndex) {
|
||||||
Expects(_panel != nullptr);
|
Expects(_panel != nullptr);
|
||||||
Expects(index >= 0 && index < _scopes.size());
|
Expects(index >= 0 && index < _scopes.size());
|
||||||
Expects(filesIndex >= 0 && filesIndex < _scopes[index].files.size());
|
Expects(documentIndex >= 0
|
||||||
|
&& documentIndex < _scopes[index].documents.size());
|
||||||
|
|
||||||
EditScans::ChooseScan(
|
EditScans::ChooseScan(
|
||||||
base::lambda_guarded(_panel.get(),
|
base::lambda_guarded(_panel.get(),
|
||||||
[=](QByteArray &&content) {
|
[=](QByteArray &&content) {
|
||||||
base::take(_scopeFilesTypeBox);
|
base::take(_scopeDocumentTypeBox);
|
||||||
editScope(index, filesIndex);
|
editScope(index, documentIndex);
|
||||||
uploadScan(std::move(content));
|
uploadScan(std::move(content));
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
void PanelController::editScope(int index, int filesIndex) {
|
void PanelController::editScope(int index, int documentIndex) {
|
||||||
Expects(_panel != nullptr);
|
Expects(_panel != nullptr);
|
||||||
Expects(index >= 0 && index < _scopes.size());
|
Expects(index >= 0 && index < _scopes.size());
|
||||||
Expects((filesIndex < 0)
|
Expects((documentIndex < 0)
|
||||||
|| (filesIndex >= 0 && filesIndex < _scopes[index].files.size()));
|
|| (documentIndex >= 0
|
||||||
|
&& documentIndex < _scopes[index].documents.size()));
|
||||||
|
|
||||||
_editScope = &_scopes[index];
|
_editScope = &_scopes[index];
|
||||||
_editScopeFilesIndex = filesIndex;
|
_editDocumentIndex = documentIndex;
|
||||||
|
|
||||||
_form->startValueEdit(_editScope->fields);
|
_form->startValueEdit(_editScope->fields);
|
||||||
if (_editScopeFilesIndex >= 0) {
|
if (_editDocumentIndex >= 0) {
|
||||||
_form->startValueEdit(_editScope->files[_editScopeFilesIndex]);
|
_form->startValueEdit(_editScope->documents[_editDocumentIndex]);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto content = [&]() -> object_ptr<Ui::RpWidget> {
|
auto content = [&]() -> object_ptr<Ui::RpWidget> {
|
||||||
switch (_editScope->type) {
|
switch (_editScope->type) {
|
||||||
case Scope::Type::Identity:
|
case Scope::Type::Identity:
|
||||||
case Scope::Type::Address: {
|
case Scope::Type::Address: {
|
||||||
const auto &files = _editScope->files;
|
const auto &documents = _editScope->documents;
|
||||||
auto result = (_editScopeFilesIndex >= 0)
|
auto result = (_editDocumentIndex >= 0)
|
||||||
? object_ptr<PanelEditDocument>(
|
? object_ptr<PanelEditDocument>(
|
||||||
_panel.get(),
|
_panel.get(),
|
||||||
this,
|
this,
|
||||||
GetDocumentScheme(
|
GetDocumentScheme(
|
||||||
_editScope->type,
|
_editScope->type,
|
||||||
files[_editScopeFilesIndex]->type),
|
documents[_editDocumentIndex]->type),
|
||||||
_editScope->fields->data.parsedInEdit,
|
_editScope->fields->data.parsedInEdit,
|
||||||
files[_editScopeFilesIndex]->data.parsedInEdit,
|
documents[_editDocumentIndex]->data.parsedInEdit,
|
||||||
valueFiles(*files[_editScopeFilesIndex]),
|
valueFiles(*documents[_editDocumentIndex]),
|
||||||
(_editScope->selfieRequired
|
(_editScope->selfieRequired
|
||||||
? valueSelfie(*files[_editScopeFilesIndex])
|
? valueSelfie(*documents[_editDocumentIndex])
|
||||||
: nullptr))
|
: nullptr))
|
||||||
: object_ptr<PanelEditDocument>(
|
: object_ptr<PanelEditDocument>(
|
||||||
_panel.get(),
|
_panel.get(),
|
||||||
|
@ -850,8 +703,8 @@ void PanelController::processValueSaveFinished(
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto value1 = _editScope->fields;
|
const auto value1 = _editScope->fields;
|
||||||
const auto value2 = (_editScopeFilesIndex >= 0)
|
const auto value2 = (_editDocumentIndex >= 0)
|
||||||
? _editScope->files[_editScopeFilesIndex].get()
|
? _editScope->documents[_editDocumentIndex].get()
|
||||||
: nullptr;
|
: nullptr;
|
||||||
if (value == value1 || value == value2) {
|
if (value == value1 || value == value2) {
|
||||||
if (!_form->savingValue(value1)
|
if (!_form->savingValue(value1)
|
||||||
|
@ -925,8 +778,8 @@ void PanelController::processVerificationNeeded(
|
||||||
std::vector<ScanInfo> PanelController::valueFiles(
|
std::vector<ScanInfo> PanelController::valueFiles(
|
||||||
const Value &value) const {
|
const Value &value) const {
|
||||||
auto result = std::vector<ScanInfo>();
|
auto result = std::vector<ScanInfo>();
|
||||||
for (const auto &file : value.filesInEdit) {
|
for (const auto &scan : value.scansInEdit) {
|
||||||
result.push_back(collectScanInfo(file));
|
result.push_back(collectScanInfo(scan));
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -943,9 +796,9 @@ std::unique_ptr<ScanInfo> PanelController::valueSelfie(
|
||||||
void PanelController::cancelValueEdit() {
|
void PanelController::cancelValueEdit() {
|
||||||
if (const auto scope = base::take(_editScope)) {
|
if (const auto scope = base::take(_editScope)) {
|
||||||
_form->cancelValueEdit(scope->fields);
|
_form->cancelValueEdit(scope->fields);
|
||||||
const auto index = std::exchange(_editScopeFilesIndex, -1);
|
const auto index = std::exchange(_editDocumentIndex, -1);
|
||||||
if (index >= 0) {
|
if (index >= 0) {
|
||||||
_form->cancelValueEdit(scope->files[index]);
|
_form->cancelValueEdit(scope->documents[index]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -955,9 +808,9 @@ void PanelController::saveScope(ValueMap &&data, ValueMap &&filesData) {
|
||||||
Expects(_editScope != nullptr);
|
Expects(_editScope != nullptr);
|
||||||
|
|
||||||
_form->saveValueEdit(_editScope->fields, std::move(data));
|
_form->saveValueEdit(_editScope->fields, std::move(data));
|
||||||
if (_editScopeFilesIndex >= 0) {
|
if (_editDocumentIndex >= 0) {
|
||||||
_form->saveValueEdit(
|
_form->saveValueEdit(
|
||||||
_editScope->files[_editScopeFilesIndex],
|
_editScope->documents[_editDocumentIndex],
|
||||||
std::move(filesData));
|
std::move(filesData));
|
||||||
} else {
|
} else {
|
||||||
Assert(filesData.fields.empty());
|
Assert(filesData.fields.empty());
|
||||||
|
@ -971,9 +824,9 @@ bool PanelController::editScopeChanged(
|
||||||
|
|
||||||
if (_form->editValueChanged(_editScope->fields, data)) {
|
if (_form->editValueChanged(_editScope->fields, data)) {
|
||||||
return true;
|
return true;
|
||||||
} else if (_editScopeFilesIndex >= 0) {
|
} else if (_editDocumentIndex >= 0) {
|
||||||
return _form->editValueChanged(
|
return _form->editValueChanged(
|
||||||
_editScope->files[_editScopeFilesIndex],
|
_editScope->documents[_editDocumentIndex],
|
||||||
filesData);
|
filesData);
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -15,6 +15,14 @@ namespace Passport {
|
||||||
class FormController;
|
class FormController;
|
||||||
class Panel;
|
class Panel;
|
||||||
|
|
||||||
|
struct EditDocumentScheme;
|
||||||
|
struct EditContactScheme;
|
||||||
|
|
||||||
|
EditDocumentScheme GetDocumentScheme(
|
||||||
|
Scope::Type type,
|
||||||
|
base::optional<Value::Type> scansType = base::none);
|
||||||
|
EditContactScheme GetContactScheme(Scope::Type type);
|
||||||
|
|
||||||
struct ScanInfo {
|
struct ScanInfo {
|
||||||
FileKey key;
|
FileKey key;
|
||||||
QString status;
|
QString status;
|
||||||
|
@ -47,7 +55,7 @@ public:
|
||||||
|
|
||||||
not_null<UserData*> bot() const;
|
not_null<UserData*> bot() const;
|
||||||
QString privacyPolicyUrl() const;
|
QString privacyPolicyUrl() const;
|
||||||
|
void submitForm();
|
||||||
void submitPassword(const QString &password);
|
void submitPassword(const QString &password);
|
||||||
rpl::producer<QString> passwordError() const;
|
rpl::producer<QString> passwordError() const;
|
||||||
QString passwordHint() const;
|
QString passwordHint() const;
|
||||||
|
@ -87,14 +95,10 @@ public:
|
||||||
rpl::lifetime &lifetime();
|
rpl::lifetime &lifetime();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct Row {
|
|
||||||
QString title;
|
|
||||||
QString description;
|
|
||||||
};
|
|
||||||
void ensurePanelCreated();
|
void ensurePanelCreated();
|
||||||
|
|
||||||
void editScope(int index, int filesIndex);
|
void editScope(int index, int documentIndex);
|
||||||
void editWithUpload(int index, int filesIndex);
|
void editWithUpload(int index, int documentIndex);
|
||||||
int findNonEmptyIndex(
|
int findNonEmptyIndex(
|
||||||
const std::vector<not_null<const Value*>> &files) const;
|
const std::vector<not_null<const Value*>> &files) const;
|
||||||
void requestScopeFilesType(int index);
|
void requestScopeFilesType(int index);
|
||||||
|
@ -104,8 +108,6 @@ private:
|
||||||
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);
|
||||||
|
|
||||||
Row collectRowInfo(const Scope &scope) const;
|
|
||||||
QString collectRowReadyString(const Scope &scope) const;
|
|
||||||
ScanInfo collectScanInfo(const EditFile &file) const;
|
ScanInfo collectScanInfo(const EditFile &file) const;
|
||||||
QString getDefaultContactValue(Scope::Type type) const;
|
QString getDefaultContactValue(Scope::Type type) const;
|
||||||
|
|
||||||
|
@ -116,8 +118,8 @@ private:
|
||||||
base::lambda<bool()> _panelHasUnsavedChanges;
|
base::lambda<bool()> _panelHasUnsavedChanges;
|
||||||
BoxPointer _confirmForgetChangesBox;
|
BoxPointer _confirmForgetChangesBox;
|
||||||
Scope *_editScope = nullptr;
|
Scope *_editScope = nullptr;
|
||||||
int _editScopeFilesIndex = -1;
|
int _editDocumentIndex = -1;
|
||||||
BoxPointer _scopeFilesTypeBox;
|
BoxPointer _scopeDocumentTypeBox;
|
||||||
std::map<not_null<const Value*>, BoxPointer> _verificationBoxes;
|
std::map<not_null<const Value*>, BoxPointer> _verificationBoxes;
|
||||||
|
|
||||||
rpl::lifetime _lifetime;
|
rpl::lifetime _lifetime;
|
||||||
|
|
|
@ -159,7 +159,7 @@ void VerifyBox::prepare() {
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
PanelEditContact::Scheme::Scheme(ValueType type) : type(type) {
|
EditContactScheme::EditContactScheme(ValueType type) : type(type) {
|
||||||
}
|
}
|
||||||
|
|
||||||
PanelEditContact::PanelEditContact(
|
PanelEditContact::PanelEditContact(
|
||||||
|
|
|
@ -20,26 +20,28 @@ namespace Passport {
|
||||||
|
|
||||||
class PanelController;
|
class PanelController;
|
||||||
|
|
||||||
|
struct EditContactScheme {
|
||||||
|
enum class ValueType {
|
||||||
|
Phone,
|
||||||
|
Text,
|
||||||
|
};
|
||||||
|
explicit EditContactScheme(ValueType type);
|
||||||
|
|
||||||
|
ValueType type;
|
||||||
|
|
||||||
|
QString aboutExisting;
|
||||||
|
QString newHeader;
|
||||||
|
base::lambda<QString()> newPlaceholder;
|
||||||
|
QString aboutNew;
|
||||||
|
base::lambda<bool(const QString &value)> validate;
|
||||||
|
base::lambda<QString(const QString &value)> format;
|
||||||
|
base::lambda<QString(const QString &value)> postprocess;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
class PanelEditContact : public Ui::RpWidget {
|
class PanelEditContact : public Ui::RpWidget {
|
||||||
public:
|
public:
|
||||||
struct Scheme {
|
using Scheme = EditContactScheme;
|
||||||
enum class ValueType {
|
|
||||||
Phone,
|
|
||||||
Text,
|
|
||||||
};
|
|
||||||
explicit Scheme(ValueType type);
|
|
||||||
|
|
||||||
ValueType type;
|
|
||||||
|
|
||||||
QString aboutExisting;
|
|
||||||
QString newHeader;
|
|
||||||
base::lambda<QString()> newPlaceholder;
|
|
||||||
QString aboutNew;
|
|
||||||
base::lambda<bool(const QString &value)> validate;
|
|
||||||
base::lambda<QString(const QString &value)> format;
|
|
||||||
base::lambda<QString(const QString &value)> postprocess;
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
PanelEditContact(
|
PanelEditContact(
|
||||||
QWidget *parent,
|
QWidget *parent,
|
||||||
|
|
|
@ -234,7 +234,7 @@ not_null<Ui::RpWidget*> PanelEditDocument::setupContent(
|
||||||
|
|
||||||
for (auto i = 0, count = int(_scheme.rows.size()); i != count; ++i) {
|
for (auto i = 0, count = int(_scheme.rows.size()); i != count; ++i) {
|
||||||
const auto &row = _scheme.rows[i];
|
const auto &row = _scheme.rows[i];
|
||||||
auto fields = (row.type == Scheme::ValueType::Fields)
|
auto fields = (row.valueClass == Scheme::ValueClass::Fields)
|
||||||
? &data
|
? &data
|
||||||
: scanData;
|
: scanData;
|
||||||
if (!fields) {
|
if (!fields) {
|
||||||
|
@ -289,7 +289,7 @@ PanelEditDocument::Result PanelEditDocument::collect() const {
|
||||||
auto result = Result();
|
auto result = Result();
|
||||||
for (const auto [i, field] : _details) {
|
for (const auto [i, field] : _details) {
|
||||||
const auto &row = _scheme.rows[i];
|
const auto &row = _scheme.rows[i];
|
||||||
auto &fields = (row.type == Scheme::ValueType::Fields)
|
auto &fields = (row.valueClass == Scheme::ValueClass::Fields)
|
||||||
? result.data
|
? result.data
|
||||||
: result.filesData;
|
: result.filesData;
|
||||||
fields.fields[row.key] = field->valueCurrent();
|
fields.fields[row.key] = field->valueCurrent();
|
||||||
|
|
|
@ -26,26 +26,28 @@ class EditScans;
|
||||||
class PanelDetailsRow;
|
class PanelDetailsRow;
|
||||||
enum class PanelDetailsType;
|
enum class PanelDetailsType;
|
||||||
|
|
||||||
|
struct EditDocumentScheme {
|
||||||
|
enum class ValueClass {
|
||||||
|
Fields,
|
||||||
|
Scans,
|
||||||
|
};
|
||||||
|
struct Row {
|
||||||
|
ValueClass valueClass = ValueClass::Fields;
|
||||||
|
PanelDetailsType inputType = PanelDetailsType();
|
||||||
|
QString key;
|
||||||
|
QString label;
|
||||||
|
base::lambda<bool(const QString &value)> validate;
|
||||||
|
base::lambda<QString(const QString &value)> format;
|
||||||
|
};
|
||||||
|
std::vector<Row> rows;
|
||||||
|
QString rowsHeader;
|
||||||
|
QString scansHeader;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
class PanelEditDocument : public Ui::RpWidget {
|
class PanelEditDocument : public Ui::RpWidget {
|
||||||
public:
|
public:
|
||||||
struct Scheme {
|
using Scheme = EditDocumentScheme;
|
||||||
enum class ValueType {
|
|
||||||
Fields,
|
|
||||||
Scans,
|
|
||||||
};
|
|
||||||
struct Row {
|
|
||||||
ValueType type = ValueType::Fields;
|
|
||||||
PanelDetailsType inputType = PanelDetailsType();
|
|
||||||
QString key;
|
|
||||||
QString label;
|
|
||||||
base::lambda<bool(const QString &value)> validate;
|
|
||||||
base::lambda<QString(const QString &value)> format;
|
|
||||||
};
|
|
||||||
std::vector<Row> rows;
|
|
||||||
QString rowsHeader;
|
|
||||||
QString scansHeader;
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
PanelEditDocument(
|
PanelEditDocument(
|
||||||
QWidget *parent,
|
QWidget *parent,
|
||||||
|
|
|
@ -145,6 +145,10 @@ PanelForm::PanelForm(
|
||||||
void PanelForm::setupControls() {
|
void PanelForm::setupControls() {
|
||||||
const auto inner = setupContent();
|
const auto inner = setupContent();
|
||||||
|
|
||||||
|
_submit->addClickHandler([=] {
|
||||||
|
_controller->submitForm();
|
||||||
|
});
|
||||||
|
|
||||||
using namespace rpl::mappers;
|
using namespace rpl::mappers;
|
||||||
|
|
||||||
_topShadow->toggleOn(
|
_topShadow->toggleOn(
|
||||||
|
|
Loading…
Add table
Reference in a new issue