| 
									
										
										
										
											2021-03-25 19:27:30 +04:00
										 |  |  | /*
 | 
					
						
							|  |  |  | This file is part of Telegram Desktop, | 
					
						
							|  |  |  | the official desktop application for the Telegram messaging service. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | For license and copyright information please follow this link: | 
					
						
							|  |  |  | https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
 | 
					
						
							|  |  |  | */ | 
					
						
							|  |  |  | #include "payments/ui/payments_edit_card.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "payments/ui/payments_panel_delegate.h"
 | 
					
						
							| 
									
										
										
										
											2021-03-26 19:23:12 +04:00
										 |  |  | #include "payments/ui/payments_field.h"
 | 
					
						
							| 
									
										
										
										
											2021-03-29 16:16:54 +04:00
										 |  |  | #include "stripe/stripe_card_validator.h"
 | 
					
						
							| 
									
										
										
										
											2021-03-25 19:27:30 +04:00
										 |  |  | #include "ui/widgets/scroll_area.h"
 | 
					
						
							|  |  |  | #include "ui/widgets/buttons.h"
 | 
					
						
							|  |  |  | #include "ui/widgets/labels.h"
 | 
					
						
							| 
									
										
										
										
											2021-03-31 21:15:49 +04:00
										 |  |  | #include "ui/widgets/checkbox.h"
 | 
					
						
							| 
									
										
										
										
											2021-03-25 19:27:30 +04:00
										 |  |  | #include "ui/wrap/vertical_layout.h"
 | 
					
						
							|  |  |  | #include "ui/wrap/fade_wrap.h"
 | 
					
						
							|  |  |  | #include "lang/lang_keys.h"
 | 
					
						
							|  |  |  | #include "styles/style_payments.h"
 | 
					
						
							|  |  |  | #include "styles/style_passport.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-29 16:16:54 +04:00
										 |  |  | #include <QtCore/QRegularExpression>
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-25 19:27:30 +04:00
										 |  |  | namespace Payments::Ui { | 
					
						
							|  |  |  | namespace { | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-29 16:16:54 +04:00
										 |  |  | struct SimpleFieldState { | 
					
						
							|  |  |  | 	QString value; | 
					
						
							|  |  |  | 	int position = 0; | 
					
						
							|  |  |  | }; | 
					
						
							| 
									
										
										
										
											2021-03-25 19:27:30 +04:00
										 |  |  | 
 | 
					
						
							|  |  |  | [[nodiscard]] uint32 ExtractYear(const QString &value) { | 
					
						
							|  |  |  | 	return value.split('/').value(1).toInt() + 2000; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | [[nodiscard]] uint32 ExtractMonth(const QString &value) { | 
					
						
							|  |  |  | 	return value.split('/').value(0).toInt(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-29 16:16:54 +04:00
										 |  |  | [[nodiscard]] QString RemoveNonNumbers(QString value) { | 
					
						
							|  |  |  | 	return value.replace(QRegularExpression("[^0-9]"), QString()); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | [[nodiscard]] SimpleFieldState NumbersOnlyState(SimpleFieldState state) { | 
					
						
							|  |  |  | 	return { | 
					
						
							|  |  |  | 		.value = RemoveNonNumbers(state.value), | 
					
						
							| 
									
										
										
										
											2021-10-19 17:00:21 +04:00
										 |  |  | 		.position = int(RemoveNonNumbers( | 
					
						
							|  |  |  | 			state.value.mid(0, state.position)).size()), | 
					
						
							| 
									
										
										
										
											2021-03-29 16:16:54 +04:00
										 |  |  | 	}; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | [[nodiscard]] SimpleFieldState PostprocessCardValidateResult( | 
					
						
							|  |  |  | 		SimpleFieldState result) { | 
					
						
							|  |  |  | 	const auto groups = Stripe::CardNumberFormat(result.value); | 
					
						
							|  |  |  | 	auto position = 0; | 
					
						
							|  |  |  | 	for (const auto length : groups) { | 
					
						
							|  |  |  | 		position += length; | 
					
						
							|  |  |  | 		if (position >= result.value.size()) { | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		result.value.insert(position, QChar(' ')); | 
					
						
							|  |  |  | 		if (result.position >= position) { | 
					
						
							|  |  |  | 			++result.position; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		++position; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return result; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | [[nodiscard]] SimpleFieldState PostprocessExpireDateValidateResult( | 
					
						
							|  |  |  | 		SimpleFieldState result) { | 
					
						
							|  |  |  | 	if (result.value.isEmpty()) { | 
					
						
							|  |  |  | 		return result; | 
					
						
							| 
									
										
										
										
											2022-06-16 18:30:12 +04:00
										 |  |  | 	} else if (result.value[0] == '1' | 
					
						
							|  |  |  | 		&& (result.value.size() > 1) | 
					
						
							|  |  |  | 		&& result.value[1] > '2') { | 
					
						
							| 
									
										
										
										
											2021-03-29 16:16:54 +04:00
										 |  |  | 		result.value = result.value.mid(0, 2); | 
					
						
							|  |  |  | 		return result; | 
					
						
							|  |  |  | 	} else if (result.value[0] > '1') { | 
					
						
							|  |  |  | 		result.value = '0' + result.value; | 
					
						
							|  |  |  | 		++result.position; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if (result.value.size() > 1) { | 
					
						
							|  |  |  | 		if (result.value.size() > 4) { | 
					
						
							|  |  |  | 			result.value = result.value.mid(0, 4); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		result.value.insert(2, '/'); | 
					
						
							|  |  |  | 		if (result.position >= 2) { | 
					
						
							|  |  |  | 			++result.position; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return result; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | [[nodiscard]] bool IsBackspace(const FieldValidateRequest &request) { | 
					
						
							|  |  |  | 	return (request.wasAnchor == request.wasPosition) | 
					
						
							|  |  |  | 		&& (request.wasPosition == request.nowPosition + 1) | 
					
						
							| 
									
										
										
										
											2021-10-19 17:00:21 +04:00
										 |  |  | 		&& (request.wasValue.mid(0, request.wasPosition - 1) | 
					
						
							|  |  |  | 			== request.nowValue.mid(0, request.nowPosition)) | 
					
						
							|  |  |  | 		&& (request.wasValue.mid(request.wasPosition) | 
					
						
							|  |  |  | 			== request.nowValue.mid(request.nowPosition)); | 
					
						
							| 
									
										
										
										
											2021-03-29 16:16:54 +04:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | [[nodiscard]] bool IsDelete(const FieldValidateRequest &request) { | 
					
						
							|  |  |  | 	return (request.wasAnchor == request.wasPosition) | 
					
						
							|  |  |  | 		&& (request.wasPosition == request.nowPosition) | 
					
						
							| 
									
										
										
										
											2021-10-19 17:00:21 +04:00
										 |  |  | 		&& (request.wasValue.mid(0, request.wasPosition) | 
					
						
							|  |  |  | 			== request.nowValue.mid(0, request.nowPosition)) | 
					
						
							|  |  |  | 		&& (request.wasValue.mid(request.wasPosition + 1) | 
					
						
							|  |  |  | 			== request.nowValue.mid(request.nowPosition)); | 
					
						
							| 
									
										
										
										
											2021-03-29 16:16:54 +04:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | template < | 
					
						
							|  |  |  | 	typename ValueValidator, | 
					
						
							|  |  |  | 	typename ValueValidateResult = decltype( | 
					
						
							|  |  |  | 		std::declval<ValueValidator>()(QString()))> | 
					
						
							|  |  |  | [[nodiscard]] auto ComplexNumberValidator( | 
					
						
							|  |  |  | 		ValueValidator valueValidator, | 
					
						
							|  |  |  | 		Fn<SimpleFieldState(SimpleFieldState)> postprocess) { | 
					
						
							|  |  |  | 	using namespace Stripe; | 
					
						
							|  |  |  | 	return [=](FieldValidateRequest request) { | 
					
						
							|  |  |  | 		const auto realNowState = [&] { | 
					
						
							|  |  |  | 			const auto backspaced = IsBackspace(request); | 
					
						
							|  |  |  | 			const auto deleted = IsDelete(request); | 
					
						
							|  |  |  | 			if (!backspaced && !deleted) { | 
					
						
							|  |  |  | 				return NumbersOnlyState({ | 
					
						
							|  |  |  | 					.value = request.nowValue, | 
					
						
							|  |  |  | 					.position = request.nowPosition, | 
					
						
							|  |  |  | 				}); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			const auto realWasState = NumbersOnlyState({ | 
					
						
							|  |  |  | 				.value = request.wasValue, | 
					
						
							|  |  |  | 				.position = request.wasPosition, | 
					
						
							|  |  |  | 			}); | 
					
						
							|  |  |  | 			const auto changedValue = deleted | 
					
						
							|  |  |  | 				? (realWasState.value.mid(0, realWasState.position) | 
					
						
							|  |  |  | 					+ realWasState.value.mid(realWasState.position + 1)) | 
					
						
							|  |  |  | 				: (realWasState.position > 1) | 
					
						
							|  |  |  | 				? (realWasState.value.mid(0, realWasState.position - 1) | 
					
						
							|  |  |  | 					+ realWasState.value.mid(realWasState.position)) | 
					
						
							|  |  |  | 				: realWasState.value.mid(realWasState.position); | 
					
						
							|  |  |  | 			return SimpleFieldState{ | 
					
						
							|  |  |  | 				.value = changedValue, | 
					
						
							|  |  |  | 				.position = (deleted | 
					
						
							|  |  |  | 					? realWasState.position | 
					
						
							|  |  |  | 					: std::max(realWasState.position - 1, 0)) | 
					
						
							|  |  |  | 			}; | 
					
						
							|  |  |  | 		}(); | 
					
						
							|  |  |  | 		const auto result = valueValidator(realNowState.value); | 
					
						
							|  |  |  | 		const auto postprocessed = postprocess(realNowState); | 
					
						
							|  |  |  | 		return FieldValidateResult{ | 
					
						
							|  |  |  | 			.value = postprocessed.value, | 
					
						
							|  |  |  | 			.position = postprocessed.position, | 
					
						
							|  |  |  | 			.invalid = (result.state == ValidationState::Invalid), | 
					
						
							|  |  |  | 			.finished = result.finished, | 
					
						
							|  |  |  | 		}; | 
					
						
							|  |  |  | 	}; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | [[nodiscard]] auto CardNumberValidator() { | 
					
						
							|  |  |  | 	return ComplexNumberValidator( | 
					
						
							|  |  |  | 		Stripe::ValidateCard, | 
					
						
							|  |  |  | 		PostprocessCardValidateResult); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | [[nodiscard]] auto ExpireDateValidator() { | 
					
						
							|  |  |  | 	return ComplexNumberValidator( | 
					
						
							|  |  |  | 		Stripe::ValidateExpireDate, | 
					
						
							|  |  |  | 		PostprocessExpireDateValidateResult); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | [[nodiscard]] auto CvcValidator(Fn<QString()> number) { | 
					
						
							|  |  |  | 	using namespace Stripe; | 
					
						
							|  |  |  | 	return [=](FieldValidateRequest request) { | 
					
						
							|  |  |  | 		const auto realNowState = NumbersOnlyState({ | 
					
						
							|  |  |  | 			.value = request.nowValue, | 
					
						
							|  |  |  | 			.position = request.nowPosition, | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 		const auto result = ValidateCvc(number(), realNowState.value); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		return FieldValidateResult{ | 
					
						
							|  |  |  | 			.value = realNowState.value, | 
					
						
							|  |  |  | 			.position = realNowState.position, | 
					
						
							|  |  |  | 			.invalid = (result.state == ValidationState::Invalid), | 
					
						
							|  |  |  | 			.finished = result.finished, | 
					
						
							|  |  |  | 		}; | 
					
						
							|  |  |  | 	}; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | [[nodiscard]] auto CardHolderNameValidator() { | 
					
						
							|  |  |  | 	return [=](FieldValidateRequest request) { | 
					
						
							|  |  |  | 		return FieldValidateResult{ | 
					
						
							|  |  |  | 			.value = request.nowValue.toUpper(), | 
					
						
							|  |  |  | 			.position = request.nowPosition, | 
					
						
							|  |  |  | 			.invalid = request.nowValue.isEmpty(), | 
					
						
							|  |  |  | 		}; | 
					
						
							|  |  |  | 	}; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-25 19:27:30 +04:00
										 |  |  | } // namespace
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | EditCard::EditCard( | 
					
						
							|  |  |  | 	QWidget *parent, | 
					
						
							| 
									
										
										
										
											2021-03-25 20:58:52 +04:00
										 |  |  | 	const NativeMethodDetails &native, | 
					
						
							| 
									
										
										
										
											2021-03-25 19:27:30 +04:00
										 |  |  | 	CardField field, | 
					
						
							|  |  |  | 	not_null<PanelDelegate*> delegate) | 
					
						
							|  |  |  | : _delegate(delegate) | 
					
						
							|  |  |  | , _native(native) | 
					
						
							|  |  |  | , _scroll(this, st::passportPanelScroll) | 
					
						
							|  |  |  | , _topShadow(this) | 
					
						
							|  |  |  | , _bottomShadow(this) | 
					
						
							| 
									
										
										
										
											2021-03-31 10:19:14 +04:00
										 |  |  | , _submit( | 
					
						
							|  |  |  | 	this, | 
					
						
							|  |  |  | 	tr::lng_about_done(), | 
					
						
							|  |  |  | 	st::paymentsPanelButton) | 
					
						
							|  |  |  | , _cancel( | 
					
						
							| 
									
										
										
										
											2021-03-25 19:27:30 +04:00
										 |  |  | 		this, | 
					
						
							| 
									
										
										
										
											2021-03-31 10:19:14 +04:00
										 |  |  | 		tr::lng_cancel(), | 
					
						
							|  |  |  | 		st::paymentsPanelButton) { | 
					
						
							| 
									
										
										
										
											2021-03-25 19:27:30 +04:00
										 |  |  | 	setupControls(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void EditCard::setFocus(CardField field) { | 
					
						
							|  |  |  | 	_focusField = field; | 
					
						
							| 
									
										
										
										
											2021-03-26 19:23:12 +04:00
										 |  |  | 	if (const auto control = lookupField(field)) { | 
					
						
							|  |  |  | 		_scroll->ensureWidgetVisible(control->widget()); | 
					
						
							|  |  |  | 		control->setFocus(); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void EditCard::setFocusFast(CardField field) { | 
					
						
							|  |  |  | 	_focusField = field; | 
					
						
							|  |  |  | 	if (const auto control = lookupField(field)) { | 
					
						
							|  |  |  | 		_scroll->ensureWidgetVisible(control->widget()); | 
					
						
							| 
									
										
										
										
											2021-03-25 19:27:30 +04:00
										 |  |  | 		control->setFocusFast(); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void EditCard::showError(CardField field) { | 
					
						
							| 
									
										
										
										
											2021-03-26 19:23:12 +04:00
										 |  |  | 	if (const auto control = lookupField(field)) { | 
					
						
							|  |  |  | 		_scroll->ensureWidgetVisible(control->widget()); | 
					
						
							|  |  |  | 		control->showError(); | 
					
						
							| 
									
										
										
										
											2021-03-25 19:27:30 +04:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void EditCard::setupControls() { | 
					
						
							|  |  |  | 	const auto inner = setupContent(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-31 10:19:14 +04:00
										 |  |  | 	_submit->addClickHandler([=] { | 
					
						
							| 
									
										
										
										
											2021-03-31 21:15:49 +04:00
										 |  |  | 		_delegate->panelValidateCard(collect(), (_save && _save->checked())); | 
					
						
							| 
									
										
										
										
											2021-03-25 19:27:30 +04:00
										 |  |  | 	}); | 
					
						
							| 
									
										
										
										
											2021-03-31 10:19:14 +04:00
										 |  |  | 	_cancel->addClickHandler([=] { | 
					
						
							|  |  |  | 		_delegate->panelCancelEdit(); | 
					
						
							|  |  |  | 	}); | 
					
						
							| 
									
										
										
										
											2021-03-25 19:27:30 +04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	using namespace rpl::mappers; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	_topShadow->toggleOn( | 
					
						
							|  |  |  | 		_scroll->scrollTopValue() | rpl::map(_1 > 0)); | 
					
						
							|  |  |  | 	_bottomShadow->toggleOn(rpl::combine( | 
					
						
							|  |  |  | 		_scroll->scrollTopValue(), | 
					
						
							|  |  |  | 		_scroll->heightValue(), | 
					
						
							|  |  |  | 		inner->heightValue(), | 
					
						
							|  |  |  | 		_1 + _2 < _3)); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | not_null<RpWidget*> EditCard::setupContent() { | 
					
						
							|  |  |  | 	const auto inner = _scroll->setOwnedWidget( | 
					
						
							|  |  |  | 		object_ptr<VerticalLayout>(this)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	_scroll->widthValue( | 
					
						
							|  |  |  | 	) | rpl::start_with_next([=](int width) { | 
					
						
							|  |  |  | 		inner->resizeToWidth(width); | 
					
						
							|  |  |  | 	}, inner->lifetime()); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	const auto showBox = [=](object_ptr<BoxContent> box) { | 
					
						
							|  |  |  | 		_delegate->panelShowBox(std::move(box)); | 
					
						
							|  |  |  | 	}; | 
					
						
							| 
									
										
										
										
											2021-04-01 19:06:48 +04:00
										 |  |  | 	auto last = (Field*)nullptr; | 
					
						
							|  |  |  | 	const auto make = [&](QWidget *parent, FieldConfig &&config) { | 
					
						
							|  |  |  | 		auto result = std::make_unique<Field>(parent, std::move(config)); | 
					
						
							|  |  |  | 		if (last) { | 
					
						
							|  |  |  | 			last->setNextField(result.get()); | 
					
						
							|  |  |  | 			result->setPreviousField(last); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		last = result.get(); | 
					
						
							|  |  |  | 		return result; | 
					
						
							|  |  |  | 	}; | 
					
						
							| 
									
										
										
										
											2021-03-26 19:23:12 +04:00
										 |  |  | 	const auto add = [&](FieldConfig &&config) { | 
					
						
							| 
									
										
										
										
											2021-04-01 19:06:48 +04:00
										 |  |  | 		auto result = make(inner, std::move(config)); | 
					
						
							| 
									
										
										
										
											2021-03-26 19:23:12 +04:00
										 |  |  | 		inner->add(result->ownedWidget(), st::paymentsFieldPadding); | 
					
						
							|  |  |  | 		return result; | 
					
						
							|  |  |  | 	}; | 
					
						
							|  |  |  | 	_number = add({ | 
					
						
							|  |  |  | 		.type = FieldType::CardNumber, | 
					
						
							|  |  |  | 		.placeholder = tr::lng_payments_card_number(), | 
					
						
							| 
									
										
										
										
											2021-03-29 16:16:54 +04:00
										 |  |  | 		.validator = CardNumberValidator(), | 
					
						
							| 
									
										
										
										
											2021-03-26 19:23:12 +04:00
										 |  |  | 	}); | 
					
						
							|  |  |  | 	auto container = inner->add( | 
					
						
							|  |  |  | 		object_ptr<FixedHeightWidget>( | 
					
						
							| 
									
										
										
										
											2021-03-25 19:27:30 +04:00
										 |  |  | 			inner, | 
					
						
							| 
									
										
										
										
											2021-03-26 19:23:12 +04:00
										 |  |  | 			_number->widget()->height()), | 
					
						
							|  |  |  | 		st::paymentsFieldPadding); | 
					
						
							| 
									
										
										
										
											2021-04-01 19:06:48 +04:00
										 |  |  | 	_expire = make(container, { | 
					
						
							| 
									
										
										
										
											2021-03-26 19:23:12 +04:00
										 |  |  | 		.type = FieldType::CardExpireDate, | 
					
						
							| 
									
										
										
										
											2021-04-27 13:55:27 +04:00
										 |  |  | 		.placeholder = tr::lng_payments_card_expire_date(), | 
					
						
							| 
									
										
										
										
											2021-03-29 16:16:54 +04:00
										 |  |  | 		.validator = ExpireDateValidator(), | 
					
						
							| 
									
										
										
										
											2021-03-26 19:23:12 +04:00
										 |  |  | 	}); | 
					
						
							| 
									
										
										
										
											2021-04-01 19:06:48 +04:00
										 |  |  | 	_cvc = make(container, { | 
					
						
							| 
									
										
										
										
											2021-03-26 19:23:12 +04:00
										 |  |  | 		.type = FieldType::CardCVC, | 
					
						
							| 
									
										
										
										
											2021-04-27 13:55:27 +04:00
										 |  |  | 		.placeholder = tr::lng_payments_card_cvc(), | 
					
						
							| 
									
										
										
										
											2021-03-29 16:16:54 +04:00
										 |  |  | 		.validator = CvcValidator([=] { return _number->value(); }), | 
					
						
							| 
									
										
										
										
											2021-03-26 19:23:12 +04:00
										 |  |  | 	}); | 
					
						
							|  |  |  | 	container->widthValue( | 
					
						
							|  |  |  | 	) | rpl::start_with_next([=](int width) { | 
					
						
							|  |  |  | 		const auto left = (width - st::paymentsExpireCvcSkip) / 2; | 
					
						
							|  |  |  | 		const auto right = width - st::paymentsExpireCvcSkip - left; | 
					
						
							|  |  |  | 		_expire->widget()->resizeToWidth(left); | 
					
						
							|  |  |  | 		_cvc->widget()->resizeToWidth(right); | 
					
						
							|  |  |  | 		_expire->widget()->moveToLeft(0, 0, width); | 
					
						
							|  |  |  | 		_cvc->widget()->moveToRight(0, 0, width); | 
					
						
							|  |  |  | 	}, container->lifetime()); | 
					
						
							| 
									
										
										
										
											2021-03-29 16:16:54 +04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if (_native.needCardholderName) { | 
					
						
							|  |  |  | 		_name = add({ | 
					
						
							| 
									
										
										
										
											2021-04-12 12:50:31 +04:00
										 |  |  | 			.type = FieldType::Text, | 
					
						
							| 
									
										
										
										
											2021-03-29 16:16:54 +04:00
										 |  |  | 			.placeholder = tr::lng_payments_card_holder(), | 
					
						
							|  |  |  | 			.validator = CardHolderNameValidator(), | 
					
						
							|  |  |  | 		}); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-26 19:23:12 +04:00
										 |  |  | 	if (_native.needCountry || _native.needZip) { | 
					
						
							|  |  |  | 		inner->add( | 
					
						
							|  |  |  | 			object_ptr<Ui::FlatLabel>( | 
					
						
							| 
									
										
										
										
											2021-03-25 19:27:30 +04:00
										 |  |  | 				inner, | 
					
						
							| 
									
										
										
										
											2021-03-26 19:23:12 +04:00
										 |  |  | 				tr::lng_payments_billing_address(), | 
					
						
							|  |  |  | 				st::paymentsBillingInformationTitle), | 
					
						
							|  |  |  | 			st::paymentsBillingInformationTitlePadding); | 
					
						
							| 
									
										
										
										
											2021-03-25 19:27:30 +04:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	if (_native.needCountry) { | 
					
						
							| 
									
										
										
										
											2021-03-26 19:23:12 +04:00
										 |  |  | 		_country = add({ | 
					
						
							|  |  |  | 			.type = FieldType::Country, | 
					
						
							|  |  |  | 			.placeholder = tr::lng_payments_billing_country(), | 
					
						
							| 
									
										
										
										
											2021-03-29 16:16:54 +04:00
										 |  |  | 			.validator = RequiredFinishedValidator(), | 
					
						
							| 
									
										
										
										
											2021-03-26 21:09:09 +04:00
										 |  |  | 			.showBox = showBox, | 
					
						
							|  |  |  | 			.defaultCountry = _native.defaultCountry, | 
					
						
							| 
									
										
										
										
											2021-03-26 19:23:12 +04:00
										 |  |  | 		}); | 
					
						
							| 
									
										
										
										
											2021-03-25 19:27:30 +04:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	if (_native.needZip) { | 
					
						
							| 
									
										
										
										
											2021-03-26 19:23:12 +04:00
										 |  |  | 		_zip = add({ | 
					
						
							|  |  |  | 			.type = FieldType::Text, | 
					
						
							|  |  |  | 			.placeholder = tr::lng_payments_billing_zip_code(), | 
					
						
							| 
									
										
										
										
											2021-03-29 16:16:54 +04:00
										 |  |  | 			.validator = RequiredValidator(), | 
					
						
							| 
									
										
										
										
											2021-03-26 19:23:12 +04:00
										 |  |  | 		}); | 
					
						
							| 
									
										
										
										
											2021-03-29 16:16:54 +04:00
										 |  |  | 		if (_country) { | 
					
						
							|  |  |  | 			_country->finished( | 
					
						
							|  |  |  | 			) | rpl::start_with_next([=] { | 
					
						
							|  |  |  | 				_zip->setFocus(); | 
					
						
							|  |  |  | 			}, lifetime()); | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2021-03-25 19:27:30 +04:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-03-31 21:15:49 +04:00
										 |  |  | 	if (_native.canSaveInformation) { | 
					
						
							|  |  |  | 		_save = inner->add( | 
					
						
							|  |  |  | 			object_ptr<Ui::Checkbox>( | 
					
						
							|  |  |  | 				inner, | 
					
						
							|  |  |  | 				tr::lng_payments_save_information(tr::now), | 
					
						
							|  |  |  | 				false), | 
					
						
							|  |  |  | 			st::paymentsSaveCheckboxPadding); | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2021-04-01 19:06:48 +04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	last->submitted( | 
					
						
							|  |  |  | 	) | rpl::start_with_next([=] { | 
					
						
							|  |  |  | 		_delegate->panelValidateCard(collect(), _save && _save->checked()); | 
					
						
							|  |  |  | 	}, lifetime()); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-25 19:27:30 +04:00
										 |  |  | 	return inner; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void EditCard::resizeEvent(QResizeEvent *e) { | 
					
						
							|  |  |  | 	updateControlsGeometry(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void EditCard::focusInEvent(QFocusEvent *e) { | 
					
						
							| 
									
										
										
										
											2021-03-26 19:23:12 +04:00
										 |  |  | 	if (const auto control = lookupField(_focusField)) { | 
					
						
							| 
									
										
										
										
											2021-03-25 19:27:30 +04:00
										 |  |  | 		control->setFocusFast(); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void EditCard::updateControlsGeometry() { | 
					
						
							| 
									
										
										
										
											2021-03-31 10:19:14 +04:00
										 |  |  | 	const auto &padding = st::paymentsPanelPadding; | 
					
						
							|  |  |  | 	const auto buttonsHeight = padding.top() | 
					
						
							|  |  |  | 		+ _cancel->height() | 
					
						
							|  |  |  | 		+ padding.bottom(); | 
					
						
							|  |  |  | 	const auto buttonsTop = height() - buttonsHeight; | 
					
						
							|  |  |  | 	_scroll->setGeometry(0, 0, width(), buttonsTop); | 
					
						
							| 
									
										
										
										
											2021-03-25 19:27:30 +04:00
										 |  |  | 	_topShadow->resizeToWidth(width()); | 
					
						
							|  |  |  | 	_topShadow->moveToLeft(0, 0); | 
					
						
							|  |  |  | 	_bottomShadow->resizeToWidth(width()); | 
					
						
							| 
									
										
										
										
											2021-03-31 10:19:14 +04:00
										 |  |  | 	_bottomShadow->moveToLeft(0, buttonsTop - st::lineWidth); | 
					
						
							|  |  |  | 	auto right = padding.right(); | 
					
						
							|  |  |  | 	_submit->moveToRight(right, buttonsTop + padding.top()); | 
					
						
							|  |  |  | 	right += _submit->width() + padding.left(); | 
					
						
							|  |  |  | 	_cancel->moveToRight(right, buttonsTop + padding.top()); | 
					
						
							| 
									
										
										
										
											2021-03-25 19:27:30 +04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	_scroll->updateBars(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-26 19:23:12 +04:00
										 |  |  | auto EditCard::lookupField(CardField field) const -> Field* { | 
					
						
							| 
									
										
										
										
											2021-03-25 19:27:30 +04:00
										 |  |  | 	switch (field) { | 
					
						
							| 
									
										
										
										
											2021-03-26 19:23:12 +04:00
										 |  |  | 	case CardField::Number: return _number.get(); | 
					
						
							| 
									
										
										
										
											2021-03-29 16:16:54 +04:00
										 |  |  | 	case CardField::Cvc: return _cvc.get(); | 
					
						
							| 
									
										
										
										
											2021-03-26 19:23:12 +04:00
										 |  |  | 	case CardField::ExpireDate: return _expire.get(); | 
					
						
							|  |  |  | 	case CardField::Name: return _name.get(); | 
					
						
							|  |  |  | 	case CardField::AddressCountry: return _country.get(); | 
					
						
							|  |  |  | 	case CardField::AddressZip: return _zip.get(); | 
					
						
							| 
									
										
										
										
											2021-03-25 19:27:30 +04:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	Unexpected("Unknown field in EditCard::controlForField."); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | UncheckedCardDetails EditCard::collect() const { | 
					
						
							|  |  |  | 	return { | 
					
						
							| 
									
										
										
										
											2021-03-26 19:23:12 +04:00
										 |  |  | 		.number = _number ? _number->value() : QString(), | 
					
						
							|  |  |  | 		.cvc = _cvc ? _cvc->value() : QString(), | 
					
						
							|  |  |  | 		.expireYear = _expire ? ExtractYear(_expire->value()) : 0, | 
					
						
							|  |  |  | 		.expireMonth = _expire ? ExtractMonth(_expire->value()) : 0, | 
					
						
							|  |  |  | 		.cardholderName = _name ? _name->value() : QString(), | 
					
						
							|  |  |  | 		.addressCountry = _country ? _country->value() : QString(), | 
					
						
							|  |  |  | 		.addressZip = _zip ? _zip->value() : QString(), | 
					
						
							| 
									
										
										
										
											2021-03-25 19:27:30 +04:00
										 |  |  | 	}; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | } // namespace Payments::Ui
 |