Show scans/selfie saving errors.

This commit is contained in:
John Preston 2018-04-13 14:54:17 +04:00
parent f8b2e474b9
commit e7ce4ca10a
11 changed files with 169 additions and 1 deletions

View File

@ -1583,6 +1583,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_passport_confirm_phone" = "We've sent an SMS with a confirmation code to your phone {phone}.";
"lng_passport_confirm_email" = "We've sent a confirmation code to your email {email}.";
"lng_passport_sure_cancel" = "If you continue your changes will be lost.";
"lng_passport_scans_limit_reached" = "Scans limit reached.";
// Wnd specific

View File

@ -58,6 +58,11 @@ rpl::producer<bool> Button::toggledValue() const {
return rpl::never<bool>();
}
void Button::setColorOverride(base::optional<QColor> textColorOverride) {
_textColorOverride = textColorOverride;
update();
}
void Button::paintEvent(QPaintEvent *e) {
Painter p(this);
@ -69,7 +74,11 @@ void Button::paintEvent(QPaintEvent *e) {
auto outerw = width();
p.setFont(_st.font);
p.setPen(paintOver ? _st.textFgOver : _st.textFg);
p.setPen(_textColorOverride
? QPen(*_textColorOverride)
: paintOver
? _st.textFgOver
: _st.textFg);
p.drawTextLeft(
_st.padding.left(),
_st.padding.top(),

View File

@ -29,6 +29,8 @@ public:
Button *toggleOn(rpl::producer<bool> &&toggled);
rpl::producer<bool> toggledValue() const;
void setColorOverride(base::optional<QColor> textColorOverride);
protected:
int resizeGetHeight(int newWidth) override;
void onStateChanged(
@ -48,6 +50,7 @@ private:
int _originalWidth = 0;
int _textWidth = 0;
std::unique_ptr<Ui::ToggleView> _toggle;
base::optional<QColor> _textColorOverride;
};

View File

@ -25,6 +25,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace Passport {
namespace {
constexpr auto kDocumentScansLimit = 20;
QImage ReadImage(bytes::const_span buffer) {
return App::readImage(QByteArray::fromRawData(
reinterpret_cast<const char*>(buffer.data()),
@ -241,11 +243,14 @@ auto FormController::prepareFinalData() const -> FinalData {
addValueToJSON(key, value);
}
};
auto hasErrors = false;
const auto scopes = ComputeScopes(this);
for (const auto &scope : scopes) {
const auto ready = ComputeScopeRowReadyString(scope);
if (ready.isEmpty()) {
hasErrors = true;
_valueError.fire_copy(scope.fields);
continue;
}
addValue(scope.fields);
if (!scope.documents.empty()) {
@ -257,6 +262,9 @@ auto FormController::prepareFinalData() const -> FinalData {
}
}
}
if (hasErrors) {
return {};
}
auto json = QJsonObject();
json.insert("secure_data", secureData);
@ -274,6 +282,9 @@ void FormController::submit() {
}
const auto prepared = prepareFinalData();
if (prepared.hashes.empty()) {
return;
}
const auto credentialsEncryptedData = EncryptData(
bytes::make_span(prepared.credentials));
const auto credentialsEncryptedSecret = EncryptCredentialsSecret(
@ -433,6 +444,10 @@ QString FormController::passwordHint() const {
void FormController::uploadScan(
not_null<const Value*> value,
QByteArray &&content) {
if (!canAddScan(value)) {
_view->showToast(lang(lng_passport_scans_limit_reached));
return;
}
const auto nonconst = findValue(value);
auto scanIndex = int(nonconst->scansInEdit.size());
nonconst->scansInEdit.emplace_back(
@ -538,6 +553,12 @@ void FormController::scanDeleteRestore(
const auto nonconst = findValue(value);
auto &scan = nonconst->scansInEdit[scanIndex];
if (scan.deleted && !deleted) {
if (!canAddScan(value)) {
_view->showToast(lang(lng_passport_scans_limit_reached));
return;
}
}
scan.deleted = deleted;
_scanUpdated.fire(&scan);
}
@ -553,6 +574,13 @@ void FormController::selfieDeleteRestore(
_scanUpdated.fire(&scan);
}
bool FormController::canAddScan(not_null<const Value*> value) const {
const auto scansCount = ranges::count_if(
value->scansInEdit,
[](const EditFile &scan) { return !scan.deleted; });
return (scansCount < kDocumentScansLimit);
}
void FormController::subscribeToUploader() {
if (_uploaderSubscriptions) {
return;

View File

@ -212,6 +212,7 @@ public:
rpl::producer<QString> passwordError() const;
QString passwordHint() const;
bool canAddScan(not_null<const Value*> value) const;
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);

View File

@ -45,6 +45,7 @@ public:
virtual void editScope(int index) = 0;
virtual void showBox(object_ptr<BoxContent> box) = 0;
virtual void showToast(const QString &text) = 0;
virtual ~ViewController() {
}

View File

@ -14,6 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "passport/passport_panel_edit_scans.h"
#include "passport/passport_panel.h"
#include "boxes/confirm_box.h"
#include "ui/toast/toast.h"
#include "ui/countryinput.h"
#include "layout.h"
@ -363,6 +364,14 @@ QString PanelController::defaultPhoneNumber() const {
return _form->defaultPhoneNumber();
}
bool PanelController::canAddScan() const {
Expects(_editScope != nullptr);
Expects(_editDocumentIndex >= 0
&& _editDocumentIndex < _editScope->documents.size());
return _form->canAddScan(_editScope->documents[_editDocumentIndex]);
}
void PanelController::uploadScan(QByteArray &&content) {
Expects(_editScope != nullptr);
Expects(_editDocumentIndex >= 0
@ -858,6 +867,14 @@ void PanelController::showBox(object_ptr<BoxContent> box) {
_panel->showBox(std::move(box));
}
void PanelController::showToast(const QString &text) {
Expects(_panel != nullptr);
auto toast = Ui::Toast::Config();
toast.text = text;
Ui::Toast::Show(_panel.get(), toast);
}
rpl::lifetime &PanelController::lifetime() {
return _lifetime;
}

View File

@ -60,6 +60,7 @@ public:
rpl::producer<QString> passwordError() const;
QString passwordHint() const;
bool canAddScan() const;
void uploadScan(QByteArray &&content);
void deleteScan(int fileIndex);
void restoreScan(int fileIndex);
@ -89,6 +90,7 @@ public:
void cancelEditScope();
void showBox(object_ptr<BoxContent> box) override;
void showToast(const QString &text) override;
void cancelAuth();

View File

@ -300,6 +300,13 @@ PanelEditDocument::Result PanelEditDocument::collect() const {
}
bool PanelEditDocument::validate() {
if (const auto error = _editScans->validateGetErrorTop()) {
const auto errortop = _editScans->mapToGlobal(QPoint(0, *error));
const auto scrolltop = _scroll->mapToGlobal(QPoint(0, 0));
const auto scrolldelta = errortop.y() - scrolltop.y();
_scroll->scrollToY(_scroll->scrollTop() + scrolldelta);
return false;
}
auto first = QPointer<PanelDetailsRow>();
for (const auto [i, field] : base::reversed(_details)) {
const auto &row = _scheme.rows[i];

View File

@ -196,6 +196,22 @@ EditScans::EditScans(
setupContent(header);
}
base::optional<int> EditScans::validateGetErrorTop() {
const auto exists = ranges::find(
_files,
false,
[](const ScanInfo &file) { return file.deleted; }) != end(_files);
if (!exists) {
toggleError(true);
return (_files.size() > 5) ? _upload->y() : _header->y();
}
if (_selfie && (!_selfie->key.id || _selfie->deleted)) {
toggleSelfieError(true);
return _selfieHeader->y();
}
return base::none;
}
void EditScans::setupContent(const QString &header) {
const auto inner = _content.data();
inner->move(0, 0);
@ -307,6 +323,9 @@ void EditScans::updateScan(ScanInfo &&info) {
Assert(_selfie != nullptr);
if (_selfie->key.id) {
updateRow(_selfieRow->entity(), info);
if (!info.deleted) {
hideSelfieError();
}
} else {
createSelfieRow(info);
_selfieWrap->resizeToWidth(width());
@ -325,6 +344,9 @@ void EditScans::updateScan(ScanInfo &&info) {
scan->setStatus(i->status);
scan->setImage(i->thumb);
scan->setDeleted(i->deleted);
if (!i->deleted) {
hideError();
}
} else {
_files.push_back(std::move(info));
pushScan(_files.back());
@ -352,6 +374,8 @@ void EditScans::createSelfieRow(const ScanInfo &info) {
) | rpl::start_with_next([=] {
_controller->restoreSelfie();
}, row->lifetime());
hideSelfieError();
}
void EditScans::pushScan(const ScanInfo &info) {
@ -373,6 +397,8 @@ void EditScans::pushScan(const ScanInfo &info) {
) | rpl::start_with_next([=] {
_controller->restoreScan(index);
}, scan->lifetime());
hideError();
}
base::unique_qptr<Ui::SlideWrap<ScanButton>> EditScans::createScan(
@ -393,6 +419,10 @@ base::unique_qptr<Ui::SlideWrap<ScanButton>> EditScans::createScan(
}
void EditScans::chooseScan() {
if (!_controller->canAddScan()) {
_controller->showToast(lang(lng_passport_scans_limit_reached));
return;
}
ChooseScan(base::lambda_guarded(this, [=](QByteArray &&content) {
_controller->uploadScan(std::move(content));
}));
@ -437,4 +467,59 @@ rpl::producer<QString> EditScans::uploadButtonText() const {
: lng_passport_upload_more) | Info::Profile::ToUpperValue();
}
void EditScans::hideError() {
toggleError(false);
}
void EditScans::toggleError(bool shown) {
if (_errorShown != shown) {
_errorShown = shown;
_errorAnimation.start(
[=] { errorAnimationCallback(); },
_errorShown ? 0. : 1.,
_errorShown ? 1. : 0.,
st::passportDetailsField.duration);
}
}
void EditScans::errorAnimationCallback() {
const auto error = _errorAnimation.current(_errorShown ? 1. : 0.);
if (error == 0.) {
_upload->setColorOverride(base::none);
} else {
_upload->setColorOverride(anim::color(
st::passportUploadButton.textFg,
st::boxTextFgError,
error));
}
}
void EditScans::hideSelfieError() {
toggleSelfieError(false);
}
void EditScans::toggleSelfieError(bool shown) {
if (_selfieErrorShown != shown) {
_selfieErrorShown = shown;
_selfieErrorAnimation.start(
[=] { selfieErrorAnimationCallback(); },
_selfieErrorShown ? 0. : 1.,
_selfieErrorShown ? 1. : 0.,
st::passportDetailsField.duration);
}
}
void EditScans::selfieErrorAnimationCallback() {
const auto error = _selfieErrorAnimation.current(
_selfieErrorShown ? 1. : 0.);
if (error == 0.) {
_selfieUpload->setColorOverride(base::none);
} else {
_selfieUpload->setColorOverride(anim::color(
st::passportUploadButton.textFg,
st::boxTextFgError,
error));
}
}
} // namespace Passport

View File

@ -39,6 +39,8 @@ public:
std::vector<ScanInfo> &&files,
std::unique_ptr<ScanInfo> &&selfie);
base::optional<int> validateGetErrorTop();
static void ChooseScan(base::lambda<void(QByteArray&&)> callback);
private:
@ -55,6 +57,14 @@ private:
rpl::producer<QString> uploadButtonText() const;
void toggleError(bool shown);
void hideError();
void errorAnimationCallback();
void toggleSelfieError(bool shown);
void hideSelfieError();
void selfieErrorAnimationCallback();
not_null<PanelController*> _controller;
std::vector<ScanInfo> _files;
std::unique_ptr<ScanInfo> _selfie;
@ -66,11 +76,15 @@ private:
std::vector<base::unique_qptr<Ui::SlideWrap<ScanButton>>> _rows;
QPointer<Info::Profile::Button> _upload;
rpl::event_stream<rpl::producer<QString>> _uploadTexts;
bool _errorShown = false;
Animation _errorAnimation;
QPointer<Ui::SlideWrap<Ui::FlatLabel>> _selfieHeader;
QPointer<Ui::VerticalLayout> _selfieWrap;
base::unique_qptr<Ui::SlideWrap<ScanButton>> _selfieRow;
QPointer<Info::Profile::Button> _selfieUpload;
bool _selfieErrorShown = false;
Animation _selfieErrorAnimation;
};