Allow to add selfie in passport.

This commit is contained in:
John Preston 2018-04-10 23:00:52 +04:00
parent 11fd757e99
commit ccb57a6d69
9 changed files with 435 additions and 135 deletions

View File

@ -1548,6 +1548,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_passport_scan_index" = "Scan {index}"; "lng_passport_scan_index" = "Scan {index}";
"lng_passport_upload_scans" = "Upload scans"; "lng_passport_upload_scans" = "Upload scans";
"lng_passport_upload_more" = "Upload additional scans"; "lng_passport_upload_more" = "Upload additional scans";
"lng_passport_selfie_title" = "Selfie";
"lng_passport_selfie_name" = "Photo";
"lng_passport_selfie_description" = "Take a picture of yourself holding hour document.";
"lng_passport_upload_selfie" = "Upload selfie";
"lng_passport_personal_details" = "Personal details"; "lng_passport_personal_details" = "Personal details";
"lng_passport_choose_image" = "Choose scan image"; "lng_passport_choose_image" = "Choose scan image";
"lng_passport_delete_scan_undo" = "Undo"; "lng_passport_delete_scan_undo" = "Undo";

View File

@ -242,6 +242,18 @@ bool FormController::validateValueSecrets(Value &value) {
return false; return false;
} }
} }
if (value.selfie) {
auto &file = *value.selfie;
file.secret = DecryptValueSecret(
file.encryptedSecret,
_secret,
file.hash);
if (file.secret.empty()) {
LOG(("API Error: Could not decrypt selfie secret. "
"Forgetting files and data :("));
return false;
}
}
return true; return true;
} }
@ -266,8 +278,53 @@ void FormController::uploadScan(
nonconst, nonconst,
File(), File(),
nullptr); nullptr);
const auto fileId = rand_value<uint64>();
auto &file = nonconst->filesInEdit.back(); auto &file = nonconst->filesInEdit.back();
encryptFile(file, std::move(content), [=](UploadScanData &&result) {
Expects(fileIndex >= 0 && fileIndex < nonconst->filesInEdit.size());
uploadEncryptedFile(
nonconst->filesInEdit[fileIndex],
std::move(result));
});
}
void FormController::deleteScan(
not_null<const Value*> value,
int fileIndex) {
scanDeleteRestore(value, fileIndex, true);
}
void FormController::restoreScan(
not_null<const Value*> value,
int fileIndex) {
scanDeleteRestore(value, fileIndex, false);
}
void FormController::uploadSelfie(
not_null<const Value*> value,
QByteArray &&content) {
const auto nonconst = findValue(value);
nonconst->selfieInEdit = EditFile{ nonconst, File(), nullptr };
auto &file = *nonconst->selfieInEdit;
encryptFile(file, std::move(content), [=](UploadScanData &&result) {
uploadEncryptedFile(
*nonconst->selfieInEdit,
std::move(result));
});
}
void FormController::deleteSelfie(not_null<const Value*> value) {
selfieDeleteRestore(value, true);
}
void FormController::restoreSelfie(not_null<const Value*> value) {
selfieDeleteRestore(value, false);
}
void FormController::prepareFile(
EditFile &file,
const QByteArray &content) {
const auto fileId = rand_value<uint64>();
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();
@ -277,17 +334,14 @@ void FormController::uploadScan(
file.fields.downloadOffset = file.fields.size; file.fields.downloadOffset = file.fields.size;
_scanUpdated.fire(&file); _scanUpdated.fire(&file);
encryptScan(nonconst, fileIndex, std::move(content));
} }
void FormController::encryptScan( void FormController::encryptFile(
not_null<Value*> value, EditFile &file,
int fileIndex, QByteArray &&content,
QByteArray &&content) { base::lambda<void(UploadScanData &&result)> callback) {
Expects(fileIndex >= 0 && fileIndex < value->filesInEdit.size()); prepareFile(file, content);
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([
=, =,
@ -309,27 +363,12 @@ void FormController::encryptScan(
result.md5checksum.data()); result.md5checksum.data());
crl::on_main([=, encrypted = std::move(result)]() mutable { crl::on_main([=, encrypted = std::move(result)]() mutable {
if (weak.lock()) { if (weak.lock()) {
uploadEncryptedScan( callback(std::move(encrypted));
value ,
fileIndex,
std::move(encrypted));
} }
}); });
}); });
} }
void FormController::deleteScan(
not_null<const Value*> value,
int fileIndex) {
scanDeleteRestore(value, fileIndex, true);
}
void FormController::restoreScan(
not_null<const Value*> value,
int fileIndex) {
scanDeleteRestore(value, fileIndex, false);
}
void FormController::scanDeleteRestore( void FormController::scanDeleteRestore(
not_null<const Value*> value, not_null<const Value*> value,
int fileIndex, int fileIndex,
@ -342,6 +381,17 @@ void FormController::scanDeleteRestore(
_scanUpdated.fire(&file); _scanUpdated.fire(&file);
} }
void FormController::selfieDeleteRestore(
not_null<const Value*> value,
bool deleted) {
Expects(value->selfieInEdit.has_value());
const auto nonconst = findValue(value);
auto &file = *nonconst->selfieInEdit;
file.deleted = deleted;
_scanUpdated.fire(&file);
}
void FormController::subscribeToUploader() { void FormController::subscribeToUploader() {
if (_uploaderSubscriptions) { if (_uploaderSubscriptions) {
return; return;
@ -365,15 +415,11 @@ void FormController::subscribeToUploader() {
}, _uploaderSubscriptions); }, _uploaderSubscriptions);
} }
void FormController::uploadEncryptedScan( void FormController::uploadEncryptedFile(
not_null<Value*> value, EditFile &file,
int fileIndex,
UploadScanData &&data) { UploadScanData &&data) {
Expects(fileIndex >= 0 && fileIndex < value->filesInEdit.size());
subscribeToUploader(); subscribeToUploader();
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>(
@ -548,27 +594,43 @@ void FormController::startValueEdit(not_null<const Value*> value) {
if (savingValue(nonconst)) { if (savingValue(nonconst)) {
return; return;
} }
loadFiles(nonconst->files); for (auto &file : nonconst->files) {
loadFile(file);
}
if (nonconst->selfie) {
loadFile(*nonconst->selfie);
}
nonconst->filesInEdit = ranges::view::all( nonconst->filesInEdit = ranges::view::all(
nonconst->files nonconst->files
) | ranges::view::transform([=](const File &file) { ) | ranges::view::transform([=](const File &file) {
return EditFile(nonconst, file, nullptr); return EditFile(nonconst, file, nullptr);
}) | ranges::to_vector; }) | ranges::to_vector;
if (nonconst->selfie) {
nonconst->selfieInEdit = EditFile(
nonconst,
*nonconst->selfie,
nullptr);
} else {
nonconst->selfieInEdit = base::none;
}
nonconst->data.parsedInEdit = nonconst->data.parsed; nonconst->data.parsedInEdit = nonconst->data.parsed;
} }
void FormController::loadFiles(std::vector<File> &files) { void FormController::loadFile(File &file) {
for (auto &file : files) {
if (!file.image.isNull()) { if (!file.image.isNull()) {
file.downloadOffset = file.size; file.downloadOffset = file.size;
continue; return;
} }
const auto key = FileKey{ file.id, file.dcId }; const auto key = FileKey{ file.id, file.dcId };
const auto i = _fileLoaders.find(key); const auto i = _fileLoaders.find(key);
if (i == _fileLoaders.end()) { if (i != _fileLoaders.end()) {
return;
}
file.downloadOffset = 0; file.downloadOffset = 0;
const auto [i, ok] = _fileLoaders.emplace( const auto [j, ok] = _fileLoaders.emplace(
key, key,
std::make_unique<mtpFileLoader>( std::make_unique<mtpFileLoader>(
file.dcId, file.dcId,
@ -581,7 +643,7 @@ void FormController::loadFiles(std::vector<File> &files) {
LoadToCacheAsWell, LoadToCacheAsWell,
LoadFromCloudOrLocal, LoadFromCloudOrLocal,
false)); false));
const auto loader = i->second.get(); const auto loader = j->second.get();
loader->connect(loader, &mtpFileLoader::progress, [=] { loader->connect(loader, &mtpFileLoader::progress, [=] {
if (loader->finished()) { if (loader->finished()) {
fileLoadDone(key, loader->bytes()); fileLoadDone(key, loader->bytes());
@ -594,8 +656,6 @@ void FormController::loadFiles(std::vector<File> &files) {
}); });
loader->start(); loader->start();
} }
}
}
void FormController::fileLoadDone(FileKey key, const QByteArray &bytes) { void FormController::fileLoadDone(FileKey key, const QByteArray &bytes) {
if (const auto [value, file] = findFile(key); file != nullptr) { if (const auto [value, file] = findFile(key); file != nullptr) {
@ -666,6 +726,7 @@ void FormController::clearValueEdit(not_null<Value*> value) {
return; return;
} }
value->filesInEdit.clear(); value->filesInEdit.clear();
value->selfieInEdit = base::none;
value->data.encryptedSecretInEdit.clear(); value->data.encryptedSecretInEdit.clear();
value->data.hashInEdit.clear(); value->data.hashInEdit.clear();
value->data.parsedInEdit = ValueMap(); value->data.parsedInEdit = ValueMap();
@ -787,7 +848,6 @@ void FormController::saveEncryptedValue(not_null<Value*> value) {
inputFiles.push_back(inputFile(file)); inputFiles.push_back(inputFile(file));
} }
if (value->data.secret.empty()) { if (value->data.secret.empty()) {
value->data.secret = GenerateSecretBytes(); value->data.secret = GenerateSecretBytes();
} }
@ -890,9 +950,18 @@ void FormController::sendSaveRequest(
value->saveRequestId = 0; value->saveRequestId = 0;
const auto &data = result.c_secureValue(); const auto &data = result.c_secureValue();
value->files = parseFiles( value->files = data.has_files()
? parseFiles(
data.vfiles.v, data.vfiles.v,
base::take(value->filesInEdit)); base::take(value->filesInEdit))
: std::vector<File>();
auto selfiesInEdit = std::vector<EditFile>();
if (auto selfie = base::take(value->selfieInEdit)) {
selfiesInEdit.push_back(std::move(*selfie));
}
value->selfie = data.has_selfie()
? parseFile(data.vselfie, selfiesInEdit)
: base::none;
value->data.encryptedSecret = std::move( value->data.encryptedSecret = std::move(
value->data.encryptedSecretInEdit); value->data.encryptedSecretInEdit);
value->data.parsed = std::move(value->data.parsedInEdit); value->data.parsed = std::move(value->data.parsedInEdit);
@ -1110,32 +1179,40 @@ auto FormController::parseFiles(
auto result = std::vector<File>(); auto result = std::vector<File>();
result.reserve(data.size()); result.reserve(data.size());
auto index = 0;
for (const auto &file : data) { for (const auto &file : data) {
switch (file.type()) { if (auto normal = parseFile(file, editData)) {
case mtpc_secureFileEmpty: { result.push_back(std::move(*normal));
} break;
case mtpc_secureFile: {
const auto &fields = file.c_secureFile();
auto normal = File();
normal.id = fields.vid.v;
normal.accessHash = fields.vaccess_hash.v;
normal.size = fields.vsize.v;
normal.date = fields.vdate.v;
normal.dcId = fields.vdc_id.v;
normal.hash = bytes::make_vector(fields.vfile_hash.v);
normal.encryptedSecret = bytes::make_vector(fields.vsecret.v);
fillDownloadedFile(normal, editData);
result.push_back(std::move(normal));
} break;
} }
++index;
} }
return result; return result;
} }
auto FormController::parseFile(
const MTPSecureFile &data,
const std::vector<EditFile> &editData) const
-> base::optional<File> {
switch (data.type()) {
case mtpc_secureFileEmpty:
return base::none;
case mtpc_secureFile: {
const auto &fields = data.c_secureFile();
auto result = File();
result.id = fields.vid.v;
result.accessHash = fields.vaccess_hash.v;
result.size = fields.vsize.v;
result.date = fields.vdate.v;
result.dcId = fields.vdc_id.v;
result.hash = bytes::make_vector(fields.vfile_hash.v);
result.encryptedSecret = bytes::make_vector(fields.vsecret.v);
fillDownloadedFile(result, editData);
return result;
} break;
}
Unexpected("Type in FormController::parseFile.");
}
void FormController::fillDownloadedFile( void FormController::fillDownloadedFile(
File &destination, File &destination,
const std::vector<EditFile> &source) const { const std::vector<EditFile> &source) const {
@ -1180,6 +1257,9 @@ auto FormController::parseValue(
if (data.has_files()) { if (data.has_files()) {
result.files = parseFiles(data.vfiles.v); result.files = parseFiles(data.vfiles.v);
} }
if (data.has_selfie()) {
result.selfie = parseFile(data.vselfie);
}
if (data.has_plain_data()) { if (data.has_plain_data()) {
switch (data.vplain_data.type()) { switch (data.vplain_data.type()) {
case mtpc_securePlainPhone: { case mtpc_securePlainPhone: {
@ -1197,35 +1277,53 @@ auto FormController::parseValue(
} }
auto FormController::findEditFile(const FullMsgId &fullId) -> EditFile* { auto FormController::findEditFile(const FullMsgId &fullId) -> EditFile* {
const auto found = [&](const EditFile &file) {
return (file.uploadData && file.uploadData->fullId == fullId);
};
for (auto &[type, value] : _form.values) { for (auto &[type, value] : _form.values) {
for (auto &file : value.filesInEdit) { for (auto &file : value.filesInEdit) {
if (file.uploadData && file.uploadData->fullId == fullId) { if (found(file)) {
return &file; return &file;
} }
} }
if (value.selfieInEdit && found(*value.selfieInEdit)) {
return &*value.selfieInEdit;
}
} }
return nullptr; return nullptr;
} }
auto FormController::findEditFile(const FileKey &key) -> EditFile* { auto FormController::findEditFile(const FileKey &key) -> EditFile* {
const auto found = [&](const EditFile &file) {
return (file.fields.dcId == key.dcId && file.fields.id == key.id);
};
for (auto &[type, value] : _form.values) { for (auto &[type, value] : _form.values) {
for (auto &file : value.filesInEdit) { for (auto &file : value.filesInEdit) {
if (file.fields.dcId == key.dcId && file.fields.id == key.id) { if (found(file)) {
return &file; return &file;
} }
} }
if (value.selfieInEdit && found(*value.selfieInEdit)) {
return &*value.selfieInEdit;
}
} }
return nullptr; return nullptr;
} }
auto FormController::findFile(const FileKey &key) auto FormController::findFile(const FileKey &key)
-> std::pair<Value*, File*> { -> std::pair<Value*, File*> {
const auto found = [&](const File &file) {
return (file.dcId == key.dcId) && (file.id == key.id);
};
for (auto &[type, value] : _form.values) { for (auto &[type, value] : _form.values) {
for (auto &file : value.files) { for (auto &file : value.files) {
if (file.dcId == key.dcId && file.id == key.id) { if (found(file)) {
return { &value, &file }; return { &value, &file };
} }
} }
if (value.selfie && found(*value.selfie)) {
return { &value, &*value.selfie };
}
} }
return { nullptr, nullptr }; return { nullptr, nullptr };
} }

View File

@ -212,6 +212,9 @@ public:
void uploadScan(not_null<const Value*> value, QByteArray &&content); void uploadScan(not_null<const Value*> value, QByteArray &&content);
void deleteScan(not_null<const Value*> value, int fileIndex); void deleteScan(not_null<const Value*> value, int fileIndex);
void restoreScan(not_null<const Value*> value, int fileIndex); void restoreScan(not_null<const Value*> value, int fileIndex);
void uploadSelfie(not_null<const Value*> value, QByteArray &&content);
void deleteSelfie(not_null<const Value*> value);
void restoreSelfie(not_null<const Value*> value);
rpl::producer<> secretReadyEvents() const; rpl::producer<> secretReadyEvents() const;
@ -257,6 +260,9 @@ private:
std::vector<File> parseFiles( std::vector<File> parseFiles(
const QVector<MTPSecureFile> &data, const QVector<MTPSecureFile> &data,
const std::vector<EditFile> &editData = {}) const; const std::vector<EditFile> &editData = {}) const;
base::optional<File> parseFile(
const MTPSecureFile &data,
const std::vector<EditFile> &editData = {}) const;
void fillDownloadedFile( void fillDownloadedFile(
File &destination, File &destination,
const std::vector<EditFile> &source) const; const std::vector<EditFile> &source) const;
@ -275,25 +281,33 @@ private:
bool validateValueSecrets(Value &value); bool validateValueSecrets(Value &value);
void resetValue(Value &value); void resetValue(Value &value);
void loadFiles(std::vector<File> &files); void loadFile(File &file);
void fileLoadDone(FileKey key, const QByteArray &bytes); void fileLoadDone(FileKey key, const QByteArray &bytes);
void fileLoadProgress(FileKey key, int offset); void fileLoadProgress(FileKey key, int offset);
void fileLoadFail(FileKey key); void fileLoadFail(FileKey key);
void generateSecret(bytes::const_span password); void generateSecret(bytes::const_span password);
void subscribeToUploader(); void subscribeToUploader();
void encryptScan( void encryptFile(
not_null<Value*> value, EditFile &file,
int fileIndex, QByteArray &&content,
QByteArray &&content); base::lambda<void(UploadScanData &&result)> callback);
void uploadEncryptedScan( void prepareFile(
not_null<Value*> value, EditFile &file,
int fileIndex, const QByteArray &content);
void uploadEncryptedFile(
EditFile &file,
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(not_null<const Value*> value, int fileIndex, bool deleted); void scanDeleteRestore(
not_null<const Value*> value,
int fileIndex,
bool deleted);
void selfieDeleteRestore(
not_null<const Value*> value,
bool deleted);
QString getPhoneFromValue(not_null<const Value*> value) const; QString getPhoneFromValue(not_null<const Value*> value) const;
QString getEmailFromValue(not_null<const Value*> value) const; QString getEmailFromValue(not_null<const Value*> value) const;

View File

@ -356,7 +356,8 @@ QString PanelController::defaultPhoneNumber() const {
void PanelController::uploadScan(QByteArray &&content) { void PanelController::uploadScan(QByteArray &&content) {
Expects(_editScope != nullptr); Expects(_editScope != nullptr);
Expects(_editScopeFilesIndex >= 0); Expects(_editScopeFilesIndex >= 0
&& _editScopeFilesIndex < _editScope->files.size());
_form->uploadScan( _form->uploadScan(
_editScope->files[_editScopeFilesIndex], _editScope->files[_editScopeFilesIndex],
@ -365,7 +366,8 @@ void PanelController::uploadScan(QByteArray &&content) {
void PanelController::deleteScan(int fileIndex) { void PanelController::deleteScan(int fileIndex) {
Expects(_editScope != nullptr); Expects(_editScope != nullptr);
Expects(_editScopeFilesIndex >= 0); Expects(_editScopeFilesIndex >= 0
&& _editScopeFilesIndex < _editScope->files.size());
_form->deleteScan( _form->deleteScan(
_editScope->files[_editScopeFilesIndex], _editScope->files[_editScopeFilesIndex],
@ -374,13 +376,45 @@ void PanelController::deleteScan(int fileIndex) {
void PanelController::restoreScan(int fileIndex) { void PanelController::restoreScan(int fileIndex) {
Expects(_editScope != nullptr); Expects(_editScope != nullptr);
Expects(_editScopeFilesIndex >= 0); Expects(_editScopeFilesIndex >= 0
&& _editScopeFilesIndex < _editScope->files.size());
_form->restoreScan( _form->restoreScan(
_editScope->files[_editScopeFilesIndex], _editScope->files[_editScopeFilesIndex],
fileIndex); fileIndex);
} }
void PanelController::uploadSelfie(QByteArray &&content) {
Expects(_editScope != nullptr);
Expects(_editScopeFilesIndex >= 0
&& _editScopeFilesIndex < _editScope->files.size());
Expects(_editScope->selfieRequired);
_form->uploadSelfie(
_editScope->files[_editScopeFilesIndex],
std::move(content));
}
void PanelController::deleteSelfie() {
Expects(_editScope != nullptr);
Expects(_editScopeFilesIndex >= 0
&& _editScopeFilesIndex < _editScope->files.size());
Expects(_editScope->selfieRequired);
_form->deleteSelfie(
_editScope->files[_editScopeFilesIndex]);
}
void PanelController::restoreSelfie() {
Expects(_editScope != nullptr);
Expects(_editScopeFilesIndex >= 0
&& _editScopeFilesIndex < _editScope->files.size());
Expects(_editScope->selfieRequired);
_form->restoreSelfie(
_editScope->files[_editScopeFilesIndex]);
}
rpl::producer<ScanInfo> PanelController::scanUpdated() const { rpl::producer<ScanInfo> PanelController::scanUpdated() const {
return _form->scanUpdated( return _form->scanUpdated(
) | rpl::filter([=](not_null<const EditFile*> file) { ) | rpl::filter([=](not_null<const EditFile*> file) {
@ -422,11 +456,17 @@ ScanInfo PanelController::collectScanInfo(const EditFile &file) const {
return formatDownloadText(0, file.fields.size); return formatDownloadText(0, file.fields.size);
} }
}(); }();
auto isSelfie = (_editScope != nullptr)
&& (_editScopeFilesIndex >= 0)
&& (file.value == _editScope->files[_editScopeFilesIndex])
&& (_editScope->files[_editScopeFilesIndex]->selfieInEdit.has_value())
&& (&file == &*_editScope->files[_editScopeFilesIndex]->selfieInEdit);
return { return {
FileKey{ file.fields.id, file.fields.dcId }, FileKey{ file.fields.id, file.fields.dcId },
status, status,
file.fields.image, file.fields.image,
file.deleted }; file.deleted,
isSelfie };
} }
QString PanelController::getDefaultContactValue(Scope::Type type) const { QString PanelController::getDefaultContactValue(Scope::Type type) const {
@ -594,7 +634,10 @@ void PanelController::editScope(int index, int filesIndex) {
_editScope->files[_editScopeFilesIndex]->type), _editScope->files[_editScopeFilesIndex]->type),
_editScope->fields->data.parsedInEdit, _editScope->fields->data.parsedInEdit,
_editScope->files[_editScopeFilesIndex]->data.parsedInEdit, _editScope->files[_editScopeFilesIndex]->data.parsedInEdit,
valueFiles(*_editScope->files[_editScopeFilesIndex])) valueFiles(*_editScope->files[_editScopeFilesIndex]),
(_editScope->selfieRequired
? valueSelfie(*_editScope->files[_editScopeFilesIndex])
: nullptr))
: object_ptr<PanelEditDocument>( : object_ptr<PanelEditDocument>(
_panel.get(), _panel.get(),
this, this,
@ -735,6 +778,15 @@ std::vector<ScanInfo> PanelController::valueFiles(
return result; return result;
} }
std::unique_ptr<ScanInfo> PanelController::valueSelfie(
const Value &value) const {
if (value.selfieInEdit) {
return std::make_unique<ScanInfo>(
collectScanInfo(*value.selfieInEdit));
}
return std::make_unique<ScanInfo>();
}
void PanelController::cancelValueEdit() { void PanelController::cancelValueEdit() {
if (const auto scope = base::take(_editScope)) { if (const auto scope = base::take(_editScope)) {
_form->cancelValueEdit(scope->fields); _form->cancelValueEdit(scope->fields);

View File

@ -20,6 +20,7 @@ struct ScanInfo {
QString status; QString status;
QImage thumb; QImage thumb;
bool deleted = false; bool deleted = false;
bool selfie = false;
}; };
@ -54,6 +55,9 @@ public:
void uploadScan(QByteArray &&content); void uploadScan(QByteArray &&content);
void deleteScan(int fileIndex); void deleteScan(int fileIndex);
void restoreScan(int fileIndex); void restoreScan(int fileIndex);
void uploadSelfie(QByteArray &&content);
void deleteSelfie();
void restoreSelfie();
rpl::producer<ScanInfo> scanUpdated() const; rpl::producer<ScanInfo> scanUpdated() const;
QString defaultEmail() const; QString defaultEmail() const;
@ -92,6 +96,7 @@ private:
void requestScopeFilesType(int index); void requestScopeFilesType(int index);
void cancelValueEdit(); void cancelValueEdit();
std::vector<ScanInfo> valueFiles(const Value &value) const; std::vector<ScanInfo> valueFiles(const Value &value) const;
std::unique_ptr<ScanInfo> valueSelfie(const Value &value) const;
void processValueSaveFinished(not_null<const Value*> value); void processValueSaveFinished(not_null<const Value*> value);
void processVerificationNeeded(not_null<const Value*> value); void processVerificationNeeded(not_null<const Value*> value);

View File

@ -135,7 +135,8 @@ PanelEditDocument::PanelEditDocument(
Scheme scheme, Scheme scheme,
const ValueMap &data, const ValueMap &data,
const ValueMap &scanData, const ValueMap &scanData,
std::vector<ScanInfo> &&files) std::vector<ScanInfo> &&files,
std::unique_ptr<ScanInfo> &&selfie)
: _controller(controller) : _controller(controller)
, _scheme(std::move(scheme)) , _scheme(std::move(scheme))
, _scroll(this, st::passportPanelScroll) , _scroll(this, st::passportPanelScroll)
@ -145,7 +146,7 @@ PanelEditDocument::PanelEditDocument(
this, this,
langFactory(lng_passport_save_value), langFactory(lng_passport_save_value),
st::passportPanelSaveValue) { st::passportPanelSaveValue) {
setupControls(data, &scanData, std::move(files)); setupControls(data, &scanData, std::move(files), std::move(selfie));
} }
PanelEditDocument::PanelEditDocument( PanelEditDocument::PanelEditDocument(
@ -162,14 +163,19 @@ PanelEditDocument::PanelEditDocument(
this, this,
langFactory(lng_passport_save_value), langFactory(lng_passport_save_value),
st::passportPanelSaveValue) { st::passportPanelSaveValue) {
setupControls(data, nullptr, {}); setupControls(data, nullptr, {}, nullptr);
} }
void PanelEditDocument::setupControls( void PanelEditDocument::setupControls(
const ValueMap &data, const ValueMap &data,
const ValueMap *scanData, const ValueMap *scanData,
std::vector<ScanInfo> &&files) { std::vector<ScanInfo> &&files,
const auto inner = setupContent(data, scanData, std::move(files)); std::unique_ptr<ScanInfo> &&selfie) {
const auto inner = setupContent(
data,
scanData,
std::move(files),
std::move(selfie));
using namespace rpl::mappers; using namespace rpl::mappers;
@ -185,7 +191,8 @@ void PanelEditDocument::setupControls(
not_null<Ui::RpWidget*> PanelEditDocument::setupContent( not_null<Ui::RpWidget*> PanelEditDocument::setupContent(
const ValueMap &data, const ValueMap &data,
const ValueMap *scanData, const ValueMap *scanData,
std::vector<ScanInfo> &&files) { std::vector<ScanInfo> &&files,
std::unique_ptr<ScanInfo> &&selfie) {
const auto inner = _scroll->setOwnedWidget( const auto inner = _scroll->setOwnedWidget(
object_ptr<Ui::VerticalLayout>(this)); object_ptr<Ui::VerticalLayout>(this));
_scroll->widthValue( _scroll->widthValue(
@ -199,12 +206,14 @@ not_null<Ui::RpWidget*> PanelEditDocument::setupContent(
inner, inner,
_controller, _controller,
_scheme.scansHeader, _scheme.scansHeader,
std::move(files))); std::move(files),
} std::move(selfie)));
} else {
inner->add(object_ptr<BoxContentDivider>( inner->add(object_ptr<BoxContentDivider>(
inner, inner,
st::passportFormDividerHeight)); st::passportFormDividerHeight));
}
inner->add( inner->add(
object_ptr<Ui::FlatLabel>( object_ptr<Ui::FlatLabel>(
inner, inner,

View File

@ -52,7 +52,8 @@ public:
Scheme scheme, Scheme scheme,
const ValueMap &data, const ValueMap &data,
const ValueMap &scanData, const ValueMap &scanData,
std::vector<ScanInfo> &&files); std::vector<ScanInfo> &&files,
std::unique_ptr<ScanInfo> &&selfie);
PanelEditDocument( PanelEditDocument(
QWidget *parent, QWidget *parent,
not_null<PanelController*> controller, not_null<PanelController*> controller,
@ -70,11 +71,13 @@ private:
void setupControls( void setupControls(
const ValueMap &data, const ValueMap &data,
const ValueMap *scanData, const ValueMap *scanData,
std::vector<ScanInfo> &&files); std::vector<ScanInfo> &&files,
std::unique_ptr<ScanInfo> &&selfie);
not_null<Ui::RpWidget*> setupContent( not_null<Ui::RpWidget*> setupContent(
const ValueMap &data, const ValueMap &data,
const ValueMap *scanData, const ValueMap *scanData,
std::vector<ScanInfo> &&files); std::vector<ScanInfo> &&files,
std::unique_ptr<ScanInfo> &&selfie);
void updateControlsGeometry(); void updateControlsGeometry();
Result collect() const; Result collect() const;

View File

@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "passport/passport_panel_edit_scans.h" #include "passport/passport_panel_edit_scans.h"
#include "passport/passport_panel_controller.h" #include "passport/passport_panel_controller.h"
#include "passport/passport_panel_details_row.h"
#include "info/profile/info_profile_button.h" #include "info/profile/info_profile_button.h"
#include "info/profile/info_profile_values.h" #include "info/profile/info_profile_values.h"
#include "ui/widgets/buttons.h" #include "ui/widgets/buttons.h"
@ -185,10 +186,12 @@ EditScans::EditScans(
QWidget *parent, QWidget *parent,
not_null<PanelController*> controller, not_null<PanelController*> controller,
const QString &header, const QString &header,
std::vector<ScanInfo> &&files) std::vector<ScanInfo> &&files,
std::unique_ptr<ScanInfo> &&selfie)
: RpWidget(parent) : RpWidget(parent)
, _controller(controller) , _controller(controller)
, _files(std::move(files)) , _files(std::move(files))
, _selfie(std::move(selfie))
, _content(this) { , _content(this) {
setupContent(header); setupContent(header);
} }
@ -233,6 +236,48 @@ void EditScans::setupContent(const QString &header) {
_upload->addClickHandler([=] { _upload->addClickHandler([=] {
chooseScan(); chooseScan();
}); });
inner->add(object_ptr<BoxContentDivider>(
inner,
st::passportFormDividerHeight));
if (_selfie) {
_selfieHeader = inner->add(
object_ptr<Ui::SlideWrap<Ui::FlatLabel>>(
inner,
object_ptr<Ui::FlatLabel>(
inner,
lang(lng_passport_selfie_title),
Ui::FlatLabel::InitType::Simple,
st::passportFormHeader),
st::passportUploadHeaderPadding));
_selfieHeader->toggle(_selfie->key.id != 0, anim::type::instant);
_selfieWrap = inner->add(object_ptr<Ui::VerticalLayout>(inner));
if (_selfie->key.id) {
createSelfieRow(*_selfie);
}
_selfieUpload = inner->add(
object_ptr<Info::Profile::Button>(
inner,
Lang::Viewer(
lng_passport_upload_selfie
) | Info::Profile::ToUpperValue(),
st::passportUploadButton),
st::passportUploadButtonPadding);
_selfieUpload->addClickHandler([=] {
chooseSelfie();
});
inner->add(object_ptr<PanelLabel>(
inner,
object_ptr<Ui::FlatLabel>(
_content,
lang(lng_passport_selfie_description),
Ui::FlatLabel::InitType::Simple,
st::passportFormLabel),
st::passportFormLabelPadding));
}
_controller->scanUpdated( _controller->scanUpdated(
) | rpl::start_with_next([=](ScanInfo &&info) { ) | rpl::start_with_next([=](ScanInfo &&info) {
updateScan(std::move(info)); updateScan(std::move(info));
@ -250,11 +295,32 @@ void EditScans::setupContent(const QString &header) {
} }
void EditScans::updateScan(ScanInfo &&info) { void EditScans::updateScan(ScanInfo &&info) {
const auto updateRow = [&](
not_null<ScanButton*> button,
const ScanInfo &info) {
button->setStatus(info.status);
button->setImage(info.thumb);
button->setDeleted(info.deleted);
};
if (info.selfie) {
Assert(info.key.id != 0);
Assert(_selfie != nullptr);
if (_selfie->key.id) {
updateRow(_selfieRow->entity(), info);
} else {
createSelfieRow(info);
_selfieWrap->resizeToWidth(width());
_selfieRow->show(anim::type::normal);
_selfieHeader->show(anim::type::normal);
}
*_selfie = std::move(info);
return;
}
const auto i = ranges::find(_files, info.key, [](const ScanInfo &file) { const auto i = ranges::find(_files, info.key, [](const ScanInfo &file) {
return file.key; return file.key;
}); });
if (i != _files.end()) { if (i != _files.end()) {
*i = info; *i = std::move(info);
const auto scan = _rows[i - _files.begin()]->entity(); const auto scan = _rows[i - _files.begin()]->entity();
scan->setStatus(i->status); scan->setStatus(i->status);
scan->setImage(i->thumb); scan->setImage(i->thumb);
@ -270,20 +336,33 @@ void EditScans::updateScan(ScanInfo &&info) {
} }
} }
void EditScans::createSelfieRow(const ScanInfo &info) {
_selfieRow = createScan(
_selfieWrap,
info,
lang(lng_passport_selfie_name));
const auto row = _selfieRow->entity();
row->deleteClicks(
) | rpl::start_with_next([=] {
_controller->deleteSelfie();
}, row->lifetime());
row->restoreClicks(
) | rpl::start_with_next([=] {
_controller->restoreSelfie();
}, row->lifetime());
}
void EditScans::pushScan(const ScanInfo &info) { void EditScans::pushScan(const ScanInfo &info) {
const auto index = _rows.size(); const auto index = _rows.size();
_rows.push_back(base::unique_qptr<Ui::SlideWrap<ScanButton>>( _rows.push_back(createScan(
_wrap->add(object_ptr<Ui::SlideWrap<ScanButton>>(
_wrap, _wrap,
object_ptr<ScanButton>( info,
_wrap, lng_passport_scan_index(lt_index, QString::number(index + 1))));
st::passportScanRow,
lng_passport_scan_index(lt_index, QString::number(index + 1)),
info.status,
info.deleted)))));
_rows.back()->hide(anim::type::instant); _rows.back()->hide(anim::type::instant);
const auto scan = _rows.back()->entity(); const auto scan = _rows.back()->entity();
scan->setImage(info.thumb);
scan->deleteClicks( scan->deleteClicks(
) | rpl::start_with_next([=] { ) | rpl::start_with_next([=] {
@ -296,12 +375,35 @@ void EditScans::pushScan(const ScanInfo &info) {
}, scan->lifetime()); }, scan->lifetime());
} }
base::unique_qptr<Ui::SlideWrap<ScanButton>> EditScans::createScan(
not_null<Ui::VerticalLayout*> parent,
const ScanInfo &info,
const QString &name) {
auto result = base::unique_qptr<Ui::SlideWrap<ScanButton>>(
parent->add(object_ptr<Ui::SlideWrap<ScanButton>>(
parent,
object_ptr<ScanButton>(
parent,
st::passportScanRow,
name,
info.status,
info.deleted))));
result->entity()->setImage(info.thumb);
return result;
}
void EditScans::chooseScan() { void EditScans::chooseScan() {
ChooseScan(base::lambda_guarded(this, [=](QByteArray &&content) { ChooseScan(base::lambda_guarded(this, [=](QByteArray &&content) {
_controller->uploadScan(std::move(content)); _controller->uploadScan(std::move(content));
})); }));
} }
void EditScans::chooseSelfie() {
ChooseScan(base::lambda_guarded(this, [=](QByteArray &&content) {
_controller->uploadSelfie(std::move(content));
}));
}
void EditScans::ChooseScan(base::lambda<void(QByteArray&&)> callback) { void EditScans::ChooseScan(base::lambda<void(QByteArray&&)> callback) {
const auto filter = FileDialog::AllFilesFilter() const auto filter = FileDialog::AllFilesFilter()
+ qsl(";;Image files (*") + qsl(";;Image files (*")

View File

@ -36,20 +36,28 @@ public:
QWidget *parent, QWidget *parent,
not_null<PanelController*> controller, not_null<PanelController*> controller,
const QString &header, const QString &header,
std::vector<ScanInfo> &&files); std::vector<ScanInfo> &&files,
std::unique_ptr<ScanInfo> &&selfie);
static void ChooseScan(base::lambda<void(QByteArray&&)> callback); static void ChooseScan(base::lambda<void(QByteArray&&)> callback);
private: private:
void setupContent(const QString &header); void setupContent(const QString &header);
void chooseScan(); void chooseScan();
void chooseSelfie();
void updateScan(ScanInfo &&info); void updateScan(ScanInfo &&info);
void pushScan(const ScanInfo &info); void pushScan(const ScanInfo &info);
void createSelfieRow(const ScanInfo &info);
base::unique_qptr<Ui::SlideWrap<ScanButton>> createScan(
not_null<Ui::VerticalLayout*> parent,
const ScanInfo &info,
const QString &name);
rpl::producer<QString> uploadButtonText() const; rpl::producer<QString> uploadButtonText() const;
not_null<PanelController*> _controller; not_null<PanelController*> _controller;
std::vector<ScanInfo> _files; std::vector<ScanInfo> _files;
std::unique_ptr<ScanInfo> _selfie;
object_ptr<Ui::VerticalLayout> _content; object_ptr<Ui::VerticalLayout> _content;
QPointer<Ui::SlideWrap<BoxContentDivider>> _divider; QPointer<Ui::SlideWrap<BoxContentDivider>> _divider;
@ -59,6 +67,11 @@ private:
QPointer<Info::Profile::Button> _upload; QPointer<Info::Profile::Button> _upload;
rpl::event_stream<rpl::producer<QString>> _uploadTexts; rpl::event_stream<rpl::producer<QString>> _uploadTexts;
QPointer<Ui::SlideWrap<Ui::FlatLabel>> _selfieHeader;
QPointer<Ui::VerticalLayout> _selfieWrap;
base::unique_qptr<Ui::SlideWrap<ScanButton>> _selfieRow;
QPointer<Info::Profile::Button> _selfieUpload;
}; };
} // namespace Passport } // namespace Passport