mirror of https://github.com/procxx/kepka.git
Add translations support to passport.
This commit is contained in:
parent
6558a32794
commit
f76a2bc224
|
@ -1620,9 +1620,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
"lng_passport_choose_image" = "Choose scan image";
|
"lng_passport_choose_image" = "Choose scan image";
|
||||||
"lng_passport_delete_scan_undo" = "Undo";
|
"lng_passport_delete_scan_undo" = "Undo";
|
||||||
"lng_passport_scan_uploaded" = "Uploaded on {date}";
|
"lng_passport_scan_uploaded" = "Uploaded on {date}";
|
||||||
"lng_passport_first_name" = "Name";
|
"lng_passport_first_name" = "First name";
|
||||||
"lng_passport_middle_name" = "Middle name";
|
"lng_passport_middle_name" = "Middle name";
|
||||||
"lng_passport_last_name" = "Surname";
|
"lng_passport_last_name" = "Last name";
|
||||||
"lng_passport_birth_date" = "Date of birth";
|
"lng_passport_birth_date" = "Date of birth";
|
||||||
"lng_passport_gender" = "Gender";
|
"lng_passport_gender" = "Gender";
|
||||||
"lng_passport_gender_male" = "Male";
|
"lng_passport_gender_male" = "Male";
|
||||||
|
@ -1638,6 +1638,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
"lng_passport_city" = "City";
|
"lng_passport_city" = "City";
|
||||||
"lng_passport_state" = "State";
|
"lng_passport_state" = "State";
|
||||||
"lng_passport_postcode" = "Postcode";
|
"lng_passport_postcode" = "Postcode";
|
||||||
|
"lng_passport_translation" = "Translation";
|
||||||
"lng_passport_use_existing" = "USE {existing}";
|
"lng_passport_use_existing" = "USE {existing}";
|
||||||
"lng_passport_use_existing_phone" = "Use the same phone number as on Telegram.";
|
"lng_passport_use_existing_phone" = "Use the same phone number as on Telegram.";
|
||||||
"lng_passport_new_phone" = "Or enter a new phone number";
|
"lng_passport_new_phone" = "Or enter a new phone number";
|
||||||
|
|
|
@ -30,6 +30,7 @@ namespace Passport {
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
constexpr auto kDocumentScansLimit = 20;
|
constexpr auto kDocumentScansLimit = 20;
|
||||||
|
constexpr auto kTranslationScansLimit = 20;
|
||||||
constexpr auto kShortPollTimeout = TimeMs(3000);
|
constexpr auto kShortPollTimeout = TimeMs(3000);
|
||||||
constexpr auto kRememberCredentialsDelay = TimeMs(1800 * 1000);
|
constexpr auto kRememberCredentialsDelay = TimeMs(1800 * 1000);
|
||||||
|
|
||||||
|
@ -209,11 +210,11 @@ QString ValueCredentialsKey(Value::Type type) {
|
||||||
Unexpected("Type in ValueCredentialsKey.");
|
Unexpected("Type in ValueCredentialsKey.");
|
||||||
}
|
}
|
||||||
|
|
||||||
QString SpecialScanCredentialsKey(SpecialFile type) {
|
QString SpecialScanCredentialsKey(FileType type) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case SpecialFile::FrontSide: return "front_side";
|
case FileType::FrontSide: return "front_side";
|
||||||
case SpecialFile::ReverseSide: return "reverse_side";
|
case FileType::ReverseSide: return "reverse_side";
|
||||||
case SpecialFile::Selfie: return "selfie";
|
case FileType::Selfie: return "selfie";
|
||||||
}
|
}
|
||||||
Unexpected("Type in SpecialScanCredentialsKey.");
|
Unexpected("Type in SpecialScanCredentialsKey.");
|
||||||
}
|
}
|
||||||
|
@ -236,7 +237,12 @@ bool ValueChanged(not_null<const Value*> value, const ValueMap &data) {
|
||||||
};
|
};
|
||||||
|
|
||||||
auto filesCount = 0;
|
auto filesCount = 0;
|
||||||
for (const auto &scan : value->scansInEdit) {
|
for (const auto &scan : value->filesInEdit(FileType::Scan)) {
|
||||||
|
if (FileChanged(scan)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (const auto &scan : value->filesInEdit(FileType::Translation)) {
|
||||||
if (FileChanged(scan)) {
|
if (FileChanged(scan)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -277,9 +283,11 @@ FormRequest::FormRequest(
|
||||||
|
|
||||||
EditFile::EditFile(
|
EditFile::EditFile(
|
||||||
not_null<const Value*> value,
|
not_null<const Value*> value,
|
||||||
|
FileType type,
|
||||||
const File &fields,
|
const File &fields,
|
||||||
std::unique_ptr<UploadScanData> &&uploadData)
|
std::unique_ptr<UploadScanData> &&uploadData)
|
||||||
: value(value)
|
: value(value)
|
||||||
|
, type(type)
|
||||||
, 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)) {
|
||||||
|
@ -326,17 +334,31 @@ RequestedValue::RequestedValue(Value::Type type) : type(type) {
|
||||||
Value::Value(Type type) : type(type) {
|
Value::Value(Type type) : type(type) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Value::requiresSpecialScan(SpecialFile type) const {
|
bool Value::requiresScan(FileType type) const {
|
||||||
|
if (type == FileType::Scan) {
|
||||||
|
return (this->type == Type::UtilityBill)
|
||||||
|
|| (this->type == Type::BankStatement)
|
||||||
|
|| (this->type == Type::RentalAgreement)
|
||||||
|
|| (this->type == Type::PassportRegistration)
|
||||||
|
|| (this->type == Type::TemporaryRegistration);
|
||||||
|
} else if (type == FileType::Translation) {
|
||||||
|
return translationRequired;
|
||||||
|
} else {
|
||||||
|
return requiresSpecialScan(type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Value::requiresSpecialScan(FileType type) const {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case SpecialFile::FrontSide:
|
case FileType::FrontSide:
|
||||||
return (this->type == Type::Passport)
|
return (this->type == Type::Passport)
|
||||||
|| (this->type == Type::DriverLicense)
|
|| (this->type == Type::DriverLicense)
|
||||||
|| (this->type == Type::IdentityCard)
|
|| (this->type == Type::IdentityCard)
|
||||||
|| (this->type == Type::InternalPassport);
|
|| (this->type == Type::InternalPassport);
|
||||||
case SpecialFile::ReverseSide:
|
case FileType::ReverseSide:
|
||||||
return (this->type == Type::DriverLicense)
|
return (this->type == Type::DriverLicense)
|
||||||
|| (this->type == Type::IdentityCard);
|
|| (this->type == Type::IdentityCard);
|
||||||
case SpecialFile::Selfie:
|
case FileType::Selfie:
|
||||||
return selfieRequired;
|
return selfieRequired;
|
||||||
}
|
}
|
||||||
Unexpected("Special scan type in requiresSpecialScan.");
|
Unexpected("Special scan type in requiresSpecialScan.");
|
||||||
|
@ -357,15 +379,15 @@ void Value::fillDataFrom(Value &&other) {
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Value::scansAreFilled() const {
|
bool Value::scansAreFilled() const {
|
||||||
if (!requiresSpecialScan(SpecialFile::FrontSide) && scans.empty()) {
|
if (requiresScan(FileType::Translation) && _translations.empty()) {
|
||||||
return false;
|
return false;
|
||||||
} else if (translationRequired && translations.empty()) {
|
} else if (requiresScan(FileType::Scan) && _scans.empty()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const auto types = {
|
const auto types = {
|
||||||
SpecialFile::FrontSide,
|
FileType::FrontSide,
|
||||||
SpecialFile::ReverseSide,
|
FileType::ReverseSide,
|
||||||
SpecialFile::Selfie
|
FileType::Selfie
|
||||||
};
|
};
|
||||||
for (const auto type : types) {
|
for (const auto type : types) {
|
||||||
if (requiresSpecialScan(type)
|
if (requiresSpecialScan(type)
|
||||||
|
@ -376,6 +398,163 @@ bool Value::scansAreFilled() const {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Value::saveInEdit() {
|
||||||
|
const auto saveList = [&](FileType type) {
|
||||||
|
filesInEdit(type) = ranges::view::all(
|
||||||
|
files(type)
|
||||||
|
) | ranges::view::transform([=](const File &file) {
|
||||||
|
return EditFile(this, type, file, nullptr);
|
||||||
|
}) | ranges::to_vector;
|
||||||
|
};
|
||||||
|
saveList(FileType::Scan);
|
||||||
|
saveList(FileType::Translation);
|
||||||
|
|
||||||
|
specialScansInEdit.clear();
|
||||||
|
for (const auto &[type, scan] : specialScans) {
|
||||||
|
specialScansInEdit.emplace(type, EditFile(
|
||||||
|
this,
|
||||||
|
type,
|
||||||
|
scan,
|
||||||
|
nullptr));
|
||||||
|
}
|
||||||
|
data.parsedInEdit = data.parsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Value::clearEditData() {
|
||||||
|
filesInEdit(FileType::Scan).clear();
|
||||||
|
filesInEdit(FileType::Translation).clear();
|
||||||
|
specialScansInEdit.clear();
|
||||||
|
data.encryptedSecretInEdit.clear();
|
||||||
|
data.hashInEdit.clear();
|
||||||
|
data.parsedInEdit = ValueMap();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Value::uploadingScan() const {
|
||||||
|
const auto uploading = [](const EditFile &file) {
|
||||||
|
return file.uploadData
|
||||||
|
&& file.uploadData->fullId
|
||||||
|
&& !file.deleted;
|
||||||
|
};
|
||||||
|
const auto uploadingInList = [&](FileType type) {
|
||||||
|
const auto &list = filesInEdit(type);
|
||||||
|
return ranges::find_if(list, uploading) != end(list);
|
||||||
|
};
|
||||||
|
if (uploadingInList(FileType::Scan)
|
||||||
|
|| uploadingInList(FileType::Translation)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (ranges::find_if(specialScansInEdit, [&](const auto &pair) {
|
||||||
|
return uploading(pair.second);
|
||||||
|
}) != end(specialScansInEdit)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Value::saving() const {
|
||||||
|
return (saveRequestId != 0)
|
||||||
|
|| (verification.requestId != 0)
|
||||||
|
|| (verification.codeLength != 0)
|
||||||
|
|| uploadingScan();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<File> &Value::files(FileType type) {
|
||||||
|
switch (type) {
|
||||||
|
case FileType::Scan: return _scans;
|
||||||
|
case FileType::Translation: return _translations;
|
||||||
|
}
|
||||||
|
Unexpected("Type in Value::files().");
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::vector<File> &Value::files(FileType type) const {
|
||||||
|
switch (type) {
|
||||||
|
case FileType::Scan: return _scans;
|
||||||
|
case FileType::Translation: return _translations;
|
||||||
|
}
|
||||||
|
Unexpected("Type in Value::files() const.");
|
||||||
|
}
|
||||||
|
|
||||||
|
QString &Value::fileMissingError(FileType type) {
|
||||||
|
switch (type) {
|
||||||
|
case FileType::Scan: return _scanMissingError;
|
||||||
|
case FileType::Translation: return _translationMissingError;
|
||||||
|
}
|
||||||
|
Unexpected("Type in Value::fileMissingError().");
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString &Value::fileMissingError(FileType type) const {
|
||||||
|
switch (type) {
|
||||||
|
case FileType::Scan: return _scanMissingError;
|
||||||
|
case FileType::Translation: return _translationMissingError;
|
||||||
|
}
|
||||||
|
Unexpected("Type in Value::fileMissingError() const.");
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<EditFile> &Value::filesInEdit(FileType type) {
|
||||||
|
switch (type) {
|
||||||
|
case FileType::Scan: return _scansInEdit;
|
||||||
|
case FileType::Translation: return _translationsInEdit;
|
||||||
|
}
|
||||||
|
Unexpected("Type in Value::filesInEdit().");
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::vector<EditFile> &Value::filesInEdit(FileType type) const {
|
||||||
|
switch (type) {
|
||||||
|
case FileType::Scan: return _scansInEdit;
|
||||||
|
case FileType::Translation: return _translationsInEdit;
|
||||||
|
}
|
||||||
|
Unexpected("Type in Value::filesInEdit() const.");
|
||||||
|
}
|
||||||
|
|
||||||
|
EditFile &Value::fileInEdit(FileType type, base::optional<int> fileIndex) {
|
||||||
|
switch (type) {
|
||||||
|
case FileType::Scan:
|
||||||
|
case FileType::Translation: {
|
||||||
|
auto &list = filesInEdit(type);
|
||||||
|
Assert(fileIndex.has_value());
|
||||||
|
Assert(*fileIndex >= 0 && *fileIndex < list.size());
|
||||||
|
return list[*fileIndex];
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
const auto i = specialScansInEdit.find(type);
|
||||||
|
Assert(!fileIndex.has_value());
|
||||||
|
Assert(i != end(specialScansInEdit));
|
||||||
|
return i->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
const EditFile &Value::fileInEdit(
|
||||||
|
FileType type,
|
||||||
|
base::optional<int> fileIndex) const {
|
||||||
|
switch (type) {
|
||||||
|
case FileType::Scan:
|
||||||
|
case FileType::Translation: {
|
||||||
|
auto &list = filesInEdit(type);
|
||||||
|
Assert(fileIndex.has_value());
|
||||||
|
Assert(*fileIndex >= 0 && *fileIndex < list.size());
|
||||||
|
return list[*fileIndex];
|
||||||
|
} break;
|
||||||
|
}
|
||||||
|
const auto i = specialScansInEdit.find(type);
|
||||||
|
Assert(!fileIndex.has_value());
|
||||||
|
Assert(i != end(specialScansInEdit));
|
||||||
|
return i->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<EditFile> Value::takeAllFilesInEdit() {
|
||||||
|
auto result = base::take(filesInEdit(FileType::Scan));
|
||||||
|
auto &translation = filesInEdit(FileType::Translation);
|
||||||
|
auto &special = specialScansInEdit;
|
||||||
|
result.reserve(result.size() + translation.size() + special.size());
|
||||||
|
|
||||||
|
for (auto &scan : base::take(translation)) {
|
||||||
|
result.push_back(std::move(scan));
|
||||||
|
}
|
||||||
|
for (auto &[type, scan] : base::take(special)) {
|
||||||
|
result.push_back(std::move(scan));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
FormController::FormController(
|
FormController::FormController(
|
||||||
not_null<Window::Controller*> controller,
|
not_null<Window::Controller*> controller,
|
||||||
const FormRequest &request)
|
const FormRequest &request)
|
||||||
|
@ -417,12 +596,20 @@ auto FormController::prepareFinalData() -> FinalData {
|
||||||
{ "secret", value->data.secret }
|
{ "secret", value->data.secret }
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
if (!value->scans.empty()) {
|
const auto addList = [&](
|
||||||
auto files = QJsonArray();
|
const QString &key,
|
||||||
for (const auto &scan : value->scans) {
|
const std::vector<File> &list) {
|
||||||
files.append(GetJSONFromFile(scan));
|
if (!list.empty()) {
|
||||||
|
auto files = QJsonArray();
|
||||||
|
for (const auto &scan : list) {
|
||||||
|
files.append(GetJSONFromFile(scan));
|
||||||
|
}
|
||||||
|
object.insert(key, files);
|
||||||
}
|
}
|
||||||
object.insert("files", files);
|
};
|
||||||
|
addList("files", value->files(FileType::Scan));
|
||||||
|
if (value->translationRequired) {
|
||||||
|
addList("translation", value->files(FileType::Translation));
|
||||||
}
|
}
|
||||||
for (const auto &[type, scan] : value->specialScans) {
|
for (const auto &[type, scan] : value->specialScans) {
|
||||||
if (value->requiresSpecialScan(type)) {
|
if (value->requiresSpecialScan(type)) {
|
||||||
|
@ -894,26 +1081,21 @@ void FormController::fillErrors() {
|
||||||
LOG(("API Error: Value not found for error type."));
|
LOG(("API Error: Value not found for error type."));
|
||||||
return nullptr;
|
return nullptr;
|
||||||
};
|
};
|
||||||
using List = std::vector<File>;
|
const auto scan = [&](
|
||||||
const auto findScan = [&](List &list, bytes::const_span hash) -> File* {
|
Value &value,
|
||||||
|
FileType type,
|
||||||
|
bytes::const_span hash) -> File* {
|
||||||
|
auto &list = value.files(type);
|
||||||
const auto i = ranges::find_if(list, [&](const File &scan) {
|
const auto i = ranges::find_if(list, [&](const File &scan) {
|
||||||
return !bytes::compare(hash, scan.hash);
|
return !bytes::compare(hash, scan.hash);
|
||||||
});
|
});
|
||||||
if (i != end(list)) {
|
if (i != end(list)) {
|
||||||
return &*i;
|
return &*i;
|
||||||
}
|
}
|
||||||
return nullptr;
|
|
||||||
};
|
|
||||||
const auto scan = [&](Value &value, bytes::const_span hash) -> File* {
|
|
||||||
if (const auto translation = findScan(value.translations, hash)) {
|
|
||||||
return translation;
|
|
||||||
} else if (const auto scan = findScan(value.scans, hash)) {
|
|
||||||
return scan;
|
|
||||||
}
|
|
||||||
LOG(("API Error: File not found for error value."));
|
LOG(("API Error: File not found for error value."));
|
||||||
return nullptr;
|
return nullptr;
|
||||||
};
|
};
|
||||||
const auto setSpecialScanError = [&](SpecialFile type, auto &&data) {
|
const auto setSpecialScanError = [&](FileType type, auto &&data) {
|
||||||
if (const auto value = find(data.vtype)) {
|
if (const auto value = find(data.vtype)) {
|
||||||
if (value->requiresSpecialScan(type)) {
|
if (value->requiresSpecialScan(type)) {
|
||||||
const auto i = value->specialScans.find(type);
|
const auto i = value->specialScans.find(type);
|
||||||
|
@ -945,37 +1127,40 @@ void FormController::fillErrors() {
|
||||||
}, [&](const MTPDsecureValueErrorFile &data) {
|
}, [&](const MTPDsecureValueErrorFile &data) {
|
||||||
const auto hash = bytes::make_span(data.vfile_hash.v);
|
const auto hash = bytes::make_span(data.vfile_hash.v);
|
||||||
if (const auto value = find(data.vtype)) {
|
if (const auto value = find(data.vtype)) {
|
||||||
if (const auto file = scan(*value, hash)) {
|
if (const auto file = scan(*value, FileType::Scan, hash)) {
|
||||||
file->error = qs(data.vtext);
|
if (value->requiresScan(FileType::Scan)) {
|
||||||
|
file->error = qs(data.vtext);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [&](const MTPDsecureValueErrorFiles &data) {
|
}, [&](const MTPDsecureValueErrorFiles &data) {
|
||||||
if (const auto value = find(data.vtype)) {
|
if (const auto value = find(data.vtype)) {
|
||||||
if (CanRequireScans(value->type)) {
|
if (value->requiresScan(FileType::Scan)) {
|
||||||
value->scanMissingError = qs(data.vtext);
|
value->fileMissingError(FileType::Scan)
|
||||||
|
= qs(data.vtext);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [&](const MTPDsecureValueErrorTranslationFile &data) {
|
}, [&](const MTPDsecureValueErrorTranslationFile &data) {
|
||||||
const auto hash = bytes::make_span(data.vfile_hash.v);
|
const auto hash = bytes::make_span(data.vfile_hash.v);
|
||||||
if (const auto value = find(data.vtype)) {
|
if (const auto value = find(data.vtype)) {
|
||||||
if (value->translationRequired) {
|
const auto file = scan(*value, FileType::Translation, hash);
|
||||||
if (const auto file = scan(*value, hash)) {
|
if (file && value->requiresScan(FileType::Translation)) {
|
||||||
file->error = qs(data.vtext);
|
file->error = qs(data.vtext);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [&](const MTPDsecureValueErrorTranslationFiles &data) {
|
}, [&](const MTPDsecureValueErrorTranslationFiles &data) {
|
||||||
if (const auto value = find(data.vtype)) {
|
if (const auto value = find(data.vtype)) {
|
||||||
if (value->translationRequired) {
|
if (value->requiresScan(FileType::Translation)) {
|
||||||
value->translationMissingError = qs(data.vtext);
|
value->fileMissingError(FileType::Translation)
|
||||||
|
= qs(data.vtext);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [&](const MTPDsecureValueErrorFrontSide &data) {
|
}, [&](const MTPDsecureValueErrorFrontSide &data) {
|
||||||
setSpecialScanError(SpecialFile::FrontSide, data);
|
setSpecialScanError(FileType::FrontSide, data);
|
||||||
}, [&](const MTPDsecureValueErrorReverseSide &data) {
|
}, [&](const MTPDsecureValueErrorReverseSide &data) {
|
||||||
setSpecialScanError(SpecialFile::ReverseSide, data);
|
setSpecialScanError(FileType::ReverseSide, data);
|
||||||
}, [&](const MTPDsecureValueErrorSelfie &data) {
|
}, [&](const MTPDsecureValueErrorSelfie &data) {
|
||||||
setSpecialScanError(SpecialFile::Selfie, data);
|
setSpecialScanError(FileType::Selfie, data);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1027,13 +1212,18 @@ bool FormController::validateValueSecrets(Value &value) const {
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
for (auto &scan : value.scans) {
|
for (auto &scan : value.files(FileType::Scan)) {
|
||||||
if (!validateFileSecret(scan)) {
|
if (!validateFileSecret(scan)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (auto &[type, file] : value.specialScans) {
|
for (auto &scan : value.files(FileType::Translation)) {
|
||||||
if (!validateFileSecret(file)) {
|
if (!validateFileSecret(scan)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (auto &[type, scan] : value.specialScans) {
|
||||||
|
if (!validateFileSecret(scan)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1054,73 +1244,51 @@ const PasswordSettings &FormController::passwordSettings() const {
|
||||||
|
|
||||||
void FormController::uploadScan(
|
void FormController::uploadScan(
|
||||||
not_null<const Value*> value,
|
not_null<const Value*> value,
|
||||||
|
FileType type,
|
||||||
QByteArray &&content) {
|
QByteArray &&content) {
|
||||||
if (!canAddScan(value)) {
|
if (!canAddScan(value, type)) {
|
||||||
_view->showToast(lang(lng_passport_scans_limit_reached));
|
_view->showToast(lang(lng_passport_scans_limit_reached));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const auto nonconst = findValue(value);
|
const auto nonconst = findValue(value);
|
||||||
auto scanIndex = int(nonconst->scansInEdit.size());
|
const auto fileIndex = [&]() -> base::optional<int> {
|
||||||
nonconst->scansInEdit.emplace_back(
|
auto scanInEdit = EditFile{ nonconst, type, File(), nullptr };
|
||||||
nonconst,
|
if (type == FileType::Scan || type == FileType::Translation) {
|
||||||
File(),
|
auto &list = nonconst->filesInEdit(type);
|
||||||
nullptr);
|
auto scanIndex = int(list.size());
|
||||||
auto &scan = nonconst->scansInEdit.back();
|
list.push_back(std::move(scanInEdit));
|
||||||
|
return list.size() - 1;
|
||||||
|
}
|
||||||
|
auto i = nonconst->specialScansInEdit.find(type);
|
||||||
|
if (i != nonconst->specialScansInEdit.end()) {
|
||||||
|
i->second = std::move(scanInEdit);
|
||||||
|
} else {
|
||||||
|
i = nonconst->specialScansInEdit.emplace(
|
||||||
|
type,
|
||||||
|
std::move(scanInEdit)).first;
|
||||||
|
}
|
||||||
|
return base::none;
|
||||||
|
}();
|
||||||
|
auto &scan = nonconst->fileInEdit(type, fileIndex);
|
||||||
encryptFile(scan, std::move(content), [=](UploadScanData &&result) {
|
encryptFile(scan, std::move(content), [=](UploadScanData &&result) {
|
||||||
Expects(scanIndex >= 0 && scanIndex < nonconst->scansInEdit.size());
|
|
||||||
|
|
||||||
uploadEncryptedFile(
|
uploadEncryptedFile(
|
||||||
nonconst->scansInEdit[scanIndex],
|
nonconst->fileInEdit(type, fileIndex),
|
||||||
std::move(result));
|
std::move(result));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void FormController::deleteScan(
|
void FormController::deleteScan(
|
||||||
not_null<const Value*> value,
|
not_null<const Value*> value,
|
||||||
int scanIndex) {
|
FileType type,
|
||||||
scanDeleteRestore(value, scanIndex, true);
|
base::optional<int> fileIndex) {
|
||||||
|
scanDeleteRestore(value, type, fileIndex, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FormController::restoreScan(
|
void FormController::restoreScan(
|
||||||
not_null<const Value*> value,
|
not_null<const Value*> value,
|
||||||
int scanIndex) {
|
FileType type,
|
||||||
scanDeleteRestore(value, scanIndex, false);
|
base::optional<int> fileIndex) {
|
||||||
}
|
scanDeleteRestore(value, type, fileIndex, false);
|
||||||
|
|
||||||
void FormController::uploadSpecialScan(
|
|
||||||
not_null<const Value*> value,
|
|
||||||
SpecialFile type,
|
|
||||||
QByteArray &&content) {
|
|
||||||
const auto nonconst = findValue(value);
|
|
||||||
auto scanInEdit = EditFile{ nonconst, File(), nullptr };
|
|
||||||
auto i = nonconst->specialScansInEdit.find(type);
|
|
||||||
if (i != nonconst->specialScansInEdit.end()) {
|
|
||||||
i->second = std::move(scanInEdit);
|
|
||||||
} else {
|
|
||||||
i = nonconst->specialScansInEdit.emplace(
|
|
||||||
type,
|
|
||||||
std::move(scanInEdit)).first;
|
|
||||||
}
|
|
||||||
auto &file = i->second;
|
|
||||||
encryptFile(file, std::move(content), [=](UploadScanData &&result) {
|
|
||||||
const auto i = nonconst->specialScansInEdit.find(type);
|
|
||||||
Assert(i != nonconst->specialScansInEdit.end());
|
|
||||||
uploadEncryptedFile(
|
|
||||||
i->second,
|
|
||||||
std::move(result));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void FormController::deleteSpecialScan(
|
|
||||||
not_null<const Value*> value,
|
|
||||||
SpecialFile type) {
|
|
||||||
specialScanDeleteRestore(value, type, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
void FormController::restoreSpecialScan(
|
|
||||||
not_null<const Value*> value,
|
|
||||||
SpecialFile type) {
|
|
||||||
specialScanDeleteRestore(value, type, false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void FormController::prepareFile(
|
void FormController::prepareFile(
|
||||||
|
@ -1173,14 +1341,13 @@ void FormController::encryptFile(
|
||||||
|
|
||||||
void FormController::scanDeleteRestore(
|
void FormController::scanDeleteRestore(
|
||||||
not_null<const Value*> value,
|
not_null<const Value*> value,
|
||||||
int scanIndex,
|
FileType type,
|
||||||
|
base::optional<int> fileIndex,
|
||||||
bool deleted) {
|
bool deleted) {
|
||||||
Expects(scanIndex >= 0 && scanIndex < value->scansInEdit.size());
|
|
||||||
|
|
||||||
const auto nonconst = findValue(value);
|
const auto nonconst = findValue(value);
|
||||||
auto &scan = nonconst->scansInEdit[scanIndex];
|
auto &scan = nonconst->fileInEdit(type, fileIndex);
|
||||||
if (scan.deleted && !deleted) {
|
if (scan.deleted && !deleted) {
|
||||||
if (!canAddScan(value)) {
|
if (!canAddScan(value, type)) {
|
||||||
_view->showToast(lang(lng_passport_scans_limit_reached));
|
_view->showToast(lang(lng_passport_scans_limit_reached));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -1189,23 +1356,21 @@ void FormController::scanDeleteRestore(
|
||||||
_scanUpdated.fire(&scan);
|
_scanUpdated.fire(&scan);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FormController::specialScanDeleteRestore(
|
bool FormController::canAddScan(
|
||||||
not_null<const Value*> value,
|
not_null<const Value*> value,
|
||||||
SpecialFile type,
|
FileType type) const {
|
||||||
bool deleted) {
|
const auto limit = (type == FileType::Scan)
|
||||||
const auto nonconst = findValue(value);
|
? kDocumentScansLimit
|
||||||
const auto i = nonconst->specialScansInEdit.find(type);
|
: (type == FileType::Translation)
|
||||||
Assert(i != nonconst->specialScansInEdit.end());
|
? kTranslationScansLimit
|
||||||
auto &scan = i->second;
|
: -1;
|
||||||
scan.deleted = deleted;
|
if (limit < 0) {
|
||||||
_scanUpdated.fire(&scan);
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FormController::canAddScan(not_null<const Value*> value) const {
|
|
||||||
const auto scansCount = ranges::count_if(
|
const auto scansCount = ranges::count_if(
|
||||||
value->scansInEdit,
|
value->filesInEdit(type),
|
||||||
[](const EditFile &scan) { return !scan.deleted; });
|
[](const EditFile &scan) { return !scan.deleted; });
|
||||||
return (scansCount < kDocumentScansLimit);
|
return (scansCount < limit);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FormController::subscribeToUploader() {
|
void FormController::subscribeToUploader() {
|
||||||
|
@ -1411,14 +1576,14 @@ not_null<Value*> FormController::findValue(not_null<const Value*> value) {
|
||||||
void FormController::startValueEdit(not_null<const Value*> value) {
|
void FormController::startValueEdit(not_null<const Value*> value) {
|
||||||
const auto nonconst = findValue(value);
|
const auto nonconst = findValue(value);
|
||||||
++nonconst->editScreens;
|
++nonconst->editScreens;
|
||||||
if (savingValue(nonconst)) {
|
if (nonconst->saving()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
for (auto &scan : nonconst->scans) {
|
for (auto &scan : nonconst->files(FileType::Scan)) {
|
||||||
loadFile(scan);
|
loadFile(scan);
|
||||||
}
|
}
|
||||||
if (nonconst->translationRequired) {
|
if (nonconst->translationRequired) {
|
||||||
for (auto &scan : nonconst->translations) {
|
for (auto &scan : nonconst->files(FileType::Translation)) {
|
||||||
loadFile(scan);
|
loadFile(scan);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1427,27 +1592,7 @@ void FormController::startValueEdit(not_null<const Value*> value) {
|
||||||
loadFile(scan);
|
loadFile(scan);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
nonconst->scansInEdit = ranges::view::all(
|
nonconst->saveInEdit();
|
||||||
nonconst->scans
|
|
||||||
) | ranges::view::transform([=](const File &file) {
|
|
||||||
return EditFile(nonconst, file, nullptr);
|
|
||||||
}) | ranges::to_vector;
|
|
||||||
|
|
||||||
nonconst->translationsInEdit = ranges::view::all(
|
|
||||||
nonconst->translations
|
|
||||||
) | ranges::view::transform([=](const File &file) {
|
|
||||||
return EditFile(nonconst, file, nullptr);
|
|
||||||
}) | ranges::to_vector;
|
|
||||||
|
|
||||||
nonconst->specialScansInEdit.clear();
|
|
||||||
for (const auto &[type, scan] : nonconst->specialScans) {
|
|
||||||
nonconst->specialScansInEdit.emplace(type, EditFile(
|
|
||||||
nonconst,
|
|
||||||
scan,
|
|
||||||
nullptr));
|
|
||||||
}
|
|
||||||
|
|
||||||
nonconst->data.parsedInEdit = nonconst->data.parsed;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void FormController::loadFile(File &file) {
|
void FormController::loadFile(File &file) {
|
||||||
|
@ -1531,41 +1676,6 @@ void FormController::fileLoadFail(FileKey key) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FormController::savingValue(not_null<const Value*> value) const {
|
|
||||||
return (value->saveRequestId != 0)
|
|
||||||
|| (value->verification.requestId != 0)
|
|
||||||
|| (value->verification.codeLength != 0)
|
|
||||||
|| uploadingScan(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool FormController::uploadingScan(not_null<const Value*> value) const {
|
|
||||||
const auto uploading = [](const EditFile &file) {
|
|
||||||
return file.uploadData
|
|
||||||
&& file.uploadData->fullId
|
|
||||||
&& !file.deleted;
|
|
||||||
};
|
|
||||||
if (ranges::find_if(value->scansInEdit, uploading)
|
|
||||||
!= end(value->scansInEdit)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
if (ranges::find_if(value->specialScansInEdit, [&](const auto &pair) {
|
|
||||||
return uploading(pair.second);
|
|
||||||
}) != end(value->specialScansInEdit)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
for (const auto &scan : value->scansInEdit) {
|
|
||||||
if (uploading(scan)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (const auto &[type, scan] : value->specialScansInEdit) {
|
|
||||||
if (uploading(scan)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void FormController::cancelValueEdit(not_null<const Value*> value) {
|
void FormController::cancelValueEdit(not_null<const Value*> value) {
|
||||||
Expects(value->editScreens > 0);
|
Expects(value->editScreens > 0);
|
||||||
|
|
||||||
|
@ -1575,7 +1685,7 @@ void FormController::cancelValueEdit(not_null<const Value*> value) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void FormController::valueEditFailed(not_null<Value*> value) {
|
void FormController::valueEditFailed(not_null<Value*> value) {
|
||||||
Expects(!savingValue(value));
|
Expects(!value->saving());
|
||||||
|
|
||||||
if (value->editScreens == 0) {
|
if (value->editScreens == 0) {
|
||||||
clearValueEdit(value);
|
clearValueEdit(value);
|
||||||
|
@ -1583,20 +1693,16 @@ void FormController::valueEditFailed(not_null<Value*> value) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void FormController::clearValueEdit(not_null<Value*> value) {
|
void FormController::clearValueEdit(not_null<Value*> value) {
|
||||||
if (savingValue(value)) {
|
if (value->saving()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
value->scansInEdit.clear();
|
value->clearEditData();
|
||||||
value->specialScansInEdit.clear();
|
|
||||||
value->data.encryptedSecretInEdit.clear();
|
|
||||||
value->data.hashInEdit.clear();
|
|
||||||
value->data.parsedInEdit = ValueMap();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void FormController::cancelValueVerification(not_null<const Value*> value) {
|
void FormController::cancelValueVerification(not_null<const Value*> value) {
|
||||||
const auto nonconst = findValue(value);
|
const auto nonconst = findValue(value);
|
||||||
clearValueVerification(nonconst);
|
clearValueVerification(nonconst);
|
||||||
if (!savingValue(nonconst)) {
|
if (!nonconst->saving()) {
|
||||||
valueEditFailed(nonconst);
|
valueEditFailed(nonconst);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1619,7 +1725,7 @@ bool FormController::isEncryptedValue(Value::Type type) const {
|
||||||
void FormController::saveValueEdit(
|
void FormController::saveValueEdit(
|
||||||
not_null<const Value*> value,
|
not_null<const Value*> value,
|
||||||
ValueMap &&data) {
|
ValueMap &&data) {
|
||||||
if (savingValue(value) || _submitRequestId) {
|
if (value->saving() || _submitRequestId) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1630,11 +1736,7 @@ void FormController::saveValueEdit(
|
||||||
if (!ValueChanged(nonconst, data)) {
|
if (!ValueChanged(nonconst, data)) {
|
||||||
nonconst->saveRequestId = -1;
|
nonconst->saveRequestId = -1;
|
||||||
crl::on_main(this, [=] {
|
crl::on_main(this, [=] {
|
||||||
base::take(nonconst->scansInEdit);
|
nonconst->clearEditData();
|
||||||
base::take(nonconst->specialScansInEdit);
|
|
||||||
base::take(nonconst->data.encryptedSecretInEdit);
|
|
||||||
base::take(nonconst->data.hashInEdit);
|
|
||||||
base::take(nonconst->data.parsedInEdit);
|
|
||||||
nonconst->saveRequestId = 0;
|
nonconst->saveRequestId = 0;
|
||||||
_valueSaveFinished.fire_copy(nonconst);
|
_valueSaveFinished.fire_copy(nonconst);
|
||||||
});
|
});
|
||||||
|
@ -1650,7 +1752,7 @@ void FormController::saveValueEdit(
|
||||||
}
|
}
|
||||||
|
|
||||||
void FormController::deleteValueEdit(not_null<const Value*> value) {
|
void FormController::deleteValueEdit(not_null<const Value*> value) {
|
||||||
if (savingValue(value) || _submitRequestId) {
|
if (value->saving() || _submitRequestId) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1689,24 +1791,21 @@ void FormController::saveEncryptedValue(not_null<Value*> value) {
|
||||||
MTP_long(file.fields.id),
|
MTP_long(file.fields.id),
|
||||||
MTP_long(file.fields.accessHash));
|
MTP_long(file.fields.accessHash));
|
||||||
};
|
};
|
||||||
|
const auto wrapList = [&](not_null<const Value*> value, FileType type) {
|
||||||
auto files = QVector<MTPInputSecureFile>();
|
const auto &list = value->filesInEdit(type);
|
||||||
files.reserve(value->scansInEdit.size());
|
auto result = QVector<MTPInputSecureFile>();
|
||||||
for (const auto &scan : value->scansInEdit) {
|
result.reserve(list.size());
|
||||||
if (scan.deleted) {
|
for (const auto &scan : value->filesInEdit(type)) {
|
||||||
continue;
|
if (scan.deleted) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
result.push_back(wrapFile(scan));
|
||||||
}
|
}
|
||||||
files.push_back(wrapFile(scan));
|
return result;
|
||||||
}
|
};
|
||||||
|
|
||||||
auto translations = QVector<MTPInputSecureFile>();
|
const auto files = wrapList(value, FileType::Scan);
|
||||||
translations.reserve(value->translationsInEdit.size());
|
const auto translations = wrapList(value, FileType::Translation);
|
||||||
for (const auto &scan : value->translationsInEdit) {
|
|
||||||
if (scan.deleted) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
translations.push_back(wrapFile(scan));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value->data.secret.empty()) {
|
if (value->data.secret.empty()) {
|
||||||
value->data.secret = GenerateSecretBytes();
|
value->data.secret = GenerateSecretBytes();
|
||||||
|
@ -1720,37 +1819,37 @@ void FormController::saveEncryptedValue(not_null<Value*> value) {
|
||||||
_secret,
|
_secret,
|
||||||
value->data.hashInEdit);
|
value->data.hashInEdit);
|
||||||
|
|
||||||
const auto hasSpecialFile = [&](SpecialFile type) {
|
const auto hasSpecialFile = [&](FileType type) {
|
||||||
const auto i = value->specialScansInEdit.find(type);
|
const auto i = value->specialScansInEdit.find(type);
|
||||||
return (i != end(value->specialScansInEdit) && !i->second.deleted);
|
return (i != end(value->specialScansInEdit) && !i->second.deleted);
|
||||||
};
|
};
|
||||||
const auto specialFile = [&](SpecialFile type) {
|
const auto specialFile = [&](FileType type) {
|
||||||
const auto i = value->specialScansInEdit.find(type);
|
const auto i = value->specialScansInEdit.find(type);
|
||||||
return (i != end(value->specialScansInEdit) && !i->second.deleted)
|
return (i != end(value->specialScansInEdit) && !i->second.deleted)
|
||||||
? wrapFile(i->second)
|
? wrapFile(i->second)
|
||||||
: MTPInputSecureFile();
|
: MTPInputSecureFile();
|
||||||
};
|
};
|
||||||
const auto frontSide = specialFile(SpecialFile::FrontSide);
|
const auto frontSide = specialFile(FileType::FrontSide);
|
||||||
const auto reverseSide = specialFile(SpecialFile::ReverseSide);
|
const auto reverseSide = specialFile(FileType::ReverseSide);
|
||||||
const auto selfie = specialFile(SpecialFile::Selfie);
|
const auto selfie = specialFile(FileType::Selfie);
|
||||||
|
|
||||||
const auto type = ConvertType(value->type);
|
const auto type = ConvertType(value->type);
|
||||||
const auto flags = (value->data.parsedInEdit.fields.empty()
|
const auto flags = (value->data.parsedInEdit.fields.empty()
|
||||||
? MTPDinputSecureValue::Flag(0)
|
? MTPDinputSecureValue::Flag(0)
|
||||||
: MTPDinputSecureValue::Flag::f_data)
|
: MTPDinputSecureValue::Flag::f_data)
|
||||||
| (hasSpecialFile(SpecialFile::FrontSide)
|
| (hasSpecialFile(FileType::FrontSide)
|
||||||
? MTPDinputSecureValue::Flag::f_front_side
|
? MTPDinputSecureValue::Flag::f_front_side
|
||||||
: MTPDinputSecureValue::Flag(0))
|
: MTPDinputSecureValue::Flag(0))
|
||||||
| (hasSpecialFile(SpecialFile::ReverseSide)
|
| (hasSpecialFile(FileType::ReverseSide)
|
||||||
? MTPDinputSecureValue::Flag::f_reverse_side
|
? MTPDinputSecureValue::Flag::f_reverse_side
|
||||||
: MTPDinputSecureValue::Flag(0))
|
: MTPDinputSecureValue::Flag(0))
|
||||||
| (hasSpecialFile(SpecialFile::Selfie)
|
| (hasSpecialFile(FileType::Selfie)
|
||||||
? MTPDinputSecureValue::Flag::f_selfie
|
? MTPDinputSecureValue::Flag::f_selfie
|
||||||
: MTPDinputSecureValue::Flag(0))
|
: MTPDinputSecureValue::Flag(0))
|
||||||
| (value->translationsInEdit.empty()
|
| (translations.empty()
|
||||||
? MTPDinputSecureValue::Flag(0)
|
? MTPDinputSecureValue::Flag(0)
|
||||||
: MTPDinputSecureValue::Flag::f_translation)
|
: MTPDinputSecureValue::Flag::f_translation)
|
||||||
| (value->scansInEdit.empty()
|
| (files.empty()
|
||||||
? MTPDinputSecureValue::Flag(0)
|
? MTPDinputSecureValue::Flag(0)
|
||||||
: MTPDinputSecureValue::Flag::f_files);
|
: MTPDinputSecureValue::Flag::f_files);
|
||||||
Assert(flags != MTPDinputSecureValue::Flags(0));
|
Assert(flags != MTPDinputSecureValue::Flags(0));
|
||||||
|
@ -1809,10 +1908,7 @@ void FormController::sendSaveRequest(
|
||||||
data,
|
data,
|
||||||
MTP_long(_secretId)
|
MTP_long(_secretId)
|
||||||
)).done([=](const MTPSecureValue &result) {
|
)).done([=](const MTPSecureValue &result) {
|
||||||
auto scansInEdit = base::take(value->scansInEdit);
|
auto scansInEdit = value->takeAllFilesInEdit();
|
||||||
for (auto &[type, scan] : base::take(value->specialScansInEdit)) {
|
|
||||||
scansInEdit.push_back(std::move(scan));
|
|
||||||
}
|
|
||||||
|
|
||||||
auto refreshed = parseValue(result, scansInEdit);
|
auto refreshed = parseValue(result, scansInEdit);
|
||||||
decryptValue(refreshed);
|
decryptValue(refreshed);
|
||||||
|
@ -2157,23 +2253,28 @@ auto FormController::parseValue(
|
||||||
result.data.encryptedSecret = bytes::make_vector(fields.vsecret.v);
|
result.data.encryptedSecret = bytes::make_vector(fields.vsecret.v);
|
||||||
}
|
}
|
||||||
if (data.has_files()) {
|
if (data.has_files()) {
|
||||||
result.scans = parseFiles(data.vfiles.v, editData);
|
result.files(FileType::Scan) = parseFiles(data.vfiles.v, editData);
|
||||||
|
}
|
||||||
|
if (data.has_translation()) {
|
||||||
|
result.files(FileType::Translation) = parseFiles(
|
||||||
|
data.vtranslation.v,
|
||||||
|
editData);
|
||||||
}
|
}
|
||||||
const auto parseSpecialScan = [&](
|
const auto parseSpecialScan = [&](
|
||||||
SpecialFile type,
|
FileType type,
|
||||||
const MTPSecureFile &file) {
|
const MTPSecureFile &file) {
|
||||||
if (auto parsed = parseFile(file, editData)) {
|
if (auto parsed = parseFile(file, editData)) {
|
||||||
result.specialScans.emplace(type, std::move(*parsed));
|
result.specialScans.emplace(type, std::move(*parsed));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
if (data.has_front_side()) {
|
if (data.has_front_side()) {
|
||||||
parseSpecialScan(SpecialFile::FrontSide, data.vfront_side);
|
parseSpecialScan(FileType::FrontSide, data.vfront_side);
|
||||||
}
|
}
|
||||||
if (data.has_reverse_side()) {
|
if (data.has_reverse_side()) {
|
||||||
parseSpecialScan(SpecialFile::ReverseSide, data.vreverse_side);
|
parseSpecialScan(FileType::ReverseSide, data.vreverse_side);
|
||||||
}
|
}
|
||||||
if (data.has_selfie()) {
|
if (data.has_selfie()) {
|
||||||
parseSpecialScan(SpecialFile::Selfie, data.vselfie);
|
parseSpecialScan(FileType::Selfie, data.vselfie);
|
||||||
}
|
}
|
||||||
if (data.has_plain_data()) {
|
if (data.has_plain_data()) {
|
||||||
switch (data.vplain_data.type()) {
|
switch (data.vplain_data.type()) {
|
||||||
|
@ -2190,18 +2291,25 @@ auto FormController::parseValue(
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto FormController::findEditFile(const FullMsgId &fullId) -> EditFile* {
|
template <typename Condition>
|
||||||
const auto found = [&](const EditFile &file) {
|
EditFile *FormController::findEditFileByCondition(Condition &&condition) {
|
||||||
return (file.uploadData && file.uploadData->fullId == fullId);
|
for (auto &pair : _form.values) {
|
||||||
};
|
auto &value = pair.second;
|
||||||
for (auto &[type, value] : _form.values) {
|
const auto foundInList = [&](FileType type) -> EditFile* {
|
||||||
for (auto &scan : value.scansInEdit) {
|
for (auto &scan : value.filesInEdit(type)) {
|
||||||
if (found(scan)) {
|
if (condition(scan)) {
|
||||||
return &scan;
|
return &scan;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return nullptr;
|
||||||
|
};
|
||||||
|
if (const auto result = foundInList(FileType::Scan)) {
|
||||||
|
return result;
|
||||||
|
} else if (const auto other = foundInList(FileType::Translation)) {
|
||||||
|
return other;
|
||||||
}
|
}
|
||||||
for (auto &[special, scan] : value.specialScansInEdit) {
|
for (auto &[special, scan] : value.specialScansInEdit) {
|
||||||
if (found(scan)) {
|
if (condition(scan)) {
|
||||||
return &scan;
|
return &scan;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2209,23 +2317,16 @@ auto FormController::findEditFile(const FullMsgId &fullId) -> EditFile* {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto FormController::findEditFile(const FileKey &key) -> EditFile* {
|
EditFile *FormController::findEditFile(const FullMsgId &fullId) {
|
||||||
const auto found = [&](const EditFile &file) {
|
return findEditFileByCondition([&](const EditFile &file) {
|
||||||
|
return (file.uploadData && file.uploadData->fullId == fullId);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
EditFile *FormController::findEditFile(const FileKey &key) {
|
||||||
|
return findEditFileByCondition([&](const EditFile &file) {
|
||||||
return (file.fields.dcId == key.dcId && file.fields.id == key.id);
|
return (file.fields.dcId == key.dcId && file.fields.id == key.id);
|
||||||
};
|
});
|
||||||
for (auto &[type, value] : _form.values) {
|
|
||||||
for (auto &scan : value.scansInEdit) {
|
|
||||||
if (found(scan)) {
|
|
||||||
return &scan;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (auto &[special, scan] : value.specialScansInEdit) {
|
|
||||||
if (found(scan)) {
|
|
||||||
return &scan;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto FormController::findFile(const FileKey &key)
|
auto FormController::findFile(const FileKey &key)
|
||||||
|
@ -2233,11 +2334,20 @@ auto FormController::findFile(const FileKey &key)
|
||||||
const auto found = [&](const File &file) {
|
const auto found = [&](const File &file) {
|
||||||
return (file.dcId == key.dcId) && (file.id == key.id);
|
return (file.dcId == key.dcId) && (file.id == key.id);
|
||||||
};
|
};
|
||||||
for (auto &[type, value] : _form.values) {
|
for (auto &pair : _form.values) {
|
||||||
for (auto &scan : value.scans) {
|
auto &value = pair.second;
|
||||||
if (found(scan)) {
|
const auto foundInList = [&](FileType type) -> File* {
|
||||||
return { &value, &scan };
|
for (auto &scan : value.files(type)) {
|
||||||
|
if (found(scan)) {
|
||||||
|
return &scan;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
return nullptr;
|
||||||
|
};
|
||||||
|
if (const auto result = foundInList(FileType::Scan)) {
|
||||||
|
return { &value, result };
|
||||||
|
} else if (const auto other = foundInList(FileType::Translation)) {
|
||||||
|
return { &value, other };
|
||||||
}
|
}
|
||||||
for (auto &[special, scan] : value.specialScans) {
|
for (auto &[special, scan] : value.specialScans) {
|
||||||
if (found(scan)) {
|
if (found(scan)) {
|
||||||
|
|
|
@ -81,6 +81,14 @@ private:
|
||||||
|
|
||||||
struct Value;
|
struct Value;
|
||||||
|
|
||||||
|
enum class FileType {
|
||||||
|
Scan,
|
||||||
|
Translation,
|
||||||
|
FrontSide,
|
||||||
|
ReverseSide,
|
||||||
|
Selfie,
|
||||||
|
};
|
||||||
|
|
||||||
struct File {
|
struct File {
|
||||||
uint64 id = 0;
|
uint64 id = 0;
|
||||||
uint64 accessHash = 0;
|
uint64 accessHash = 0;
|
||||||
|
@ -99,10 +107,12 @@ struct File {
|
||||||
struct EditFile {
|
struct EditFile {
|
||||||
EditFile(
|
EditFile(
|
||||||
not_null<const Value*> value,
|
not_null<const Value*> value,
|
||||||
|
FileType type,
|
||||||
const File &fields,
|
const File &fields,
|
||||||
std::unique_ptr<UploadScanData> &&uploadData);
|
std::unique_ptr<UploadScanData> &&uploadData);
|
||||||
|
|
||||||
not_null<const Value*> value;
|
not_null<const Value*> value;
|
||||||
|
FileType type;
|
||||||
File fields;
|
File fields;
|
||||||
UploadScanDataPointer uploadData;
|
UploadScanDataPointer uploadData;
|
||||||
std::shared_ptr<bool> guard;
|
std::shared_ptr<bool> guard;
|
||||||
|
@ -141,12 +151,6 @@ struct Verification {
|
||||||
|
|
||||||
struct Form;
|
struct Form;
|
||||||
|
|
||||||
enum class SpecialFile {
|
|
||||||
FrontSide,
|
|
||||||
ReverseSide,
|
|
||||||
Selfie,
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Value {
|
struct Value {
|
||||||
enum class Type {
|
enum class Type {
|
||||||
PersonalDetails,
|
PersonalDetails,
|
||||||
|
@ -172,20 +176,32 @@ struct Value {
|
||||||
// It should be preserved through re-parsing (for example when saving).
|
// It should be preserved through re-parsing (for example when saving).
|
||||||
// So we hide "operator=(Value&&)" in private and instead provide this.
|
// So we hide "operator=(Value&&)" in private and instead provide this.
|
||||||
void fillDataFrom(Value &&other);
|
void fillDataFrom(Value &&other);
|
||||||
bool requiresSpecialScan(SpecialFile type) const;
|
bool requiresSpecialScan(FileType type) const;
|
||||||
|
bool requiresScan(FileType type) const;
|
||||||
bool scansAreFilled() const;
|
bool scansAreFilled() const;
|
||||||
|
void saveInEdit();
|
||||||
|
void clearEditData();
|
||||||
|
bool uploadingScan() const;
|
||||||
|
bool saving() const;
|
||||||
|
|
||||||
|
std::vector<File> &files(FileType type);
|
||||||
|
const std::vector<File> &files(FileType type) const;
|
||||||
|
QString &fileMissingError(FileType type);
|
||||||
|
const QString &fileMissingError(FileType type) const;
|
||||||
|
std::vector<EditFile> &filesInEdit(FileType type);
|
||||||
|
const std::vector<EditFile> &filesInEdit(FileType type) const;
|
||||||
|
EditFile &fileInEdit(FileType type, base::optional<int> fileIndex);
|
||||||
|
const EditFile &fileInEdit(
|
||||||
|
FileType type,
|
||||||
|
base::optional<int> fileIndex) const;
|
||||||
|
|
||||||
|
std::vector<EditFile> takeAllFilesInEdit();
|
||||||
|
|
||||||
Type type;
|
Type type;
|
||||||
ValueData data;
|
ValueData data;
|
||||||
std::vector<File> scans;
|
std::map<FileType, File> specialScans;
|
||||||
std::vector<File> translations;
|
|
||||||
std::map<SpecialFile, File> specialScans;
|
|
||||||
QString error;
|
QString error;
|
||||||
QString scanMissingError;
|
std::map<FileType, EditFile> specialScansInEdit;
|
||||||
QString translationMissingError;
|
|
||||||
std::vector<EditFile> scansInEdit;
|
|
||||||
std::vector<EditFile> translationsInEdit;
|
|
||||||
std::map<SpecialFile, EditFile> specialScansInEdit;
|
|
||||||
Verification verification;
|
Verification verification;
|
||||||
bytes::vector submitHash;
|
bytes::vector submitHash;
|
||||||
|
|
||||||
|
@ -199,6 +215,13 @@ struct Value {
|
||||||
private:
|
private:
|
||||||
Value &operator=(Value &&other) = default;
|
Value &operator=(Value &&other) = default;
|
||||||
|
|
||||||
|
std::vector<File> _scans;
|
||||||
|
std::vector<File> _translations;
|
||||||
|
std::vector<EditFile> _scansInEdit;
|
||||||
|
std::vector<EditFile> _translationsInEdit;
|
||||||
|
QString _scanMissingError;
|
||||||
|
QString _translationMissingError;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
bool ValueChanged(not_null<const Value*> value, const ValueMap &data);
|
bool ValueChanged(not_null<const Value*> value, const ValueMap &data);
|
||||||
|
@ -299,20 +322,19 @@ public:
|
||||||
void reloadAndSubmitPassword(const QByteArray &password);
|
void reloadAndSubmitPassword(const QByteArray &password);
|
||||||
void cancelPassword();
|
void cancelPassword();
|
||||||
|
|
||||||
bool canAddScan(not_null<const Value*> value) const;
|
bool canAddScan(not_null<const Value*> value, FileType type) const;
|
||||||
void uploadScan(not_null<const Value*> value, QByteArray &&content);
|
void uploadScan(
|
||||||
void deleteScan(not_null<const Value*> value, int fileIndex);
|
|
||||||
void restoreScan(not_null<const Value*> value, int fileIndex);
|
|
||||||
void uploadSpecialScan(
|
|
||||||
not_null<const Value*> value,
|
not_null<const Value*> value,
|
||||||
SpecialFile type,
|
FileType type,
|
||||||
QByteArray &&content);
|
QByteArray &&content);
|
||||||
void deleteSpecialScan(
|
void deleteScan(
|
||||||
not_null<const Value*> value,
|
not_null<const Value*> value,
|
||||||
SpecialFile type);
|
FileType type,
|
||||||
void restoreSpecialScan(
|
base::optional<int> fileIndex);
|
||||||
|
void restoreScan(
|
||||||
not_null<const Value*> value,
|
not_null<const Value*> value,
|
||||||
SpecialFile type);
|
FileType type,
|
||||||
|
base::optional<int> fileIndex);
|
||||||
|
|
||||||
rpl::producer<> secretReadyEvents() const;
|
rpl::producer<> secretReadyEvents() const;
|
||||||
|
|
||||||
|
@ -331,8 +353,6 @@ public:
|
||||||
void cancelValueVerification(not_null<const Value*> value);
|
void cancelValueVerification(not_null<const Value*> value);
|
||||||
void saveValueEdit(not_null<const Value*> value, ValueMap &&data);
|
void saveValueEdit(not_null<const Value*> value, ValueMap &&data);
|
||||||
void deleteValueEdit(not_null<const Value*> value);
|
void deleteValueEdit(not_null<const Value*> value);
|
||||||
bool savingValue(not_null<const Value*> value) const;
|
|
||||||
bool uploadingScan(not_null<const Value*> value) const;
|
|
||||||
|
|
||||||
void cancel();
|
void cancel();
|
||||||
void cancelSure();
|
void cancelSure();
|
||||||
|
@ -350,6 +370,9 @@ private:
|
||||||
QByteArray credentials;
|
QByteArray credentials;
|
||||||
std::vector<not_null<const Value*>> errors;
|
std::vector<not_null<const Value*>> errors;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template <typename Condition>
|
||||||
|
EditFile *findEditFileByCondition(Condition &&condition);
|
||||||
EditFile *findEditFile(const FullMsgId &fullId);
|
EditFile *findEditFile(const FullMsgId &fullId);
|
||||||
EditFile *findEditFile(const FileKey &key);
|
EditFile *findEditFile(const FileKey &key);
|
||||||
std::pair<Value*, File*> findFile(const FileKey &key);
|
std::pair<Value*, File*> findFile(const FileKey &key);
|
||||||
|
@ -432,11 +455,8 @@ private:
|
||||||
void scanUploadFail(const FullMsgId &fullId);
|
void scanUploadFail(const FullMsgId &fullId);
|
||||||
void scanDeleteRestore(
|
void scanDeleteRestore(
|
||||||
not_null<const Value*> value,
|
not_null<const Value*> value,
|
||||||
int fileIndex,
|
FileType type,
|
||||||
bool deleted);
|
base::optional<int> fileIndex,
|
||||||
void specialScanDeleteRestore(
|
|
||||||
not_null<const Value*> value,
|
|
||||||
SpecialFile type,
|
|
||||||
bool deleted);
|
bool deleted);
|
||||||
|
|
||||||
QString getPhoneFromValue(not_null<const Value*> value) const;
|
QString getPhoneFromValue(not_null<const Value*> value) const;
|
||||||
|
|
|
@ -159,14 +159,17 @@ bool ValidateForm(const Form &form) {
|
||||||
LOG(("API Error: Bad value requiring native names."));
|
LOG(("API Error: Bad value requiring native names."));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (!CanRequireScans(value.type)) {
|
if (!value.requiresScan(FileType::Scan)) {
|
||||||
Assert(value.scanMissingError.isEmpty());
|
for (const auto &scan : value.files(FileType::Scan)) {
|
||||||
}
|
|
||||||
if (!value.translationRequired) {
|
|
||||||
for (const auto &scan : value.translations) {
|
|
||||||
Assert(scan.error.isEmpty());
|
Assert(scan.error.isEmpty());
|
||||||
}
|
}
|
||||||
Assert(value.translationMissingError.isEmpty());
|
Assert(value.fileMissingError(FileType::Scan).isEmpty());
|
||||||
|
}
|
||||||
|
if (!value.requiresScan(FileType::Translation)) {
|
||||||
|
for (const auto &scan : value.files(FileType::Translation)) {
|
||||||
|
Assert(scan.error.isEmpty());
|
||||||
|
}
|
||||||
|
Assert(value.fileMissingError(FileType::Translation).isEmpty());
|
||||||
}
|
}
|
||||||
for (const auto &[type, specialScan] : value.specialScans) {
|
for (const auto &[type, specialScan] : value.specialScans) {
|
||||||
if (!value.requiresSpecialScan(type)) {
|
if (!value.requiresSpecialScan(type)) {
|
||||||
|
@ -278,6 +281,9 @@ QString ComputeScopeRowReadyString(const Scope &scope) {
|
||||||
}
|
}
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}();
|
}();
|
||||||
|
if (!scope.documents.empty() && !document) {
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
if ((document && scope.documents.size() > 1)
|
if ((document && scope.documents.size() > 1)
|
||||||
|| (!scope.details
|
|| (!scope.details
|
||||||
&& (ScopeTypeForValueType(document->type)
|
&& (ScopeTypeForValueType(document->type)
|
||||||
|
@ -307,9 +313,6 @@ QString ComputeScopeRowReadyString(const Scope &scope) {
|
||||||
}
|
}
|
||||||
}());
|
}());
|
||||||
}
|
}
|
||||||
if (!scope.documents.empty() && !document) {
|
|
||||||
return QString();
|
|
||||||
}
|
|
||||||
const auto scheme = GetDocumentScheme(
|
const auto scheme = GetDocumentScheme(
|
||||||
scope.type,
|
scope.type,
|
||||||
document ? base::make_optional(document->type) : base::none,
|
document ? base::make_optional(document->type) : base::none,
|
||||||
|
@ -372,22 +375,18 @@ ScopeRow ComputeScopeRow(const Scope &scope) {
|
||||||
if (!value->error.isEmpty()) {
|
if (!value->error.isEmpty()) {
|
||||||
errors.push_back(value->error);
|
errors.push_back(value->error);
|
||||||
}
|
}
|
||||||
if (!value->scanMissingError.isEmpty()) {
|
const auto addTypeErrors = [&](FileType type) {
|
||||||
errors.push_back(value->scanMissingError);
|
if (!value->fileMissingError(type).isEmpty()) {
|
||||||
}
|
errors.push_back(value->fileMissingError(type));
|
||||||
if (!value->translationMissingError.isEmpty()) {
|
|
||||||
errors.push_back(value->translationMissingError);
|
|
||||||
}
|
|
||||||
for (const auto &scan : value->scans) {
|
|
||||||
if (!scan.error.isEmpty()) {
|
|
||||||
errors.push_back(scan.error);
|
|
||||||
}
|
}
|
||||||
}
|
for (const auto &scan : value->files(type)) {
|
||||||
for (const auto &scan : value->translations) {
|
if (!scan.error.isEmpty()) {
|
||||||
if (!scan.error.isEmpty()) {
|
errors.push_back(scan.error);
|
||||||
errors.push_back(scan.error);
|
}
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
addTypeErrors(FileType::Scan);
|
||||||
|
addTypeErrors(FileType::Translation);
|
||||||
for (const auto &[type, scan] : value->specialScans) {
|
for (const auto &[type, scan] : value->specialScans) {
|
||||||
if (!scan.error.isEmpty()) {
|
if (!scan.error.isEmpty()) {
|
||||||
errors.push_back(scan.error);
|
errors.push_back(scan.error);
|
||||||
|
|
|
@ -34,7 +34,6 @@ struct ScopeRow {
|
||||||
QString error;
|
QString error;
|
||||||
};
|
};
|
||||||
|
|
||||||
bool CanRequireScans(Value::Type type);
|
|
||||||
bool CanHaveErrors(Value::Type type);
|
bool CanHaveErrors(Value::Type type);
|
||||||
bool ValidateForm(const Form &form);
|
bool ValidateForm(const Form &form);
|
||||||
std::vector<Scope> ComputeScopes(const Form &form);
|
std::vector<Scope> ComputeScopes(const Form &form);
|
||||||
|
|
|
@ -24,6 +24,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "styles/style_boxes.h"
|
#include "styles/style_boxes.h"
|
||||||
|
|
||||||
namespace Passport {
|
namespace Passport {
|
||||||
|
namespace {
|
||||||
|
|
||||||
constexpr auto kMaxNameSize = 255;
|
constexpr auto kMaxNameSize = 255;
|
||||||
constexpr auto kMaxDocumentSize = 24;
|
constexpr auto kMaxDocumentSize = 24;
|
||||||
|
@ -32,6 +33,76 @@ constexpr auto kMinCitySize = 2;
|
||||||
constexpr auto kMaxCitySize = 64;
|
constexpr auto kMaxCitySize = 64;
|
||||||
constexpr auto kMaxPostcodeSize = 10;
|
constexpr auto kMaxPostcodeSize = 10;
|
||||||
|
|
||||||
|
ScanInfo CollectScanInfo(const EditFile &file) {
|
||||||
|
const auto status = [&] {
|
||||||
|
if (file.fields.accessHash) {
|
||||||
|
if (file.fields.downloadOffset < 0) {
|
||||||
|
return lang(lng_attach_failed);
|
||||||
|
} else if (file.fields.downloadOffset < file.fields.size) {
|
||||||
|
return formatDownloadText(
|
||||||
|
file.fields.downloadOffset,
|
||||||
|
file.fields.size);
|
||||||
|
} else {
|
||||||
|
return lng_passport_scan_uploaded(
|
||||||
|
lt_date,
|
||||||
|
langDateTimeFull(ParseDateTime(file.fields.date)));
|
||||||
|
}
|
||||||
|
} else if (file.uploadData) {
|
||||||
|
if (file.uploadData->offset < 0) {
|
||||||
|
return lang(lng_attach_failed);
|
||||||
|
} else if (file.uploadData->fullId) {
|
||||||
|
return formatDownloadText(
|
||||||
|
file.uploadData->offset,
|
||||||
|
file.uploadData->bytes.size());
|
||||||
|
} else {
|
||||||
|
return lng_passport_scan_uploaded(
|
||||||
|
lt_date,
|
||||||
|
langDateTimeFull(ParseDateTime(file.fields.date)));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return formatDownloadText(0, file.fields.size);
|
||||||
|
}
|
||||||
|
}();
|
||||||
|
return {
|
||||||
|
FileKey{ file.fields.id, file.fields.dcId },
|
||||||
|
!file.fields.error.isEmpty() ? file.fields.error : status,
|
||||||
|
file.fields.image,
|
||||||
|
file.deleted,
|
||||||
|
file.type,
|
||||||
|
file.fields.error };
|
||||||
|
}
|
||||||
|
|
||||||
|
ScanListData PrepareScanListData(const Value &value, FileType type) {
|
||||||
|
auto result = ScanListData();
|
||||||
|
for (const auto &scan : value.filesInEdit(type)) {
|
||||||
|
result.files.push_back(CollectScanInfo(scan));
|
||||||
|
}
|
||||||
|
result.errorMissing = value.fileMissingError(type);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::map<FileType, ScanInfo> PrepareSpecialFiles(const Value &value) {
|
||||||
|
auto result = std::map<FileType, ScanInfo>();
|
||||||
|
const auto types = {
|
||||||
|
FileType::FrontSide,
|
||||||
|
FileType::ReverseSide,
|
||||||
|
FileType::Selfie
|
||||||
|
};
|
||||||
|
for (const auto type : types) {
|
||||||
|
if (value.requiresSpecialScan(type)) {
|
||||||
|
const auto i = value.specialScansInEdit.find(type);
|
||||||
|
const auto j = result.emplace(
|
||||||
|
type,
|
||||||
|
(i != end(value.specialScansInEdit)
|
||||||
|
? CollectScanInfo(i->second)
|
||||||
|
: ScanInfo())).first;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
EditDocumentScheme GetDocumentScheme(
|
EditDocumentScheme GetDocumentScheme(
|
||||||
Scope::Type type,
|
Scope::Type type,
|
||||||
base::optional<Value::Type> scansType,
|
base::optional<Value::Type> scansType,
|
||||||
|
@ -576,66 +647,47 @@ void PanelController::cancelPasswordSubmit() {
|
||||||
[=] { if (*box) (*box)->closeBox(); _form->cancelPassword(); }));
|
[=] { if (*box) (*box)->closeBox(); _form->cancelPassword(); }));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PanelController::canAddScan() const {
|
bool PanelController::canAddScan(FileType type) const {
|
||||||
Expects(_editScope != nullptr);
|
Expects(_editScope != nullptr);
|
||||||
Expects(_editDocument != nullptr);
|
Expects(_editDocument != nullptr);
|
||||||
|
|
||||||
return _form->canAddScan(_editDocument);
|
return _form->canAddScan(_editDocument, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PanelController::uploadScan(QByteArray &&content) {
|
void PanelController::uploadScan(FileType type, QByteArray &&content) {
|
||||||
Expects(_editScope != nullptr);
|
Expects(_editScope != nullptr);
|
||||||
Expects(_editDocument != nullptr);
|
Expects(_editDocument != nullptr);
|
||||||
|
Expects(_editDocument->requiresScan(type));
|
||||||
|
|
||||||
_form->uploadScan(_editDocument, std::move(content));
|
_form->uploadScan(_editDocument, type, std::move(content));
|
||||||
}
|
}
|
||||||
|
|
||||||
void PanelController::deleteScan(int fileIndex) {
|
void PanelController::deleteScan(
|
||||||
|
FileType type,
|
||||||
|
base::optional<int> fileIndex) {
|
||||||
Expects(_editScope != nullptr);
|
Expects(_editScope != nullptr);
|
||||||
Expects(_editDocument != nullptr);
|
Expects(_editDocument != nullptr);
|
||||||
|
Expects(_editDocument->requiresScan(type));
|
||||||
|
|
||||||
_form->deleteScan(_editDocument, fileIndex);
|
_form->deleteScan(_editDocument, type, fileIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PanelController::restoreScan(int fileIndex) {
|
void PanelController::restoreScan(
|
||||||
|
FileType type,
|
||||||
|
base::optional<int> fileIndex) {
|
||||||
Expects(_editScope != nullptr);
|
Expects(_editScope != nullptr);
|
||||||
Expects(_editDocument != nullptr);
|
Expects(_editDocument != nullptr);
|
||||||
|
Expects(_editDocument->requiresScan(type));
|
||||||
|
|
||||||
_form->restoreScan(_editDocument, fileIndex);
|
_form->restoreScan(_editDocument, type, fileIndex);
|
||||||
}
|
|
||||||
|
|
||||||
void PanelController::uploadSpecialScan(
|
|
||||||
SpecialFile type,
|
|
||||||
QByteArray &&content) {
|
|
||||||
Expects(_editScope != nullptr);
|
|
||||||
Expects(_editDocument != nullptr);
|
|
||||||
Expects(_editDocument->requiresSpecialScan(type));
|
|
||||||
|
|
||||||
_form->uploadSpecialScan(_editDocument, type, std::move(content));
|
|
||||||
}
|
|
||||||
|
|
||||||
void PanelController::deleteSpecialScan(SpecialFile type) {
|
|
||||||
Expects(_editScope != nullptr);
|
|
||||||
Expects(_editDocument != nullptr);
|
|
||||||
Expects(_editDocument->requiresSpecialScan(type));
|
|
||||||
|
|
||||||
_form->deleteSpecialScan(_editDocument, type);
|
|
||||||
}
|
|
||||||
|
|
||||||
void PanelController::restoreSpecialScan(SpecialFile type) {
|
|
||||||
Expects(_editScope != nullptr);
|
|
||||||
Expects(_editDocument != nullptr);
|
|
||||||
Expects(_editDocument->requiresSpecialScan(type));
|
|
||||||
|
|
||||||
_form->restoreSpecialScan(_editDocument, type);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
rpl::producer<ScanInfo> PanelController::scanUpdated() const {
|
rpl::producer<ScanInfo> PanelController::scanUpdated() const {
|
||||||
return _form->scanUpdated(
|
return _form->scanUpdated(
|
||||||
) | rpl::filter([=](not_null<const EditFile*> file) {
|
) | rpl::filter([=](not_null<const EditFile*> file) {
|
||||||
return (file->value == _editDocument);
|
return (file->value == _editDocument);
|
||||||
}) | rpl::map([=](not_null<const EditFile*> file) {
|
}) | rpl::map([](not_null<const EditFile*> file) {
|
||||||
return collectScanInfo(*file);
|
return CollectScanInfo(*file);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -643,63 +695,8 @@ rpl::producer<ScopeError> PanelController::saveErrors() const {
|
||||||
return _saveErrors.events();
|
return _saveErrors.events();
|
||||||
}
|
}
|
||||||
|
|
||||||
ScanInfo PanelController::collectScanInfo(const EditFile &file) const {
|
|
||||||
Expects(_editScope != nullptr);
|
|
||||||
Expects(_editDocument != nullptr);
|
|
||||||
|
|
||||||
const auto status = [&] {
|
|
||||||
if (file.fields.accessHash) {
|
|
||||||
if (file.fields.downloadOffset < 0) {
|
|
||||||
return lang(lng_attach_failed);
|
|
||||||
} else if (file.fields.downloadOffset < file.fields.size) {
|
|
||||||
return formatDownloadText(
|
|
||||||
file.fields.downloadOffset,
|
|
||||||
file.fields.size);
|
|
||||||
} else {
|
|
||||||
return lng_passport_scan_uploaded(
|
|
||||||
lt_date,
|
|
||||||
langDateTimeFull(ParseDateTime(file.fields.date)));
|
|
||||||
}
|
|
||||||
} else if (file.uploadData) {
|
|
||||||
if (file.uploadData->offset < 0) {
|
|
||||||
return lang(lng_attach_failed);
|
|
||||||
} else if (file.uploadData->fullId) {
|
|
||||||
return formatDownloadText(
|
|
||||||
file.uploadData->offset,
|
|
||||||
file.uploadData->bytes.size());
|
|
||||||
} else {
|
|
||||||
return lng_passport_scan_uploaded(
|
|
||||||
lt_date,
|
|
||||||
langDateTimeFull(ParseDateTime(file.fields.date)));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return formatDownloadText(0, file.fields.size);
|
|
||||||
}
|
|
||||||
}();
|
|
||||||
const auto specialType = [&]() -> base::optional<SpecialFile> {
|
|
||||||
if (file.value != _editDocument) {
|
|
||||||
return base::none;
|
|
||||||
}
|
|
||||||
for (const auto &[type, scan] : _editDocument->specialScansInEdit) {
|
|
||||||
if (&file == &scan) {
|
|
||||||
return type;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return base::none;
|
|
||||||
}();
|
|
||||||
return {
|
|
||||||
FileKey{ file.fields.id, file.fields.dcId },
|
|
||||||
!file.fields.error.isEmpty() ? file.fields.error : status,
|
|
||||||
file.fields.image,
|
|
||||||
file.deleted,
|
|
||||||
specialType,
|
|
||||||
file.fields.error };
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<ScopeError> PanelController::collectSaveErrors(
|
std::vector<ScopeError> PanelController::collectSaveErrors(
|
||||||
not_null<const Value*> value) const {
|
not_null<const Value*> value) const {
|
||||||
using General = ScopeError::General;
|
|
||||||
|
|
||||||
auto result = std::vector<ScopeError>();
|
auto result = std::vector<ScopeError>();
|
||||||
for (const auto &[key, value] : value->data.parsedInEdit.fields) {
|
for (const auto &[key, value] : value->data.parsedInEdit.fields) {
|
||||||
if (!value.error.isEmpty()) {
|
if (!value.error.isEmpty()) {
|
||||||
|
@ -740,7 +737,8 @@ bool PanelController::hasValueDocument() const {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return !_editDocument->data.parsed.fields.empty()
|
return !_editDocument->data.parsed.fields.empty()
|
||||||
|| !_editDocument->scans.empty()
|
|| !_editDocument->files(FileType::Scan).empty()
|
||||||
|
|| !_editDocument->files(FileType::Translation).empty()
|
||||||
|| !_editDocument->specialScans.empty();
|
|| !_editDocument->specialScans.empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -978,25 +976,22 @@ void PanelController::editWithUpload(int index, int documentIndex) {
|
||||||
&& documentIndex < _scopes[index].documents.size());
|
&& documentIndex < _scopes[index].documents.size());
|
||||||
|
|
||||||
const auto document = _scopes[index].documents[documentIndex];
|
const auto document = _scopes[index].documents[documentIndex];
|
||||||
const auto requiresSpecialScan = document->requiresSpecialScan(
|
const auto type = document->requiresSpecialScan(FileType::FrontSide)
|
||||||
SpecialFile::FrontSide);
|
? FileType::FrontSide
|
||||||
const auto allowMany = !requiresSpecialScan;
|
: FileType::Scan;
|
||||||
|
const auto allowMany = (type == FileType::Scan);
|
||||||
const auto widget = _panel->widget();
|
const auto widget = _panel->widget();
|
||||||
EditScans::ChooseScan(widget.get(), [=](QByteArray &&content) {
|
EditScans::ChooseScan(widget.get(), type, [=](QByteArray &&content) {
|
||||||
if (_scopeDocumentTypeBox) {
|
if (_scopeDocumentTypeBox) {
|
||||||
_scopeDocumentTypeBox = BoxPointer();
|
_scopeDocumentTypeBox = BoxPointer();
|
||||||
}
|
}
|
||||||
if (!_editScope || !_editDocument) {
|
if (!_editScope || !_editDocument) {
|
||||||
startScopeEdit(index, documentIndex);
|
startScopeEdit(index, documentIndex);
|
||||||
}
|
}
|
||||||
if (requiresSpecialScan) {
|
uploadScan(type, std::move(content));
|
||||||
uploadSpecialScan(SpecialFile::FrontSide, std::move(content));
|
|
||||||
} else {
|
|
||||||
uploadScan(std::move(content));
|
|
||||||
}
|
|
||||||
}, [=](ReadScanError error) {
|
}, [=](ReadScanError error) {
|
||||||
readScanError(error);
|
readScanError(error);
|
||||||
}, allowMany);
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
void PanelController::readScanError(ReadScanError error) {
|
void PanelController::readScanError(ReadScanError error) {
|
||||||
|
@ -1027,11 +1022,11 @@ bool PanelController::editRequiresScanUpload(
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const auto document = _scopes[index].documents[documentIndex];
|
const auto document = _scopes[index].documents[documentIndex];
|
||||||
if (document->requiresSpecialScan(SpecialFile::FrontSide)) {
|
if (document->requiresSpecialScan(FileType::FrontSide)) {
|
||||||
const auto &scans = document->specialScans;
|
const auto &scans = document->specialScans;
|
||||||
return (scans.find(SpecialFile::FrontSide) == end(scans));
|
return (scans.find(FileType::FrontSide) == end(scans));
|
||||||
}
|
}
|
||||||
return document->scans.empty();
|
return document->files(FileType::Scan).empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
void PanelController::editScope(int index, int documentIndex) {
|
void PanelController::editScope(int index, int documentIndex) {
|
||||||
|
@ -1068,6 +1063,14 @@ void PanelController::startScopeEdit(int index, int documentIndex) {
|
||||||
case Scope::Type::Identity:
|
case Scope::Type::Identity:
|
||||||
case Scope::Type::Address: {
|
case Scope::Type::Address: {
|
||||||
Assert(_editDocument != nullptr);
|
Assert(_editDocument != nullptr);
|
||||||
|
auto scans = PrepareScanListData(
|
||||||
|
*_editDocument,
|
||||||
|
FileType::Scan);
|
||||||
|
auto translations = _editDocument->translationRequired
|
||||||
|
? base::make_optional(PrepareScanListData(
|
||||||
|
*_editDocument,
|
||||||
|
FileType::Translation))
|
||||||
|
: base::none;
|
||||||
auto result = _editValue
|
auto result = _editValue
|
||||||
? object_ptr<PanelEditDocument>(
|
? object_ptr<PanelEditDocument>(
|
||||||
_panel->widget(),
|
_panel->widget(),
|
||||||
|
@ -1080,9 +1083,9 @@ void PanelController::startScopeEdit(int index, int documentIndex) {
|
||||||
_editValue->data.parsedInEdit,
|
_editValue->data.parsedInEdit,
|
||||||
_editDocument->error,
|
_editDocument->error,
|
||||||
_editDocument->data.parsedInEdit,
|
_editDocument->data.parsedInEdit,
|
||||||
_editDocument->scanMissingError,
|
std::move(scans),
|
||||||
valueFiles(*_editDocument),
|
std::move(translations),
|
||||||
valueSpecialFiles(*_editDocument))
|
PrepareSpecialFiles(*_editDocument))
|
||||||
: object_ptr<PanelEditDocument>(
|
: object_ptr<PanelEditDocument>(
|
||||||
_panel->widget(),
|
_panel->widget(),
|
||||||
this,
|
this,
|
||||||
|
@ -1092,9 +1095,9 @@ void PanelController::startScopeEdit(int index, int documentIndex) {
|
||||||
false),
|
false),
|
||||||
_editDocument->error,
|
_editDocument->error,
|
||||||
_editDocument->data.parsedInEdit,
|
_editDocument->data.parsedInEdit,
|
||||||
_editDocument->scanMissingError,
|
std::move(scans),
|
||||||
valueFiles(*_editDocument),
|
std::move(translations),
|
||||||
valueSpecialFiles(*_editDocument));
|
PrepareSpecialFiles(*_editDocument));
|
||||||
const auto weak = make_weak(result.data());
|
const auto weak = make_weak(result.data());
|
||||||
_panelHasUnsavedChanges = [=] {
|
_panelHasUnsavedChanges = [=] {
|
||||||
return weak ? weak->hasUnsavedChanges() : false;
|
return weak ? weak->hasUnsavedChanges() : false;
|
||||||
|
@ -1183,13 +1186,13 @@ void PanelController::processValueSaveFinished(
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PanelController::uploadingScopeScan() const {
|
bool PanelController::uploadingScopeScan() const {
|
||||||
return (_editValue && _form->uploadingScan(_editValue))
|
return (_editValue && _editValue->uploadingScan())
|
||||||
|| (_editDocument && _form->uploadingScan(_editDocument));
|
|| (_editDocument && _editDocument->uploadingScan());
|
||||||
}
|
}
|
||||||
|
|
||||||
bool PanelController::savingScope() const {
|
bool PanelController::savingScope() const {
|
||||||
return (_editValue && _form->savingValue(_editValue))
|
return (_editValue && _editValue->saving())
|
||||||
|| (_editDocument && _form->savingValue(_editDocument));
|
|| (_editDocument && _editDocument->saving());
|
||||||
}
|
}
|
||||||
|
|
||||||
void PanelController::processVerificationNeeded(
|
void PanelController::processVerificationNeeded(
|
||||||
|
@ -1253,37 +1256,6 @@ void PanelController::processVerificationNeeded(
|
||||||
_verificationBoxes.emplace(value, box);
|
_verificationBoxes.emplace(value, box);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<ScanInfo> PanelController::valueFiles(
|
|
||||||
const Value &value) const {
|
|
||||||
auto result = std::vector<ScanInfo>();
|
|
||||||
for (const auto &scan : value.scansInEdit) {
|
|
||||||
result.push_back(collectScanInfo(scan));
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::map<SpecialFile, ScanInfo> PanelController::valueSpecialFiles(
|
|
||||||
const Value &value) const {
|
|
||||||
auto result = std::map<SpecialFile, ScanInfo>();
|
|
||||||
const auto types = {
|
|
||||||
SpecialFile::FrontSide,
|
|
||||||
SpecialFile::ReverseSide,
|
|
||||||
SpecialFile::Selfie
|
|
||||||
};
|
|
||||||
for (const auto type : types) {
|
|
||||||
if (value.requiresSpecialScan(type)) {
|
|
||||||
const auto i = value.specialScansInEdit.find(type);
|
|
||||||
const auto j = result.emplace(
|
|
||||||
type,
|
|
||||||
(i != end(value.specialScansInEdit)
|
|
||||||
? collectScanInfo(i->second)
|
|
||||||
: ScanInfo())).first;
|
|
||||||
j->second.special = type;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
void PanelController::cancelValueEdit() {
|
void PanelController::cancelValueEdit() {
|
||||||
Expects(_editScope != nullptr);
|
Expects(_editScope != nullptr);
|
||||||
|
|
||||||
|
|
|
@ -36,9 +36,8 @@ struct ScanInfo {
|
||||||
QString status;
|
QString status;
|
||||||
QImage thumb;
|
QImage thumb;
|
||||||
bool deleted = false;
|
bool deleted = false;
|
||||||
base::optional<SpecialFile> special;
|
FileType type = FileType();
|
||||||
QString error;
|
QString error;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ScopeError {
|
struct ScopeError {
|
||||||
|
@ -88,13 +87,10 @@ public:
|
||||||
void setupPassword();
|
void setupPassword();
|
||||||
void cancelPasswordSubmit();
|
void cancelPasswordSubmit();
|
||||||
|
|
||||||
bool canAddScan() const;
|
bool canAddScan(FileType type) const;
|
||||||
void uploadScan(QByteArray &&content);
|
void uploadScan(FileType type, QByteArray &&content);
|
||||||
void deleteScan(int fileIndex);
|
void deleteScan(FileType type, base::optional<int> fileIndex);
|
||||||
void restoreScan(int fileIndex);
|
void restoreScan(FileType type, base::optional<int> fileIndex);
|
||||||
void uploadSpecialScan(SpecialFile type, QByteArray &&content);
|
|
||||||
void deleteSpecialScan(SpecialFile type);
|
|
||||||
void restoreSpecialScan(SpecialFile type);
|
|
||||||
rpl::producer<ScanInfo> scanUpdated() const;
|
rpl::producer<ScanInfo> scanUpdated() const;
|
||||||
rpl::producer<ScopeError> saveErrors() const;
|
rpl::producer<ScopeError> saveErrors() const;
|
||||||
void readScanError(ReadScanError error);
|
void readScanError(ReadScanError error);
|
||||||
|
@ -151,8 +147,7 @@ private:
|
||||||
int findNonEmptyDocumentIndex(const Scope &scope) const;
|
int findNonEmptyDocumentIndex(const Scope &scope) const;
|
||||||
void requestScopeFilesType(int index);
|
void requestScopeFilesType(int index);
|
||||||
void cancelValueEdit();
|
void cancelValueEdit();
|
||||||
std::vector<ScanInfo> valueFiles(const Value &value) const;
|
std::map<FileType, ScanInfo> valueSpecialFiles(
|
||||||
std::map<SpecialFile, ScanInfo> valueSpecialFiles(
|
|
||||||
const Value &value) const;
|
const Value &value) const;
|
||||||
void processValueSaveFinished(not_null<const Value*> value);
|
void processValueSaveFinished(not_null<const Value*> value);
|
||||||
void processVerificationNeeded(not_null<const Value*> value);
|
void processVerificationNeeded(not_null<const Value*> value);
|
||||||
|
@ -161,7 +156,6 @@ private:
|
||||||
bool uploadingScopeScan() const;
|
bool uploadingScopeScan() const;
|
||||||
bool hasValueDocument() const;
|
bool hasValueDocument() const;
|
||||||
bool hasValueFields() const;
|
bool hasValueFields() const;
|
||||||
ScanInfo collectScanInfo(const EditFile &file) const;
|
|
||||||
std::vector<ScopeError> collectSaveErrors(
|
std::vector<ScopeError> collectSaveErrors(
|
||||||
not_null<const Value*> value) const;
|
not_null<const Value*> value) const;
|
||||||
QString getDefaultContactValue(Scope::Type type) const;
|
QString getDefaultContactValue(Scope::Type type) const;
|
||||||
|
|
|
@ -214,9 +214,9 @@ PanelEditDocument::PanelEditDocument(
|
||||||
const ValueMap &data,
|
const ValueMap &data,
|
||||||
const QString &scansError,
|
const QString &scansError,
|
||||||
const ValueMap &scansData,
|
const ValueMap &scansData,
|
||||||
const QString &missingScansError,
|
ScanListData &&scans,
|
||||||
std::vector<ScanInfo> &&files,
|
base::optional<ScanListData> &&translations,
|
||||||
std::map<SpecialFile, ScanInfo> &&specialFiles)
|
std::map<FileType, ScanInfo> &&specialFiles)
|
||||||
: _controller(controller)
|
: _controller(controller)
|
||||||
, _scheme(std::move(scheme))
|
, _scheme(std::move(scheme))
|
||||||
, _scroll(this, st::passportPanelScroll)
|
, _scroll(this, st::passportPanelScroll)
|
||||||
|
@ -231,8 +231,8 @@ PanelEditDocument::PanelEditDocument(
|
||||||
&data,
|
&data,
|
||||||
&scansError,
|
&scansError,
|
||||||
&scansData,
|
&scansData,
|
||||||
missingScansError,
|
std::move(scans),
|
||||||
std::move(files),
|
std::move(translations),
|
||||||
std::move(specialFiles));
|
std::move(specialFiles));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -242,9 +242,9 @@ PanelEditDocument::PanelEditDocument(
|
||||||
Scheme scheme,
|
Scheme scheme,
|
||||||
const QString &scansError,
|
const QString &scansError,
|
||||||
const ValueMap &scansData,
|
const ValueMap &scansData,
|
||||||
const QString &missingScansError,
|
ScanListData &&scans,
|
||||||
std::vector<ScanInfo> &&files,
|
base::optional<ScanListData> &&translations,
|
||||||
std::map<SpecialFile, ScanInfo> &&specialFiles)
|
std::map<FileType, ScanInfo> &&specialFiles)
|
||||||
: _controller(controller)
|
: _controller(controller)
|
||||||
, _scheme(std::move(scheme))
|
, _scheme(std::move(scheme))
|
||||||
, _scroll(this, st::passportPanelScroll)
|
, _scroll(this, st::passportPanelScroll)
|
||||||
|
@ -259,8 +259,8 @@ PanelEditDocument::PanelEditDocument(
|
||||||
nullptr,
|
nullptr,
|
||||||
&scansError,
|
&scansError,
|
||||||
&scansData,
|
&scansData,
|
||||||
missingScansError,
|
std::move(scans),
|
||||||
std::move(files),
|
std::move(translations),
|
||||||
std::move(specialFiles));
|
std::move(specialFiles));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -279,7 +279,7 @@ PanelEditDocument::PanelEditDocument(
|
||||||
this,
|
this,
|
||||||
langFactory(lng_passport_save_value),
|
langFactory(lng_passport_save_value),
|
||||||
st::passportPanelSaveValue) {
|
st::passportPanelSaveValue) {
|
||||||
setupControls(&error, &data, nullptr, nullptr, QString(), {}, {});
|
setupControls(&error, &data, nullptr, nullptr, {}, {}, {});
|
||||||
}
|
}
|
||||||
|
|
||||||
void PanelEditDocument::setupControls(
|
void PanelEditDocument::setupControls(
|
||||||
|
@ -287,16 +287,16 @@ void PanelEditDocument::setupControls(
|
||||||
const ValueMap *data,
|
const ValueMap *data,
|
||||||
const QString *scansError,
|
const QString *scansError,
|
||||||
const ValueMap *scansData,
|
const ValueMap *scansData,
|
||||||
const QString &missingScansError,
|
ScanListData &&scans,
|
||||||
std::vector<ScanInfo> &&files,
|
base::optional<ScanListData> &&translations,
|
||||||
std::map<SpecialFile, ScanInfo> &&specialFiles) {
|
std::map<FileType, ScanInfo> &&specialFiles) {
|
||||||
const auto inner = setupContent(
|
const auto inner = setupContent(
|
||||||
error,
|
error,
|
||||||
data,
|
data,
|
||||||
scansError,
|
scansError,
|
||||||
scansData,
|
scansData,
|
||||||
missingScansError,
|
std::move(scans),
|
||||||
std::move(files),
|
std::move(translations),
|
||||||
std::move(specialFiles));
|
std::move(specialFiles));
|
||||||
|
|
||||||
using namespace rpl::mappers;
|
using namespace rpl::mappers;
|
||||||
|
@ -315,9 +315,9 @@ not_null<Ui::RpWidget*> PanelEditDocument::setupContent(
|
||||||
const ValueMap *data,
|
const ValueMap *data,
|
||||||
const QString *scansError,
|
const QString *scansError,
|
||||||
const ValueMap *scansData,
|
const ValueMap *scansData,
|
||||||
const QString &missingScansError,
|
ScanListData &&scans,
|
||||||
std::vector<ScanInfo> &&files,
|
base::optional<ScanListData> &&translations,
|
||||||
std::map<SpecialFile, ScanInfo> &&specialFiles) {
|
std::map<FileType, ScanInfo> &&specialFiles) {
|
||||||
const auto inner = _scroll->setOwnedWidget(
|
const auto inner = _scroll->setOwnedWidget(
|
||||||
object_ptr<Ui::VerticalLayout>(this));
|
object_ptr<Ui::VerticalLayout>(this));
|
||||||
_scroll->widthValue(
|
_scroll->widthValue(
|
||||||
|
@ -331,7 +331,8 @@ not_null<Ui::RpWidget*> PanelEditDocument::setupContent(
|
||||||
inner,
|
inner,
|
||||||
_controller,
|
_controller,
|
||||||
*scansError,
|
*scansError,
|
||||||
std::move(specialFiles)));
|
std::move(specialFiles),
|
||||||
|
std::move(translations)));
|
||||||
} else if (scansData) {
|
} else if (scansData) {
|
||||||
_editScans = inner->add(
|
_editScans = inner->add(
|
||||||
object_ptr<EditScans>(
|
object_ptr<EditScans>(
|
||||||
|
@ -339,8 +340,8 @@ not_null<Ui::RpWidget*> PanelEditDocument::setupContent(
|
||||||
_controller,
|
_controller,
|
||||||
_scheme.scansHeader,
|
_scheme.scansHeader,
|
||||||
*scansError,
|
*scansError,
|
||||||
missingScansError,
|
std::move(scans),
|
||||||
std::move(files)));
|
std::move(translations)));
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto valueOrEmpty = [&](
|
const auto valueOrEmpty = [&](
|
||||||
|
|
|
@ -33,8 +33,9 @@ struct ValueMap;
|
||||||
struct ScanInfo;
|
struct ScanInfo;
|
||||||
class EditScans;
|
class EditScans;
|
||||||
class PanelDetailsRow;
|
class PanelDetailsRow;
|
||||||
enum class SpecialFile;
|
enum class FileType;
|
||||||
enum class PanelDetailsType;
|
enum class PanelDetailsType;
|
||||||
|
struct ScanListData;
|
||||||
|
|
||||||
struct EditDocumentScheme {
|
struct EditDocumentScheme {
|
||||||
enum class ValueClass {
|
enum class ValueClass {
|
||||||
|
@ -72,18 +73,18 @@ public:
|
||||||
const ValueMap &data,
|
const ValueMap &data,
|
||||||
const QString &scansError,
|
const QString &scansError,
|
||||||
const ValueMap &scansData,
|
const ValueMap &scansData,
|
||||||
const QString &missingScansError,
|
ScanListData &&scans,
|
||||||
std::vector<ScanInfo> &&files,
|
base::optional<ScanListData> &&translations,
|
||||||
std::map<SpecialFile, ScanInfo> &&specialFiles);
|
std::map<FileType, ScanInfo> &&specialFiles);
|
||||||
PanelEditDocument(
|
PanelEditDocument(
|
||||||
QWidget *parent,
|
QWidget *parent,
|
||||||
not_null<PanelController*> controller,
|
not_null<PanelController*> controller,
|
||||||
Scheme scheme,
|
Scheme scheme,
|
||||||
const QString &scansError,
|
const QString &scansError,
|
||||||
const ValueMap &scansData,
|
const ValueMap &scansData,
|
||||||
const QString &missingScansError,
|
ScanListData &&scans,
|
||||||
std::vector<ScanInfo> &&files,
|
base::optional<ScanListData> &&translations,
|
||||||
std::map<SpecialFile, ScanInfo> &&specialFiles);
|
std::map<FileType, ScanInfo> &&specialFiles);
|
||||||
PanelEditDocument(
|
PanelEditDocument(
|
||||||
QWidget *parent,
|
QWidget *parent,
|
||||||
not_null<PanelController*> controller,
|
not_null<PanelController*> controller,
|
||||||
|
@ -104,17 +105,17 @@ private:
|
||||||
const ValueMap *data,
|
const ValueMap *data,
|
||||||
const QString *scansError,
|
const QString *scansError,
|
||||||
const ValueMap *scansData,
|
const ValueMap *scansData,
|
||||||
const QString &missingScansError,
|
ScanListData &&scans,
|
||||||
std::vector<ScanInfo> &&files,
|
base::optional<ScanListData> &&translations,
|
||||||
std::map<SpecialFile, ScanInfo> &&specialFiles);
|
std::map<FileType, ScanInfo> &&specialFiles);
|
||||||
not_null<Ui::RpWidget*> setupContent(
|
not_null<Ui::RpWidget*> setupContent(
|
||||||
const QString *error,
|
const QString *error,
|
||||||
const ValueMap *data,
|
const ValueMap *data,
|
||||||
const QString *scansError,
|
const QString *scansError,
|
||||||
const ValueMap *scansData,
|
const ValueMap *scansData,
|
||||||
const QString &missingScansError,
|
ScanListData &&scans,
|
||||||
std::vector<ScanInfo> &&files,
|
base::optional<ScanListData> &&translations,
|
||||||
std::map<SpecialFile, ScanInfo> &&specialFiles);
|
std::map<FileType, ScanInfo> &&specialFiles);
|
||||||
void updateControlsGeometry();
|
void updateControlsGeometry();
|
||||||
void updateCommonError();
|
void updateCommonError();
|
||||||
|
|
||||||
|
|
|
@ -120,6 +120,173 @@ struct EditScans::SpecialScan {
|
||||||
rpl::variable<bool> rowCreated;
|
rpl::variable<bool> rowCreated;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void UpdateFileRow(
|
||||||
|
not_null<ScanButton*> button,
|
||||||
|
const ScanInfo &info) {
|
||||||
|
button->setStatus(info.status);
|
||||||
|
button->setImage(info.thumb);
|
||||||
|
button->setDeleted(info.deleted);
|
||||||
|
button->setError(!info.error.isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
base::unique_qptr<Ui::SlideWrap<ScanButton>> 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,
|
||||||
|
!info.error.isEmpty()))));
|
||||||
|
result->entity()->setImage(info.thumb);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
EditScans::List::List(
|
||||||
|
not_null<PanelController*> controller,
|
||||||
|
ScanListData &&data)
|
||||||
|
: controller(controller)
|
||||||
|
, files(std::move(data.files))
|
||||||
|
, initialCount(int(files.size()))
|
||||||
|
, errorMissing(data.errorMissing) {
|
||||||
|
}
|
||||||
|
|
||||||
|
EditScans::List::List(
|
||||||
|
not_null<PanelController*> controller,
|
||||||
|
base::optional<ScanListData> &&data)
|
||||||
|
: controller(controller)
|
||||||
|
, files(data ? std::move(data->files) : std::vector<ScanInfo>())
|
||||||
|
, initialCount(data ? base::make_optional(int(files.size())) : base::none)
|
||||||
|
, errorMissing(data ? std::move(data->errorMissing) : QString()) {
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EditScans::List::uploadedSomeMore() const {
|
||||||
|
if (!initialCount) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const auto from = begin(files) + *initialCount;
|
||||||
|
const auto till = end(files);
|
||||||
|
return std::find_if(from, till, [](const ScanInfo &file) {
|
||||||
|
return !file.deleted;
|
||||||
|
}) != till;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EditScans::List::uploadMoreRequired() const {
|
||||||
|
if (!upload) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const auto exists = ranges::find_if(
|
||||||
|
files,
|
||||||
|
[](const ScanInfo &file) { return !file.deleted; }) != end(files);
|
||||||
|
if (!exists) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const auto errorExists = ranges::find_if(
|
||||||
|
files,
|
||||||
|
[](const ScanInfo &file) { return !file.error.isEmpty(); }
|
||||||
|
) != end(files);
|
||||||
|
return (errorExists || uploadMoreError) && !uploadedSomeMore();
|
||||||
|
}
|
||||||
|
|
||||||
|
Ui::SlideWrap<ScanButton> *EditScans::List::nonDeletedErrorRow() const {
|
||||||
|
const auto nonDeletedErrorIt = ranges::find_if(
|
||||||
|
files,
|
||||||
|
[](const ScanInfo &file) {
|
||||||
|
return !file.error.isEmpty() && !file.deleted;
|
||||||
|
});
|
||||||
|
if (nonDeletedErrorIt == end(files)) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
const auto index = (nonDeletedErrorIt - begin(files));
|
||||||
|
return rows[index].get();
|
||||||
|
}
|
||||||
|
|
||||||
|
rpl::producer<QString> EditScans::List::uploadButtonText() const {
|
||||||
|
return Lang::Viewer(files.empty()
|
||||||
|
? lng_passport_upload_scans
|
||||||
|
: lng_passport_upload_more) | Info::Profile::ToUpperValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
void EditScans::List::hideError() {
|
||||||
|
toggleError(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EditScans::List::toggleError(bool shown) {
|
||||||
|
if (errorShown != shown) {
|
||||||
|
errorShown = shown;
|
||||||
|
errorAnimation.start(
|
||||||
|
[=] { errorAnimationCallback(); },
|
||||||
|
errorShown ? 0. : 1.,
|
||||||
|
errorShown ? 1. : 0.,
|
||||||
|
st::passportDetailsField.duration);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EditScans::List::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::List::updateScan(ScanInfo &&info, int width) {
|
||||||
|
const auto i = ranges::find(files, info.key, [](const ScanInfo &file) {
|
||||||
|
return file.key;
|
||||||
|
});
|
||||||
|
if (i != files.end()) {
|
||||||
|
*i = std::move(info);
|
||||||
|
const auto scan = rows[i - files.begin()]->entity();
|
||||||
|
UpdateFileRow(scan, *i);
|
||||||
|
if (!i->deleted) {
|
||||||
|
hideError();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
files.push_back(std::move(info));
|
||||||
|
pushScan(files.back());
|
||||||
|
wrap->resizeToWidth(width);
|
||||||
|
rows.back()->show(anim::type::normal);
|
||||||
|
if (divider) {
|
||||||
|
divider->hide(anim::type::normal);
|
||||||
|
}
|
||||||
|
header->show(anim::type::normal);
|
||||||
|
uploadTexts.fire(uploadButtonText());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EditScans::List::pushScan(const ScanInfo &info) {
|
||||||
|
const auto index = rows.size();
|
||||||
|
const auto type = info.type;
|
||||||
|
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->deleteClicks(
|
||||||
|
) | rpl::start_with_next([=] {
|
||||||
|
controller->deleteScan(type, index);
|
||||||
|
}, scan->lifetime());
|
||||||
|
|
||||||
|
scan->restoreClicks(
|
||||||
|
) | rpl::start_with_next([=] {
|
||||||
|
controller->restoreScan(type, index);
|
||||||
|
}, scan->lifetime());
|
||||||
|
|
||||||
|
hideError();
|
||||||
|
}
|
||||||
|
|
||||||
ScanButton::ScanButton(
|
ScanButton::ScanButton(
|
||||||
QWidget *parent,
|
QWidget *parent,
|
||||||
const style::PassportScanRow &st,
|
const style::PassportScanRow &st,
|
||||||
|
@ -258,15 +425,14 @@ EditScans::EditScans(
|
||||||
not_null<PanelController*> controller,
|
not_null<PanelController*> controller,
|
||||||
const QString &header,
|
const QString &header,
|
||||||
const QString &error,
|
const QString &error,
|
||||||
const QString &errorMissing,
|
ScanListData &&scans,
|
||||||
std::vector<ScanInfo> &&files)
|
base::optional<ScanListData> &&translations)
|
||||||
: RpWidget(parent)
|
: RpWidget(parent)
|
||||||
, _controller(controller)
|
, _controller(controller)
|
||||||
, _files(std::move(files))
|
|
||||||
, _initialCount(_files.size())
|
|
||||||
, _error(error)
|
, _error(error)
|
||||||
, _errorMissing(errorMissing)
|
, _content(this)
|
||||||
, _content(this) {
|
, _scansList(_controller, std::move(scans))
|
||||||
|
, _translationsList(_controller, std::move(translations)) {
|
||||||
setupScans(header);
|
setupScans(header);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -274,26 +440,17 @@ EditScans::EditScans(
|
||||||
QWidget *parent,
|
QWidget *parent,
|
||||||
not_null<PanelController*> controller,
|
not_null<PanelController*> controller,
|
||||||
const QString &error,
|
const QString &error,
|
||||||
std::map<SpecialFile, ScanInfo> &&specialFiles)
|
std::map<FileType, ScanInfo> &&specialFiles,
|
||||||
|
base::optional<ScanListData> &&translations)
|
||||||
: RpWidget(parent)
|
: RpWidget(parent)
|
||||||
, _controller(controller)
|
, _controller(controller)
|
||||||
, _initialCount(-1)
|
|
||||||
, _error(error)
|
, _error(error)
|
||||||
, _content(this) {
|
, _content(this)
|
||||||
|
, _scansList(_controller)
|
||||||
|
, _translationsList(_controller, std::move(translations)) {
|
||||||
setupSpecialScans(std::move(specialFiles));
|
setupSpecialScans(std::move(specialFiles));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EditScans::uploadedSomeMore() const {
|
|
||||||
if (_initialCount < 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const auto from = begin(_files) + _initialCount;
|
|
||||||
const auto till = end(_files);
|
|
||||||
return std::find_if(from, till, [](const ScanInfo &file) {
|
|
||||||
return !file.deleted;
|
|
||||||
}) != till;
|
|
||||||
}
|
|
||||||
|
|
||||||
base::optional<int> EditScans::validateGetErrorTop() {
|
base::optional<int> EditScans::validateGetErrorTop() {
|
||||||
auto result = base::optional<int>();
|
auto result = base::optional<int>();
|
||||||
const auto suggestResult = [&](int value) {
|
const auto suggestResult = [&](int value) {
|
||||||
|
@ -302,33 +459,23 @@ base::optional<int> EditScans::validateGetErrorTop() {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const auto exists = ranges::find_if(
|
|
||||||
_files,
|
|
||||||
[](const ScanInfo &file) { return !file.deleted; }) != end(_files);
|
|
||||||
const auto errorExists = ranges::find_if(
|
|
||||||
_files,
|
|
||||||
[](const ScanInfo &file) { return !file.error.isEmpty(); }
|
|
||||||
) != end(_files);
|
|
||||||
|
|
||||||
if (_commonError && !somethingChanged()) {
|
if (_commonError && !somethingChanged()) {
|
||||||
suggestResult(_commonError->y());
|
suggestResult(_commonError->y());
|
||||||
}
|
}
|
||||||
if (_upload && (!exists
|
const auto suggestList = [&](FileType type) {
|
||||||
|| ((errorExists || _uploadMoreError) && !uploadedSomeMore()))) {
|
auto &list = this->list(type);
|
||||||
toggleError(true);
|
if (list.uploadMoreRequired()) {
|
||||||
suggestResult((_files.size() > 5) ? _upload->y() : _header->y());
|
list.toggleError(true);
|
||||||
}
|
suggestResult((list.files.size() > 5)
|
||||||
|
? list.upload->y()
|
||||||
const auto nonDeletedErrorIt = ranges::find_if(
|
: list.header->y());
|
||||||
_files,
|
}
|
||||||
[](const ScanInfo &file) {
|
if (const auto row = list.nonDeletedErrorRow()) {
|
||||||
return !file.error.isEmpty() && !file.deleted;
|
//toggleError(true);
|
||||||
});
|
suggestResult(row->y());
|
||||||
if (nonDeletedErrorIt != end(_files)) {
|
}
|
||||||
const auto index = (nonDeletedErrorIt - begin(_files));
|
};
|
||||||
// toggleError(true);
|
suggestList(FileType::Scan);
|
||||||
suggestResult(_rows[index]->y());
|
|
||||||
}
|
|
||||||
for (const auto &[type, scan] : _specialScans) {
|
for (const auto &[type, scan] : _specialScans) {
|
||||||
if (!scan.file.key.id
|
if (!scan.file.key.id
|
||||||
|| scan.file.deleted
|
|| scan.file.deleted
|
||||||
|
@ -337,9 +484,26 @@ base::optional<int> EditScans::validateGetErrorTop() {
|
||||||
suggestResult(scan.header->y());
|
suggestResult(scan.header->y());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
suggestList(FileType::Translation);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
EditScans::List &EditScans::list(FileType type) {
|
||||||
|
switch (type) {
|
||||||
|
case FileType::Scan: return _scansList;
|
||||||
|
case FileType::Translation: return _translationsList;
|
||||||
|
}
|
||||||
|
Unexpected("Type in EditScans::list().");
|
||||||
|
}
|
||||||
|
|
||||||
|
const EditScans::List &EditScans::list(FileType type) const {
|
||||||
|
switch (type) {
|
||||||
|
case FileType::Scan: return _scansList;
|
||||||
|
case FileType::Translation: return _translationsList;
|
||||||
|
}
|
||||||
|
Unexpected("Type in EditScans::list() const.");
|
||||||
|
}
|
||||||
|
|
||||||
void EditScans::setupScans(const QString &header) {
|
void EditScans::setupScans(const QString &header) {
|
||||||
const auto inner = _content.data();
|
const auto inner = _content.data();
|
||||||
inner->move(0, 0);
|
inner->move(0, 0);
|
||||||
|
@ -357,80 +521,96 @@ void EditScans::setupScans(const QString &header) {
|
||||||
_commonError->toggle(true, anim::type::instant);
|
_commonError->toggle(true, anim::type::instant);
|
||||||
}
|
}
|
||||||
|
|
||||||
_divider = inner->add(
|
setupList(inner, FileType::Scan, header);
|
||||||
object_ptr<Ui::SlideWrap<BoxContentDivider>>(
|
setupList(inner, FileType::Translation, "Translations");
|
||||||
inner,
|
|
||||||
object_ptr<BoxContentDivider>(
|
|
||||||
inner,
|
|
||||||
st::passportFormDividerHeight)));
|
|
||||||
_divider->toggle(_files.empty(), anim::type::instant);
|
|
||||||
|
|
||||||
_header = inner->add(
|
|
||||||
object_ptr<Ui::SlideWrap<Ui::FlatLabel>>(
|
|
||||||
inner,
|
|
||||||
object_ptr<Ui::FlatLabel>(
|
|
||||||
inner,
|
|
||||||
header,
|
|
||||||
Ui::FlatLabel::InitType::Simple,
|
|
||||||
st::passportFormHeader),
|
|
||||||
st::passportUploadHeaderPadding));
|
|
||||||
_header->toggle(!_files.empty(), anim::type::instant);
|
|
||||||
if (!_errorMissing.isEmpty()) {
|
|
||||||
_uploadMoreError = inner->add(
|
|
||||||
object_ptr<Ui::SlideWrap<Ui::FlatLabel>>(
|
|
||||||
inner,
|
|
||||||
object_ptr<Ui::FlatLabel>(
|
|
||||||
inner,
|
|
||||||
_errorMissing,
|
|
||||||
Ui::FlatLabel::InitType::Simple,
|
|
||||||
st::passportVerifyErrorLabel),
|
|
||||||
st::passportUploadErrorPadding));
|
|
||||||
_uploadMoreError->toggle(true, anim::type::instant);
|
|
||||||
}
|
|
||||||
_wrap = inner->add(object_ptr<Ui::VerticalLayout>(inner));
|
|
||||||
for (const auto &scan : _files) {
|
|
||||||
pushScan(scan);
|
|
||||||
_rows.back()->show(anim::type::instant);
|
|
||||||
}
|
|
||||||
|
|
||||||
_upload = inner->add(
|
|
||||||
object_ptr<Info::Profile::Button>(
|
|
||||||
inner,
|
|
||||||
_uploadTexts.events_starting_with(
|
|
||||||
uploadButtonText()
|
|
||||||
) | rpl::flatten_latest(),
|
|
||||||
st::passportUploadButton),
|
|
||||||
st::passportUploadButtonPadding);
|
|
||||||
_upload->addClickHandler([=] {
|
|
||||||
chooseScan();
|
|
||||||
});
|
|
||||||
|
|
||||||
inner->add(object_ptr<BoxContentDivider>(
|
|
||||||
inner,
|
|
||||||
st::passportFormDividerHeight));
|
|
||||||
|
|
||||||
init();
|
init();
|
||||||
}
|
}
|
||||||
|
|
||||||
void EditScans::setupSpecialScans(std::map<SpecialFile, ScanInfo> &&files) {
|
void EditScans::setupList(
|
||||||
const auto requiresBothSides = files.find(SpecialFile::ReverseSide)
|
not_null<Ui::VerticalLayout*> container,
|
||||||
|
FileType type,
|
||||||
|
const QString &header) {
|
||||||
|
auto &list = this->list(type);
|
||||||
|
if (!list.initialCount) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type == FileType::Scan) {
|
||||||
|
list.divider = container->add(
|
||||||
|
object_ptr<Ui::SlideWrap<BoxContentDivider>>(
|
||||||
|
container,
|
||||||
|
object_ptr<BoxContentDivider>(
|
||||||
|
container,
|
||||||
|
st::passportFormDividerHeight)));
|
||||||
|
list.divider->toggle(list.files.empty(), anim::type::instant);
|
||||||
|
}
|
||||||
|
list.header = container->add(
|
||||||
|
object_ptr<Ui::SlideWrap<Ui::FlatLabel>>(
|
||||||
|
container,
|
||||||
|
object_ptr<Ui::FlatLabel>(
|
||||||
|
container,
|
||||||
|
header,
|
||||||
|
Ui::FlatLabel::InitType::Simple,
|
||||||
|
st::passportFormHeader),
|
||||||
|
st::passportUploadHeaderPadding));
|
||||||
|
list.header->toggle(
|
||||||
|
!list.divider || !list.files.empty(),
|
||||||
|
anim::type::instant);
|
||||||
|
if (!list.errorMissing.isEmpty()) {
|
||||||
|
list.uploadMoreError = container->add(
|
||||||
|
object_ptr<Ui::SlideWrap<Ui::FlatLabel>>(
|
||||||
|
container,
|
||||||
|
object_ptr<Ui::FlatLabel>(
|
||||||
|
container,
|
||||||
|
list.errorMissing,
|
||||||
|
Ui::FlatLabel::InitType::Simple,
|
||||||
|
st::passportVerifyErrorLabel),
|
||||||
|
st::passportUploadErrorPadding));
|
||||||
|
list.uploadMoreError->toggle(true, anim::type::instant);
|
||||||
|
}
|
||||||
|
list.wrap = container->add(object_ptr<Ui::VerticalLayout>(container));
|
||||||
|
for (const auto &scan : list.files) {
|
||||||
|
list.pushScan(scan);
|
||||||
|
list.rows.back()->show(anim::type::instant);
|
||||||
|
}
|
||||||
|
|
||||||
|
list.upload = container->add(
|
||||||
|
object_ptr<Info::Profile::Button>(
|
||||||
|
container,
|
||||||
|
list.uploadTexts.events_starting_with(
|
||||||
|
list.uploadButtonText()
|
||||||
|
) | rpl::flatten_latest(),
|
||||||
|
st::passportUploadButton),
|
||||||
|
st::passportUploadButtonPadding);
|
||||||
|
list.upload->addClickHandler([=] {
|
||||||
|
chooseScan(type);
|
||||||
|
});
|
||||||
|
|
||||||
|
container->add(object_ptr<BoxContentDivider>(
|
||||||
|
container,
|
||||||
|
st::passportFormDividerHeight));
|
||||||
|
}
|
||||||
|
|
||||||
|
void EditScans::setupSpecialScans(std::map<FileType, ScanInfo> &&files) {
|
||||||
|
const auto requiresBothSides = files.find(FileType::ReverseSide)
|
||||||
!= end(files);
|
!= end(files);
|
||||||
const auto title = [&](SpecialFile type) {
|
const auto title = [&](FileType type) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case SpecialFile::FrontSide:
|
case FileType::FrontSide:
|
||||||
return lang(requiresBothSides
|
return lang(requiresBothSides
|
||||||
? lng_passport_front_side_title
|
? lng_passport_front_side_title
|
||||||
: lng_passport_main_page_title);
|
: lng_passport_main_page_title);
|
||||||
case SpecialFile::ReverseSide:
|
case FileType::ReverseSide:
|
||||||
return lang(lng_passport_reverse_side_title);
|
return lang(lng_passport_reverse_side_title);
|
||||||
case SpecialFile::Selfie:
|
case FileType::Selfie:
|
||||||
return lang(lng_passport_selfie_title);
|
return lang(lng_passport_selfie_title);
|
||||||
}
|
}
|
||||||
Unexpected("Type in special row title.");
|
Unexpected("Type in special row title.");
|
||||||
};
|
};
|
||||||
const auto uploadKey = [=](SpecialFile type, bool hasScan) {
|
const auto uploadKey = [=](FileType type, bool hasScan) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case SpecialFile::FrontSide:
|
case FileType::FrontSide:
|
||||||
return requiresBothSides
|
return requiresBothSides
|
||||||
? (hasScan
|
? (hasScan
|
||||||
? lng_passport_reupload_front_side
|
? lng_passport_reupload_front_side
|
||||||
|
@ -438,26 +618,26 @@ void EditScans::setupSpecialScans(std::map<SpecialFile, ScanInfo> &&files) {
|
||||||
: (hasScan
|
: (hasScan
|
||||||
? lng_passport_reupload_main_page
|
? lng_passport_reupload_main_page
|
||||||
: lng_passport_upload_main_page);
|
: lng_passport_upload_main_page);
|
||||||
case SpecialFile::ReverseSide:
|
case FileType::ReverseSide:
|
||||||
return hasScan
|
return hasScan
|
||||||
? lng_passport_reupload_reverse_side
|
? lng_passport_reupload_reverse_side
|
||||||
: lng_passport_upload_reverse_side;
|
: lng_passport_upload_reverse_side;
|
||||||
case SpecialFile::Selfie:
|
case FileType::Selfie:
|
||||||
return hasScan
|
return hasScan
|
||||||
? lng_passport_reupload_selfie
|
? lng_passport_reupload_selfie
|
||||||
: lng_passport_upload_selfie;
|
: lng_passport_upload_selfie;
|
||||||
}
|
}
|
||||||
Unexpected("Type in special row upload key.");
|
Unexpected("Type in special row upload key.");
|
||||||
};
|
};
|
||||||
const auto description = [&](SpecialFile type) {
|
const auto description = [&](FileType type) {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case SpecialFile::FrontSide:
|
case FileType::FrontSide:
|
||||||
return lang(requiresBothSides
|
return lang(requiresBothSides
|
||||||
? lng_passport_front_side_description
|
? lng_passport_front_side_description
|
||||||
: lng_passport_main_page_description);
|
: lng_passport_main_page_description);
|
||||||
case SpecialFile::ReverseSide:
|
case FileType::ReverseSide:
|
||||||
return lang(lng_passport_reverse_side_description);
|
return lang(lng_passport_reverse_side_description);
|
||||||
case SpecialFile::Selfie:
|
case FileType::Selfie:
|
||||||
return lang(lng_passport_selfie_description);
|
return lang(lng_passport_selfie_description);
|
||||||
}
|
}
|
||||||
Unexpected("Type in special row upload key.");
|
Unexpected("Type in special row upload key.");
|
||||||
|
@ -511,7 +691,7 @@ void EditScans::setupSpecialScans(std::map<SpecialFile, ScanInfo> &&files) {
|
||||||
st::passportUploadButton),
|
st::passportUploadButton),
|
||||||
st::passportUploadButtonPadding);
|
st::passportUploadButtonPadding);
|
||||||
scan.upload->addClickHandler([=, type = type] {
|
scan.upload->addClickHandler([=, type = type] {
|
||||||
chooseSpecialScan(type);
|
chooseScan(type);
|
||||||
});
|
});
|
||||||
|
|
||||||
inner->add(object_ptr<Ui::DividerLabel>(
|
inner->add(object_ptr<Ui::DividerLabel>(
|
||||||
|
@ -524,6 +704,8 @@ void EditScans::setupSpecialScans(std::map<SpecialFile, ScanInfo> &&files) {
|
||||||
st::passportFormLabelPadding));
|
st::passportFormLabelPadding));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setupList(inner, FileType::Translation, lang(lng_passport_translation));
|
||||||
|
|
||||||
init();
|
init();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -545,29 +727,11 @@ void EditScans::init() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void EditScans::updateScan(ScanInfo &&info) {
|
void EditScans::updateScan(ScanInfo &&info) {
|
||||||
if (info.special) {
|
if (info.type != FileType::Scan && info.type != FileType::Translation) {
|
||||||
updateSpecialScan(*info.special, std::move(info));
|
updateSpecialScan(std::move(info));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const auto i = ranges::find(_files, info.key, [](const ScanInfo &file) {
|
list(info.type).updateScan(std::move(info), width());
|
||||||
return file.key;
|
|
||||||
});
|
|
||||||
if (i != _files.end()) {
|
|
||||||
*i = std::move(info);
|
|
||||||
const auto scan = _rows[i - _files.begin()]->entity();
|
|
||||||
updateFileRow(scan, *i);
|
|
||||||
if (!i->deleted) {
|
|
||||||
hideError();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
_files.push_back(std::move(info));
|
|
||||||
pushScan(_files.back());
|
|
||||||
_wrap->resizeToWidth(width());
|
|
||||||
_rows.back()->show(anim::type::normal);
|
|
||||||
_divider->hide(anim::type::normal);
|
|
||||||
_header->show(anim::type::normal);
|
|
||||||
_uploadTexts.fire(uploadButtonText());
|
|
||||||
}
|
|
||||||
updateErrorLabels();
|
updateErrorLabels();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -579,35 +743,46 @@ void EditScans::scanFieldsChanged(bool changed) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void EditScans::updateErrorLabels() {
|
void EditScans::updateErrorLabels() {
|
||||||
if (_uploadMoreError) {
|
const auto updateList = [&](FileType type) {
|
||||||
_uploadMoreError->toggle(!uploadedSomeMore(), anim::type::normal);
|
auto &list = this->list(type);
|
||||||
}
|
if (list.uploadMoreError) {
|
||||||
|
list.uploadMoreError->toggle(
|
||||||
|
!list.uploadedSomeMore(),
|
||||||
|
anim::type::normal);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
updateList(FileType::Scan);
|
||||||
|
updateList(FileType::Translation);
|
||||||
if (_commonError) {
|
if (_commonError) {
|
||||||
_commonError->toggle(!somethingChanged(), anim::type::normal);
|
_commonError->toggle(!somethingChanged(), anim::type::normal);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EditScans::somethingChanged() const {
|
bool EditScans::somethingChanged() const {
|
||||||
return uploadedSomeMore() || _scanFieldsChanged || _specialScanChanged;
|
return list(FileType::Scan).uploadedSomeMore()
|
||||||
|
|| list(FileType::Translation).uploadedSomeMore()
|
||||||
|
|| _scanFieldsChanged
|
||||||
|
|| _specialScanChanged;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EditScans::updateSpecialScan(SpecialFile type, ScanInfo &&info) {
|
void EditScans::updateSpecialScan(ScanInfo &&info) {
|
||||||
Expects(info.key.id != 0);
|
Expects(info.key.id != 0);
|
||||||
|
|
||||||
|
const auto type = info.type;
|
||||||
const auto i = _specialScans.find(type);
|
const auto i = _specialScans.find(type);
|
||||||
if (i == end(_specialScans)) {
|
if (i == end(_specialScans)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
auto &scan = i->second;
|
auto &scan = i->second;
|
||||||
if (scan.file.key.id) {
|
if (scan.file.key.id) {
|
||||||
updateFileRow(scan.row->entity(), info);
|
UpdateFileRow(scan.row->entity(), info);
|
||||||
scan.rowCreated = !info.deleted;
|
scan.rowCreated = !info.deleted;
|
||||||
if (scan.file.key.id != info.key.id) {
|
if (scan.file.key.id != info.key.id) {
|
||||||
specialScanChanged(type, true);
|
specialScanChanged(type, true);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
const auto requiresBothSides
|
const auto requiresBothSides
|
||||||
= (_specialScans.find(SpecialFile::ReverseSide)
|
= (_specialScans.find(FileType::ReverseSide)
|
||||||
!= end(_specialScans));
|
!= end(_specialScans));
|
||||||
createSpecialScanRow(scan, info, requiresBothSides);
|
createSpecialScanRow(scan, info, requiresBothSides);
|
||||||
scan.wrap->resizeToWidth(width());
|
scan.wrap->resizeToWidth(width());
|
||||||
|
@ -618,117 +793,60 @@ void EditScans::updateSpecialScan(SpecialFile type, ScanInfo &&info) {
|
||||||
scan.file = std::move(info);
|
scan.file = std::move(info);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EditScans::updateFileRow(
|
|
||||||
not_null<ScanButton*> button,
|
|
||||||
const ScanInfo &info) {
|
|
||||||
button->setStatus(info.status);
|
|
||||||
button->setImage(info.thumb);
|
|
||||||
button->setDeleted(info.deleted);
|
|
||||||
button->setError(!info.error.isEmpty());
|
|
||||||
}
|
|
||||||
|
|
||||||
void EditScans::createSpecialScanRow(
|
void EditScans::createSpecialScanRow(
|
||||||
SpecialScan &scan,
|
SpecialScan &scan,
|
||||||
const ScanInfo &info,
|
const ScanInfo &info,
|
||||||
bool requiresBothSides) {
|
bool requiresBothSides) {
|
||||||
Expects(scan.file.special.has_value());
|
Expects(scan.file.type != FileType::Scan
|
||||||
|
&& scan.file.type != FileType::Translation);
|
||||||
|
|
||||||
const auto type = *scan.file.special;
|
const auto type = scan.file.type;
|
||||||
const auto name = [&] {
|
const auto name = [&] {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case SpecialFile::FrontSide:
|
case FileType::FrontSide:
|
||||||
return lang(requiresBothSides
|
return lang(requiresBothSides
|
||||||
? lng_passport_front_side_name
|
? lng_passport_front_side_name
|
||||||
: lng_passport_main_page_name);
|
: lng_passport_main_page_name);
|
||||||
case SpecialFile::ReverseSide:
|
case FileType::ReverseSide:
|
||||||
return lang(lng_passport_reverse_side_name);
|
return lang(lng_passport_reverse_side_name);
|
||||||
case SpecialFile::Selfie:
|
case FileType::Selfie:
|
||||||
return lang(lng_passport_selfie_name);
|
return lang(lng_passport_selfie_name);
|
||||||
}
|
}
|
||||||
Unexpected("Type in special file name.");
|
Unexpected("Type in special file name.");
|
||||||
}();
|
}();
|
||||||
scan.row = createScan(scan.wrap, info, name);
|
scan.row = CreateScan(scan.wrap, info, name);
|
||||||
const auto row = scan.row->entity();
|
const auto row = scan.row->entity();
|
||||||
|
|
||||||
row->deleteClicks(
|
row->deleteClicks(
|
||||||
) | rpl::start_with_next([=] {
|
) | rpl::start_with_next([=] {
|
||||||
_controller->deleteSpecialScan(type);
|
_controller->deleteScan(type, base::none);
|
||||||
}, row->lifetime());
|
}, row->lifetime());
|
||||||
|
|
||||||
row->restoreClicks(
|
row->restoreClicks(
|
||||||
) | rpl::start_with_next([=] {
|
) | rpl::start_with_next([=] {
|
||||||
_controller->restoreSpecialScan(type);
|
_controller->restoreScan(type, base::none);
|
||||||
}, row->lifetime());
|
}, row->lifetime());
|
||||||
|
|
||||||
scan.rowCreated = !info.deleted;
|
scan.rowCreated = !info.deleted;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EditScans::pushScan(const ScanInfo &info) {
|
void EditScans::chooseScan(FileType type) {
|
||||||
const auto index = _rows.size();
|
if (!_controller->canAddScan(type)) {
|
||||||
_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->deleteClicks(
|
|
||||||
) | rpl::start_with_next([=] {
|
|
||||||
_controller->deleteScan(index);
|
|
||||||
}, scan->lifetime());
|
|
||||||
|
|
||||||
scan->restoreClicks(
|
|
||||||
) | rpl::start_with_next([=] {
|
|
||||||
_controller->restoreScan(index);
|
|
||||||
}, scan->lifetime());
|
|
||||||
|
|
||||||
hideError();
|
|
||||||
}
|
|
||||||
|
|
||||||
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,
|
|
||||||
!info.error.isEmpty()))));
|
|
||||||
result->entity()->setImage(info.thumb);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
void EditScans::chooseScan() {
|
|
||||||
if (!_controller->canAddScan()) {
|
|
||||||
_controller->showToast(lang(lng_passport_scans_limit_reached));
|
_controller->showToast(lang(lng_passport_scans_limit_reached));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
ChooseScan(this, [=](QByteArray &&content) {
|
ChooseScan(this, type, [=](QByteArray &&content) {
|
||||||
_controller->uploadScan(std::move(content));
|
_controller->uploadScan(type, std::move(content));
|
||||||
}, [=](ReadScanError error) {
|
}, [=](ReadScanError error) {
|
||||||
_controller->readScanError(error);
|
_controller->readScanError(error);
|
||||||
}, true);
|
});
|
||||||
}
|
|
||||||
|
|
||||||
void EditScans::chooseSpecialScan(SpecialFile type) {
|
|
||||||
ChooseScan(this, [=](QByteArray &&content) {
|
|
||||||
_controller->uploadSpecialScan(type, std::move(content));
|
|
||||||
}, [=](ReadScanError error) {
|
|
||||||
_controller->readScanError(error);
|
|
||||||
}, false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void EditScans::ChooseScan(
|
void EditScans::ChooseScan(
|
||||||
QPointer<QWidget> parent,
|
QPointer<QWidget> parent,
|
||||||
|
FileType type,
|
||||||
Fn<void(QByteArray&&)> doneCallback,
|
Fn<void(QByteArray&&)> doneCallback,
|
||||||
Fn<void(ReadScanError)> errorCallback,
|
Fn<void(ReadScanError)> errorCallback) {
|
||||||
bool allowMany) {
|
|
||||||
Expects(parent != nullptr);
|
Expects(parent != nullptr);
|
||||||
|
|
||||||
const auto processFiles = std::make_shared<Fn<void(QStringList&&)>>();
|
const auto processFiles = std::make_shared<Fn<void(QStringList&&)>>();
|
||||||
|
@ -802,6 +920,8 @@ void EditScans::ChooseScan(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
const auto allowMany = (type == FileType::Scan)
|
||||||
|
|| (type == FileType::Translation);
|
||||||
(allowMany ? FileDialog::GetOpenPaths : FileDialog::GetOpenPath)(
|
(allowMany ? FileDialog::GetOpenPaths : FileDialog::GetOpenPath)(
|
||||||
parent,
|
parent,
|
||||||
lang(lng_passport_choose_image),
|
lang(lng_passport_choose_image),
|
||||||
|
@ -810,44 +930,11 @@ void EditScans::ChooseScan(
|
||||||
nullptr);
|
nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
rpl::producer<QString> EditScans::uploadButtonText() const {
|
void EditScans::hideSpecialScanError(FileType type) {
|
||||||
return Lang::Viewer(_files.empty()
|
|
||||||
? lng_passport_upload_scans
|
|
||||||
: 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::hideSpecialScanError(SpecialFile type) {
|
|
||||||
toggleSpecialScanError(type, false);
|
toggleSpecialScanError(type, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void EditScans::specialScanChanged(SpecialFile type, bool changed) {
|
void EditScans::specialScanChanged(FileType type, bool changed) {
|
||||||
hideSpecialScanError(type);
|
hideSpecialScanError(type);
|
||||||
if (_specialScanChanged != changed) {
|
if (_specialScanChanged != changed) {
|
||||||
_specialScanChanged = changed;
|
_specialScanChanged = changed;
|
||||||
|
@ -855,13 +942,13 @@ void EditScans::specialScanChanged(SpecialFile type, bool changed) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto EditScans::findSpecialScan(SpecialFile type) -> SpecialScan& {
|
auto EditScans::findSpecialScan(FileType type) -> SpecialScan& {
|
||||||
const auto i = _specialScans.find(type);
|
const auto i = _specialScans.find(type);
|
||||||
Assert(i != end(_specialScans));
|
Assert(i != end(_specialScans));
|
||||||
return i->second;
|
return i->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
void EditScans::toggleSpecialScanError(SpecialFile type, bool shown) {
|
void EditScans::toggleSpecialScanError(FileType type, bool shown) {
|
||||||
auto &scan = findSpecialScan(type);
|
auto &scan = findSpecialScan(type);
|
||||||
if (scan.errorShown != shown) {
|
if (scan.errorShown != shown) {
|
||||||
scan.errorShown = shown;
|
scan.errorShown = shown;
|
||||||
|
@ -873,7 +960,7 @@ void EditScans::toggleSpecialScanError(SpecialFile type, bool shown) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void EditScans::specialScanErrorAnimationCallback(SpecialFile type) {
|
void EditScans::specialScanErrorAnimationCallback(FileType type) {
|
||||||
auto &scan = findSpecialScan(type);
|
auto &scan = findSpecialScan(type);
|
||||||
const auto error = scan.errorAnimation.current(
|
const auto error = scan.errorAnimation.current(
|
||||||
scan.errorShown ? 1. : 0.);
|
scan.errorShown ? 1. : 0.);
|
||||||
|
|
|
@ -26,7 +26,7 @@ class Button;
|
||||||
|
|
||||||
namespace Passport {
|
namespace Passport {
|
||||||
|
|
||||||
enum class SpecialFile;
|
enum class FileType;
|
||||||
class PanelController;
|
class PanelController;
|
||||||
class ScanButton;
|
class ScanButton;
|
||||||
struct ScanInfo;
|
struct ScanInfo;
|
||||||
|
@ -38,6 +38,11 @@ enum class ReadScanError {
|
||||||
Unknown,
|
Unknown,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct ScanListData {
|
||||||
|
std::vector<ScanInfo> files;
|
||||||
|
QString errorMissing;
|
||||||
|
};
|
||||||
|
|
||||||
class EditScans : public Ui::RpWidget {
|
class EditScans : public Ui::RpWidget {
|
||||||
public:
|
public:
|
||||||
EditScans(
|
EditScans(
|
||||||
|
@ -45,13 +50,14 @@ public:
|
||||||
not_null<PanelController*> controller,
|
not_null<PanelController*> controller,
|
||||||
const QString &header,
|
const QString &header,
|
||||||
const QString &error,
|
const QString &error,
|
||||||
const QString &errorMissing,
|
ScanListData &&scans,
|
||||||
std::vector<ScanInfo> &&files);
|
base::optional<ScanListData> &&translations);
|
||||||
EditScans(
|
EditScans(
|
||||||
QWidget *parent,
|
QWidget *parent,
|
||||||
not_null<PanelController*> controller,
|
not_null<PanelController*> controller,
|
||||||
const QString &error,
|
const QString &error,
|
||||||
std::map<SpecialFile, ScanInfo> &&specialFiles);
|
std::map<FileType, ScanInfo> &&specialFiles,
|
||||||
|
base::optional<ScanListData> &&translations);
|
||||||
|
|
||||||
base::optional<int> validateGetErrorTop();
|
base::optional<int> validateGetErrorTop();
|
||||||
|
|
||||||
|
@ -59,27 +65,59 @@ public:
|
||||||
|
|
||||||
static void ChooseScan(
|
static void ChooseScan(
|
||||||
QPointer<QWidget> parent,
|
QPointer<QWidget> parent,
|
||||||
|
FileType type,
|
||||||
Fn<void(QByteArray&&)> doneCallback,
|
Fn<void(QByteArray&&)> doneCallback,
|
||||||
Fn<void(ReadScanError)> errorCallback,
|
Fn<void(ReadScanError)> errorCallback);
|
||||||
bool allowMany);
|
|
||||||
|
|
||||||
~EditScans();
|
~EditScans();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct SpecialScan;
|
struct SpecialScan;
|
||||||
|
struct List {
|
||||||
|
List(not_null<PanelController*> controller, ScanListData &&data);
|
||||||
|
List(
|
||||||
|
not_null<PanelController*> controller,
|
||||||
|
base::optional<ScanListData> &&data = base::none);
|
||||||
|
|
||||||
|
bool uploadedSomeMore() const;
|
||||||
|
bool uploadMoreRequired() const;
|
||||||
|
Ui::SlideWrap<ScanButton> *nonDeletedErrorRow() const;
|
||||||
|
rpl::producer<QString> uploadButtonText() const;
|
||||||
|
void toggleError(bool shown);
|
||||||
|
void hideError();
|
||||||
|
void errorAnimationCallback();
|
||||||
|
void updateScan(ScanInfo &&info, int width);
|
||||||
|
void pushScan(const ScanInfo &info);
|
||||||
|
|
||||||
|
not_null<PanelController*> controller;
|
||||||
|
std::vector<ScanInfo> files;
|
||||||
|
base::optional<int> initialCount;
|
||||||
|
QString errorMissing;
|
||||||
|
QPointer<Ui::SlideWrap<BoxContentDivider>> divider;
|
||||||
|
QPointer<Ui::SlideWrap<Ui::FlatLabel>> header;
|
||||||
|
QPointer<Ui::SlideWrap<Ui::FlatLabel>> uploadMoreError;
|
||||||
|
QPointer<Ui::VerticalLayout> wrap;
|
||||||
|
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;
|
||||||
|
};
|
||||||
|
|
||||||
|
List &list(FileType type);
|
||||||
|
const List &list(FileType type) const;
|
||||||
|
|
||||||
void setupScans(const QString &header);
|
void setupScans(const QString &header);
|
||||||
void setupSpecialScans(std::map<SpecialFile, ScanInfo> &&files);
|
void setupList(
|
||||||
|
not_null<Ui::VerticalLayout*> container,
|
||||||
|
FileType type,
|
||||||
|
const QString &header);
|
||||||
|
void setupSpecialScans(std::map<FileType, ScanInfo> &&files);
|
||||||
void init();
|
void init();
|
||||||
|
|
||||||
void chooseScan();
|
void chooseScan(FileType type);
|
||||||
void chooseSpecialScan(SpecialFile type);
|
|
||||||
void updateScan(ScanInfo &&info);
|
void updateScan(ScanInfo &&info);
|
||||||
void updateSpecialScan(SpecialFile type, ScanInfo &&info);
|
void updateSpecialScan(ScanInfo &&info);
|
||||||
void updateFileRow(
|
|
||||||
not_null<ScanButton*> button,
|
|
||||||
const ScanInfo &info);
|
|
||||||
void pushScan(const ScanInfo &info);
|
|
||||||
void createSpecialScanRow(
|
void createSpecialScanRow(
|
||||||
SpecialScan &scan,
|
SpecialScan &scan,
|
||||||
const ScanInfo &info,
|
const ScanInfo &info,
|
||||||
|
@ -88,43 +126,27 @@ private:
|
||||||
not_null<Ui::VerticalLayout*> parent,
|
not_null<Ui::VerticalLayout*> parent,
|
||||||
const ScanInfo &info,
|
const ScanInfo &info,
|
||||||
const QString &name);
|
const QString &name);
|
||||||
SpecialScan &findSpecialScan(SpecialFile type);
|
SpecialScan &findSpecialScan(FileType type);
|
||||||
|
|
||||||
rpl::producer<QString> uploadButtonText() const;
|
|
||||||
|
|
||||||
void updateErrorLabels();
|
void updateErrorLabels();
|
||||||
void toggleError(bool shown);
|
|
||||||
void hideError();
|
|
||||||
void errorAnimationCallback();
|
|
||||||
bool uploadedSomeMore() const;
|
|
||||||
bool somethingChanged() const;
|
bool somethingChanged() const;
|
||||||
|
|
||||||
void toggleSpecialScanError(SpecialFile type, bool shown);
|
void toggleSpecialScanError(FileType type, bool shown);
|
||||||
void hideSpecialScanError(SpecialFile type);
|
void hideSpecialScanError(FileType type);
|
||||||
void specialScanErrorAnimationCallback(SpecialFile type);
|
void specialScanErrorAnimationCallback(FileType type);
|
||||||
void specialScanChanged(SpecialFile type, bool changed);
|
void specialScanChanged(FileType type, bool changed);
|
||||||
|
|
||||||
not_null<PanelController*> _controller;
|
not_null<PanelController*> _controller;
|
||||||
std::vector<ScanInfo> _files;
|
|
||||||
int _initialCount = 0;
|
|
||||||
QString _error;
|
QString _error;
|
||||||
QString _errorMissing;
|
|
||||||
|
|
||||||
object_ptr<Ui::VerticalLayout> _content;
|
object_ptr<Ui::VerticalLayout> _content;
|
||||||
QPointer<Ui::SlideWrap<BoxContentDivider>> _divider;
|
|
||||||
QPointer<Ui::SlideWrap<Ui::FlatLabel>> _header;
|
|
||||||
QPointer<Ui::SlideWrap<Ui::FlatLabel>> _commonError;
|
QPointer<Ui::SlideWrap<Ui::FlatLabel>> _commonError;
|
||||||
QPointer<Ui::SlideWrap<Ui::FlatLabel>> _uploadMoreError;
|
|
||||||
QPointer<Ui::VerticalLayout> _wrap;
|
|
||||||
std::vector<base::unique_qptr<Ui::SlideWrap<ScanButton>>> _rows;
|
|
||||||
QPointer<Info::Profile::Button> _upload;
|
|
||||||
rpl::event_stream<rpl::producer<QString>> _uploadTexts;
|
|
||||||
bool _scanFieldsChanged = false;
|
bool _scanFieldsChanged = false;
|
||||||
bool _specialScanChanged = false;
|
bool _specialScanChanged = false;
|
||||||
bool _errorShown = false;
|
|
||||||
Animation _errorAnimation;
|
|
||||||
|
|
||||||
std::map<SpecialFile, SpecialScan> _specialScans;
|
List _scansList;
|
||||||
|
std::map<FileType, SpecialScan> _specialScans;
|
||||||
|
List _translationsList;
|
||||||
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue