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_upload_scans" = "Upload 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_choose_image" = "Choose scan image";
"lng_passport_delete_scan_undo" = "Undo";

View File

@ -242,6 +242,18 @@ bool FormController::validateValueSecrets(Value &value) {
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;
}
@ -266,8 +278,53 @@ void FormController::uploadScan(
nonconst,
File(),
nullptr);
const auto fileId = rand_value<uint64>();
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.id = fileId;
file.fields.dcId = MTP::maindc();
@ -277,17 +334,14 @@ void FormController::uploadScan(
file.fields.downloadOffset = file.fields.size;
_scanUpdated.fire(&file);
encryptScan(nonconst, fileIndex, std::move(content));
}
void FormController::encryptScan(
not_null<Value*> value,
int fileIndex,
QByteArray &&content) {
Expects(fileIndex >= 0 && fileIndex < value->filesInEdit.size());
void FormController::encryptFile(
EditFile &file,
QByteArray &&content,
base::lambda<void(UploadScanData &&result)> callback) {
prepareFile(file, content);
const auto &file = value->filesInEdit[fileIndex];
const auto weak = std::weak_ptr<bool>(file.guard);
crl::async([
=,
@ -309,27 +363,12 @@ void FormController::encryptScan(
result.md5checksum.data());
crl::on_main([=, encrypted = std::move(result)]() mutable {
if (weak.lock()) {
uploadEncryptedScan(
value ,
fileIndex,
std::move(encrypted));
callback(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(
not_null<const Value*> value,
int fileIndex,
@ -342,6 +381,17 @@ void FormController::scanDeleteRestore(
_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() {
if (_uploaderSubscriptions) {
return;
@ -365,15 +415,11 @@ void FormController::subscribeToUploader() {
}, _uploaderSubscriptions);
}
void FormController::uploadEncryptedScan(
not_null<Value*> value,
int fileIndex,
void FormController::uploadEncryptedFile(
EditFile &file,
UploadScanData &&data) {
Expects(fileIndex >= 0 && fileIndex < value->filesInEdit.size());
subscribeToUploader();
auto &file = value->filesInEdit[fileIndex];
file.uploadData = std::make_unique<UploadScanData>(std::move(data));
auto prepared = std::make_shared<FileLoadResult>(
@ -548,53 +594,67 @@ void FormController::startValueEdit(not_null<const Value*> value) {
if (savingValue(nonconst)) {
return;
}
loadFiles(nonconst->files);
for (auto &file : nonconst->files) {
loadFile(file);
}
if (nonconst->selfie) {
loadFile(*nonconst->selfie);
}
nonconst->filesInEdit = ranges::view::all(
nonconst->files
) | ranges::view::transform([=](const File &file) {
return EditFile(nonconst, file, nullptr);
}) | ranges::to_vector;
if (nonconst->selfie) {
nonconst->selfieInEdit = EditFile(
nonconst,
*nonconst->selfie,
nullptr);
} else {
nonconst->selfieInEdit = base::none;
}
nonconst->data.parsedInEdit = nonconst->data.parsed;
}
void FormController::loadFiles(std::vector<File> &files) {
for (auto &file : files) {
if (!file.image.isNull()) {
file.downloadOffset = file.size;
continue;
}
const auto key = FileKey{ file.id, file.dcId };
const auto i = _fileLoaders.find(key);
if (i == _fileLoaders.end()) {
file.downloadOffset = 0;
const auto [i, ok] = _fileLoaders.emplace(
key,
std::make_unique<mtpFileLoader>(
file.dcId,
file.id,
file.accessHash,
0,
SecureFileLocation,
QString(),
file.size,
LoadToCacheAsWell,
LoadFromCloudOrLocal,
false));
const auto loader = i->second.get();
loader->connect(loader, &mtpFileLoader::progress, [=] {
if (loader->finished()) {
fileLoadDone(key, loader->bytes());
} else {
fileLoadProgress(key, loader->currentOffset());
}
});
loader->connect(loader, &mtpFileLoader::failed, [=] {
fileLoadFail(key);
});
loader->start();
}
void FormController::loadFile(File &file) {
if (!file.image.isNull()) {
file.downloadOffset = file.size;
return;
}
const auto key = FileKey{ file.id, file.dcId };
const auto i = _fileLoaders.find(key);
if (i != _fileLoaders.end()) {
return;
}
file.downloadOffset = 0;
const auto [j, ok] = _fileLoaders.emplace(
key,
std::make_unique<mtpFileLoader>(
file.dcId,
file.id,
file.accessHash,
0,
SecureFileLocation,
QString(),
file.size,
LoadToCacheAsWell,
LoadFromCloudOrLocal,
false));
const auto loader = j->second.get();
loader->connect(loader, &mtpFileLoader::progress, [=] {
if (loader->finished()) {
fileLoadDone(key, loader->bytes());
} else {
fileLoadProgress(key, loader->currentOffset());
}
});
loader->connect(loader, &mtpFileLoader::failed, [=] {
fileLoadFail(key);
});
loader->start();
}
void FormController::fileLoadDone(FileKey key, const QByteArray &bytes) {
@ -666,6 +726,7 @@ void FormController::clearValueEdit(not_null<Value*> value) {
return;
}
value->filesInEdit.clear();
value->selfieInEdit = base::none;
value->data.encryptedSecretInEdit.clear();
value->data.hashInEdit.clear();
value->data.parsedInEdit = ValueMap();
@ -787,7 +848,6 @@ void FormController::saveEncryptedValue(not_null<Value*> value) {
inputFiles.push_back(inputFile(file));
}
if (value->data.secret.empty()) {
value->data.secret = GenerateSecretBytes();
}
@ -890,9 +950,18 @@ void FormController::sendSaveRequest(
value->saveRequestId = 0;
const auto &data = result.c_secureValue();
value->files = parseFiles(
data.vfiles.v,
base::take(value->filesInEdit));
value->files = data.has_files()
? parseFiles(
data.vfiles.v,
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.encryptedSecretInEdit);
value->data.parsed = std::move(value->data.parsedInEdit);
@ -1110,32 +1179,40 @@ auto FormController::parseFiles(
auto result = std::vector<File>();
result.reserve(data.size());
auto index = 0;
for (const auto &file : data) {
switch (file.type()) {
case mtpc_secureFileEmpty: {
} 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;
if (auto normal = parseFile(file, editData)) {
result.push_back(std::move(*normal));
}
++index;
}
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(
File &destination,
const std::vector<EditFile> &source) const {
@ -1180,6 +1257,9 @@ auto FormController::parseValue(
if (data.has_files()) {
result.files = parseFiles(data.vfiles.v);
}
if (data.has_selfie()) {
result.selfie = parseFile(data.vselfie);
}
if (data.has_plain_data()) {
switch (data.vplain_data.type()) {
case mtpc_securePlainPhone: {
@ -1197,35 +1277,53 @@ auto FormController::parseValue(
}
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 &file : value.filesInEdit) {
if (file.uploadData && file.uploadData->fullId == fullId) {
if (found(file)) {
return &file;
}
}
if (value.selfieInEdit && found(*value.selfieInEdit)) {
return &*value.selfieInEdit;
}
}
return nullptr;
}
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 &file : value.filesInEdit) {
if (file.fields.dcId == key.dcId && file.fields.id == key.id) {
if (found(file)) {
return &file;
}
}
if (value.selfieInEdit && found(*value.selfieInEdit)) {
return &*value.selfieInEdit;
}
}
return nullptr;
}
auto FormController::findFile(const FileKey &key)
-> 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 &file : value.files) {
if (file.dcId == key.dcId && file.id == key.id) {
if (found(file)) {
return { &value, &file };
}
}
if (value.selfie && found(*value.selfie)) {
return { &value, &*value.selfie };
}
}
return { nullptr, nullptr };
}

View File

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

View File

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

View File

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

View File

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

View File

@ -52,7 +52,8 @@ public:
Scheme scheme,
const ValueMap &data,
const ValueMap &scanData,
std::vector<ScanInfo> &&files);
std::vector<ScanInfo> &&files,
std::unique_ptr<ScanInfo> &&selfie);
PanelEditDocument(
QWidget *parent,
not_null<PanelController*> controller,
@ -70,11 +71,13 @@ private:
void setupControls(
const ValueMap &data,
const ValueMap *scanData,
std::vector<ScanInfo> &&files);
std::vector<ScanInfo> &&files,
std::unique_ptr<ScanInfo> &&selfie);
not_null<Ui::RpWidget*> setupContent(
const ValueMap &data,
const ValueMap *scanData,
std::vector<ScanInfo> &&files);
std::vector<ScanInfo> &&files,
std::unique_ptr<ScanInfo> &&selfie);
void updateControlsGeometry();
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_controller.h"
#include "passport/passport_panel_details_row.h"
#include "info/profile/info_profile_button.h"
#include "info/profile/info_profile_values.h"
#include "ui/widgets/buttons.h"
@ -185,10 +186,12 @@ EditScans::EditScans(
QWidget *parent,
not_null<PanelController*> controller,
const QString &header,
std::vector<ScanInfo> &&files)
std::vector<ScanInfo> &&files,
std::unique_ptr<ScanInfo> &&selfie)
: RpWidget(parent)
, _controller(controller)
, _files(std::move(files))
, _selfie(std::move(selfie))
, _content(this) {
setupContent(header);
}
@ -233,6 +236,48 @@ void EditScans::setupContent(const QString &header) {
_upload->addClickHandler([=] {
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(
) | rpl::start_with_next([=](ScanInfo &&info) {
updateScan(std::move(info));
@ -250,11 +295,32 @@ void EditScans::setupContent(const QString &header) {
}
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) {
return file.key;
});
if (i != _files.end()) {
*i = info;
*i = std::move(info);
const auto scan = _rows[i - _files.begin()]->entity();
scan->setStatus(i->status);
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) {
const auto index = _rows.size();
_rows.push_back(base::unique_qptr<Ui::SlideWrap<ScanButton>>(
_wrap->add(object_ptr<Ui::SlideWrap<ScanButton>>(
_wrap,
object_ptr<ScanButton>(
_wrap,
st::passportScanRow,
lng_passport_scan_index(lt_index, QString::number(index + 1)),
info.status,
info.deleted)))));
_rows.push_back(createScan(
_wrap,
info,
lng_passport_scan_index(lt_index, QString::number(index + 1))));
_rows.back()->hide(anim::type::instant);
const auto scan = _rows.back()->entity();
scan->setImage(info.thumb);
scan->deleteClicks(
) | rpl::start_with_next([=] {
@ -296,12 +375,35 @@ void EditScans::pushScan(const ScanInfo &info) {
}, 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() {
ChooseScan(base::lambda_guarded(this, [=](QByteArray &&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) {
const auto filter = FileDialog::AllFilesFilter()
+ qsl(";;Image files (*")

View File

@ -36,20 +36,28 @@ public:
QWidget *parent,
not_null<PanelController*> controller,
const QString &header,
std::vector<ScanInfo> &&files);
std::vector<ScanInfo> &&files,
std::unique_ptr<ScanInfo> &&selfie);
static void ChooseScan(base::lambda<void(QByteArray&&)> callback);
private:
void setupContent(const QString &header);
void chooseScan();
void chooseSelfie();
void updateScan(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;
not_null<PanelController*> _controller;
std::vector<ScanInfo> _files;
std::unique_ptr<ScanInfo> _selfie;
object_ptr<Ui::VerticalLayout> _content;
QPointer<Ui::SlideWrap<BoxContentDivider>> _divider;
@ -59,6 +67,11 @@ private:
QPointer<Info::Profile::Button> _upload;
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