mirror of https://github.com/procxx/kepka.git
Handle errors when saving passport values.
This commit is contained in:
parent
4f1a633019
commit
03b7a3ca2b
|
@ -1604,6 +1604,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
"lng_passport_success" = "Authorization successfull!";
|
"lng_passport_success" = "Authorization successfull!";
|
||||||
"lng_passport_stop_sure" = "Are you sure you want to stop this authorization?";
|
"lng_passport_stop_sure" = "Are you sure you want to stop this authorization?";
|
||||||
"lng_passport_stop" = "Stop";
|
"lng_passport_stop" = "Stop";
|
||||||
|
"lng_passport_restart_sure" = "Unexpected error has occurred. Perhaps some changes were done from a different Telegram application. Would you like to restart this authorization?";
|
||||||
|
"lng_passport_restart" = "Restart";
|
||||||
|
|
||||||
// Wnd specific
|
// Wnd specific
|
||||||
|
|
||||||
|
|
|
@ -30,5 +30,29 @@ inline QString ProxyConfigError() {
|
||||||
return qsl("The proxy you are using is not configured correctly and will be disabled. Please find another one.");
|
return qsl("The proxy you are using is not configured correctly and will be disabled. Please find another one.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline QString NoAuthorizationBot() {
|
||||||
|
return qsl("Could not get authorization bot.");
|
||||||
|
}
|
||||||
|
|
||||||
|
inline QString SecureSaveError() {
|
||||||
|
return qsl("Error saving value.");
|
||||||
|
}
|
||||||
|
|
||||||
|
inline QString SecureAcceptError() {
|
||||||
|
return qsl("Error acception form.");
|
||||||
|
}
|
||||||
|
|
||||||
|
inline QString PassportCorrupted() {
|
||||||
|
return qsl("It seems your Telegram Passport data was corrupted.\n\nYou can reset your Telegram Passport and restart this authorization.");
|
||||||
|
}
|
||||||
|
|
||||||
|
inline QString PassportCorruptedReset() {
|
||||||
|
return qsl("Reset");
|
||||||
|
}
|
||||||
|
|
||||||
|
inline QString PassportCorruptedResetSure() {
|
||||||
|
return qsl("Are you sure you want to reset your Telegram Passport data?");
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Hard
|
} // namespace Hard
|
||||||
} // namespace Lang
|
} // namespace Lang
|
||||||
|
|
|
@ -137,8 +137,10 @@ passportFormPolicy: FlatLabel(passportFormLabel) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
passportFormPolicyPadding: margins(22px, 7px, 22px, 28px);
|
passportFormPolicyPadding: margins(22px, 7px, 22px, 28px);
|
||||||
passportContactNewFieldPadding: margins(22px, 0px, 22px, 28px);
|
passportContactNewFieldPadding: margins(22px, 0px, 22px, 14px);
|
||||||
passportContactFieldPadding: margins(22px, 14px, 22px, 28px);
|
passportContactFieldPadding: margins(22px, 14px, 22px, 14px);
|
||||||
|
passportContactErrorPadding: margins(22px, 0px, 22px, 0px);
|
||||||
|
passportContactErrorMargin: margins(0px, 0px, 0px, 14px);
|
||||||
|
|
||||||
passportRowPadding: margins(22px, 8px, 25px, 8px);
|
passportRowPadding: margins(22px, 8px, 25px, 8px);
|
||||||
passportRowIconSkip: 10px;
|
passportRowIconSkip: 10px;
|
||||||
|
|
|
@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "passport/passport_panel_controller.h"
|
#include "passport/passport_panel_controller.h"
|
||||||
#include "boxes/confirm_box.h"
|
#include "boxes/confirm_box.h"
|
||||||
#include "lang/lang_keys.h"
|
#include "lang/lang_keys.h"
|
||||||
|
#include "lang/lang_hardcoded.h"
|
||||||
#include "base/openssl_help.h"
|
#include "base/openssl_help.h"
|
||||||
#include "base/qthelp_url.h"
|
#include "base/qthelp_url.h"
|
||||||
#include "mainwindow.h"
|
#include "mainwindow.h"
|
||||||
|
@ -28,6 +29,35 @@ namespace {
|
||||||
|
|
||||||
constexpr auto kDocumentScansLimit = 20;
|
constexpr auto kDocumentScansLimit = 20;
|
||||||
|
|
||||||
|
bool ForwardServiceErrorRequired(const QString &error) {
|
||||||
|
return (error == qstr("BOT_INVALID"))
|
||||||
|
|| (error == qstr("PUBLIC_KEY_REQUIRED"))
|
||||||
|
|| (error == qstr("PUBLIC_KEY_INVALID"))
|
||||||
|
|| (error == qstr("SCOPE_EMPTY"))
|
||||||
|
|| (error == qstr("PAYLOAD_EMPTY"));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SaveErrorRequiresRestart(const QString &error) {
|
||||||
|
return (error == qstr("PASSWORD_REQUIRED"))
|
||||||
|
|| (error == qstr("SECURE_SECRET_REQUIRED"))
|
||||||
|
|| (error == qstr("SECURE_SECRET_INVALID"));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AcceptErrorRequiresRestart(const QString &error) {
|
||||||
|
return (error == qstr("PASSWORD_REQUIRED"))
|
||||||
|
|| (error == qstr("SECURE_SECRET_REQUIRED"))
|
||||||
|
|| (error == qstr("SECURE_VALUE_EMPTY"))
|
||||||
|
|| (error == qstr("SECURE_VALUE_HASH_INVALID"));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::map<QString, QString> GetTexts(const ValueMap &map) {
|
||||||
|
auto result = std::map<QString, QString>();
|
||||||
|
for (const auto &[key, value] : map.fields) {
|
||||||
|
result[key] = value.text;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
QImage ReadImage(bytes::const_span buffer) {
|
QImage ReadImage(bytes::const_span buffer) {
|
||||||
return App::readImage(QByteArray::fromRawData(
|
return App::readImage(QByteArray::fromRawData(
|
||||||
reinterpret_cast<const char*>(buffer.data()),
|
reinterpret_cast<const char*>(buffer.data()),
|
||||||
|
@ -137,15 +167,15 @@ EditFile::EditFile(
|
||||||
not_null<const Value*> value,
|
not_null<const Value*> value,
|
||||||
const File &fields,
|
const File &fields,
|
||||||
std::unique_ptr<UploadScanData> &&uploadData)
|
std::unique_ptr<UploadScanData> &&uploadData)
|
||||||
: value(value)
|
: value(value)
|
||||||
, fields(std::move(fields))
|
, fields(std::move(fields))
|
||||||
, uploadData(std::move(uploadData))
|
, uploadData(std::move(uploadData))
|
||||||
, guard(std::make_shared<bool>(true)) {
|
, guard(std::make_shared<bool>(true)) {
|
||||||
}
|
}
|
||||||
|
|
||||||
UploadScanDataPointer::UploadScanDataPointer(
|
UploadScanDataPointer::UploadScanDataPointer(
|
||||||
std::unique_ptr<UploadScanData> &&value)
|
std::unique_ptr<UploadScanData> &&value)
|
||||||
: _value(std::move(value)) {
|
: _value(std::move(value)) {
|
||||||
}
|
}
|
||||||
|
|
||||||
UploadScanDataPointer::UploadScanDataPointer(
|
UploadScanDataPointer::UploadScanDataPointer(
|
||||||
|
@ -211,6 +241,7 @@ bytes::vector FormController::passwordHashForAuth(
|
||||||
}
|
}
|
||||||
|
|
||||||
auto FormController::prepareFinalData() -> FinalData {
|
auto FormController::prepareFinalData() -> FinalData {
|
||||||
|
auto errors = std::vector<not_null<const Value*>>();
|
||||||
auto hashes = QVector<MTPSecureValueHash>();
|
auto hashes = QVector<MTPSecureValueHash>();
|
||||||
auto secureData = QJsonObject();
|
auto secureData = QJsonObject();
|
||||||
const auto addValueToJSON = [&](
|
const auto addValueToJSON = [&](
|
||||||
|
@ -244,13 +275,11 @@ auto FormController::prepareFinalData() -> FinalData {
|
||||||
addValueToJSON(key, value);
|
addValueToJSON(key, value);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
auto hasErrors = false;
|
|
||||||
const auto scopes = ComputeScopes(this);
|
const auto scopes = ComputeScopes(this);
|
||||||
for (const auto &scope : scopes) {
|
for (const auto &scope : scopes) {
|
||||||
const auto ready = ComputeScopeRowReadyString(scope);
|
const auto ready = ComputeScopeRowReadyString(scope);
|
||||||
if (ready.isEmpty()) {
|
if (ready.isEmpty()) {
|
||||||
hasErrors = true;
|
errors.push_back(scope.fields);
|
||||||
findValue(scope.fields)->error = QString();
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
addValue(scope.fields);
|
addValue(scope.fields);
|
||||||
|
@ -263,28 +292,28 @@ auto FormController::prepareFinalData() -> FinalData {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (hasErrors) {
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
auto json = QJsonObject();
|
auto json = QJsonObject();
|
||||||
json.insert("secure_data", secureData);
|
if (errors.empty()) {
|
||||||
json.insert("payload", _request.payload);
|
json.insert("secure_data", secureData);
|
||||||
|
json.insert("payload", _request.payload);
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
hashes,
|
hashes,
|
||||||
QJsonDocument(json).toJson(QJsonDocument::Compact)
|
QJsonDocument(json).toJson(QJsonDocument::Compact),
|
||||||
|
errors
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FormController::submit() {
|
std::vector<not_null<const Value*>> FormController::submitGetErrors() {
|
||||||
if (_submitRequestId || _submitSuccess|| _cancelled) {
|
if (_submitRequestId || _submitSuccess|| _cancelled) {
|
||||||
return true;
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto prepared = prepareFinalData();
|
const auto prepared = prepareFinalData();
|
||||||
if (prepared.hashes.empty()) {
|
if (!prepared.errors.empty()) {
|
||||||
return false;
|
return prepared.errors;
|
||||||
}
|
}
|
||||||
const auto credentialsEncryptedData = EncryptData(
|
const auto credentialsEncryptedData = EncryptData(
|
||||||
bytes::make_span(prepared.credentials));
|
bytes::make_span(prepared.credentials));
|
||||||
|
@ -313,10 +342,15 @@ bool FormController::submit() {
|
||||||
[=] { cancel(); });
|
[=] { cancel(); });
|
||||||
}).fail([=](const RPCError &error) {
|
}).fail([=](const RPCError &error) {
|
||||||
_submitRequestId = 0;
|
_submitRequestId = 0;
|
||||||
_view->show(Box<InformBox>(
|
if (AcceptErrorRequiresRestart(error.type())) {
|
||||||
"Failed sending data :(\n" + error.type()));
|
suggestRestart();
|
||||||
|
} else {
|
||||||
|
_view->show(Box<InformBox>(
|
||||||
|
Lang::Hard::SecureAcceptError() + "\n" + error.type()));
|
||||||
|
}
|
||||||
}).send();
|
}).send();
|
||||||
return true;
|
|
||||||
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
void FormController::submitPassword(const QString &password) {
|
void FormController::submitPassword(const QString &password) {
|
||||||
|
@ -407,10 +441,14 @@ void FormController::decryptValue(Value &value) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!value.data.original.isEmpty()) {
|
if (!value.data.original.isEmpty()) {
|
||||||
value.data.parsed.fields = DeserializeData(DecryptData(
|
const auto fields = DeserializeData(DecryptData(
|
||||||
bytes::make_span(value.data.original),
|
bytes::make_span(value.data.original),
|
||||||
value.data.hash,
|
value.data.hash,
|
||||||
value.data.secret));
|
value.data.secret));
|
||||||
|
value.data.parsed.fields.clear();
|
||||||
|
for (const auto [key, text] : fields) {
|
||||||
|
value.data.parsed.fields[key] = { text };
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -991,11 +1029,11 @@ bool FormController::editValueChanged(
|
||||||
for (const auto &[key, value] : data.fields) {
|
for (const auto &[key, value] : data.fields) {
|
||||||
const auto i = existing.find(key);
|
const auto i = existing.find(key);
|
||||||
if (i != existing.end()) {
|
if (i != existing.end()) {
|
||||||
if (i->second != value) {
|
if (i->second.text != value.text) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
existing.erase(i);
|
existing.erase(i);
|
||||||
} else if (!value.isEmpty()) {
|
} else if (!value.text.isEmpty()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1018,7 +1056,6 @@ void FormController::saveValueEdit(
|
||||||
base::take(nonconst->data.encryptedSecretInEdit);
|
base::take(nonconst->data.encryptedSecretInEdit);
|
||||||
base::take(nonconst->data.hashInEdit);
|
base::take(nonconst->data.hashInEdit);
|
||||||
base::take(nonconst->data.parsedInEdit);
|
base::take(nonconst->data.parsedInEdit);
|
||||||
base::take(nonconst->error);
|
|
||||||
nonconst->saveRequestId = 0;
|
nonconst->saveRequestId = 0;
|
||||||
_valueSaveFinished.fire_copy(nonconst);
|
_valueSaveFinished.fire_copy(nonconst);
|
||||||
});
|
});
|
||||||
|
@ -1049,7 +1086,7 @@ void FormController::deleteValueEdit(not_null<const Value*> value) {
|
||||||
_valueSaveFinished.fire_copy(value);
|
_valueSaveFinished.fire_copy(value);
|
||||||
}).fail([=](const RPCError &error) {
|
}).fail([=](const RPCError &error) {
|
||||||
nonconst->saveRequestId = 0;
|
nonconst->saveRequestId = 0;
|
||||||
valueSaveFailed(nonconst, error);
|
valueSaveShowError(nonconst, error);
|
||||||
}).send();
|
}).send();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1090,7 +1127,7 @@ void FormController::saveEncryptedValue(not_null<Value*> value) {
|
||||||
value->data.secret = GenerateSecretBytes();
|
value->data.secret = GenerateSecretBytes();
|
||||||
}
|
}
|
||||||
const auto encryptedData = EncryptData(
|
const auto encryptedData = EncryptData(
|
||||||
SerializeData(value->data.parsedInEdit.fields),
|
SerializeData(GetTexts(value->data.parsedInEdit)),
|
||||||
value->data.secret);
|
value->data.secret);
|
||||||
value->data.hashInEdit = encryptedData.hash;
|
value->data.hashInEdit = encryptedData.hash;
|
||||||
value->data.encryptedSecretInEdit = EncryptValueSecret(
|
value->data.encryptedSecretInEdit = EncryptValueSecret(
|
||||||
|
@ -1197,18 +1234,37 @@ void FormController::sendSaveRequest(
|
||||||
_valueSaveFinished.fire_copy(value);
|
_valueSaveFinished.fire_copy(value);
|
||||||
}).fail([=](const RPCError &error) {
|
}).fail([=](const RPCError &error) {
|
||||||
value->saveRequestId = 0;
|
value->saveRequestId = 0;
|
||||||
if (error.type() == qstr("PHONE_VERIFICATION_NEEDED")) {
|
const auto code = error.type();
|
||||||
|
if (code == qstr("PHONE_VERIFICATION_NEEDED")) {
|
||||||
if (value->type == Value::Type::Phone) {
|
if (value->type == Value::Type::Phone) {
|
||||||
startPhoneVerification(value);
|
startPhoneVerification(value);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
} else if (error.type() == qstr("EMAIL_VERIFICATION_NEEDED")) {
|
} else if (code == qstr("PHONE_NUMBER_INVALID")) {
|
||||||
|
if (value->type == Value::Type::Phone) {
|
||||||
|
value->data.parsedInEdit.fields["value"].error
|
||||||
|
= lang(lng_bad_phone);
|
||||||
|
valueSaveFailed(value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else if (code == qstr("EMAIL_VERIFICATION_NEEDED")) {
|
||||||
if (value->type == Value::Type::Email) {
|
if (value->type == Value::Type::Email) {
|
||||||
startEmailVerification(value);
|
startEmailVerification(value);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
} else if (code == qstr("EMAIL_INVALID")) {
|
||||||
|
if (value->type == Value::Type::Email) {
|
||||||
|
value->data.parsedInEdit.fields["value"].error
|
||||||
|
= lang(lng_cloud_password_bad_email);
|
||||||
|
valueSaveFailed(value);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (SaveErrorRequiresRestart(code)) {
|
||||||
|
suggestRestart();
|
||||||
|
} else {
|
||||||
|
valueSaveShowError(value, error);
|
||||||
}
|
}
|
||||||
valueSaveFailed(value, error);
|
|
||||||
}).send();
|
}).send();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1233,7 +1289,7 @@ QString FormController::getPlainTextFromValue(
|
||||||
|
|
||||||
const auto i = value->data.parsedInEdit.fields.find("value");
|
const auto i = value->data.parsedInEdit.fields.find("value");
|
||||||
Assert(i != end(value->data.parsedInEdit.fields));
|
Assert(i != end(value->data.parsedInEdit.fields));
|
||||||
return i->second;
|
return i->second.text;
|
||||||
}
|
}
|
||||||
|
|
||||||
void FormController::startPhoneVerification(not_null<Value*> value) {
|
void FormController::startPhoneVerification(not_null<Value*> value) {
|
||||||
|
@ -1291,7 +1347,7 @@ void FormController::startPhoneVerification(not_null<Value*> value) {
|
||||||
_verificationNeeded.fire_copy(value);
|
_verificationNeeded.fire_copy(value);
|
||||||
}).fail([=](const RPCError &error) {
|
}).fail([=](const RPCError &error) {
|
||||||
value->verification.requestId = 0;
|
value->verification.requestId = 0;
|
||||||
valueSaveFailed(value, error);
|
valueSaveShowError(value, error);
|
||||||
}).send();
|
}).send();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1308,7 +1364,7 @@ void FormController::startEmailVerification(not_null<Value*> value) {
|
||||||
: -1;
|
: -1;
|
||||||
_verificationNeeded.fire_copy(value);
|
_verificationNeeded.fire_copy(value);
|
||||||
}).fail([=](const RPCError &error) {
|
}).fail([=](const RPCError &error) {
|
||||||
valueSaveFailed(value, error);
|
valueSaveShowError(value, error);
|
||||||
}).send();
|
}).send();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1326,10 +1382,15 @@ void FormController::requestPhoneCall(not_null<Value*> value) {
|
||||||
}).send();
|
}).send();
|
||||||
}
|
}
|
||||||
|
|
||||||
void FormController::valueSaveFailed(
|
void FormController::valueSaveShowError(
|
||||||
not_null<Value*> value,
|
not_null<Value*> value,
|
||||||
const RPCError &error) {
|
const RPCError &error) {
|
||||||
_view->show(Box<InformBox>("Error saving value:\n" + error.type()));
|
_view->show(Box<InformBox>(
|
||||||
|
Lang::Hard::SecureSaveError() + "\n" + error.type()));
|
||||||
|
valueSaveFailed(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FormController::valueSaveFailed(not_null<Value*> value) {
|
||||||
valueEditFailed(value);
|
valueEditFailed(value);
|
||||||
_valueSaveFinished.fire_copy(value);
|
_valueSaveFinished.fire_copy(value);
|
||||||
}
|
}
|
||||||
|
@ -1378,16 +1439,24 @@ void FormController::generateSecret(bytes::const_span password) {
|
||||||
callback();
|
callback();
|
||||||
}
|
}
|
||||||
}).fail([=](const RPCError &error) {
|
}).fail([=](const RPCError &error) {
|
||||||
// #TODO wrong password hash error?
|
|
||||||
Ui::show(Box<InformBox>("Saving encrypted value failed."));
|
|
||||||
_saveSecretRequestId = 0;
|
_saveSecretRequestId = 0;
|
||||||
|
suggestRestart();
|
||||||
}).send();
|
}).send();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FormController::suggestRestart() {
|
||||||
|
_suggestingRestart = true;
|
||||||
|
_view->show(Box<ConfirmBox>(
|
||||||
|
lang(lng_passport_restart_sure),
|
||||||
|
lang(lng_passport_restart),
|
||||||
|
[=] { _controller->showPassportForm(_request); },
|
||||||
|
[=] { cancel(); }));
|
||||||
|
}
|
||||||
|
|
||||||
void FormController::requestForm() {
|
void FormController::requestForm() {
|
||||||
if (_request.payload.isEmpty()) {
|
if (_request.payload.isEmpty()) {
|
||||||
_formRequestId = -1;
|
_formRequestId = -1;
|
||||||
Ui::show(Box<InformBox>(lang(lng_passport_form_error)));
|
formFail("PAYLOAD_EMPTY");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_formRequestId = request(MTPaccount_GetAuthorizationForm(
|
_formRequestId = request(MTPaccount_GetAuthorizationForm(
|
||||||
|
@ -1398,7 +1467,7 @@ void FormController::requestForm() {
|
||||||
_formRequestId = 0;
|
_formRequestId = 0;
|
||||||
formDone(result);
|
formDone(result);
|
||||||
}).fail([=](const RPCError &error) {
|
}).fail([=](const RPCError &error) {
|
||||||
formFail(error);
|
formFail(error.type());
|
||||||
}).send();
|
}).send();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1496,11 +1565,11 @@ auto FormController::parseValue(
|
||||||
switch (data.vplain_data.type()) {
|
switch (data.vplain_data.type()) {
|
||||||
case mtpc_securePlainPhone: {
|
case mtpc_securePlainPhone: {
|
||||||
const auto &fields = data.vplain_data.c_securePlainPhone();
|
const auto &fields = data.vplain_data.c_securePlainPhone();
|
||||||
result.data.parsed.fields["value"] = qs(fields.vphone);
|
result.data.parsed.fields["value"].text = qs(fields.vphone);
|
||||||
} break;
|
} break;
|
||||||
case mtpc_securePlainEmail: {
|
case mtpc_securePlainEmail: {
|
||||||
const auto &fields = data.vplain_data.c_securePlainEmail();
|
const auto &fields = data.vplain_data.c_securePlainEmail();
|
||||||
result.data.parsed.fields["value"] = qs(fields.vemail);
|
result.data.parsed.fields["value"].text = qs(fields.vemail);
|
||||||
} break;
|
} break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1596,8 +1665,10 @@ void FormController::parseForm(const MTPaccount_AuthorizationForm &result) {
|
||||||
_bot = App::userLoaded(_request.botId);
|
_bot = App::userLoaded(_request.botId);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FormController::formFail(const RPCError &error) {
|
void FormController::formFail(const QString &error) {
|
||||||
Ui::show(Box<InformBox>(lang(lng_passport_form_error)));
|
_serviceErrorText = error;
|
||||||
|
_view->showCriticalError(
|
||||||
|
lang(lng_passport_form_error) + "\n" + error);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FormController::requestPassword() {
|
void FormController::requestPassword() {
|
||||||
|
@ -1606,7 +1677,7 @@ void FormController::requestPassword() {
|
||||||
_passwordRequestId = 0;
|
_passwordRequestId = 0;
|
||||||
passwordDone(result);
|
passwordDone(result);
|
||||||
}).fail([=](const RPCError &error) {
|
}).fail([=](const RPCError &error) {
|
||||||
formFail(error);
|
formFail(error.type());
|
||||||
}).send();
|
}).send();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1627,7 +1698,7 @@ void FormController::passwordDone(const MTPaccount_Password &result) {
|
||||||
|
|
||||||
void FormController::showForm() {
|
void FormController::showForm() {
|
||||||
if (!_bot) {
|
if (!_bot) {
|
||||||
Ui::show(Box<InformBox>("Could not get authorization bot."));
|
formFail(Lang::Hard::NoAuthorizationBot());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (!_password.salt.empty()) {
|
if (!_password.salt.empty()) {
|
||||||
|
@ -1639,10 +1710,6 @@ void FormController::showForm() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void FormController::passwordFail(const RPCError &error) {
|
|
||||||
Ui::show(Box<InformBox>("Could not get authorization form."));
|
|
||||||
}
|
|
||||||
|
|
||||||
void FormController::parsePassword(const MTPDaccount_noPassword &result) {
|
void FormController::parsePassword(const MTPDaccount_noPassword &result) {
|
||||||
_password.unconfirmedPattern = qs(result.vemail_unconfirmed_pattern);
|
_password.unconfirmedPattern = qs(result.vemail_unconfirmed_pattern);
|
||||||
_password.newSalt = bytes::make_vector(result.vnew_salt.v);
|
_password.newSalt = bytes::make_vector(result.vnew_salt.v);
|
||||||
|
@ -1661,25 +1728,41 @@ void FormController::parsePassword(const MTPDaccount_password &result) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void FormController::cancel() {
|
void FormController::cancel() {
|
||||||
if (!_submitSuccess) {
|
if (!_submitSuccess && _serviceErrorText.isEmpty()) {
|
||||||
_view->show(Box<ConfirmBox>(
|
_view->show(Box<ConfirmBox>(
|
||||||
lang(lng_passport_stop_sure),
|
lang(lng_passport_stop_sure),
|
||||||
lang(lng_passport_stop),
|
lang(lng_passport_stop),
|
||||||
[=] { cancelSure(); }));
|
[=] { cancelSure(); },
|
||||||
|
[=] { cancelAbort(); }));
|
||||||
} else {
|
} else {
|
||||||
cancelSure();
|
cancelSure();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FormController::cancelAbort() {
|
||||||
|
if (_cancelled || _submitSuccess) {
|
||||||
|
return;
|
||||||
|
} else if (_suggestingRestart) {
|
||||||
|
suggestRestart();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void FormController::cancelSure() {
|
void FormController::cancelSure() {
|
||||||
if (!_cancelled) {
|
if (!_cancelled) {
|
||||||
_cancelled = true;
|
_cancelled = true;
|
||||||
|
|
||||||
const auto url = qthelp::url_append_query(
|
if (!_request.callbackUrl.isEmpty()
|
||||||
_request.callbackUrl,
|
&& (_serviceErrorText.isEmpty()
|
||||||
_submitSuccess ? "tg_passport=success" : "tg_passport=cancel");
|
|| ForwardServiceErrorRequired(_serviceErrorText))) {
|
||||||
UrlClickHandler::doOpen(url);
|
const auto url = qthelp::url_append_query(
|
||||||
|
_request.callbackUrl,
|
||||||
|
(_submitSuccess
|
||||||
|
? "tg_passport=success"
|
||||||
|
: (_serviceErrorText.isEmpty()
|
||||||
|
? "tg_passport=cancel"
|
||||||
|
: "tg_passport=error&error=" + _serviceErrorText)));
|
||||||
|
UrlClickHandler::doOpen(url);
|
||||||
|
}
|
||||||
const auto timeout = _view->closeGetDuration();
|
const auto timeout = _view->closeGetDuration();
|
||||||
App::CallDelayed(timeout, this, [=] {
|
App::CallDelayed(timeout, this, [=] {
|
||||||
_controller->clearPassportForm();
|
_controller->clearPassportForm();
|
||||||
|
|
|
@ -84,6 +84,7 @@ struct File {
|
||||||
|
|
||||||
int downloadOffset = 0;
|
int downloadOffset = 0;
|
||||||
QImage image;
|
QImage image;
|
||||||
|
QString error;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct EditFile {
|
struct EditFile {
|
||||||
|
@ -99,8 +100,13 @@ struct EditFile {
|
||||||
bool deleted = false;
|
bool deleted = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct ValueField {
|
||||||
|
QString text;
|
||||||
|
QString error;
|
||||||
|
};
|
||||||
|
|
||||||
struct ValueMap {
|
struct ValueMap {
|
||||||
std::map<QString, QString> fields;
|
std::map<QString, ValueField> fields;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ValueData {
|
struct ValueData {
|
||||||
|
@ -146,13 +152,13 @@ struct Value {
|
||||||
ValueData data;
|
ValueData data;
|
||||||
std::vector<File> scans;
|
std::vector<File> scans;
|
||||||
std::vector<EditFile> scansInEdit;
|
std::vector<EditFile> scansInEdit;
|
||||||
|
QString scanMissingError;
|
||||||
base::optional<File> selfie;
|
base::optional<File> selfie;
|
||||||
base::optional<EditFile> selfieInEdit;
|
base::optional<EditFile> selfieInEdit;
|
||||||
Verification verification;
|
Verification verification;
|
||||||
bytes::vector submitHash;
|
bytes::vector submitHash;
|
||||||
|
|
||||||
int editScreens = 0;
|
int editScreens = 0;
|
||||||
base::optional<QString> error;
|
|
||||||
mtpRequestId saveRequestId = 0;
|
mtpRequestId saveRequestId = 0;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
@ -208,7 +214,7 @@ public:
|
||||||
void show();
|
void show();
|
||||||
UserData *bot() const;
|
UserData *bot() const;
|
||||||
QString privacyPolicyUrl() const;
|
QString privacyPolicyUrl() const;
|
||||||
bool submit();
|
std::vector<not_null<const Value*>> submitGetErrors();
|
||||||
void submitPassword(const QString &password);
|
void submitPassword(const QString &password);
|
||||||
rpl::producer<QString> passwordError() const;
|
rpl::producer<QString> passwordError() const;
|
||||||
QString passwordHint() const;
|
QString passwordHint() const;
|
||||||
|
@ -253,6 +259,7 @@ private:
|
||||||
struct FinalData {
|
struct FinalData {
|
||||||
QVector<MTPSecureValueHash> hashes;
|
QVector<MTPSecureValueHash> hashes;
|
||||||
QByteArray credentials;
|
QByteArray credentials;
|
||||||
|
std::vector<not_null<const Value*>> errors;
|
||||||
};
|
};
|
||||||
EditFile *findEditFile(const FullMsgId &fullId);
|
EditFile *findEditFile(const FullMsgId &fullId);
|
||||||
EditFile *findEditFile(const FileKey &key);
|
EditFile *findEditFile(const FileKey &key);
|
||||||
|
@ -263,7 +270,7 @@ private:
|
||||||
void requestPassword();
|
void requestPassword();
|
||||||
|
|
||||||
void formDone(const MTPaccount_AuthorizationForm &result);
|
void formDone(const MTPaccount_AuthorizationForm &result);
|
||||||
void formFail(const RPCError &error);
|
void formFail(const QString &error);
|
||||||
void parseForm(const MTPaccount_AuthorizationForm &result);
|
void parseForm(const MTPaccount_AuthorizationForm &result);
|
||||||
void showForm();
|
void showForm();
|
||||||
Value parseValue(
|
Value parseValue(
|
||||||
|
@ -280,7 +287,6 @@ private:
|
||||||
const std::vector<EditFile> &source) const;
|
const std::vector<EditFile> &source) const;
|
||||||
|
|
||||||
void passwordDone(const MTPaccount_Password &result);
|
void passwordDone(const MTPaccount_Password &result);
|
||||||
void passwordFail(const RPCError &error);
|
|
||||||
void parsePassword(const MTPDaccount_noPassword &settings);
|
void parsePassword(const MTPDaccount_noPassword &settings);
|
||||||
void parsePassword(const MTPDaccount_password &settings);
|
void parsePassword(const MTPDaccount_password &settings);
|
||||||
bytes::vector passwordHashForAuth(bytes::const_span password) const;
|
bytes::vector passwordHashForAuth(bytes::const_span password) const;
|
||||||
|
@ -327,7 +333,8 @@ private:
|
||||||
QString getPlainTextFromValue(not_null<const Value*> value) const;
|
QString getPlainTextFromValue(not_null<const Value*> value) const;
|
||||||
void startPhoneVerification(not_null<Value*> value);
|
void startPhoneVerification(not_null<Value*> value);
|
||||||
void startEmailVerification(not_null<Value*> value);
|
void startEmailVerification(not_null<Value*> value);
|
||||||
void valueSaveFailed(not_null<Value*> value, const RPCError &error);
|
void valueSaveShowError(not_null<Value*> value, const RPCError &error);
|
||||||
|
void valueSaveFailed(not_null<Value*> value);
|
||||||
void requestPhoneCall(not_null<Value*> value);
|
void requestPhoneCall(not_null<Value*> value);
|
||||||
void verificationError(
|
void verificationError(
|
||||||
not_null<Value*> value,
|
not_null<Value*> value,
|
||||||
|
@ -345,7 +352,9 @@ private:
|
||||||
const MTPInputSecureValue &data);
|
const MTPInputSecureValue &data);
|
||||||
FinalData prepareFinalData();
|
FinalData prepareFinalData();
|
||||||
|
|
||||||
|
void suggestRestart();
|
||||||
void cancelSure();
|
void cancelSure();
|
||||||
|
void cancelAbort();
|
||||||
|
|
||||||
not_null<Window::Controller*> _controller;
|
not_null<Window::Controller*> _controller;
|
||||||
FormRequest _request;
|
FormRequest _request;
|
||||||
|
@ -373,6 +382,8 @@ private:
|
||||||
rpl::event_stream<QString> _passwordError;
|
rpl::event_stream<QString> _passwordError;
|
||||||
mtpRequestId _submitRequestId = 0;
|
mtpRequestId _submitRequestId = 0;
|
||||||
bool _submitSuccess = false;
|
bool _submitSuccess = false;
|
||||||
|
bool _suggestingRestart = false;
|
||||||
|
QString _serviceErrorText;
|
||||||
|
|
||||||
rpl::lifetime _uploaderSubscriptions;
|
rpl::lifetime _uploaderSubscriptions;
|
||||||
rpl::lifetime _lifetime;
|
rpl::lifetime _lifetime;
|
||||||
|
|
|
@ -147,20 +147,26 @@ QString ComputeScopeRowReadyString(const Scope &scope) {
|
||||||
const auto i = fields.find(row.key);
|
const auto i = fields.find(row.key);
|
||||||
if (i == end(fields)) {
|
if (i == end(fields)) {
|
||||||
return QString();
|
return QString();
|
||||||
} else if (row.validate && !row.validate(i->second)) {
|
}
|
||||||
|
const auto text = i->second.text;
|
||||||
|
if (row.validate && !row.validate(text)) {
|
||||||
return QString();
|
return QString();
|
||||||
}
|
}
|
||||||
pushListValue(format ? format(i->second) : i->second);
|
pushListValue(format ? format(text) : text);
|
||||||
|
} else if (scope.documents.empty()) {
|
||||||
|
continue;
|
||||||
} else if (!document) {
|
} else if (!document) {
|
||||||
return QString();
|
return QString();
|
||||||
} else {
|
} else {
|
||||||
const auto i = document->data.parsed.fields.find(row.key);
|
const auto i = document->data.parsed.fields.find(row.key);
|
||||||
if (i == end(document->data.parsed.fields)) {
|
if (i == end(document->data.parsed.fields)) {
|
||||||
return QString();
|
return QString();
|
||||||
} else if (row.validate && !row.validate(i->second)) {
|
}
|
||||||
|
const auto text = i->second.text;
|
||||||
|
if (row.validate && !row.validate(text)) {
|
||||||
return QString();
|
return QString();
|
||||||
}
|
}
|
||||||
pushListValue(i->second);
|
pushListValue(text);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return list.join(", ");
|
return list.join(", ");
|
||||||
|
@ -171,7 +177,7 @@ QString ComputeScopeRowReadyString(const Scope &scope) {
|
||||||
const auto &fields = scope.fields->data.parsed.fields;
|
const auto &fields = scope.fields->data.parsed.fields;
|
||||||
const auto i = fields.find("value");
|
const auto i = fields.find("value");
|
||||||
return (i != end(fields))
|
return (i != end(fields))
|
||||||
? (format ? format(i->second) : i->second)
|
? (format ? format(i->second.text) : i->second.text)
|
||||||
: QString();
|
: QString();
|
||||||
} break;
|
} break;
|
||||||
}
|
}
|
||||||
|
@ -182,13 +188,14 @@ ScopeRow ComputeScopeRow(const Scope &scope) {
|
||||||
const auto addReadyError = [&](ScopeRow &&row) {
|
const auto addReadyError = [&](ScopeRow &&row) {
|
||||||
const auto ready = ComputeScopeRowReadyString(scope);
|
const auto ready = ComputeScopeRowReadyString(scope);
|
||||||
row.ready = ready;
|
row.ready = ready;
|
||||||
row.error = scope.fields->error.has_value()
|
// #TODO passport bot errors
|
||||||
? (!scope.fields->error->isEmpty()
|
//row.error = scope.fields->error.has_value()
|
||||||
? *scope.fields->error
|
// ? (!scope.fields->error->isEmpty()
|
||||||
: !ready.isEmpty()
|
// ? *scope.fields->error
|
||||||
? ready
|
// : !ready.isEmpty()
|
||||||
: row.description)
|
// ? ready
|
||||||
: QString();
|
// : row.description)
|
||||||
|
// : QString();
|
||||||
return row;
|
return row;
|
||||||
};
|
};
|
||||||
switch (scope.type) {
|
switch (scope.type) {
|
||||||
|
|
|
@ -43,6 +43,7 @@ public:
|
||||||
virtual void showAskPassword() = 0;
|
virtual void showAskPassword() = 0;
|
||||||
virtual void showNoPassword() = 0;
|
virtual void showNoPassword() = 0;
|
||||||
virtual void showPasswordUnconfirmed() = 0;
|
virtual void showPasswordUnconfirmed() = 0;
|
||||||
|
virtual void showCriticalError(const QString &error) = 0;
|
||||||
virtual void editScope(int index) = 0;
|
virtual void editScope(int index) = 0;
|
||||||
|
|
||||||
virtual void showBox(object_ptr<BoxContent> box) = 0;
|
virtual void showBox(object_ptr<BoxContent> box) = 0;
|
||||||
|
|
|
@ -15,6 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "ui/widgets/shadow.h"
|
#include "ui/widgets/shadow.h"
|
||||||
#include "ui/widgets/buttons.h"
|
#include "ui/widgets/buttons.h"
|
||||||
#include "ui/widgets/labels.h"
|
#include "ui/widgets/labels.h"
|
||||||
|
#include "ui/wrap/padding_wrap.h"
|
||||||
#include "ui/wrap/fade_wrap.h"
|
#include "ui/wrap/fade_wrap.h"
|
||||||
#include "lang/lang_keys.h"
|
#include "lang/lang_keys.h"
|
||||||
#include "window/layer_widget.h"
|
#include "window/layer_widget.h"
|
||||||
|
@ -231,6 +232,23 @@ void Panel::showPasswordUnconfirmed() {
|
||||||
setBackAllowed(false);
|
setBackAllowed(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Panel::showCriticalError(const QString &error) {
|
||||||
|
auto container = base::make_unique_q<Ui::PaddingWrap<Ui::FlatLabel>>(
|
||||||
|
_body,
|
||||||
|
object_ptr<Ui::FlatLabel>(
|
||||||
|
_body,
|
||||||
|
error,
|
||||||
|
Ui::FlatLabel::InitType::Simple,
|
||||||
|
st::passportErrorLabel),
|
||||||
|
style::margins(0, st::passportPanelHeight / 3, 0, 0));
|
||||||
|
container->widthValue(
|
||||||
|
) | rpl::start_with_next([label = container->entity()](int width) {
|
||||||
|
label->resize(width, label->height());
|
||||||
|
}, container->lifetime());
|
||||||
|
showInner(std::move(container));
|
||||||
|
setBackAllowed(false);
|
||||||
|
}
|
||||||
|
|
||||||
void Panel::showForm() {
|
void Panel::showForm() {
|
||||||
showInner(base::make_unique_q<PanelForm>(_body, _controller));
|
showInner(base::make_unique_q<PanelForm>(_body, _controller));
|
||||||
setBackAllowed(false);
|
setBackAllowed(false);
|
||||||
|
@ -484,7 +502,7 @@ void Panel::paintOpaqueBorder(Painter &p) const {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Panel::closeEvent(QCloseEvent *e) {
|
void Panel::closeEvent(QCloseEvent *e) {
|
||||||
// #TODO
|
// #TODO passport
|
||||||
}
|
}
|
||||||
|
|
||||||
void Panel::mousePressEvent(QMouseEvent *e) {
|
void Panel::mousePressEvent(QMouseEvent *e) {
|
||||||
|
|
|
@ -39,6 +39,7 @@ public:
|
||||||
void showNoPassword();
|
void showNoPassword();
|
||||||
void showPasswordUnconfirmed();
|
void showPasswordUnconfirmed();
|
||||||
void showForm();
|
void showForm();
|
||||||
|
void showCriticalError(const QString &error);
|
||||||
void showEditValue(object_ptr<Ui::RpWidget> form);
|
void showEditValue(object_ptr<Ui::RpWidget> form);
|
||||||
void showBox(object_ptr<BoxContent> box);
|
void showBox(object_ptr<BoxContent> box);
|
||||||
|
|
||||||
|
|
|
@ -359,6 +359,13 @@ void PanelController::fillRows(
|
||||||
}
|
}
|
||||||
for (const auto &scope : _scopes) {
|
for (const auto &scope : _scopes) {
|
||||||
const auto row = ComputeScopeRow(scope);
|
const auto row = ComputeScopeRow(scope);
|
||||||
|
const auto main = scope.fields;
|
||||||
|
if (!row.ready.isEmpty()) {
|
||||||
|
_submitErrors.erase(
|
||||||
|
ranges::remove(_submitErrors, main),
|
||||||
|
_submitErrors.end());
|
||||||
|
}
|
||||||
|
const auto submitError = base::contains(_submitErrors, main);
|
||||||
callback(
|
callback(
|
||||||
row.title,
|
row.title,
|
||||||
(!row.error.isEmpty()
|
(!row.error.isEmpty()
|
||||||
|
@ -367,7 +374,7 @@ void PanelController::fillRows(
|
||||||
? row.ready
|
? row.ready
|
||||||
: row.description),
|
: row.description),
|
||||||
!row.ready.isEmpty(),
|
!row.ready.isEmpty(),
|
||||||
!row.error.isEmpty());
|
!row.error.isEmpty() || submitError);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -380,7 +387,8 @@ rpl::producer<> PanelController::refillRows() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
void PanelController::submitForm() {
|
void PanelController::submitForm() {
|
||||||
if (!_form->submit()) {
|
_submitErrors = _form->submitGetErrors();
|
||||||
|
if (!_submitErrors.empty()) {
|
||||||
_submitFailed.fire({});
|
_submitFailed.fire({});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -466,6 +474,10 @@ rpl::producer<ScanInfo> PanelController::scanUpdated() const {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
rpl::producer<ScopeError> PanelController::saveErrors() const {
|
||||||
|
return _saveErrors.events();
|
||||||
|
}
|
||||||
|
|
||||||
ScanInfo PanelController::collectScanInfo(const EditFile &file) const {
|
ScanInfo PanelController::collectScanInfo(const EditFile &file) const {
|
||||||
Expects(_editScope != nullptr);
|
Expects(_editScope != nullptr);
|
||||||
Expects(_editDocument != nullptr);
|
Expects(_editDocument != nullptr);
|
||||||
|
@ -507,7 +519,34 @@ ScanInfo PanelController::collectScanInfo(const EditFile &file) const {
|
||||||
status,
|
status,
|
||||||
file.fields.image,
|
file.fields.image,
|
||||||
file.deleted,
|
file.deleted,
|
||||||
isSelfie };
|
isSelfie,
|
||||||
|
file.fields.error };
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<ScopeError> PanelController::collectErrors(
|
||||||
|
not_null<const Value*> value) const {
|
||||||
|
auto result = std::vector<ScopeError>();
|
||||||
|
if (!value->scanMissingError.isEmpty()) {
|
||||||
|
result.push_back({ FileKey(), value->scanMissingError });
|
||||||
|
}
|
||||||
|
const auto addFileError = [&](const EditFile &file) {
|
||||||
|
if (!file.fields.error.isEmpty()) {
|
||||||
|
const auto key = FileKey{ file.fields.id, file.fields.dcId };
|
||||||
|
result.push_back({ key, file.fields.error });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
for (const auto &scan : value->scansInEdit) {
|
||||||
|
addFileError(scan);
|
||||||
|
}
|
||||||
|
if (value->selfieInEdit) {
|
||||||
|
addFileError(*value->selfieInEdit);
|
||||||
|
}
|
||||||
|
for (const auto &[key, value] : value->data.parsedInEdit.fields) {
|
||||||
|
if (!value.error.isEmpty()) {
|
||||||
|
result.push_back({ key, value.error });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto PanelController::deleteValueLabel() const
|
auto PanelController::deleteValueLabel() const
|
||||||
|
@ -625,6 +664,11 @@ void PanelController::showPasswordUnconfirmed() {
|
||||||
_panel->showPasswordUnconfirmed();
|
_panel->showPasswordUnconfirmed();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PanelController::showCriticalError(const QString &error) {
|
||||||
|
ensurePanelCreated();
|
||||||
|
_panel->showCriticalError(error);
|
||||||
|
}
|
||||||
|
|
||||||
void PanelController::ensurePanelCreated() {
|
void PanelController::ensurePanelCreated() {
|
||||||
if (!_panel) {
|
if (!_panel) {
|
||||||
_panel = std::make_unique<Panel>(this);
|
_panel = std::make_unique<Panel>(this);
|
||||||
|
@ -783,7 +827,7 @@ void PanelController::editScope(int index, int documentIndex) {
|
||||||
const auto valueIt = parsed.fields.find("value");
|
const auto valueIt = parsed.fields.find("value");
|
||||||
const auto value = (valueIt == end(parsed.fields)
|
const auto value = (valueIt == end(parsed.fields)
|
||||||
? QString()
|
? QString()
|
||||||
: valueIt->second);
|
: valueIt->second.text);
|
||||||
const auto existing = getDefaultContactValue(_editScope->type);
|
const auto existing = getDefaultContactValue(_editScope->type);
|
||||||
_panelHasUnsavedChanges = nullptr;
|
_panelHasUnsavedChanges = nullptr;
|
||||||
return object_ptr<PanelEditContact>(
|
return object_ptr<PanelEditContact>(
|
||||||
|
@ -829,7 +873,13 @@ void PanelController::processValueSaveFinished(
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((_editValue == value || _editDocument == value) && !savingScope()) {
|
if ((_editValue == value || _editDocument == value) && !savingScope()) {
|
||||||
_panel->showForm();
|
if (auto errors = collectErrors(value); !errors.empty()) {
|
||||||
|
for (auto &&error : errors) {
|
||||||
|
_saveErrors.fire(std::move(error));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
_panel->showForm();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -849,7 +899,7 @@ void PanelController::processVerificationNeeded(
|
||||||
}
|
}
|
||||||
const auto textIt = value->data.parsedInEdit.fields.find("value");
|
const auto textIt = value->data.parsedInEdit.fields.find("value");
|
||||||
Assert(textIt != end(value->data.parsedInEdit.fields));
|
Assert(textIt != end(value->data.parsedInEdit.fields));
|
||||||
const auto text = textIt->second;
|
const auto text = textIt->second.text;
|
||||||
const auto type = value->type;
|
const auto type = value->type;
|
||||||
const auto update = _form->verificationUpdate(
|
const auto update = _form->verificationUpdate(
|
||||||
) | rpl::filter([=](not_null<const Value*> field) {
|
) | rpl::filter([=](not_null<const Value*> field) {
|
||||||
|
|
|
@ -29,6 +29,16 @@ struct ScanInfo {
|
||||||
QImage thumb;
|
QImage thumb;
|
||||||
bool deleted = false;
|
bool deleted = false;
|
||||||
bool selfie = false;
|
bool selfie = false;
|
||||||
|
QString error;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ScopeError {
|
||||||
|
// FileKey:id != 0 - file_hash error (bad scan / selfie)
|
||||||
|
// FileKey:id == 0 - vector<file_hash> error (scan missing)
|
||||||
|
// QString - data_hash with such key error (bad value)
|
||||||
|
base::variant<FileKey, QString> key;
|
||||||
|
QString text;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -68,6 +78,7 @@ public:
|
||||||
void deleteSelfie();
|
void deleteSelfie();
|
||||||
void restoreSelfie();
|
void restoreSelfie();
|
||||||
rpl::producer<ScanInfo> scanUpdated() const;
|
rpl::producer<ScanInfo> scanUpdated() const;
|
||||||
|
rpl::producer<ScopeError> saveErrors() const;
|
||||||
|
|
||||||
base::optional<rpl::producer<QString>> deleteValueLabel() const;
|
base::optional<rpl::producer<QString>> deleteValueLabel() const;
|
||||||
void deleteValue();
|
void deleteValue();
|
||||||
|
@ -78,6 +89,7 @@ public:
|
||||||
void showAskPassword() override;
|
void showAskPassword() override;
|
||||||
void showNoPassword() override;
|
void showNoPassword() override;
|
||||||
void showPasswordUnconfirmed() override;
|
void showPasswordUnconfirmed() override;
|
||||||
|
void showCriticalError(const QString &error) override;
|
||||||
|
|
||||||
void fillRows(
|
void fillRows(
|
||||||
base::lambda<void(
|
base::lambda<void(
|
||||||
|
@ -123,12 +135,16 @@ private:
|
||||||
bool hasValueDocument() const;
|
bool hasValueDocument() const;
|
||||||
bool hasValueFields() const;
|
bool hasValueFields() const;
|
||||||
ScanInfo collectScanInfo(const EditFile &file) const;
|
ScanInfo collectScanInfo(const EditFile &file) const;
|
||||||
|
std::vector<ScopeError> collectErrors(
|
||||||
|
not_null<const Value*> value) const;
|
||||||
QString getDefaultContactValue(Scope::Type type) const;
|
QString getDefaultContactValue(Scope::Type type) const;
|
||||||
void deleteValueSure(bool withDetails);
|
void deleteValueSure(bool withDetails);
|
||||||
|
|
||||||
not_null<FormController*> _form;
|
not_null<FormController*> _form;
|
||||||
std::vector<Scope> _scopes;
|
std::vector<Scope> _scopes;
|
||||||
rpl::event_stream<> _submitFailed;
|
rpl::event_stream<> _submitFailed;
|
||||||
|
std::vector<not_null<const Value*>> _submitErrors;
|
||||||
|
rpl::event_stream<ScopeError> _saveErrors;
|
||||||
|
|
||||||
std::unique_ptr<Panel> _panel;
|
std::unique_ptr<Panel> _panel;
|
||||||
base::lambda<bool()> _panelHasUnsavedChanges;
|
base::lambda<bool()> _panelHasUnsavedChanges;
|
||||||
|
|
|
@ -16,6 +16,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "ui/widgets/buttons.h"
|
#include "ui/widgets/buttons.h"
|
||||||
#include "ui/widgets/shadow.h"
|
#include "ui/widgets/shadow.h"
|
||||||
#include "ui/wrap/vertical_layout.h"
|
#include "ui/wrap/vertical_layout.h"
|
||||||
|
#include "ui/wrap/slide_wrap.h"
|
||||||
#include "ui/wrap/fade_wrap.h"
|
#include "ui/wrap/fade_wrap.h"
|
||||||
#include "boxes/abstract_box.h"
|
#include "boxes/abstract_box.h"
|
||||||
#include "boxes/confirm_phone_box.h"
|
#include "boxes/confirm_phone_box.h"
|
||||||
|
@ -260,6 +261,18 @@ void PanelEditContact::setupControls(
|
||||||
}, _field->lifetime());
|
}, _field->lifetime());
|
||||||
|
|
||||||
_content->add(std::move(wrap), fieldPadding);
|
_content->add(std::move(wrap), fieldPadding);
|
||||||
|
const auto errorWrap = _content->add(
|
||||||
|
object_ptr<Ui::SlideWrap<Ui::FlatLabel>>(
|
||||||
|
_content,
|
||||||
|
object_ptr<Ui::FlatLabel>(
|
||||||
|
_content,
|
||||||
|
QString(),
|
||||||
|
Ui::FlatLabel::InitType::Simple,
|
||||||
|
st::passportVerifyErrorLabel),
|
||||||
|
st::passportContactErrorPadding),
|
||||||
|
st::passportContactErrorMargin);
|
||||||
|
errorWrap->hide(anim::type::instant);
|
||||||
|
|
||||||
_content->add(
|
_content->add(
|
||||||
object_ptr<PanelLabel>(
|
object_ptr<PanelLabel>(
|
||||||
_content,
|
_content,
|
||||||
|
@ -282,12 +295,24 @@ void PanelEditContact::setupControls(
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_controller->saveErrors(
|
||||||
|
) | rpl::start_with_next([=](const ScopeError &error) {
|
||||||
|
if (error.key == QString("value")) {
|
||||||
|
_field->showError();
|
||||||
|
errorWrap->entity()->setText(error.text);
|
||||||
|
errorWrap->show(anim::type::normal);
|
||||||
|
}
|
||||||
|
}, lifetime());
|
||||||
|
|
||||||
const auto submit = [=] {
|
const auto submit = [=] {
|
||||||
crl::on_main(this, [=] {
|
crl::on_main(this, [=] {
|
||||||
save();
|
save();
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
connect(_field, &Ui::MaskedInputField::submitted, submit);
|
connect(_field, &Ui::MaskedInputField::submitted, submit);
|
||||||
|
connect(_field, &Ui::MaskedInputField::changed, [=] {
|
||||||
|
errorWrap->hide(anim::type::normal);
|
||||||
|
});
|
||||||
_done->addClickHandler(submit);
|
_done->addClickHandler(submit);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -323,7 +348,7 @@ void PanelEditContact::save() {
|
||||||
|
|
||||||
void PanelEditContact::save(const QString &value) {
|
void PanelEditContact::save(const QString &value) {
|
||||||
auto data = ValueMap();
|
auto data = ValueMap();
|
||||||
data.fields["value"] = value;
|
data.fields["value"].text = value;
|
||||||
_controller->saveScope(std::move(data), {});
|
_controller->saveScope(std::move(data), {});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -305,7 +305,7 @@ not_null<Ui::RpWidget*> PanelEditDocument::setupContent(
|
||||||
if (const auto i = fields.find(key); i != fields.end()) {
|
if (const auto i = fields.find(key); i != fields.end()) {
|
||||||
return i->second;
|
return i->second;
|
||||||
}
|
}
|
||||||
return QString();
|
return ValueField();
|
||||||
};
|
};
|
||||||
|
|
||||||
for (auto i = 0, count = int(_scheme.rows.size()); i != count; ++i) {
|
for (auto i = 0, count = int(_scheme.rows.size()); i != count; ++i) {
|
||||||
|
@ -316,13 +316,14 @@ not_null<Ui::RpWidget*> PanelEditDocument::setupContent(
|
||||||
if (!fields) {
|
if (!fields) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
const auto current = valueOrEmpty(*fields, row.key);
|
||||||
_details.emplace(i, inner->add(PanelDetailsRow::Create(
|
_details.emplace(i, inner->add(PanelDetailsRow::Create(
|
||||||
inner,
|
inner,
|
||||||
row.inputType,
|
row.inputType,
|
||||||
_controller,
|
_controller,
|
||||||
row.label,
|
row.label,
|
||||||
valueOrEmpty(*fields, row.key),
|
current.text,
|
||||||
QString(),
|
current.error,
|
||||||
row.lengthLimit)));
|
row.lengthLimit)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -382,7 +383,7 @@ PanelEditDocument::Result PanelEditDocument::collect() const {
|
||||||
auto &fields = (row.valueClass == Scheme::ValueClass::Fields)
|
auto &fields = (row.valueClass == Scheme::ValueClass::Fields)
|
||||||
? result.data
|
? result.data
|
||||||
: result.filesData;
|
: result.filesData;
|
||||||
fields.fields[row.key] = field->valueCurrent();
|
fields.fields[row.key].text = field->valueCurrent();
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue