Choose document type in passport.

This commit is contained in:
John Preston 2018-04-10 11:51:19 +04:00
parent 4e2a109a46
commit 62389f5ef7
9 changed files with 330 additions and 36 deletions

View File

@ -1512,10 +1512,23 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_passport_identity_title" = "Identity document";
"lng_passport_identity_description" = "Upload a scan of your passport or other ID";
"lng_passport_identity_passport" = "Passport";
"lng_passport_identity_passport_upload" = "Upload a scan of your passport";
"lng_passport_identity_card" = "Identity card";
"lng_passport_identity_card_upload" = "Upload a scan of your identity card";
"lng_passport_identity_license" = "Driver's license";
"lng_passport_identity_license_upload" = "Upload a scan of your driver's license";
"lng_passport_identity_about" = "Your document must contain your photograph, name and surname, date of birth, citizenship, document issue date and document number.";
"lng_passport_address_title" = "Residential address";
"lng_passport_address_description" = "Upload a proof of your address";
"lng_passport_address_bill" = "Utility bill";
"lng_passport_address_bill_upload" = "Upload a scan of your utility bill";
"lng_passport_address_statement" = "Bank statement";
"lng_passport_address_statement_upload" = "Upload a scan of your bank statement";
"lng_passport_address_agreement" = "Tenancy agreement";
"lng_passport_address_agreement_upload" = "Upload a scan of your tenancy agreement";
"lng_passport_address_about" = "To confirm your address please upload a scan or photo of the selected document (all pages).";
"lng_passport_document_type" = "Please choose the type of your document:";
"lng_passport_upload_document" = "Upload document";
"lng_passport_phone_title" = "Phone number";
"lng_passport_phone_description" = "Enter your phone number";
"lng_passport_email_title" = "Email";
@ -1528,7 +1541,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_passport_save_value" = "Save";
"lng_passport_saving" = "Saving...";
"lng_passport_uploading" = "Uploading...";
"lng_passport_upload_header" = "Scans";
"lng_passport_scan_index" = "Scan {index}";
"lng_passport_upload_scans" = "Upload scans";
"lng_passport_upload_more" = "Upload additional scans";

View File

@ -119,7 +119,7 @@ passportFormUserpic: UserpicButton(passportPasswordUserpic) {
passportFormUserpicPadding: margins(0px, 5px, 0px, 10px);
passportFormDividerHeight: 13px;
passportFormLabel: FlatLabel(defaultFlatLabel) {
minWidth: 285px;
minWidth: 245px;
align: align(topleft);
textFg: windowSubTextFg;
}
@ -194,3 +194,5 @@ passportContactField: InputField(defaultInputField) {
passportDetailsFieldLeft: 116px;
passportDetailsFieldTop: 2px;
passportDetailsFieldSkipMin: 12px;
passportRequestTypeSkip: 16px;

View File

@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "lang/lang_keys.h"
#include "passport/passport_panel_edit_document.h"
#include "passport/passport_panel_edit_contact.h"
#include "passport/passport_panel_edit_scans.h"
#include "passport/passport_panel.h"
#include "boxes/confirm_box.h"
#include "layout.h"
@ -17,7 +18,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace Passport {
namespace {
PanelEditDocument::Scheme GetDocumentScheme(Scope::Type type) {
PanelEditDocument::Scheme GetDocumentScheme(
Scope::Type type,
base::optional<Value::Type> scansType = base::none) {
using Scheme = PanelEditDocument::Scheme;
const auto DontValidate = nullptr;
@ -43,6 +46,21 @@ PanelEditDocument::Scheme GetDocumentScheme(Scope::Type type) {
case Scope::Type::Identity: {
auto result = Scheme();
result.rowsHeader = lang(lng_passport_personal_details);
if (scansType) {
switch (*scansType) {
case Value::Type::Passport:
result.scansHeader = lang(lng_passport_identity_passport);
break;
case Value::Type::DriverLicense:
result.scansHeader = lang(lng_passport_identity_license);
break;
case Value::Type::IdentityCard:
result.scansHeader = lang(lng_passport_identity_card);
break;
default:
Unexpected("scansType in GetDocumentScheme:Identity.");
}
}
result.rows = {
{
Scheme::ValueType::Fields,
@ -93,6 +111,21 @@ PanelEditDocument::Scheme GetDocumentScheme(Scope::Type type) {
case Scope::Type::Address: {
auto result = Scheme();
result.rowsHeader = lang(lng_passport_address);
if (scansType) {
switch (*scansType) {
case Value::Type::UtilityBill:
result.scansHeader = lang(lng_passport_address_bill);
break;
case Value::Type::BankStatement:
result.scansHeader = lang(lng_passport_address_statement);
break;
case Value::Type::RentalAgreement:
result.scansHeader = lang(lng_passport_address_agreement);
break;
default:
Unexpected("scansType in GetDocumentScheme:Identity.");
}
}
result.rows = {
{
Scheme::ValueType::Fields,
@ -413,14 +446,121 @@ void PanelController::ensurePanelCreated() {
}
}
int PanelController::findNonEmptyIndex(
const std::vector<not_null<const Value*>> &files) const {
const auto i = ranges::find_if(files, [](not_null<const Value*> file) {
return !file->files.empty();
});
if (i != end(files)) {
return (i - begin(files));
}
// Only an uploaded scan counts as non-empty value.
//const auto j = ranges::find_if(files, [](not_null<const Value*> file) {
// return !file->data.parsed.fields.empty();
//});
//if (j != end(files)) {
// return (j - begin(files));
//}
return -1;
}
void PanelController::editScope(int index) {
Expects(_panel != nullptr);
Expects(index >= 0 && index < _scopes.size());
_editScope = &_scopes[index];
if (_scopes[index].files.size() > 1) {
const auto filesIndex = findNonEmptyIndex(_scopes[index].files);
if (filesIndex >= 0) {
editScope(index, filesIndex);
} else {
requestScopeFilesType(index);
}
} else if (_scopes[index].files.empty()) {
editScope(index, -1);
} else {
editWithUpload(index, 0);
}
}
// #TODO select type for files index
_editScopeFilesIndex = _scopes[index].files.empty() ? -1 : 0;
void PanelController::requestScopeFilesType(int index) {
Expects(_panel != nullptr);
Expects(index >= 0 && index < _scopes.size());
const auto type = _scopes[index].type;
const auto box = std::make_shared<QPointer<BoxContent>>();
*box = [&] {
if (type == Scope::Type::Identity) {
return show(RequestIdentityType(
[=](int filesIndex) {
editWithUpload(index, filesIndex);
(*box)->closeBox();
},
ranges::view::all(
_scopes[index].files
) | ranges::view::transform([](auto value) {
return value->type;
}) | ranges::view::transform([](Value::Type type) {
switch (type) {
case Value::Type::Passport:
return lang(lng_passport_identity_passport);
case Value::Type::IdentityCard:
return lang(lng_passport_identity_card);
case Value::Type::DriverLicense:
return lang(lng_passport_identity_license);
default:
Unexpected("IdentityType in requestScopeFilesType");
}
}) | ranges::to_vector));
} else if (type == Scope::Type::Address) {
return show(RequestAddressType(
[=](int filesIndex) {
editWithUpload(index, filesIndex);
(*box)->closeBox();
},
ranges::view::all(
_scopes[index].files
) | ranges::view::transform([](auto value) {
return value->type;
}) | ranges::view::transform([](Value::Type type) {
switch (type) {
case Value::Type::UtilityBill:
return lang(lng_passport_address_bill);
case Value::Type::BankStatement:
return lang(lng_passport_address_statement);
case Value::Type::RentalAgreement:
return lang(lng_passport_address_agreement);
default:
Unexpected("AddressType in requestScopeFilesType");
}
}) | ranges::to_vector));
} else {
Unexpected("Type in processVerificationNeeded.");
}
}();
}
void PanelController::editWithUpload(int index, int filesIndex) {
Expects(_panel != nullptr);
Expects(index >= 0 && index < _scopes.size());
Expects(filesIndex >= 0 && filesIndex < _scopes[index].files.size());
EditScans::ChooseScan(
base::lambda_guarded(_panel.get(),
[=](QByteArray &&content) {
editScope(index, filesIndex);
uploadScan(std::move(content));
}));
}
void PanelController::editScope(int index, int filesIndex) {
Expects(_panel != nullptr);
Expects(index >= 0 && index < _scopes.size());
Expects((filesIndex < 0)
|| (filesIndex >= 0 && filesIndex < _scopes[index].files.size()));
_editScope = &_scopes[index];
_editScopeFilesIndex = filesIndex;
_form->startValueEdit(_editScope->fields);
if (_editScopeFilesIndex >= 0) {
@ -435,14 +575,16 @@ void PanelController::editScope(int index) {
? object_ptr<PanelEditDocument>(
_panel.get(),
this,
std::move(GetDocumentScheme(_editScope->type)),
GetDocumentScheme(
_editScope->type,
_editScope->files[_editScopeFilesIndex]->type),
_editScope->fields->data.parsedInEdit,
_editScope->files[_editScopeFilesIndex]->data.parsedInEdit,
valueFiles(*_editScope->files[_editScopeFilesIndex]))
: object_ptr<PanelEditDocument>(
_panel.get(),
this,
std::move(GetDocumentScheme(_editScope->type)),
GetDocumentScheme(_editScope->type),
_editScope->fields->data.parsedInEdit);
const auto weak = make_weak(result.data());
_panelHasUnsavedChanges = [=] {

View File

@ -85,6 +85,11 @@ public:
private:
void ensurePanelCreated();
void editScope(int index, int filesIndex);
void editWithUpload(int index, int filesIndex);
int findNonEmptyIndex(
const std::vector<not_null<const Value*>> &files) const;
void requestScopeFilesType(int index);
void cancelValueEdit();
std::vector<ScanInfo> valueFiles(const Value &value) const;
void processValueSaveFinished(not_null<const Value*> value);

View File

@ -29,7 +29,7 @@ namespace {
class VerifyBox : public BoxContent {
public:
VerifyBox(
QWidget *parent,
QWidget*,
const QString &title,
const QString &text,
int codeLength,
@ -58,7 +58,7 @@ private:
};
VerifyBox::VerifyBox(
QWidget *parent,
QWidget*,
const QString &title,
const QString &text,
int codeLength,
@ -124,6 +124,7 @@ void VerifyBox::setupControls(
waiter->resizeToWidth(innerWidth);
waiter->moveToLeft(st::boxPadding.left(), y);
y += waiter->height() + st::boxPadding.bottom();
_height = y;
_submit = [=] {
submit(_code->getLastText());
@ -136,7 +137,6 @@ void VerifyBox::setupControls(
connect(_code, &SentCodeField::changed, [=] {
problem->hide(anim::type::normal);
});
_height = y;
}
void VerifyBox::setInnerFocus() {

View File

@ -15,6 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/widgets/labels.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/shadow.h"
#include "ui/widgets/checkbox.h"
#include "ui/wrap/vertical_layout.h"
#include "ui/wrap/fade_wrap.h"
#include "boxes/abstract_box.h"
@ -25,6 +26,103 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "styles/style_passport.h"
namespace Passport {
namespace {
class RequestTypeBox : public BoxContent {
public:
RequestTypeBox(
QWidget*,
const QString &title,
const QString &about,
std::vector<QString> labels,
base::lambda<void(int index)> submit);
protected:
void prepare() override;
private:
void setupControls(
const QString &about,
std::vector<QString> labels,
base::lambda<void(int index)> submit);
QString _title;
base::lambda<void()> _submit;
int _height = 0;
};
RequestTypeBox::RequestTypeBox(
QWidget*,
const QString &title,
const QString &about,
std::vector<QString> labels,
base::lambda<void(int index)> submit)
: _title(title) {
setupControls(about, std::move(labels), submit);
}
void RequestTypeBox::prepare() {
setTitle([=] { return _title; });
addButton(langFactory(lng_passport_upload_document), [=] { _submit(); });
addButton(langFactory(lng_cancel), [=] { closeBox(); });
setDimensions(st::boxWidth, _height);
}
void RequestTypeBox::setupControls(
const QString &about,
std::vector<QString> labels,
base::lambda<void(int index)> submit) {
const auto header = Ui::CreateChild<Ui::FlatLabel>(
this,
lang(lng_passport_document_type),
Ui::FlatLabel::InitType::Simple,
st::passportFormLabel);
const auto group = std::make_shared<Ui::RadiobuttonGroup>(0);
auto buttons = std::vector<QPointer<Ui::Radiobutton>>();
auto index = 0;
for (const auto &label : labels) {
buttons.push_back(Ui::CreateChild<Ui::Radiobutton>(
this,
group,
index++,
label,
st::defaultBoxCheckbox));
}
const auto description = Ui::CreateChild<Ui::FlatLabel>(
this,
about,
Ui::FlatLabel::InitType::Simple,
st::passportFormLabel);
auto y = 0;
const auto innerWidth = st::boxWidth
- st::boxPadding.left()
- st::boxPadding.right();
header->resizeToWidth(innerWidth);
header->moveToLeft(st::boxPadding.left(), y);
y += header->height() + st::passportRequestTypeSkip;
for (const auto &button : buttons) {
button->resizeToNaturalWidth(innerWidth);
button->moveToLeft(st::boxPadding.left(), y);
y += button->heightNoMargins() + st::passportRequestTypeSkip;
}
description->resizeToWidth(innerWidth);
description->moveToLeft(st::boxPadding.left(), y);
y += description->height() + st::passportRequestTypeSkip;
_height = y;
_submit = [=] {
const auto value = group->hasValue() ? group->value() : -1;
if (value >= 0) {
submit(value);
}
};
}
} // namespace
struct PanelEditDocument::Result {
ValueMap data;
@ -97,7 +195,11 @@ not_null<Ui::RpWidget*> PanelEditDocument::setupContent(
if (scanData) {
_editScans = inner->add(
object_ptr<EditScans>(inner, _controller, std::move(files)));
object_ptr<EditScans>(
inner,
_controller,
_scheme.scansHeader,
std::move(files)));
}
inner->add(object_ptr<BoxContentDivider>(
@ -187,4 +289,24 @@ void PanelEditDocument::save() {
std::move(result.filesData));
}
object_ptr<BoxContent> RequestIdentityType(
base::lambda<void(int index)> submit,
std::vector<QString> labels) {
return Box<RequestTypeBox>(
lang(lng_passport_identity_title),
lang(lng_passport_identity_about),
std::move(labels),
submit);
}
object_ptr<BoxContent> RequestAddressType(
base::lambda<void(int index)> submit,
std::vector<QString> labels) {
return Box<RequestTypeBox>(
lang(lng_passport_address_title),
lang(lng_passport_address_about),
std::move(labels),
submit);
}
} // namespace Passport

View File

@ -40,6 +40,7 @@ public:
};
std::vector<Row> rows;
QString rowsHeader;
QString scansHeader;
};
@ -91,4 +92,11 @@ private:
};
object_ptr<BoxContent> RequestIdentityType(
base::lambda<void(int index)> submit,
std::vector<QString> labels);
object_ptr<BoxContent> RequestAddressType(
base::lambda<void(int index)> submit,
std::vector<QString> labels);
} // namespace Passport

View File

@ -184,15 +184,16 @@ void ScanButton::paintEvent(QPaintEvent *e) {
EditScans::EditScans(
QWidget *parent,
not_null<PanelController*> controller,
const QString &header,
std::vector<ScanInfo> &&files)
: RpWidget(parent)
, _controller(controller)
, _files(std::move(files))
, _content(this) {
setupContent();
setupContent(header);
}
void EditScans::setupContent() {
void EditScans::setupContent(const QString &header) {
const auto inner = _content.data();
inner->move(0, 0);
@ -209,7 +210,7 @@ void EditScans::setupContent() {
inner,
object_ptr<Ui::FlatLabel>(
inner,
lang(lng_passport_upload_header),
header,
Ui::FlatLabel::InitType::Simple,
st::passportFormHeader),
st::passportUploadHeaderPadding));
@ -296,35 +297,36 @@ void EditScans::pushScan(const ScanInfo &info) {
}
void EditScans::chooseScan() {
ChooseScan(base::lambda_guarded(this, [=](QByteArray &&content) {
_controller->uploadScan(std::move(content));
}));
}
void EditScans::ChooseScan(base::lambda<void(QByteArray&&)> callback) {
const auto filter = FileDialog::AllFilesFilter()
+ qsl(";;Image files (*")
+ cImgExtensions().join(qsl(" *"))
+ qsl(")");
const auto callback = [=](FileDialog::OpenResult &&result) {
const auto processFile = [=](FileDialog::OpenResult &&result) {
if (result.paths.size() == 1) {
encryptScan(result.paths.front());
auto content = [&] {
QFile f(result.paths.front());
if (!f.open(QIODevice::ReadOnly)) {
return QByteArray();
}
return f.readAll();
}();
if (!content.isEmpty()) {
callback(std::move(content));
}
} else if (!result.remoteContent.isEmpty()) {
encryptScanContent(std::move(result.remoteContent));
callback(std::move(result.remoteContent));
}
};
FileDialog::GetOpenPath(
lang(lng_passport_choose_image),
filter,
base::lambda_guarded(this, callback));
}
void EditScans::encryptScan(const QString &path) {
encryptScanContent([&] {
QFile f(path);
if (!f.open(QIODevice::ReadOnly)) {
return QByteArray();
}
return f.readAll();
}());
}
void EditScans::encryptScanContent(QByteArray &&content) {
_controller->uploadScan(std::move(content));
processFile);
}
rpl::producer<QString> EditScans::uploadButtonText() const {

View File

@ -35,13 +35,14 @@ public:
EditScans(
QWidget *parent,
not_null<PanelController*> controller,
const QString &header,
std::vector<ScanInfo> &&files);
static void ChooseScan(base::lambda<void(QByteArray&&)> callback);
private:
void setupContent();
void setupContent(const QString &header);
void chooseScan();
void encryptScan(const QString &path);
void encryptScanContent(QByteArray &&content);
void updateScan(ScanInfo &&info);
void pushScan(const ScanInfo &info);