mirror of
https://github.com/vale981/tdesktop
synced 2025-03-06 02:01:40 -05:00
Show scans/selfie saving errors.
This commit is contained in:
parent
f8b2e474b9
commit
e7ce4ca10a
11 changed files with 169 additions and 1 deletions
|
@ -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
|
||||
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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;
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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() {
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
};
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue