Improve name/postcode validation.
This commit is contained in:
		
							parent
							
								
									6aecb81c23
								
							
						
					
					
						commit
						308fb19da4
					
				
					 8 changed files with 165 additions and 57 deletions
				
			
		|  | @ -1591,6 +1591,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL | |||
| "lng_passport_gender_male" = "Male"; | ||||
| "lng_passport_gender_female" = "Female"; | ||||
| "lng_passport_country" = "Country"; | ||||
| "lng_passport_residence_country" = "Residence"; | ||||
| "lng_passport_country_choose" = "Choose country"; | ||||
| "lng_passport_document_number" = "Card Number"; | ||||
| "lng_passport_expiry_date" = "Expiry date"; | ||||
|  |  | |||
|  | @ -839,7 +839,7 @@ bool Messenger::openLocalUrl(const QString &url) { | |||
| 	auto command = urlTrimmed.midRef(qstr("tg://").size()); | ||||
| 
 | ||||
| 	const auto showPassportForm = [](const QMap<QString, QString> ¶ms) { | ||||
| 		if (const auto botId = params.value("bot_id", QString()).toInt()) { | ||||
| 		const auto botId = params.value("bot_id", QString()).toInt(); | ||||
| 		const auto scope = params.value("scope", QString()); | ||||
| 		const auto callback = params.value("callback_url", QString()); | ||||
| 		const auto publicKey = params.value("public_key", QString()); | ||||
|  | @ -857,7 +857,6 @@ bool Messenger::openLocalUrl(const QString &url) { | |||
| 				return true; | ||||
| 			} | ||||
| 		} | ||||
| 		} | ||||
| 		return false; | ||||
| 	}; | ||||
| 
 | ||||
|  |  | |||
|  | @ -158,7 +158,7 @@ QString ComputeScopeRowReadyString(const Scope &scope) { | |||
| 					return QString(); | ||||
| 				} | ||||
| 				const auto text = i->second.text; | ||||
| 				if (row.validate && !row.validate(text)) { | ||||
| 				if (row.error && row.error(text).has_value()) { | ||||
| 					return QString(); | ||||
| 				} | ||||
| 				pushListValue(format ? format(text) : text); | ||||
|  | @ -170,7 +170,7 @@ QString ComputeScopeRowReadyString(const Scope &scope) { | |||
| 					return QString(); | ||||
| 				} | ||||
| 				const auto text = i->second.text; | ||||
| 				if (row.validate && !row.validate(text)) { | ||||
| 				if (row.error && row.error(text).has_value()) { | ||||
| 					return QString(); | ||||
| 				} | ||||
| 				pushListValue(text); | ||||
|  |  | |||
|  | @ -28,8 +28,7 @@ constexpr auto kMaxDocumentSize = 24; | |||
| constexpr auto kMaxStreetSize = 64; | ||||
| constexpr auto kMinCitySize = 2; | ||||
| constexpr auto kMaxCitySize = 64; | ||||
| constexpr auto kMinPostcodeSize = 2; | ||||
| constexpr auto kMaxPostcodeSize = 12; | ||||
| constexpr auto kMaxPostcodeSize = 10; | ||||
| 
 | ||||
| EditDocumentScheme GetDocumentScheme( | ||||
| 		Scope::Type type, | ||||
|  | @ -50,32 +49,53 @@ EditDocumentScheme GetDocumentScheme( | |||
| 		return value; | ||||
| 	}; | ||||
| 	const auto DontValidate = nullptr; | ||||
| 	const auto LimitedValidate = [](int max, int min = 1) { | ||||
| 	const auto FromBoolean = [](auto validation) { | ||||
| 		return [=](const QString &value) { | ||||
| 			return validation(value) | ||||
| 				? base::none | ||||
| 				: base::make_optional(QString()); | ||||
| 		}; | ||||
| 	}; | ||||
| 	const auto LimitedValidate = [=](int max, int min = 1) { | ||||
| 		return FromBoolean([=](const QString &value) { | ||||
| 			return (value.size() >= min) && (value.size() <= max); | ||||
| 		}); | ||||
| 	}; | ||||
| 	using Result = base::optional<QString>; | ||||
| 	const auto NameValidate = [](const QString &value) -> Result { | ||||
| 		if (value.isEmpty() || value.size() > kMaxNameSize) { | ||||
| 			return QString(); | ||||
| 		} else if (!QRegularExpression( | ||||
| 			"^[a-zA-Z\\- ]+$" | ||||
| 		).match(value).hasMatch()) { | ||||
| 			return "Use latin characters only.";// lang(lng_passport_bad_name);
 | ||||
| 		} | ||||
| 		return base::none; | ||||
| 	}; | ||||
| 	const auto NameValidate = LimitedValidate(kMaxNameSize); | ||||
| 
 | ||||
| 	const auto DocumentValidate = LimitedValidate(kMaxDocumentSize); | ||||
| 	const auto StreetValidate = LimitedValidate(kMaxStreetSize); | ||||
| 	const auto CityValidate = LimitedValidate(kMaxCitySize, kMinCitySize); | ||||
| 	const auto PostcodeValidate = LimitedValidate( | ||||
| 		kMaxPostcodeSize, | ||||
| 		kMinPostcodeSize); | ||||
| 	const auto DateValidate = [](const QString &value) { | ||||
| 	const auto PostcodeValidate = FromBoolean([](const QString &value) { | ||||
| 		return QRegularExpression( | ||||
| 			QString("^[a-zA-Z0-9\\-]{2,%1}$").arg(kMaxPostcodeSize) | ||||
| 		).match(value).hasMatch(); | ||||
| 	}); | ||||
| 	const auto DateValidateBoolean = [](const QString &value) { | ||||
| 		return QRegularExpression( | ||||
| 			"^\\d{2}\\.\\d{2}\\.\\d{4}$" | ||||
| 		).match(value).hasMatch(); | ||||
| 	}; | ||||
| 	const auto DateOrEmptyValidate = [=](const QString &value) { | ||||
| 		return value.isEmpty() || DateValidate(value); | ||||
| 	}; | ||||
| 	const auto GenderValidate = [](const QString &value) { | ||||
| 	const auto DateValidate = FromBoolean(DateValidateBoolean); | ||||
| 	const auto DateOrEmptyValidate = FromBoolean([=](const QString &value) { | ||||
| 		return value.isEmpty() || DateValidateBoolean(value); | ||||
| 	}); | ||||
| 	const auto GenderValidate = FromBoolean([](const QString &value) { | ||||
| 		return value == qstr("male") || value == qstr("female"); | ||||
| 	}; | ||||
| 	const auto CountryValidate = [=](const QString &value) { | ||||
| 	}); | ||||
| 	const auto CountryValidate = FromBoolean([=](const QString &value) { | ||||
| 		return !CountryFormat(value).isEmpty(); | ||||
| 	}; | ||||
| 	}); | ||||
| 
 | ||||
| 	switch (type) { | ||||
| 	case Scope::Type::Identity: { | ||||
|  | @ -142,6 +162,14 @@ EditDocumentScheme GetDocumentScheme( | |||
| 				CountryValidate, | ||||
| 				CountryFormat, | ||||
| 			}, | ||||
| 			{ | ||||
| 				ValueClass::Fields, | ||||
| 				PanelDetailsType::Country, | ||||
| 				qsl("residence_country_code"), | ||||
| 				lang(lng_passport_residence_country), | ||||
| 				CountryValidate, | ||||
| 				CountryFormat, | ||||
| 			}, | ||||
| 			{ | ||||
| 				ValueClass::Scans, | ||||
| 				PanelDetailsType::Text, | ||||
|  | @ -234,7 +262,7 @@ EditDocumentScheme GetDocumentScheme( | |||
| 			}, | ||||
| 			{ | ||||
| 				ValueClass::Fields, | ||||
| 				PanelDetailsType::Text, | ||||
| 				PanelDetailsType::Postcode, | ||||
| 				qsl("post_code"), | ||||
| 				lang(lng_passport_postcode), | ||||
| 				PostcodeValidate, | ||||
|  |  | |||
|  | @ -22,9 +22,60 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL | |||
| namespace Passport { | ||||
| namespace { | ||||
| 
 | ||||
| class TextRow : public PanelDetailsRow { | ||||
| class PostcodeInput : public Ui::MaskedInputField { | ||||
| public: | ||||
| 	TextRow( | ||||
| 	PostcodeInput( | ||||
| 		QWidget *parent, | ||||
| 		const style::InputField &st, | ||||
| 		base::lambda<QString()> placeholderFactory, | ||||
| 		const QString &val); | ||||
| 
 | ||||
| protected: | ||||
| 	void correctValue( | ||||
| 		const QString &was, | ||||
| 		int wasCursor, | ||||
| 		QString &now, | ||||
| 		int &nowCursor) override; | ||||
| 
 | ||||
| }; | ||||
| 
 | ||||
| PostcodeInput::PostcodeInput( | ||||
| 	QWidget *parent, | ||||
| 	const style::InputField &st, | ||||
| 	base::lambda<QString()> placeholderFactory, | ||||
| 	const QString &val) | ||||
| : MaskedInputField(parent, st, std::move(placeholderFactory), val) { | ||||
| 	if (!QRegularExpression("^[a-zA-Z0-9\\-]+$").match(val).hasMatch()) { | ||||
| 		setText(QString()); | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| void PostcodeInput::correctValue( | ||||
| 		const QString &was, | ||||
| 		int wasCursor, | ||||
| 		QString &now, | ||||
| 		int &nowCursor) { | ||||
| 	QString newText; | ||||
| 	newText.reserve(now.size()); | ||||
| 	auto newPos = nowCursor; | ||||
| 	for (auto i = 0, l = now.size(); i < l; ++i) { | ||||
| 		const auto ch = now[i]; | ||||
| 		if ((ch >= '0' && ch <= '9') | ||||
| 			|| (ch >= 'a' && ch <= 'z') | ||||
| 			|| (ch >= 'A' && ch <= 'Z') | ||||
| 			|| (ch == '-')) { | ||||
| 			newText.append(ch); | ||||
| 		} else if (i < nowCursor) { | ||||
| 			--newPos; | ||||
| 		} | ||||
| 	} | ||||
| 	setCorrectedText(now, nowCursor, newText, newPos); | ||||
| } | ||||
| 
 | ||||
| template <typename Input> | ||||
| class AbstractTextRow : public PanelDetailsRow { | ||||
| public: | ||||
| 	AbstractTextRow( | ||||
| 		QWidget *parent, | ||||
| 		const QString &label, | ||||
| 		const QString &value, | ||||
|  | @ -39,7 +90,7 @@ private: | |||
| 	void showInnerError() override; | ||||
| 	void finishInnerAnimating() override; | ||||
| 
 | ||||
| 	object_ptr<Ui::InputField> _field; | ||||
| 	object_ptr<Input> _field; | ||||
| 	rpl::variable<QString> _value; | ||||
| 
 | ||||
| }; | ||||
|  | @ -192,7 +243,8 @@ private: | |||
| 
 | ||||
| }; | ||||
| 
 | ||||
| TextRow::TextRow( | ||||
| template <typename Input> | ||||
| AbstractTextRow<Input>::AbstractTextRow( | ||||
| 	QWidget *parent, | ||||
| 	const QString &label, | ||||
| 	const QString &value, | ||||
|  | @ -201,34 +253,40 @@ TextRow::TextRow( | |||
| , _field(this, st::passportDetailsField, nullptr, value) | ||||
| , _value(value) { | ||||
| 	_field->setMaxLength(limit); | ||||
| 	connect(_field, &Ui::InputField::changed, [=] { | ||||
| 	connect(_field, &Input::changed, [=] { | ||||
| 		_value = valueCurrent(); | ||||
| 	}); | ||||
| } | ||||
| 
 | ||||
| bool TextRow::setFocusFast() { | ||||
| template <typename Input> | ||||
| bool AbstractTextRow<Input>::setFocusFast() { | ||||
| 	_field->setFocusFast(); | ||||
| 	return true; | ||||
| } | ||||
| 
 | ||||
| QString TextRow::valueCurrent() const { | ||||
| template <typename Input> | ||||
| QString AbstractTextRow<Input>::valueCurrent() const { | ||||
| 	return _field->getLastText(); | ||||
| } | ||||
| 
 | ||||
| rpl::producer<QString> TextRow::value() const { | ||||
| template <typename Input> | ||||
| rpl::producer<QString> AbstractTextRow<Input>::value() const { | ||||
| 	return _value.value(); | ||||
| } | ||||
| 
 | ||||
| int TextRow::resizeInner(int left, int top, int width) { | ||||
| template <typename Input> | ||||
| int AbstractTextRow<Input>::resizeInner(int left, int top, int width) { | ||||
| 	_field->setGeometry(left, top, width, _field->height()); | ||||
| 	return st::semiboldFont->height; | ||||
| } | ||||
| 
 | ||||
| void TextRow::showInnerError() { | ||||
| template <typename Input> | ||||
| void AbstractTextRow<Input>::showInnerError() { | ||||
| 	_field->showError(); | ||||
| } | ||||
| 
 | ||||
| void TextRow::finishInnerAnimating() { | ||||
| template <typename Input> | ||||
| void AbstractTextRow<Input>::finishInnerAnimating() { | ||||
| 	_field->finishAnimating(); | ||||
| } | ||||
| 
 | ||||
|  | @ -905,7 +963,17 @@ object_ptr<PanelDetailsRow> PanelDetailsRow::Create( | |||
| 	auto result = [&]() -> object_ptr<PanelDetailsRow> { | ||||
| 		switch (type) { | ||||
| 		case Type::Text: | ||||
| 			return object_ptr<TextRow>(parent, label, value, limit); | ||||
| 			return object_ptr<AbstractTextRow<Ui::InputField>>( | ||||
| 				parent, | ||||
| 				label, | ||||
| 				value, | ||||
| 				limit); | ||||
| 		case Type::Postcode: | ||||
| 			return object_ptr<AbstractTextRow<PostcodeInput>>( | ||||
| 				parent, | ||||
| 				label, | ||||
| 				value, | ||||
| 				limit); | ||||
| 		case Type::Country: | ||||
| 			return object_ptr<CountryRow>(parent, controller, label, value); | ||||
| 		case Type::Gender: | ||||
|  | @ -944,7 +1012,7 @@ int PanelDetailsRow::resizeGetHeight(int newWidth) { | |||
| 	return result; | ||||
| } | ||||
| 
 | ||||
| void PanelDetailsRow::showError(const QString &error) { | ||||
| void PanelDetailsRow::showError(base::optional<QString> error) { | ||||
| 	if (!_errorHideSubscription) { | ||||
| 		_errorHideSubscription = true; | ||||
| 
 | ||||
|  | @ -955,17 +1023,24 @@ void PanelDetailsRow::showError(const QString &error) { | |||
| 	} | ||||
| 	showInnerError(); | ||||
| 	startErrorAnimation(true); | ||||
| 	if (!error.isEmpty()) { | ||||
| 	if (!error.has_value()) { | ||||
| 		return; | ||||
| 	} | ||||
| 	if (error->isEmpty()) { | ||||
| 		if (_error) { | ||||
| 			_error->hide(anim::type::normal); | ||||
| 		} | ||||
| 	} else { | ||||
| 		if (!_error) { | ||||
| 			_error.create( | ||||
| 				this, | ||||
| 				object_ptr<Ui::FlatLabel>( | ||||
| 					this, | ||||
| 					error, | ||||
| 					*error, | ||||
| 					Ui::FlatLabel::InitType::Simple, | ||||
| 					st::passportVerifyErrorLabel)); | ||||
| 		} else { | ||||
| 			_error->entity()->setText(error); | ||||
| 			_error->entity()->setText(*error); | ||||
| 		} | ||||
| 		_error->heightValue( | ||||
| 		) | rpl::start_with_next([=] { | ||||
|  |  | |||
|  | @ -25,6 +25,7 @@ class PanelController; | |||
| 
 | ||||
| enum class PanelDetailsType { | ||||
| 	Text, | ||||
| 	Postcode, | ||||
| 	Country, | ||||
| 	Date, | ||||
| 	Gender, | ||||
|  | @ -65,7 +66,7 @@ public: | |||
| 	virtual bool setFocusFast(); | ||||
| 	virtual rpl::producer<QString> value() const = 0; | ||||
| 	virtual QString valueCurrent() const = 0; | ||||
| 	void showError(const QString &error); | ||||
| 	void showError(base::optional<QString> error = base::none); | ||||
| 	bool errorShown() const; | ||||
| 	void hideError(); | ||||
| 	void finishAnimating(); | ||||
|  |  | |||
|  | @ -419,10 +419,14 @@ bool PanelEditDocument::validate() { | |||
| 	auto first = QPointer<PanelDetailsRow>(); | ||||
| 	for (const auto [i, field] : base::reversed(_details)) { | ||||
| 		const auto &row = _scheme.rows[i]; | ||||
| 		if (field->errorShown() | ||||
| 			|| (row.validate && !row.validate(field->valueCurrent()))) { | ||||
| 			field->showError(QString()); | ||||
| 		if (field->errorShown()) { | ||||
| 			field->showError(); | ||||
| 			first = field; | ||||
| 		} else if (row.error) { | ||||
| 			if (const auto error = row.error(field->valueCurrent())) { | ||||
| 				field->showError(error); | ||||
| 				first = field; | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	if (error) { | ||||
|  |  | |||
|  | @ -43,7 +43,7 @@ struct EditDocumentScheme { | |||
| 		PanelDetailsType inputType = PanelDetailsType(); | ||||
| 		QString key; | ||||
| 		QString label; | ||||
| 		base::lambda<bool(const QString &value)> validate; | ||||
| 		base::lambda<base::optional<QString>(const QString &value)> error; | ||||
| 		base::lambda<QString(const QString &value)> format; | ||||
| 		int lengthLimit = 0; | ||||
| 	}; | ||||
|  |  | |||
		Loading…
	
	Add table
		
		Reference in a new issue
	
	 John Preston
						John Preston