mirror of https://github.com/procxx/kepka.git
Update API scheme.
This commit is contained in:
parent
5cfead762d
commit
f0b7ff24b1
|
@ -1521,6 +1521,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
"lng_passport_email_title" = "Email";
|
"lng_passport_email_title" = "Email";
|
||||||
"lng_passport_email_description" = "Enter your email address";
|
"lng_passport_email_description" = "Enter your email address";
|
||||||
"lng_passport_accept_allow" = "You accept {policy} and allow their {bot} to send messages to you.";
|
"lng_passport_accept_allow" = "You accept {policy} and allow their {bot} to send messages to you.";
|
||||||
|
"lng_passport_allow" = "You allow {bot} to send messages to you.";
|
||||||
"lng_passport_policy" = "{bot} privacy policy";
|
"lng_passport_policy" = "{bot} privacy policy";
|
||||||
"lng_passport_authorize" = "Authorize";
|
"lng_passport_authorize" = "Authorize";
|
||||||
"lng_passport_form_error" = "Could not get authorization form.";
|
"lng_passport_form_error" = "Could not get authorization form.";
|
||||||
|
|
|
@ -975,30 +975,29 @@ secureFile#e0277a62 id:long access_hash:long size:int dc_id:int date:int file_ha
|
||||||
|
|
||||||
secureData#8aeabec3 data:bytes data_hash:bytes secret:bytes = SecureData;
|
secureData#8aeabec3 data:bytes data_hash:bytes secret:bytes = SecureData;
|
||||||
|
|
||||||
secureValueVerified#2c8e61e2 date:int = SecureValueVerified;
|
securePlainPhone#7d6099dd phone:string = SecurePlainData;
|
||||||
|
securePlainEmail#21ec5a5f email:string = SecurePlainData;
|
||||||
|
|
||||||
secureValueTypeIdentity#37da58ca = SecureValueType;
|
secureValueTypePersonalDetails#9d2a81e3 = SecureValueType;
|
||||||
|
secureValueTypePassport#3dac6a00 = SecureValueType;
|
||||||
|
secureValueTypeDriverLicense#6e425c4 = SecureValueType;
|
||||||
|
secureValueTypeIdentityCard#a0d0744b = SecureValueType;
|
||||||
secureValueTypeAddress#cbe31e26 = SecureValueType;
|
secureValueTypeAddress#cbe31e26 = SecureValueType;
|
||||||
|
secureValueTypeUtilityBill#fc36954e = SecureValueType;
|
||||||
|
secureValueTypeBankStatement#89137c0d = SecureValueType;
|
||||||
|
secureValueTypeRentalAgreement#8b883488 = SecureValueType;
|
||||||
secureValueTypePhone#b320aadb = SecureValueType;
|
secureValueTypePhone#b320aadb = SecureValueType;
|
||||||
secureValueTypeEmail#8e3ca7ee = SecureValueType;
|
secureValueTypeEmail#8e3ca7ee = SecureValueType;
|
||||||
|
|
||||||
secureValueIdentity#15fe72a2 flags:# data:SecureData files:Vector<SecureFile> verified:flags.0?SecureValueVerified = SecureValue;
|
secureValue#ec4134c8 flags:# type:SecureValueType data:flags.0?SecureData files:flags.1?Vector<SecureFile> plain_data:flags.2?SecurePlainData selfie:flags.3?SecureFile hash:bytes = SecureValue;
|
||||||
secureValueAddress#88109b79 flags:# data:SecureData files:Vector<SecureFile> verified:flags.0?SecureValueVerified = SecureValue;
|
|
||||||
secureValuePhone#e39470bf flags:# phone:string verified:flags.0?SecureValueVerified = SecureValue;
|
|
||||||
secureValueEmail#35d804cd flags:# email:string verified:flags.0?SecureValueVerified = SecureValue;
|
|
||||||
|
|
||||||
inputSecureValueIdentity#94fa65b data:SecureData files:Vector<InputSecureFile> = InputSecureValue;
|
inputSecureValue#c0da30f0 flags:# type:SecureValueType data:flags.0?SecureData files:flags.1?Vector<InputSecureFile> plain_data:flags.2?SecurePlainData selfie:flags.3?InputSecureFile = InputSecureValue;
|
||||||
inputSecureValueAddress#96689355 data:SecureData files:Vector<InputSecureFile> = InputSecureValue;
|
|
||||||
inputSecureValuePhone#9d623d96 phone:string = InputSecureValue;
|
|
||||||
inputSecureValueEmail#9e885359 email:string = InputSecureValue;
|
|
||||||
|
|
||||||
secureValueHash#ed1ecdb0 type:SecureValueType hash:bytes = SecureValueHash;
|
secureValueHash#ed1ecdb0 type:SecureValueType hash:bytes = SecureValueHash;
|
||||||
|
|
||||||
secureValueSaved#c9147049 files:Vector<SecureFile> = SecureValueSaved;
|
|
||||||
|
|
||||||
secureCredentialsEncrypted#33f0ea47 data:bytes hash:bytes secret:bytes = SecureCredentialsEncrypted;
|
secureCredentialsEncrypted#33f0ea47 data:bytes hash:bytes secret:bytes = SecureCredentialsEncrypted;
|
||||||
|
|
||||||
account.authorizationForm#4cace8c4 required_types:Vector<SecureValueType> values:Vector<SecureValue> users:Vector<User> = account.AuthorizationForm;
|
account.authorizationForm#b9d3d1f0 flags:# selfie_required:flags.1?true required_types:Vector<SecureValueType> values:Vector<SecureValue> users:Vector<User> privacy_policy_url:flags.0?string = account.AuthorizationForm;
|
||||||
|
|
||||||
account.sentEmailCode#28b1633b email_pattern:string = account.SentEmailCode;
|
account.sentEmailCode#28b1633b email_pattern:string = account.SentEmailCode;
|
||||||
|
|
||||||
|
@ -1057,8 +1056,8 @@ account.getTmpPassword#4a82327e password_hash:bytes period:int = account.TmpPass
|
||||||
account.getWebAuthorizations#182e6d6f = account.WebAuthorizations;
|
account.getWebAuthorizations#182e6d6f = account.WebAuthorizations;
|
||||||
account.resetWebAuthorization#2d01b9ef hash:long = Bool;
|
account.resetWebAuthorization#2d01b9ef hash:long = Bool;
|
||||||
account.resetWebAuthorizations#682d2594 = Bool;
|
account.resetWebAuthorizations#682d2594 = Bool;
|
||||||
account.getSecureValue#d97e77cb user_id:InputUser types:Vector<SecureValueType> = Vector<SecureValue>;
|
account.getSecureValue#73665bc2 types:Vector<SecureValueType> = Vector<SecureValue>;
|
||||||
account.saveSecureValue#78969d0b value:InputSecureValue secure_secret_id:long = SecureValueSaved;
|
account.saveSecureValue#899fe31d value:InputSecureValue secure_secret_id:long = SecureValue;
|
||||||
account.deleteSecureValue#b880bc4b types:Vector<SecureValueType> = Bool;
|
account.deleteSecureValue#b880bc4b types:Vector<SecureValueType> = Bool;
|
||||||
account.getAuthorizationForm#b86ba8e1 bot_id:int scope:string public_key:string = account.AuthorizationForm;
|
account.getAuthorizationForm#b86ba8e1 bot_id:int scope:string public_key:string = account.AuthorizationForm;
|
||||||
account.acceptAuthorization#e7027c94 bot_id:int scope:string public_key:string value_hashes:Vector<SecureValueHash> credentials:SecureCredentialsEncrypted = Bool;
|
account.acceptAuthorization#e7027c94 bot_id:int scope:string public_key:string value_hashes:Vector<SecureValueHash> credentials:SecureCredentialsEncrypted = Bool;
|
||||||
|
|
|
@ -29,6 +29,23 @@ QImage ReadImage(bytes::const_span buffer) {
|
||||||
buffer.size()));
|
buffer.size()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Value::Type ConvertType(const MTPSecureValueType &type) {
|
||||||
|
using Type = Value::Type;
|
||||||
|
switch (type.type()) {
|
||||||
|
case mtpc_secureValueTypePersonalDetails: return Type::PersonalDetails;
|
||||||
|
case mtpc_secureValueTypePassport: return Type::Passport;
|
||||||
|
case mtpc_secureValueTypeDriverLicense: return Type::DriverLicense;
|
||||||
|
case mtpc_secureValueTypeIdentityCard: return Type::IdentityCard;
|
||||||
|
case mtpc_secureValueTypeAddress: return Type::Address;
|
||||||
|
case mtpc_secureValueTypeUtilityBill: return Type::UtilityBill;
|
||||||
|
case mtpc_secureValueTypeBankStatement: return Type::BankStatement;
|
||||||
|
case mtpc_secureValueTypeRentalAgreement: return Type::RentalAgreement;
|
||||||
|
case mtpc_secureValueTypePhone: return Type::Phone;
|
||||||
|
case mtpc_secureValueTypeEmail: return Type::Email;
|
||||||
|
}
|
||||||
|
Unexpected("Type in secureValueType type.");
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
FormRequest::FormRequest(
|
FormRequest::FormRequest(
|
||||||
|
@ -43,9 +60,11 @@ FormRequest::FormRequest(
|
||||||
}
|
}
|
||||||
|
|
||||||
EditFile::EditFile(
|
EditFile::EditFile(
|
||||||
|
not_null<const Value*> value,
|
||||||
const File &fields,
|
const File &fields,
|
||||||
std::unique_ptr<UploadScanData> &&uploadData)
|
std::unique_ptr<UploadScanData> &&uploadData)
|
||||||
: fields(std::move(fields))
|
: value(value)
|
||||||
|
, fields(std::move(fields))
|
||||||
, uploadData(std::move(uploadData))
|
, uploadData(std::move(uploadData))
|
||||||
, guard(std::make_shared<bool>(true)) {
|
, guard(std::make_shared<bool>(true)) {
|
||||||
}
|
}
|
||||||
|
@ -105,6 +124,10 @@ UserData *FormController::bot() const {
|
||||||
return _bot;
|
return _bot;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString FormController::privacyPolicyUrl() const {
|
||||||
|
return _form.privacyPolicyUrl;
|
||||||
|
}
|
||||||
|
|
||||||
bytes::vector FormController::passwordHashForAuth(
|
bytes::vector FormController::passwordHashForAuth(
|
||||||
bytes::const_span password) const {
|
bytes::const_span password) const {
|
||||||
return openssl::Sha256(bytes::concatenate(
|
return openssl::Sha256(bytes::concatenate(
|
||||||
|
@ -157,7 +180,7 @@ void FormController::validateSecureSecret(
|
||||||
_secretId = 0;
|
_secretId = 0;
|
||||||
LOG(("API Error: Failed to decrypt secure secret. "
|
LOG(("API Error: Failed to decrypt secure secret. "
|
||||||
"Forgetting all files and data :("));
|
"Forgetting all files and data :("));
|
||||||
for (auto &value : _form.rows) {
|
for (auto &[type, value] : _form.values) {
|
||||||
if (!value.data.original.isEmpty()) {
|
if (!value.data.original.isEmpty()) {
|
||||||
resetValue(value);
|
resetValue(value);
|
||||||
}
|
}
|
||||||
|
@ -176,7 +199,7 @@ void FormController::validateSecureSecret(
|
||||||
void FormController::decryptValues() {
|
void FormController::decryptValues() {
|
||||||
Expects(!_secret.empty());
|
Expects(!_secret.empty());
|
||||||
|
|
||||||
for (auto &value : _form.rows) {
|
for (auto &[type, value] : _form.values) {
|
||||||
if (value.data.original.isEmpty()) {
|
if (value.data.original.isEmpty()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -234,16 +257,17 @@ QString FormController::passwordHint() const {
|
||||||
return _password.hint;
|
return _password.hint;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FormController::uploadScan(int valueIndex, QByteArray &&content) {
|
void FormController::uploadScan(
|
||||||
Expects(valueIndex >= 0 && valueIndex < _form.rows.size());
|
not_null<const Value*> value,
|
||||||
|
QByteArray &&content) {
|
||||||
auto &value = _form.rows[valueIndex];
|
const auto nonconst = findValue(value);
|
||||||
auto fileIndex = int(value.filesInEdit.size());
|
auto fileIndex = int(nonconst->filesInEdit.size());
|
||||||
value.filesInEdit.emplace_back(
|
nonconst->filesInEdit.emplace_back(
|
||||||
|
nonconst,
|
||||||
File(),
|
File(),
|
||||||
nullptr);
|
nullptr);
|
||||||
const auto fileId = rand_value<uint64>();
|
const auto fileId = rand_value<uint64>();
|
||||||
auto &file = value.filesInEdit.back();
|
auto &file = nonconst->filesInEdit.back();
|
||||||
file.fields.size = content.size();
|
file.fields.size = content.size();
|
||||||
file.fields.id = fileId;
|
file.fields.id = fileId;
|
||||||
file.fields.dcId = MTP::maindc();
|
file.fields.dcId = MTP::maindc();
|
||||||
|
@ -254,19 +278,16 @@ void FormController::uploadScan(int valueIndex, QByteArray &&content) {
|
||||||
|
|
||||||
_scanUpdated.fire(&file);
|
_scanUpdated.fire(&file);
|
||||||
|
|
||||||
encryptScan(valueIndex, fileIndex, std::move(content));
|
encryptScan(nonconst, fileIndex, std::move(content));
|
||||||
}
|
}
|
||||||
|
|
||||||
void FormController::encryptScan(
|
void FormController::encryptScan(
|
||||||
int valueIndex,
|
not_null<Value*> value,
|
||||||
int fileIndex,
|
int fileIndex,
|
||||||
QByteArray &&content) {
|
QByteArray &&content) {
|
||||||
Expects(valueIndex >= 0 && valueIndex < _form.rows.size());
|
Expects(fileIndex >= 0 && fileIndex < value->filesInEdit.size());
|
||||||
Expects(fileIndex >= 0
|
|
||||||
&& fileIndex < _form.rows[valueIndex].filesInEdit.size());
|
|
||||||
|
|
||||||
const auto &value = _form.rows[valueIndex];
|
const auto &file = value->filesInEdit[fileIndex];
|
||||||
const auto &file = value.filesInEdit[fileIndex];
|
|
||||||
const auto weak = std::weak_ptr<bool>(file.guard);
|
const auto weak = std::weak_ptr<bool>(file.guard);
|
||||||
crl::async([
|
crl::async([
|
||||||
=,
|
=,
|
||||||
|
@ -289,7 +310,7 @@ void FormController::encryptScan(
|
||||||
crl::on_main([=, encrypted = std::move(result)]() mutable {
|
crl::on_main([=, encrypted = std::move(result)]() mutable {
|
||||||
if (weak.lock()) {
|
if (weak.lock()) {
|
||||||
uploadEncryptedScan(
|
uploadEncryptedScan(
|
||||||
valueIndex,
|
value ,
|
||||||
fileIndex,
|
fileIndex,
|
||||||
std::move(encrypted));
|
std::move(encrypted));
|
||||||
}
|
}
|
||||||
|
@ -297,23 +318,26 @@ void FormController::encryptScan(
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void FormController::deleteScan(int valueIndex, int fileIndex) {
|
void FormController::deleteScan(
|
||||||
scanDeleteRestore(valueIndex, fileIndex, true);
|
not_null<const Value*> value,
|
||||||
|
int fileIndex) {
|
||||||
|
scanDeleteRestore(value, fileIndex, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FormController::restoreScan(int valueIndex, int fileIndex) {
|
void FormController::restoreScan(
|
||||||
scanDeleteRestore(valueIndex, fileIndex, false);
|
not_null<const Value*> value,
|
||||||
|
int fileIndex) {
|
||||||
|
scanDeleteRestore(value, fileIndex, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FormController::scanDeleteRestore(
|
void FormController::scanDeleteRestore(
|
||||||
int valueIndex,
|
not_null<const Value*> value,
|
||||||
int fileIndex,
|
int fileIndex,
|
||||||
bool deleted) {
|
bool deleted) {
|
||||||
Expects(valueIndex >= 0 && valueIndex < _form.rows.size());
|
Expects(fileIndex >= 0 && fileIndex < value->filesInEdit.size());
|
||||||
Expects(fileIndex >= 0
|
|
||||||
&& fileIndex < _form.rows[valueIndex].filesInEdit.size());
|
|
||||||
|
|
||||||
auto &file = _form.rows[valueIndex].filesInEdit[fileIndex];
|
const auto nonconst = findValue(value);
|
||||||
|
auto &file = nonconst->filesInEdit[fileIndex];
|
||||||
file.deleted = deleted;
|
file.deleted = deleted;
|
||||||
_scanUpdated.fire(&file);
|
_scanUpdated.fire(&file);
|
||||||
}
|
}
|
||||||
|
@ -342,16 +366,14 @@ void FormController::subscribeToUploader() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void FormController::uploadEncryptedScan(
|
void FormController::uploadEncryptedScan(
|
||||||
int valueIndex,
|
not_null<Value*> value,
|
||||||
int fileIndex,
|
int fileIndex,
|
||||||
UploadScanData &&data) {
|
UploadScanData &&data) {
|
||||||
Expects(valueIndex >= 0 && valueIndex < _form.rows.size());
|
Expects(fileIndex >= 0 && fileIndex < value->filesInEdit.size());
|
||||||
Expects(fileIndex >= 0
|
|
||||||
&& fileIndex < _form.rows[valueIndex].filesInEdit.size());
|
|
||||||
|
|
||||||
subscribeToUploader();
|
subscribeToUploader();
|
||||||
|
|
||||||
auto &file = _form.rows[valueIndex].filesInEdit[fileIndex];
|
auto &file = value->filesInEdit[fileIndex];
|
||||||
file.uploadData = std::make_unique<UploadScanData>(std::move(data));
|
file.uploadData = std::make_unique<UploadScanData>(std::move(data));
|
||||||
|
|
||||||
auto prepared = std::make_shared<FileLoadResult>(
|
auto prepared = std::make_shared<FileLoadResult>(
|
||||||
|
@ -424,27 +446,32 @@ QString FormController::defaultPhoneNumber() const {
|
||||||
return QString();
|
return QString();
|
||||||
}
|
}
|
||||||
|
|
||||||
rpl::producer<not_null<const EditFile*>> FormController::scanUpdated() const {
|
auto FormController::scanUpdated() const
|
||||||
|
-> rpl::producer<not_null<const EditFile*>> {
|
||||||
return _scanUpdated.events();
|
return _scanUpdated.events();
|
||||||
}
|
}
|
||||||
|
|
||||||
void FormController::enumerateRows(
|
const Form &FormController::form() const {
|
||||||
base::lambda<void(const Value &value)> callback) {
|
return _form;
|
||||||
ranges::for_each(_form.rows, callback);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
not_null<Value*> FormController::startValueEdit(int index) {
|
not_null<Value*> FormController::findValue(not_null<const Value*> value) {
|
||||||
Expects(index >= 0 && index < _form.rows.size());
|
const auto i = _form.values.find(value->type);
|
||||||
|
Assert(i != end(_form.values));
|
||||||
|
const auto result = &i->second;
|
||||||
|
|
||||||
auto &value = _form.rows[index];
|
Ensures(result == value);
|
||||||
loadFiles(value.files);
|
return result;
|
||||||
value.filesInEdit = ranges::view::all(
|
}
|
||||||
value.files
|
|
||||||
) | ranges::view::transform([](const File &file) {
|
void FormController::startValueEdit(not_null<const Value*> value) {
|
||||||
return EditFile(file, nullptr);
|
const auto nonconst = findValue(value);
|
||||||
|
loadFiles(nonconst->files);
|
||||||
|
nonconst->filesInEdit = ranges::view::all(
|
||||||
|
value->files
|
||||||
|
) | ranges::view::transform([=](const File &file) {
|
||||||
|
return EditFile(value, file, nullptr);
|
||||||
}) | ranges::to_vector;
|
}) | ranges::to_vector;
|
||||||
|
|
||||||
return &value;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void FormController::loadFiles(std::vector<File> &files) {
|
void FormController::loadFiles(std::vector<File> &files) {
|
||||||
|
@ -529,119 +556,169 @@ void FormController::fileLoadFail(FileKey key) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void FormController::cancelValueEdit(int index) {
|
void FormController::cancelValueEdit(not_null<const Value*> value) {
|
||||||
Expects(index >= 0 && index < _form.rows.size());
|
const auto nonconst = findValue(value);
|
||||||
|
nonconst->filesInEdit.clear();
|
||||||
_form.rows[index].filesInEdit.clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FormController::isEncryptedValue(Value::Type type) const {
|
bool FormController::isEncryptedValue(Value::Type type) const {
|
||||||
return (type == Value::Type::Identity || type == Value::Type::Address);
|
return (type != Value::Type::Phone && type != Value::Type::Email);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FormController::saveValueEdit(int index) {
|
void FormController::saveValueEdit(
|
||||||
Expects(index >= 0 && index < _form.rows.size());
|
not_null<const Value*> value,
|
||||||
|
ValueMap &&data) {
|
||||||
|
const auto nonconst = findValue(value);
|
||||||
|
nonconst->data.parsed = std::move(data);
|
||||||
|
|
||||||
if (isEncryptedValue(_form.rows[index].type)) {
|
if (isEncryptedValue(nonconst->type)) {
|
||||||
saveEncryptedValue(index);
|
saveEncryptedValue(nonconst);
|
||||||
} else {
|
} else {
|
||||||
savePlainTextValue(index);
|
savePlainTextValue(nonconst);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void FormController::saveEncryptedValue(int index) {
|
void FormController::saveEncryptedValue(not_null<Value*> value) {
|
||||||
Expects(index >= 0 && index < _form.rows.size());
|
Expects(isEncryptedValue(value->type));
|
||||||
Expects(isEncryptedValue(_form.rows[index].type));
|
|
||||||
|
|
||||||
auto &value = _form.rows[index];
|
|
||||||
if (_secret.empty()) {
|
if (_secret.empty()) {
|
||||||
_secretCallbacks.push_back([=] {
|
_secretCallbacks.push_back([=] {
|
||||||
saveEncryptedValue(index);
|
saveEncryptedValue(value);
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto inputFiles = QVector<MTPInputSecureFile>();
|
const auto inputFile = [](const EditFile &file) {
|
||||||
inputFiles.reserve(value.filesInEdit.size());
|
if (const auto uploadData = file.uploadData.get()) {
|
||||||
for (const auto &file : value.filesInEdit) {
|
return MTP_inputSecureFileUploaded(
|
||||||
if (file.deleted) {
|
|
||||||
continue;
|
|
||||||
} else if (const auto uploadData = file.uploadData.get()) {
|
|
||||||
inputFiles.push_back(MTP_inputSecureFileUploaded(
|
|
||||||
MTP_long(file.fields.id),
|
MTP_long(file.fields.id),
|
||||||
MTP_int(uploadData->partsCount),
|
MTP_int(uploadData->partsCount),
|
||||||
MTP_bytes(uploadData->md5checksum),
|
MTP_bytes(uploadData->md5checksum),
|
||||||
MTP_bytes(file.fields.hash),
|
MTP_bytes(file.fields.hash),
|
||||||
MTP_bytes(file.fields.encryptedSecret)));
|
MTP_bytes(file.fields.encryptedSecret));
|
||||||
} else {
|
|
||||||
inputFiles.push_back(MTP_inputSecureFile(
|
|
||||||
MTP_long(file.fields.id),
|
|
||||||
MTP_long(file.fields.accessHash)));
|
|
||||||
}
|
}
|
||||||
|
return MTP_inputSecureFile(
|
||||||
|
MTP_long(file.fields.id),
|
||||||
|
MTP_long(file.fields.accessHash));
|
||||||
|
};
|
||||||
|
|
||||||
|
auto inputFiles = QVector<MTPInputSecureFile>();
|
||||||
|
inputFiles.reserve(value->filesInEdit.size());
|
||||||
|
for (const auto &file : value->filesInEdit) {
|
||||||
|
if (file.deleted) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
inputFiles.push_back(inputFile(file));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (value.data.secret.empty()) {
|
|
||||||
value.data.secret = GenerateSecretBytes();
|
if (value->data.secret.empty()) {
|
||||||
|
value->data.secret = GenerateSecretBytes();
|
||||||
}
|
}
|
||||||
const auto encryptedData = EncryptData(
|
const auto encryptedData = EncryptData(
|
||||||
SerializeData(value.data.parsed.fields),
|
SerializeData(value->data.parsed.fields),
|
||||||
value.data.secret);
|
value->data.secret);
|
||||||
value.data.hash = encryptedData.hash;
|
value->data.hash = encryptedData.hash;
|
||||||
value.data.encryptedSecret = EncryptValueSecret(
|
value->data.encryptedSecret = EncryptValueSecret(
|
||||||
value.data.secret,
|
value->data.secret,
|
||||||
_secret,
|
_secret,
|
||||||
value.data.hash);
|
value->data.hash);
|
||||||
|
|
||||||
const auto wrap = [&] {
|
const auto selfie = value->selfieInEdit
|
||||||
switch (value.type) {
|
? inputFile(*value->selfieInEdit)
|
||||||
case Value::Type::Identity: return MTP_inputSecureValueIdentity;
|
: MTPInputSecureFile();
|
||||||
case Value::Type::Address: return MTP_inputSecureValueAddress;
|
|
||||||
|
const auto type = [&] {
|
||||||
|
switch (value->type) {
|
||||||
|
case Value::Type::PersonalDetails:
|
||||||
|
return MTP_secureValueTypePersonalDetails();
|
||||||
|
case Value::Type::Passport:
|
||||||
|
return MTP_secureValueTypePassport();
|
||||||
|
case Value::Type::DriverLicense:
|
||||||
|
return MTP_secureValueTypeDriverLicense();
|
||||||
|
case Value::Type::IdentityCard:
|
||||||
|
return MTP_secureValueTypeIdentityCard();
|
||||||
|
case Value::Type::Address:
|
||||||
|
return MTP_secureValueTypeAddress();
|
||||||
|
case Value::Type::UtilityBill:
|
||||||
|
return MTP_secureValueTypeUtilityBill();
|
||||||
|
case Value::Type::BankStatement:
|
||||||
|
return MTP_secureValueTypeBankStatement();
|
||||||
|
case Value::Type::RentalAgreement:
|
||||||
|
return MTP_secureValueTypeRentalAgreement();
|
||||||
}
|
}
|
||||||
Unexpected("Value type in saveEncryptedValue().");
|
Unexpected("Value type in saveEncryptedValue().");
|
||||||
}();
|
}();
|
||||||
sendSaveRequest(index, wrap(
|
const auto flags = ((value->filesInEdit.empty()
|
||||||
|
&& value->data.parsed.fields.empty())
|
||||||
|
? MTPDinputSecureValue::Flag(0)
|
||||||
|
: MTPDinputSecureValue::Flag::f_data)
|
||||||
|
| (value->filesInEdit.empty()
|
||||||
|
? MTPDinputSecureValue::Flag(0)
|
||||||
|
: MTPDinputSecureValue::Flag::f_files)
|
||||||
|
| (value->selfieInEdit
|
||||||
|
? MTPDinputSecureValue::Flag::f_selfie
|
||||||
|
: MTPDinputSecureValue::Flag(0));
|
||||||
|
if (!flags) {
|
||||||
|
request(MTPaccount_DeleteSecureValue(MTP_vector<MTPSecureValueType>(1, type))).send();
|
||||||
|
} else {
|
||||||
|
sendSaveRequest(value, MTP_inputSecureValue(
|
||||||
|
MTP_flags(flags),
|
||||||
|
type,
|
||||||
MTP_secureData(
|
MTP_secureData(
|
||||||
MTP_bytes(encryptedData.bytes),
|
MTP_bytes(encryptedData.bytes),
|
||||||
MTP_bytes(value.data.hash),
|
MTP_bytes(value->data.hash),
|
||||||
MTP_bytes(value.data.encryptedSecret)),
|
MTP_bytes(value->data.encryptedSecret)),
|
||||||
MTP_vector<MTPInputSecureFile>(inputFiles)));
|
MTP_vector<MTPInputSecureFile>(inputFiles),
|
||||||
|
MTPSecurePlainData(),
|
||||||
|
selfie));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void FormController::savePlainTextValue(int index) {
|
void FormController::savePlainTextValue(not_null<Value*> value) {
|
||||||
Expects(index >= 0 && index < _form.rows.size());
|
Expects(!isEncryptedValue(value->type));
|
||||||
Expects(!isEncryptedValue(_form.rows[index].type));
|
|
||||||
|
|
||||||
auto &value = _form.rows[index];
|
const auto text = value->data.parsed.fields[QString("value")];
|
||||||
const auto text = value.data.parsed.fields[QString("value")];
|
const auto type = [&] {
|
||||||
const auto wrap = [&] {
|
switch (value->type) {
|
||||||
switch (value.type) {
|
case Value::Type::Phone: return MTP_secureValueTypePhone();
|
||||||
case Value::Type::Phone: return MTP_inputSecureValuePhone;
|
case Value::Type::Email: return MTP_secureValueTypeEmail();
|
||||||
case Value::Type::Email: return MTP_inputSecureValueEmail;
|
|
||||||
}
|
}
|
||||||
Unexpected("Value type in savePlainTextValue().");
|
Unexpected("Value type in savePlainTextValue().");
|
||||||
}();
|
}();
|
||||||
sendSaveRequest(index, wrap(MTP_string(text)));
|
const auto plain = [&] {
|
||||||
|
switch (value->type) {
|
||||||
|
case Value::Type::Phone: return MTP_securePlainPhone;
|
||||||
|
case Value::Type::Email: return MTP_securePlainEmail;
|
||||||
|
}
|
||||||
|
Unexpected("Value type in savePlainTextValue().");
|
||||||
|
}();
|
||||||
|
sendSaveRequest(value, MTP_inputSecureValue(
|
||||||
|
MTP_flags(MTPDinputSecureValue::Flag::f_plain_data),
|
||||||
|
type,
|
||||||
|
MTPSecureData(),
|
||||||
|
MTPVector<MTPInputSecureFile>(),
|
||||||
|
plain(MTP_string(text)),
|
||||||
|
MTPInputSecureFile()));
|
||||||
}
|
}
|
||||||
|
|
||||||
void FormController::sendSaveRequest(
|
void FormController::sendSaveRequest(
|
||||||
int index,
|
not_null<Value*> value,
|
||||||
const MTPInputSecureValue &value) {
|
const MTPInputSecureValue &data) {
|
||||||
Expects(index >= 0 && index < _form.rows.size());
|
|
||||||
|
|
||||||
request(MTPaccount_SaveSecureValue(
|
request(MTPaccount_SaveSecureValue(
|
||||||
value,
|
data,
|
||||||
MTP_long(_secretId)
|
MTP_long(_secretId)
|
||||||
)).done([=](const MTPSecureValueSaved &result) {
|
)).done([=](const MTPSecureValue &result) {
|
||||||
Expects(result.type() == mtpc_secureValueSaved);
|
Expects(result.type() == mtpc_secureValue);
|
||||||
|
|
||||||
const auto &data = result.c_secureValueSaved();
|
const auto &data = result.c_secureValue();
|
||||||
_form.rows[index].files = parseFiles(
|
value->files = parseFiles(
|
||||||
data.vfiles.v,
|
data.vfiles.v,
|
||||||
base::take(_form.rows[index].filesInEdit));
|
base::take(value->filesInEdit));
|
||||||
|
|
||||||
Ui::show(Box<InformBox>("Saved"), LayerOption::KeepOther);
|
Ui::show(Box<InformBox>("Saved"), LayerOption::KeepOther);
|
||||||
}).fail([=](const RPCError &error) {
|
}).fail([=](const RPCError &error) {
|
||||||
Ui::show(Box<InformBox>("Error saving value."));
|
Ui::show(Box<InformBox>("Error saving value"));
|
||||||
}).send();
|
}).send();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -710,24 +787,6 @@ void FormController::requestForm() {
|
||||||
}).send();
|
}).send();
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename DataType>
|
|
||||||
auto FormController::parseEncryptedValue(
|
|
||||||
Value::Type type,
|
|
||||||
const DataType &data) const -> Value {
|
|
||||||
Expects(data.vdata.type() == mtpc_secureData);
|
|
||||||
|
|
||||||
auto result = Value(type);
|
|
||||||
if (data.has_verified()) {
|
|
||||||
result.verification = parseVerified(data.vverified);
|
|
||||||
}
|
|
||||||
const auto &fields = data.vdata.c_secureData();
|
|
||||||
result.data.original = fields.vdata.v;
|
|
||||||
result.data.hash = bytes::make_vector(fields.vdata_hash.v);
|
|
||||||
result.data.encryptedSecret = bytes::make_vector(fields.vsecret.v);
|
|
||||||
result.files = parseFiles(data.vfiles.v);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto FormController::parseFiles(
|
auto FormController::parseFiles(
|
||||||
const QVector<MTPSecureFile> &data,
|
const QVector<MTPSecureFile> &data,
|
||||||
const std::vector<EditFile> &editData) const
|
const std::vector<EditFile> &editData) const
|
||||||
|
@ -788,60 +847,41 @@ void FormController::fillDownloadedFile(
|
||||||
i->uploadData->bytes.size())));
|
i->uploadData->bytes.size())));
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename DataType>
|
auto FormController::parseValue(
|
||||||
auto FormController::parsePlainTextValue(
|
const MTPSecureValue &value) const -> Value {
|
||||||
Value::Type type,
|
Expects(value.type() == mtpc_secureValue);
|
||||||
const QByteArray &text,
|
|
||||||
const DataType &data) const -> Value {
|
const auto &data = value.c_secureValue();
|
||||||
|
const auto type = ConvertType(data.vtype);
|
||||||
auto result = Value(type);
|
auto result = Value(type);
|
||||||
result.data.parsed.fields[QString("value")] = QString::fromUtf8(text);
|
if (data.has_data()) {
|
||||||
if (data.has_verified()) {
|
Assert(data.vdata.type() == mtpc_secureData);
|
||||||
result.verification = parseVerified(data.vverified);
|
const auto &fields = data.vdata.c_secureData();
|
||||||
|
result.data.original = fields.vdata.v;
|
||||||
|
result.data.hash = bytes::make_vector(fields.vdata_hash.v);
|
||||||
|
result.data.encryptedSecret = bytes::make_vector(fields.vsecret.v);
|
||||||
}
|
}
|
||||||
|
if (data.has_files()) {
|
||||||
|
result.files = parseFiles(data.vfiles.v);
|
||||||
|
}
|
||||||
|
if (data.has_plain_data()) {
|
||||||
|
switch (data.vplain_data.type()) {
|
||||||
|
case mtpc_securePlainPhone: {
|
||||||
|
const auto &fields = data.vplain_data.c_securePlainPhone();
|
||||||
|
result.data.parsed.fields["value"] = qs(fields.vphone);
|
||||||
|
} break;
|
||||||
|
case mtpc_securePlainEmail: {
|
||||||
|
const auto &fields = data.vplain_data.c_securePlainEmail();
|
||||||
|
result.data.parsed.fields["value"] = qs(fields.vemail);
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// #TODO passport selfie
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto FormController::parseValue(
|
|
||||||
const MTPSecureValue &value) const -> Value {
|
|
||||||
switch (value.type()) {
|
|
||||||
case mtpc_secureValueIdentity: {
|
|
||||||
return parseEncryptedValue(
|
|
||||||
Value::Type::Identity,
|
|
||||||
value.c_secureValueIdentity());
|
|
||||||
} break;
|
|
||||||
case mtpc_secureValueAddress: {
|
|
||||||
return parseEncryptedValue(
|
|
||||||
Value::Type::Address,
|
|
||||||
value.c_secureValueAddress());
|
|
||||||
} break;
|
|
||||||
case mtpc_secureValuePhone: {
|
|
||||||
const auto &data = value.c_secureValuePhone();
|
|
||||||
return parsePlainTextValue(
|
|
||||||
Value::Type::Phone,
|
|
||||||
data.vphone.v,
|
|
||||||
data);
|
|
||||||
} break;
|
|
||||||
case mtpc_secureValueEmail: {
|
|
||||||
const auto &data = value.c_secureValueEmail();
|
|
||||||
return parsePlainTextValue(
|
|
||||||
Value::Type::Phone,
|
|
||||||
data.vemail.v,
|
|
||||||
data);
|
|
||||||
} break;
|
|
||||||
}
|
|
||||||
Unexpected("secureValue type.");
|
|
||||||
}
|
|
||||||
|
|
||||||
auto FormController::parseVerified(const MTPSecureValueVerified &data) const
|
|
||||||
-> Verification {
|
|
||||||
Expects(data.type() == mtpc_secureValueVerified);
|
|
||||||
|
|
||||||
const auto &fields = data.c_secureValueVerified();
|
|
||||||
return Verification{ fields.vdate.v };
|
|
||||||
}
|
|
||||||
|
|
||||||
auto FormController::findEditFile(const FullMsgId &fullId) -> EditFile* {
|
auto FormController::findEditFile(const FullMsgId &fullId) -> EditFile* {
|
||||||
for (auto &value : _form.rows) {
|
for (auto &[type, value] : _form.values) {
|
||||||
for (auto &file : value.filesInEdit) {
|
for (auto &file : value.filesInEdit) {
|
||||||
if (file.uploadData && file.uploadData->fullId == fullId) {
|
if (file.uploadData && file.uploadData->fullId == fullId) {
|
||||||
return &file;
|
return &file;
|
||||||
|
@ -852,7 +892,7 @@ auto FormController::findEditFile(const FullMsgId &fullId) -> EditFile* {
|
||||||
}
|
}
|
||||||
|
|
||||||
auto FormController::findEditFile(const FileKey &key) -> EditFile* {
|
auto FormController::findEditFile(const FileKey &key) -> EditFile* {
|
||||||
for (auto &value : _form.rows) {
|
for (auto &[type, value] : _form.values) {
|
||||||
for (auto &file : value.filesInEdit) {
|
for (auto &file : value.filesInEdit) {
|
||||||
if (file.fields.dcId == key.dcId && file.fields.id == key.id) {
|
if (file.fields.dcId == key.dcId && file.fields.id == key.id) {
|
||||||
return &file;
|
return &file;
|
||||||
|
@ -864,7 +904,7 @@ auto FormController::findEditFile(const FileKey &key) -> EditFile* {
|
||||||
|
|
||||||
auto FormController::findFile(const FileKey &key)
|
auto FormController::findFile(const FileKey &key)
|
||||||
-> std::pair<Value*, File*> {
|
-> std::pair<Value*, File*> {
|
||||||
for (auto &value : _form.rows) {
|
for (auto &[type, value] : _form.values) {
|
||||||
for (auto &file : value.files) {
|
for (auto &file : value.files) {
|
||||||
if (file.dcId == key.dcId && file.id == key.id) {
|
if (file.dcId == key.dcId && file.id == key.id) {
|
||||||
return { &value, &file };
|
return { &value, &file };
|
||||||
|
@ -886,49 +926,33 @@ void FormController::parseForm(const MTPaccount_AuthorizationForm &result) {
|
||||||
|
|
||||||
const auto &data = result.c_account_authorizationForm();
|
const auto &data = result.c_account_authorizationForm();
|
||||||
|
|
||||||
auto values = std::vector<Value>();
|
|
||||||
for (const auto &value : data.vvalues.v) {
|
|
||||||
values.push_back(parseValue(value));
|
|
||||||
}
|
|
||||||
const auto findValue = [&](Value::Type type) -> Value* {
|
|
||||||
for (auto &value : values) {
|
|
||||||
if (value.type == type) {
|
|
||||||
return &value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
};
|
|
||||||
|
|
||||||
App::feedUsers(data.vusers);
|
App::feedUsers(data.vusers);
|
||||||
|
|
||||||
|
for (const auto &value : data.vvalues.v) {
|
||||||
|
auto parsed = parseValue(value);
|
||||||
|
const auto type = parsed.type;
|
||||||
|
const auto alreadyIt = _form.values.find(type);
|
||||||
|
if (alreadyIt != _form.values.end()) {
|
||||||
|
LOG(("API Error: Two values for type %1 in authorization form"
|
||||||
|
"%1").arg(int(type)));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
_form.values.emplace(type, std::move(parsed));
|
||||||
|
}
|
||||||
|
_form.identitySelfieRequired = data.is_selfie_required();
|
||||||
|
if (data.has_privacy_policy_url()) {
|
||||||
|
_form.privacyPolicyUrl = qs(data.vprivacy_policy_url);
|
||||||
|
}
|
||||||
for (const auto &required : data.vrequired_types.v) {
|
for (const auto &required : data.vrequired_types.v) {
|
||||||
using Type = Value::Type;
|
const auto type = ConvertType(required);
|
||||||
|
_form.request.push_back(type);
|
||||||
const auto type = [&] {
|
_form.values.emplace(type, Value(type));
|
||||||
switch (required.type()) {
|
|
||||||
case mtpc_secureValueTypeIdentity: return Type::Identity;
|
|
||||||
case mtpc_secureValueTypeAddress: return Type::Address;
|
|
||||||
case mtpc_secureValueTypeEmail: return Type::Email;
|
|
||||||
case mtpc_secureValueTypePhone: return Type::Phone;
|
|
||||||
}
|
|
||||||
Unexpected("Type in secureValueType type.");
|
|
||||||
}();
|
|
||||||
|
|
||||||
if (auto value = findValue(type)) {
|
|
||||||
_form.rows.push_back(std::move(*value));
|
|
||||||
} else {
|
|
||||||
_form.rows.push_back(Value(type));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
_bot = App::userLoaded(_request.botId);
|
_bot = App::userLoaded(_request.botId);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FormController::formFail(const RPCError &error) {
|
void FormController::formFail(const RPCError &error) {
|
||||||
// #TODO passport testing
|
Ui::show(Box<InformBox>(lang(lng_passport_form_error)));
|
||||||
_form.rows.push_back(Value(Value::Type::Identity));
|
|
||||||
_form.rows.back().data.parsed.fields["first_name"] = "test1";
|
|
||||||
_form.rows.back().data.parsed.fields["last_name"] = "test2";
|
|
||||||
_view->editValue(0);
|
|
||||||
// Ui::show(Box<InformBox>(lang(lng_passport_form_error)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void FormController::requestPassword() {
|
void FormController::requestPassword() {
|
||||||
|
|
|
@ -67,6 +67,8 @@ private:
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct Value;
|
||||||
|
|
||||||
struct File {
|
struct File {
|
||||||
uint64 id = 0;
|
uint64 id = 0;
|
||||||
uint64 accessHash = 0;
|
uint64 accessHash = 0;
|
||||||
|
@ -83,19 +85,17 @@ struct File {
|
||||||
|
|
||||||
struct EditFile {
|
struct EditFile {
|
||||||
EditFile(
|
EditFile(
|
||||||
|
not_null<const Value*> value,
|
||||||
const File &fields,
|
const File &fields,
|
||||||
std::unique_ptr<UploadScanData> &&uploadData);
|
std::unique_ptr<UploadScanData> &&uploadData);
|
||||||
|
|
||||||
|
not_null<const Value*> value;
|
||||||
File fields;
|
File fields;
|
||||||
UploadScanDataPointer uploadData;
|
UploadScanDataPointer uploadData;
|
||||||
std::shared_ptr<bool> guard;
|
std::shared_ptr<bool> guard;
|
||||||
bool deleted = false;
|
bool deleted = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Verification {
|
|
||||||
TimeId date;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct ValueMap {
|
struct ValueMap {
|
||||||
std::map<QString, QString> fields;
|
std::map<QString, QString> fields;
|
||||||
};
|
};
|
||||||
|
@ -110,8 +110,14 @@ struct ValueData {
|
||||||
|
|
||||||
struct Value {
|
struct Value {
|
||||||
enum class Type {
|
enum class Type {
|
||||||
Identity,
|
PersonalDetails,
|
||||||
|
Passport,
|
||||||
|
DriverLicense,
|
||||||
|
IdentityCard,
|
||||||
Address,
|
Address,
|
||||||
|
UtilityBill,
|
||||||
|
BankStatement,
|
||||||
|
RentalAgreement,
|
||||||
Phone,
|
Phone,
|
||||||
Email,
|
Email,
|
||||||
};
|
};
|
||||||
|
@ -124,11 +130,15 @@ struct Value {
|
||||||
ValueData data;
|
ValueData data;
|
||||||
std::vector<File> files;
|
std::vector<File> files;
|
||||||
std::vector<EditFile> filesInEdit;
|
std::vector<EditFile> filesInEdit;
|
||||||
base::optional<Verification> verification;
|
base::optional<File> selfie;
|
||||||
|
base::optional<EditFile> selfieInEdit;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Form {
|
struct Form {
|
||||||
std::vector<Value> rows;
|
std::map<Value::Type, Value> values;
|
||||||
|
std::vector<Value::Type> request;
|
||||||
|
bool identitySelfieRequired = false;
|
||||||
|
QString privacyPolicyUrl;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct PasswordSettings {
|
struct PasswordSettings {
|
||||||
|
@ -174,14 +184,15 @@ public:
|
||||||
|
|
||||||
void show();
|
void show();
|
||||||
UserData *bot() const;
|
UserData *bot() const;
|
||||||
|
QString privacyPolicyUrl() const;
|
||||||
|
|
||||||
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;
|
||||||
|
|
||||||
void uploadScan(int valueIndex, QByteArray &&content);
|
void uploadScan(not_null<const Value*> value, QByteArray &&content);
|
||||||
void deleteScan(int valueIndex, int fileIndex);
|
void deleteScan(not_null<const Value*> value, int fileIndex);
|
||||||
void restoreScan(int valueIndex, int fileIndex);
|
void restoreScan(not_null<const Value*> value, int fileIndex);
|
||||||
|
|
||||||
rpl::producer<> secretReadyEvents() const;
|
rpl::producer<> secretReadyEvents() const;
|
||||||
|
|
||||||
|
@ -190,10 +201,10 @@ public:
|
||||||
|
|
||||||
rpl::producer<not_null<const EditFile*>> scanUpdated() const;
|
rpl::producer<not_null<const EditFile*>> scanUpdated() const;
|
||||||
|
|
||||||
void enumerateRows(base::lambda<void(const Value &value)> callback);
|
const Form &form() const;
|
||||||
not_null<Value*> startValueEdit(int index);
|
void startValueEdit(not_null<const Value*> value);
|
||||||
void cancelValueEdit(int index);
|
void cancelValueEdit(not_null<const Value*> value);
|
||||||
void saveValueEdit(int index);
|
void saveValueEdit(not_null<const Value*> value, ValueMap &&data);
|
||||||
|
|
||||||
void cancel();
|
void cancel();
|
||||||
|
|
||||||
|
@ -205,6 +216,7 @@ private:
|
||||||
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);
|
||||||
|
not_null<Value*> findValue(not_null<const Value*> value);
|
||||||
|
|
||||||
void requestForm();
|
void requestForm();
|
||||||
void requestPassword();
|
void requestPassword();
|
||||||
|
@ -214,16 +226,6 @@ private:
|
||||||
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;
|
||||||
template <typename DataType>
|
|
||||||
Value parseEncryptedValue(
|
|
||||||
Value::Type type,
|
|
||||||
const DataType &data) const;
|
|
||||||
template <typename DataType>
|
|
||||||
Value parsePlainTextValue(
|
|
||||||
Value::Type type,
|
|
||||||
const QByteArray &text,
|
|
||||||
const DataType &data) const;
|
|
||||||
Verification parseVerified(const MTPSecureValueVerified &data) 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;
|
||||||
|
@ -253,22 +255,24 @@ private:
|
||||||
|
|
||||||
void subscribeToUploader();
|
void subscribeToUploader();
|
||||||
void encryptScan(
|
void encryptScan(
|
||||||
int valueIndex,
|
not_null<Value*> value,
|
||||||
int fileIndex,
|
int fileIndex,
|
||||||
QByteArray &&content);
|
QByteArray &&content);
|
||||||
void uploadEncryptedScan(
|
void uploadEncryptedScan(
|
||||||
int valueIndex,
|
not_null<Value*> value,
|
||||||
int fileIndex,
|
int fileIndex,
|
||||||
UploadScanData &&data);
|
UploadScanData &&data);
|
||||||
void scanUploadDone(const Storage::UploadSecureDone &data);
|
void scanUploadDone(const Storage::UploadSecureDone &data);
|
||||||
void scanUploadProgress(const Storage::UploadSecureProgress &data);
|
void scanUploadProgress(const Storage::UploadSecureProgress &data);
|
||||||
void scanUploadFail(const FullMsgId &fullId);
|
void scanUploadFail(const FullMsgId &fullId);
|
||||||
void scanDeleteRestore(int valueIndex, int fileIndex, bool deleted);
|
void scanDeleteRestore(not_null<const Value*> value, int fileIndex, bool deleted);
|
||||||
|
|
||||||
bool isEncryptedValue(Value::Type type) const;
|
bool isEncryptedValue(Value::Type type) const;
|
||||||
void saveEncryptedValue(int index);
|
void saveEncryptedValue(not_null<Value*> value);
|
||||||
void savePlainTextValue(int index);
|
void savePlainTextValue(not_null<Value*> value);
|
||||||
void sendSaveRequest(int index, const MTPInputSecureValue &value);
|
void sendSaveRequest(
|
||||||
|
not_null<Value*> value,
|
||||||
|
const MTPInputSecureValue &data);
|
||||||
|
|
||||||
not_null<Window::Controller*> _controller;
|
not_null<Window::Controller*> _controller;
|
||||||
FormRequest _request;
|
FormRequest _request;
|
||||||
|
|
|
@ -0,0 +1,96 @@
|
||||||
|
/*
|
||||||
|
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"
|
||||||
|
|
||||||
|
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 },
|
||||||
|
{ Value::Type::Address, Scope::Type::Address },
|
||||||
|
{ Value::Type::UtilityBill, Scope::Type::Address },
|
||||||
|
{ Value::Type::BankStatement, Scope::Type::Address },
|
||||||
|
{ Value::Type::RentalAgreement, Scope::Type::Address },
|
||||||
|
{ Value::Type::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) {
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<Scope> ComputeScopes(not_null<FormController*> controller) {
|
||||||
|
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(
|
||||||
|
i->second.files,
|
||||||
|
type,
|
||||||
|
[](not_null<const Value*> value) { return value->type; });
|
||||||
|
if (alreadyIt != end(i->second.files)) {
|
||||||
|
LOG(("API Error: Value type %1 multiple times in request."
|
||||||
|
).arg(int(type)));
|
||||||
|
continue;
|
||||||
|
} else if (type != fieldsType) {
|
||||||
|
i->second.files.push_back(findValue(type));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
auto result = std::vector<Scope>();
|
||||||
|
result.reserve(scopes.size());
|
||||||
|
for (auto &[type, scope] : scopes) {
|
||||||
|
result.push_back(std::move(scope));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Passport
|
|
@ -7,16 +7,33 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "passport/passport_form_controller.h"
|
||||||
|
|
||||||
namespace Passport {
|
namespace Passport {
|
||||||
|
|
||||||
struct Value;
|
struct Scope {
|
||||||
|
enum class Type {
|
||||||
|
Identity,
|
||||||
|
Address,
|
||||||
|
Phone,
|
||||||
|
Email,
|
||||||
|
};
|
||||||
|
Scope(Type type, not_null<const Value*> fields);
|
||||||
|
|
||||||
|
Type type;
|
||||||
|
not_null<const Value*> fields;
|
||||||
|
std::vector<not_null<const Value*>> files;
|
||||||
|
bool selfieRequired = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<Scope> ComputeScopes(not_null<FormController*> controller);
|
||||||
|
|
||||||
class ViewController {
|
class ViewController {
|
||||||
public:
|
public:
|
||||||
virtual void showAskPassword() = 0;
|
virtual void showAskPassword() = 0;
|
||||||
virtual void showNoPassword() = 0;
|
virtual void showNoPassword() = 0;
|
||||||
virtual void showPasswordUnconfirmed() = 0;
|
virtual void showPasswordUnconfirmed() = 0;
|
||||||
virtual void editValue(int index) = 0;
|
virtual void editScope(int index) = 0;
|
||||||
|
|
||||||
virtual ~ViewController() {
|
virtual ~ViewController() {
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,52 +51,61 @@ BoxContent *BoxPointer::operator->() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
PanelController::PanelController(not_null<FormController*> form)
|
PanelController::PanelController(not_null<FormController*> form)
|
||||||
: _form(form) {
|
: _form(form)
|
||||||
|
, _scopes(ComputeScopes(_form)) {
|
||||||
_form->secretReadyEvents(
|
_form->secretReadyEvents(
|
||||||
) | rpl::start_with_next([=] {
|
) | rpl::start_with_next([=] {
|
||||||
if (_panel) {
|
if (_panel) {
|
||||||
_panel->showForm();
|
_panel->showForm();
|
||||||
}
|
}
|
||||||
}, lifetime());
|
}, lifetime());
|
||||||
|
_scopes = ComputeScopes(_form);
|
||||||
}
|
}
|
||||||
|
|
||||||
not_null<UserData*> PanelController::bot() const {
|
not_null<UserData*> PanelController::bot() const {
|
||||||
return _form->bot();
|
return _form->bot();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString PanelController::privacyPolicyUrl() const {
|
||||||
|
return _form->privacyPolicyUrl();
|
||||||
|
}
|
||||||
|
|
||||||
void PanelController::fillRows(
|
void PanelController::fillRows(
|
||||||
base::lambda<void(
|
base::lambda<void(
|
||||||
QString title,
|
QString title,
|
||||||
QString description,
|
QString description,
|
||||||
bool ready)> callback) {
|
bool ready)> callback) {
|
||||||
_form->enumerateRows([&](const Value &value) {
|
if (_scopes.empty()) {
|
||||||
switch (value.type) {
|
_scopes = ComputeScopes(_form);
|
||||||
case Value::Type::Identity:
|
}
|
||||||
|
for (const auto &scope : _scopes) {
|
||||||
|
switch (scope.type) {
|
||||||
|
case Scope::Type::Identity:
|
||||||
callback(
|
callback(
|
||||||
lang(lng_passport_identity_title),
|
lang(lng_passport_identity_title),
|
||||||
lang(lng_passport_identity_description),
|
lang(lng_passport_identity_description),
|
||||||
false);
|
false);
|
||||||
break;
|
break;
|
||||||
case Value::Type::Address:
|
case Scope::Type::Address:
|
||||||
callback(
|
callback(
|
||||||
lang(lng_passport_address_title),
|
lang(lng_passport_address_title),
|
||||||
lang(lng_passport_address_description),
|
lang(lng_passport_address_description),
|
||||||
false);
|
false);
|
||||||
break;
|
break;
|
||||||
case Value::Type::Phone:
|
case Scope::Type::Phone:
|
||||||
callback(
|
callback(
|
||||||
lang(lng_passport_phone_title),
|
lang(lng_passport_phone_title),
|
||||||
App::self()->phone(),
|
lang(lng_passport_phone_description),
|
||||||
true);
|
false);
|
||||||
break;
|
break;
|
||||||
case Value::Type::Email:
|
case Scope::Type::Email:
|
||||||
callback(
|
callback(
|
||||||
lang(lng_passport_email_title),
|
lang(lng_passport_email_title),
|
||||||
lang(lng_passport_email_description),
|
lang(lng_passport_email_description),
|
||||||
false);
|
false);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PanelController::submitPassword(const QString &password) {
|
void PanelController::submitPassword(const QString &password) {
|
||||||
|
@ -119,27 +128,40 @@ QString PanelController::defaultPhoneNumber() const {
|
||||||
return _form->defaultPhoneNumber();
|
return _form->defaultPhoneNumber();
|
||||||
}
|
}
|
||||||
|
|
||||||
void PanelController::uploadScan(int valueIndex, QByteArray &&content) {
|
void PanelController::uploadScan(QByteArray &&content) {
|
||||||
Expects(_panel != nullptr);
|
Expects(_editScope != nullptr);
|
||||||
|
Expects(_editScopeFilesIndex >= 0);
|
||||||
|
|
||||||
_form->uploadScan(valueIndex, std::move(content));
|
_form->uploadScan(
|
||||||
|
_editScope->files[_editScopeFilesIndex],
|
||||||
|
std::move(content));
|
||||||
}
|
}
|
||||||
|
|
||||||
void PanelController::deleteScan(int valueIndex, int fileIndex) {
|
void PanelController::deleteScan(int fileIndex) {
|
||||||
Expects(_panel != nullptr);
|
Expects(_editScope != nullptr);
|
||||||
|
Expects(_editScopeFilesIndex >= 0);
|
||||||
|
|
||||||
_form->deleteScan(valueIndex, fileIndex);
|
_form->deleteScan(
|
||||||
|
_editScope->files[_editScopeFilesIndex],
|
||||||
|
fileIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PanelController::restoreScan(int valueIndex, int fileIndex) {
|
void PanelController::restoreScan(int fileIndex) {
|
||||||
Expects(_panel != nullptr);
|
Expects(_editScope != nullptr);
|
||||||
|
Expects(_editScopeFilesIndex >= 0);
|
||||||
|
|
||||||
_form->restoreScan(valueIndex, fileIndex);
|
_form->restoreScan(
|
||||||
|
_editScope->files[_editScopeFilesIndex],
|
||||||
|
fileIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
rpl::producer<ScanInfo> PanelController::scanUpdated() const {
|
rpl::producer<ScanInfo> PanelController::scanUpdated() const {
|
||||||
return _form->scanUpdated(
|
return _form->scanUpdated(
|
||||||
) | rpl::map([=](not_null<const EditFile*> file) {
|
) | rpl::filter([=](not_null<const EditFile*> file) {
|
||||||
|
return (_editScope != nullptr)
|
||||||
|
&& (_editScopeFilesIndex >= 0)
|
||||||
|
&& (file->value == _editScope->files[_editScopeFilesIndex]);
|
||||||
|
}) | rpl::map([=](not_null<const EditFile*> file) {
|
||||||
return collectScanInfo(*file);
|
return collectScanInfo(*file);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -202,22 +224,29 @@ void PanelController::ensurePanelCreated() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PanelController::editValue(int index) {
|
void PanelController::editScope(int index) {
|
||||||
ensurePanelCreated(); // #TODO passport testing
|
|
||||||
Expects(_panel != nullptr);
|
Expects(_panel != nullptr);
|
||||||
|
Expects(index >= 0 && index < _scopes.size());
|
||||||
|
|
||||||
_editValue = _form->startValueEdit(index);
|
_editScope = &_scopes[index];
|
||||||
Assert(_editValue != nullptr);
|
|
||||||
|
// #TODO select type for files index
|
||||||
|
_editScopeFilesIndex = _scopes[index].files.empty() ? -1 : 0;
|
||||||
|
|
||||||
|
_form->startValueEdit(_editScope->fields);
|
||||||
|
if (_editScopeFilesIndex >= 0) {
|
||||||
|
_form->startValueEdit(_editScope->files[_editScopeFilesIndex]);
|
||||||
|
}
|
||||||
|
|
||||||
auto content = [&]() -> object_ptr<Ui::RpWidget> {
|
auto content = [&]() -> object_ptr<Ui::RpWidget> {
|
||||||
switch (_editValue->type) {
|
switch (_editScope->type) {
|
||||||
case Value::Type::Identity:
|
case Scope::Type::Identity:
|
||||||
return object_ptr<PanelEditIdentity>(
|
return object_ptr<PanelEditIdentity>(
|
||||||
_panel.get(),
|
_panel.get(),
|
||||||
this,
|
this,
|
||||||
index,
|
_editScope->fields->data.parsed,
|
||||||
_editValue->data.parsed,
|
_editScope->files[_editScopeFilesIndex]->data.parsed,
|
||||||
valueFiles(*_editValue));
|
valueFiles(*_editScope->files[_editScopeFilesIndex]));
|
||||||
}
|
}
|
||||||
return { nullptr };
|
return { nullptr };
|
||||||
}();
|
}();
|
||||||
|
@ -244,21 +273,29 @@ std::vector<ScanInfo> PanelController::valueFiles(const Value &value) const {
|
||||||
}
|
}
|
||||||
|
|
||||||
void PanelController::cancelValueEdit(int index) {
|
void PanelController::cancelValueEdit(int index) {
|
||||||
if (base::take(_editValue)) {
|
if (const auto scope = base::take(_editScope)) {
|
||||||
_form->cancelValueEdit(index);
|
_form->startValueEdit(scope->fields);
|
||||||
|
const auto index = std::exchange(_editScopeFilesIndex, -1);
|
||||||
|
if (index >= 0) {
|
||||||
|
_form->startValueEdit(scope->files[index]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void PanelController::saveValue(int index, ValueMap &&data) {
|
void PanelController::saveScope(ValueMap &&data, ValueMap &&filesData) {
|
||||||
Expects(_panel != nullptr);
|
Expects(_panel != nullptr);
|
||||||
Expects(_editValue != nullptr);
|
Expects(_editScope != nullptr);
|
||||||
|
|
||||||
_editValue->data.parsed = std::move(data);
|
const auto scope = base::take(_editScope);
|
||||||
_editValue = nullptr;
|
_form->saveValueEdit(scope->fields, std::move(data));
|
||||||
|
const auto index = std::exchange(_editScopeFilesIndex, -1);
|
||||||
|
if (index >= 0) {
|
||||||
|
_form->saveValueEdit(scope->files[index], std::move(filesData));
|
||||||
|
} else {
|
||||||
|
Assert(filesData.fields.empty());
|
||||||
|
}
|
||||||
|
|
||||||
_panel->showForm();
|
_panel->showForm();
|
||||||
|
|
||||||
_form->saveValueEdit(index);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void PanelController::cancelAuth() {
|
void PanelController::cancelAuth() {
|
||||||
|
|
|
@ -45,14 +45,15 @@ public:
|
||||||
PanelController(not_null<FormController*> form);
|
PanelController(not_null<FormController*> form);
|
||||||
|
|
||||||
not_null<UserData*> bot() const;
|
not_null<UserData*> bot() const;
|
||||||
|
QString privacyPolicyUrl() const;
|
||||||
|
|
||||||
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;
|
||||||
|
|
||||||
void uploadScan(int valueIndex, QByteArray &&content);
|
void uploadScan(QByteArray &&content);
|
||||||
void deleteScan(int valueIndex, int fileIndex);
|
void deleteScan(int fileIndex);
|
||||||
void restoreScan(int valueIndex, int fileIndex);
|
void restoreScan(int fileIndex);
|
||||||
rpl::producer<ScanInfo> scanUpdated() const;
|
rpl::producer<ScanInfo> scanUpdated() const;
|
||||||
|
|
||||||
QString defaultEmail() const;
|
QString defaultEmail() const;
|
||||||
|
@ -68,8 +69,8 @@ public:
|
||||||
QString description,
|
QString description,
|
||||||
bool ready)> callback);
|
bool ready)> callback);
|
||||||
|
|
||||||
void editValue(int index) override;
|
void editScope(int index) override;
|
||||||
void saveValue(int index, ValueMap &&data);
|
void saveScope(ValueMap &&data, ValueMap &&filesData);
|
||||||
|
|
||||||
void cancelAuth();
|
void cancelAuth();
|
||||||
|
|
||||||
|
@ -84,9 +85,11 @@ private:
|
||||||
ScanInfo collectScanInfo(const EditFile &file) const;
|
ScanInfo collectScanInfo(const EditFile &file) const;
|
||||||
|
|
||||||
not_null<FormController*> _form;
|
not_null<FormController*> _form;
|
||||||
|
std::vector<Scope> _scopes;
|
||||||
|
|
||||||
std::unique_ptr<Panel> _panel;
|
std::unique_ptr<Panel> _panel;
|
||||||
Value *_editValue = nullptr;
|
Scope *_editScope = nullptr;
|
||||||
|
int _editScopeFilesIndex = -1;
|
||||||
|
|
||||||
rpl::lifetime _lifetime;
|
rpl::lifetime _lifetime;
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,7 @@ PanelDetailsRow::PanelDetailsRow(
|
||||||
const QString &label,
|
const QString &label,
|
||||||
const QString &value)
|
const QString &value)
|
||||||
: _label(label)
|
: _label(label)
|
||||||
, _field(this, st::passportDetailsField) {
|
, _field(this, st::passportDetailsField, nullptr, value) {
|
||||||
}
|
}
|
||||||
|
|
||||||
QPointer<Ui::InputField> PanelDetailsRow::field() const {
|
QPointer<Ui::InputField> PanelDetailsRow::field() const {
|
||||||
|
|
|
@ -190,11 +190,10 @@ void ScanButton::paintEvent(QPaintEvent *e) {
|
||||||
PanelEditIdentity::PanelEditIdentity(
|
PanelEditIdentity::PanelEditIdentity(
|
||||||
QWidget*,
|
QWidget*,
|
||||||
not_null<PanelController*> controller,
|
not_null<PanelController*> controller,
|
||||||
int valueIndex,
|
|
||||||
const ValueMap &data,
|
const ValueMap &data,
|
||||||
|
const ValueMap &scanData,
|
||||||
std::vector<ScanInfo> &&files)
|
std::vector<ScanInfo> &&files)
|
||||||
: _controller(controller)
|
: _controller(controller)
|
||||||
, _valueIndex(valueIndex)
|
|
||||||
, _files(std::move(files))
|
, _files(std::move(files))
|
||||||
, _scroll(this, st::passportPanelScroll)
|
, _scroll(this, st::passportPanelScroll)
|
||||||
, _topShadow(this)
|
, _topShadow(this)
|
||||||
|
@ -203,11 +202,13 @@ PanelEditIdentity::PanelEditIdentity(
|
||||||
this,
|
this,
|
||||||
langFactory(lng_passport_save_value),
|
langFactory(lng_passport_save_value),
|
||||||
st::passportPanelSaveValue) {
|
st::passportPanelSaveValue) {
|
||||||
setupControls(data);
|
setupControls(data, scanData);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PanelEditIdentity::setupControls(const ValueMap &data) {
|
void PanelEditIdentity::setupControls(
|
||||||
const auto inner = setupContent(data);
|
const ValueMap &data,
|
||||||
|
const ValueMap &scanData) {
|
||||||
|
const auto inner = setupContent(data, scanData);
|
||||||
|
|
||||||
using namespace rpl::mappers;
|
using namespace rpl::mappers;
|
||||||
|
|
||||||
|
@ -225,7 +226,8 @@ void PanelEditIdentity::setupControls(const ValueMap &data) {
|
||||||
}
|
}
|
||||||
|
|
||||||
not_null<Ui::RpWidget*> PanelEditIdentity::setupContent(
|
not_null<Ui::RpWidget*> PanelEditIdentity::setupContent(
|
||||||
const ValueMap &data) {
|
const ValueMap &data,
|
||||||
|
const ValueMap &scanData) {
|
||||||
const auto inner = _scroll->setOwnedWidget(
|
const auto inner = _scroll->setOwnedWidget(
|
||||||
object_ptr<Ui::VerticalLayout>(this));
|
object_ptr<Ui::VerticalLayout>(this));
|
||||||
_scroll->widthValue(
|
_scroll->widthValue(
|
||||||
|
@ -261,7 +263,9 @@ not_null<Ui::RpWidget*> PanelEditIdentity::setupContent(
|
||||||
_scansUpload = inner->add(
|
_scansUpload = inner->add(
|
||||||
object_ptr<Info::Profile::Button>(
|
object_ptr<Info::Profile::Button>(
|
||||||
inner,
|
inner,
|
||||||
uploadButtonText(),
|
_scansUploadTexts.events_starting_with(
|
||||||
|
uploadButtonText()
|
||||||
|
) | rpl::flatten_latest(),
|
||||||
st::passportUploadButton),
|
st::passportUploadButton),
|
||||||
st::passportUploadButtonPadding);
|
st::passportUploadButtonPadding);
|
||||||
_scansUpload->addClickHandler([=] {
|
_scansUpload->addClickHandler([=] {
|
||||||
|
@ -315,6 +319,7 @@ void PanelEditIdentity::updateScan(ScanInfo &&info) {
|
||||||
_scans.back()->show(anim::type::normal);
|
_scans.back()->show(anim::type::normal);
|
||||||
_scansDivider->hide(anim::type::normal);
|
_scansDivider->hide(anim::type::normal);
|
||||||
_scansHeader->show(anim::type::normal);
|
_scansHeader->show(anim::type::normal);
|
||||||
|
_scansUploadTexts.fire(uploadButtonText());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -335,12 +340,12 @@ void PanelEditIdentity::pushScan(const ScanInfo &info) {
|
||||||
|
|
||||||
scan->deleteClicks(
|
scan->deleteClicks(
|
||||||
) | rpl::start_with_next([=] {
|
) | rpl::start_with_next([=] {
|
||||||
_controller->deleteScan(_valueIndex, index);
|
_controller->deleteScan(index);
|
||||||
}, scan->lifetime());
|
}, scan->lifetime());
|
||||||
|
|
||||||
scan->restoreClicks(
|
scan->restoreClicks(
|
||||||
) | rpl::start_with_next([=] {
|
) | rpl::start_with_next([=] {
|
||||||
_controller->restoreScan(_valueIndex, index);
|
_controller->restoreScan(index);
|
||||||
}, scan->lifetime());
|
}, scan->lifetime());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -394,14 +399,15 @@ void PanelEditIdentity::encryptScan(const QString &path) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void PanelEditIdentity::encryptScanContent(QByteArray &&content) {
|
void PanelEditIdentity::encryptScanContent(QByteArray &&content) {
|
||||||
_controller->uploadScan(_valueIndex, std::move(content));
|
_controller->uploadScan(std::move(content));
|
||||||
}
|
}
|
||||||
|
|
||||||
void PanelEditIdentity::save() {
|
void PanelEditIdentity::save() {
|
||||||
auto data = ValueMap();
|
auto data = ValueMap();
|
||||||
data.fields["first_name"] = _firstName->getLastText();
|
data.fields["first_name"] = _firstName->getLastText();
|
||||||
data.fields["last_name"] = _lastName->getLastText();
|
data.fields["last_name"] = _lastName->getLastText();
|
||||||
_controller->saveValue(_valueIndex, std::move(data));
|
auto scanData = ValueMap();
|
||||||
|
_controller->saveScope(std::move(data), std::move(scanData));
|
||||||
}
|
}
|
||||||
|
|
||||||
rpl::producer<QString> PanelEditIdentity::uploadButtonText() const {
|
rpl::producer<QString> PanelEditIdentity::uploadButtonText() const {
|
||||||
|
|
|
@ -41,8 +41,8 @@ public:
|
||||||
PanelEditIdentity(
|
PanelEditIdentity(
|
||||||
QWidget *parent,
|
QWidget *parent,
|
||||||
not_null<PanelController*> controller,
|
not_null<PanelController*> controller,
|
||||||
int valueIndex,
|
|
||||||
const ValueMap &data,
|
const ValueMap &data,
|
||||||
|
const ValueMap &scanData,
|
||||||
std::vector<ScanInfo> &&files);
|
std::vector<ScanInfo> &&files);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
@ -50,8 +50,10 @@ protected:
|
||||||
void resizeEvent(QResizeEvent *e) override;
|
void resizeEvent(QResizeEvent *e) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void setupControls(const ValueMap &data);
|
void setupControls(const ValueMap &data, const ValueMap &scanData);
|
||||||
not_null<Ui::RpWidget*> setupContent(const ValueMap &data);
|
not_null<Ui::RpWidget*> setupContent(
|
||||||
|
const ValueMap &data,
|
||||||
|
const ValueMap &scanData);
|
||||||
void updateControlsGeometry();
|
void updateControlsGeometry();
|
||||||
|
|
||||||
void chooseScan();
|
void chooseScan();
|
||||||
|
@ -64,8 +66,6 @@ private:
|
||||||
void save();
|
void save();
|
||||||
|
|
||||||
not_null<PanelController*> _controller;
|
not_null<PanelController*> _controller;
|
||||||
int _valueIndex = -1;
|
|
||||||
|
|
||||||
std::vector<ScanInfo> _files;
|
std::vector<ScanInfo> _files;
|
||||||
|
|
||||||
object_ptr<Ui::ScrollArea> _scroll;
|
object_ptr<Ui::ScrollArea> _scroll;
|
||||||
|
@ -77,6 +77,7 @@ private:
|
||||||
QPointer<Ui::VerticalLayout> _scansWrap;
|
QPointer<Ui::VerticalLayout> _scansWrap;
|
||||||
std::vector<base::unique_qptr<Ui::SlideWrap<ScanButton>>> _scans;
|
std::vector<base::unique_qptr<Ui::SlideWrap<ScanButton>>> _scans;
|
||||||
QPointer<Info::Profile::Button> _scansUpload;
|
QPointer<Info::Profile::Button> _scansUpload;
|
||||||
|
rpl::event_stream<rpl::producer<QString>> _scansUploadTexts;
|
||||||
|
|
||||||
QPointer<Ui::InputField> _firstName;
|
QPointer<Ui::InputField> _firstName;
|
||||||
QPointer<Ui::InputField> _lastName;
|
QPointer<Ui::InputField> _lastName;
|
||||||
|
|
|
@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "passport/passport_panel_controller.h"
|
#include "passport/passport_panel_controller.h"
|
||||||
#include "lang/lang_keys.h"
|
#include "lang/lang_keys.h"
|
||||||
#include "boxes/abstract_box.h"
|
#include "boxes/abstract_box.h"
|
||||||
|
#include "core/click_handler_types.h"
|
||||||
#include "ui/widgets/shadow.h"
|
#include "ui/widgets/shadow.h"
|
||||||
#include "ui/widgets/buttons.h"
|
#include "ui/widgets/buttons.h"
|
||||||
#include "ui/widgets/scroll_area.h"
|
#include "ui/widgets/scroll_area.h"
|
||||||
|
@ -221,27 +222,30 @@ not_null<Ui::RpWidget*> PanelForm::setupContent() {
|
||||||
title,
|
title,
|
||||||
description)));
|
description)));
|
||||||
_rows.back()->addClickHandler([=] {
|
_rows.back()->addClickHandler([=] {
|
||||||
_controller->editValue(index);
|
_controller->editScope(index);
|
||||||
});
|
});
|
||||||
_rows.back()->setReady(ready);
|
_rows.back()->setReady(ready);
|
||||||
|
++index;
|
||||||
});
|
});
|
||||||
|
const auto policyUrl = _controller->privacyPolicyUrl();
|
||||||
const auto policy = inner->add(
|
const auto policy = inner->add(
|
||||||
object_ptr<Ui::FlatLabel>(
|
object_ptr<Ui::FlatLabel>(
|
||||||
inner,
|
inner,
|
||||||
lng_passport_accept_allow(
|
(policyUrl.isEmpty()
|
||||||
|
? lng_passport_allow(lt_bot, '@' + bot->username)
|
||||||
|
: lng_passport_accept_allow(
|
||||||
lt_policy,
|
lt_policy,
|
||||||
textcmdLink(
|
textcmdLink(
|
||||||
1,
|
1,
|
||||||
lng_passport_policy(lt_bot, App::peerName(bot))),
|
lng_passport_policy(lt_bot, App::peerName(bot))),
|
||||||
lt_bot,
|
lt_bot,
|
||||||
'@' + bot->username),
|
'@' + bot->username)),
|
||||||
Ui::FlatLabel::InitType::Rich,
|
Ui::FlatLabel::InitType::Rich,
|
||||||
st::passportFormPolicy),
|
st::passportFormPolicy),
|
||||||
st::passportFormPolicyPadding);
|
st::passportFormPolicyPadding);
|
||||||
policy->setLink(1, std::make_shared<LambdaClickHandler>([=] {
|
if (!policyUrl.isEmpty()) {
|
||||||
_controller->cancelAuth();
|
policy->setLink(1, std::make_shared<UrlClickHandler>(policyUrl));
|
||||||
// #TODO passport policy
|
}
|
||||||
}));
|
|
||||||
|
|
||||||
return inner;
|
return inner;
|
||||||
}
|
}
|
||||||
|
|
|
@ -458,6 +458,7 @@
|
||||||
<(src_loc)/passport/passport_encryption.h
|
<(src_loc)/passport/passport_encryption.h
|
||||||
<(src_loc)/passport/passport_form_controller.cpp
|
<(src_loc)/passport/passport_form_controller.cpp
|
||||||
<(src_loc)/passport/passport_form_controller.h
|
<(src_loc)/passport/passport_form_controller.h
|
||||||
|
<(src_loc)/passport/passport_form_view_controller.cpp
|
||||||
<(src_loc)/passport/passport_form_view_controller.h
|
<(src_loc)/passport/passport_form_view_controller.h
|
||||||
<(src_loc)/passport/passport_panel.cpp
|
<(src_loc)/passport/passport_panel.cpp
|
||||||
<(src_loc)/passport/passport_panel.h
|
<(src_loc)/passport/passport_panel.h
|
||||||
|
|
Loading…
Reference in New Issue