Update API scheme.

This commit is contained in:
John Preston 2018-04-03 22:24:31 +04:00
parent 5cfead762d
commit f0b7ff24b1
13 changed files with 527 additions and 334 deletions

View File

@ -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.";

View File

@ -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;

View File

@ -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() {

View File

@ -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;

View File

@ -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

View File

@ -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() {
} }

View File

@ -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() {

View File

@ -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;

View File

@ -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 {

View File

@ -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 {

View File

@ -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;

View File

@ -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;
} }

View File

@ -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