mirror of https://github.com/procxx/kepka.git
				
				
				
			
		
			
				
	
	
		
			431 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			431 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C++
		
	
	
	
/*
 | 
						|
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 "passport/passport_panel_edit_contact.h"
 | 
						|
 | 
						|
#include "passport/passport_panel_controller.h"
 | 
						|
#include "passport/passport_panel_details_row.h"
 | 
						|
#include "ui/widgets/input_fields.h"
 | 
						|
#include "ui/widgets/labels.h"
 | 
						|
#include "ui/widgets/buttons.h"
 | 
						|
#include "ui/widgets/shadow.h"
 | 
						|
#include "ui/widgets/box_content_divider.h"
 | 
						|
#include "ui/wrap/vertical_layout.h"
 | 
						|
#include "ui/wrap/slide_wrap.h"
 | 
						|
#include "ui/wrap/fade_wrap.h"
 | 
						|
#include "ui/text/text_utilities.h" // Ui::Text::ToUpper
 | 
						|
#include "ui/special_fields.h"
 | 
						|
#include "boxes/abstract_box.h"
 | 
						|
#include "boxes/confirm_phone_box.h"
 | 
						|
#include "data/data_user.h"
 | 
						|
#include "main/main_session.h"
 | 
						|
#include "lang/lang_keys.h"
 | 
						|
#include "app.h"
 | 
						|
#include "styles/style_passport.h"
 | 
						|
#include "styles/style_layers.h"
 | 
						|
 | 
						|
namespace Passport {
 | 
						|
namespace {
 | 
						|
 | 
						|
class VerifyBox : public Ui::BoxContent {
 | 
						|
public:
 | 
						|
	VerifyBox(
 | 
						|
		QWidget*,
 | 
						|
		rpl::producer<QString> title,
 | 
						|
		const QString &text,
 | 
						|
		int codeLength,
 | 
						|
		Fn<void(QString code)> submit,
 | 
						|
		Fn<void()> resend,
 | 
						|
		rpl::producer<QString> call,
 | 
						|
		rpl::producer<QString> error,
 | 
						|
		rpl::producer<QString> resent);
 | 
						|
 | 
						|
	void setInnerFocus() override;
 | 
						|
 | 
						|
protected:
 | 
						|
	void prepare() override;
 | 
						|
 | 
						|
private:
 | 
						|
	void setupControls(
 | 
						|
		const QString &text,
 | 
						|
		int codeLength,
 | 
						|
		Fn<void(QString code)> submit,
 | 
						|
		Fn<void()> resend,
 | 
						|
		rpl::producer<QString> call,
 | 
						|
		rpl::producer<QString> error,
 | 
						|
		rpl::producer<QString> resent);
 | 
						|
 | 
						|
	rpl::producer<QString> _title;
 | 
						|
	Fn<void()> _submit;
 | 
						|
	QPointer<SentCodeField> _code;
 | 
						|
	QPointer<Ui::VerticalLayout> _content;
 | 
						|
 | 
						|
};
 | 
						|
 | 
						|
VerifyBox::VerifyBox(
 | 
						|
	QWidget*,
 | 
						|
	rpl::producer<QString> title,
 | 
						|
	const QString &text,
 | 
						|
	int codeLength,
 | 
						|
	Fn<void(QString code)> submit,
 | 
						|
	Fn<void()> resend,
 | 
						|
	rpl::producer<QString> call,
 | 
						|
	rpl::producer<QString> error,
 | 
						|
	rpl::producer<QString> resent)
 | 
						|
: _title(std::move(title)) {
 | 
						|
	setupControls(
 | 
						|
		text,
 | 
						|
		codeLength,
 | 
						|
		submit,
 | 
						|
		resend,
 | 
						|
		std::move(call),
 | 
						|
		std::move(error),
 | 
						|
		std::move(resent));
 | 
						|
}
 | 
						|
 | 
						|
void VerifyBox::setupControls(
 | 
						|
		const QString &text,
 | 
						|
		int codeLength,
 | 
						|
		Fn<void(QString code)> submit,
 | 
						|
		Fn<void()> resend,
 | 
						|
		rpl::producer<QString> call,
 | 
						|
		rpl::producer<QString> error,
 | 
						|
		rpl::producer<QString> resent) {
 | 
						|
	_content = Ui::CreateChild<Ui::VerticalLayout>(this);
 | 
						|
 | 
						|
	const auto small = style::margins(
 | 
						|
		st::boxPadding.left(),
 | 
						|
		0,
 | 
						|
		st::boxPadding.right(),
 | 
						|
		st::boxPadding.bottom());
 | 
						|
	const auto description = _content->add(
 | 
						|
		object_ptr<Ui::FlatLabel>(
 | 
						|
			_content,
 | 
						|
			text,
 | 
						|
			st::boxLabel),
 | 
						|
		small);
 | 
						|
	_code = _content->add(
 | 
						|
		object_ptr<SentCodeField>(
 | 
						|
			_content,
 | 
						|
			st::defaultInputField,
 | 
						|
			tr::lng_change_phone_code_title()),
 | 
						|
		small);
 | 
						|
 | 
						|
	const auto problem = _content->add(
 | 
						|
		object_ptr<Ui::FadeWrap<Ui::FlatLabel>>(
 | 
						|
			_content,
 | 
						|
			object_ptr<Ui::FlatLabel>(
 | 
						|
				_content,
 | 
						|
				QString(),
 | 
						|
				st::passportVerifyErrorLabel)),
 | 
						|
		small);
 | 
						|
	const auto waiter = _content->add(
 | 
						|
		object_ptr<Ui::FlatLabel>(
 | 
						|
			_content,
 | 
						|
			std::move(call),
 | 
						|
			st::boxDividerLabel),
 | 
						|
		small);
 | 
						|
	if (resend) {
 | 
						|
		auto link = TextWithEntities{ tr::lng_cloud_password_resend(tr::now) };
 | 
						|
		link.entities.push_back({
 | 
						|
			EntityType::CustomUrl,
 | 
						|
			0,
 | 
						|
			link.text.size(),
 | 
						|
			QString("internal:resend") });
 | 
						|
		const auto label = _content->add(
 | 
						|
			object_ptr<Ui::FlatLabel>(
 | 
						|
				_content,
 | 
						|
				rpl::single(
 | 
						|
					link
 | 
						|
				) | rpl::then(rpl::duplicate(
 | 
						|
					resent
 | 
						|
				) | rpl::map([](const QString &value) {
 | 
						|
					return TextWithEntities{ value };
 | 
						|
				})),
 | 
						|
				st::boxDividerLabel),
 | 
						|
			small);
 | 
						|
		std::move(
 | 
						|
			resent
 | 
						|
		) | rpl::start_with_next([=] {
 | 
						|
			_content->resizeToWidth(st::boxWidth);
 | 
						|
		}, _content->lifetime());
 | 
						|
		label->setClickHandlerFilter([=](auto&&...) {
 | 
						|
			resend();
 | 
						|
			return false;
 | 
						|
		});
 | 
						|
	}
 | 
						|
	std::move(
 | 
						|
		error
 | 
						|
	) | rpl::start_with_next([=](const QString &error) {
 | 
						|
		if (error.isEmpty()) {
 | 
						|
			problem->hide(anim::type::normal);
 | 
						|
		} else {
 | 
						|
			problem->entity()->setText(error);
 | 
						|
			_content->resizeToWidth(st::boxWidth);
 | 
						|
			problem->show(anim::type::normal);
 | 
						|
			_code->showError();
 | 
						|
		}
 | 
						|
	}, lifetime());
 | 
						|
 | 
						|
	_submit = [=] {
 | 
						|
		submit(_code->getDigitsOnly());
 | 
						|
	};
 | 
						|
	if (codeLength > 0) {
 | 
						|
		_code->setAutoSubmit(codeLength, _submit);
 | 
						|
	} else {
 | 
						|
		connect(_code, &SentCodeField::submitted, _submit);
 | 
						|
	}
 | 
						|
	connect(_code, &SentCodeField::changed, [=] {
 | 
						|
		problem->hide(anim::type::normal);
 | 
						|
	});
 | 
						|
}
 | 
						|
 | 
						|
void VerifyBox::setInnerFocus() {
 | 
						|
	_code->setFocusFast();
 | 
						|
}
 | 
						|
 | 
						|
void VerifyBox::prepare() {
 | 
						|
	setTitle(std::move(_title));
 | 
						|
 | 
						|
	addButton(tr::lng_change_phone_new_submit(), _submit);
 | 
						|
	addButton(tr::lng_cancel(), [=] { closeBox(); });
 | 
						|
 | 
						|
	_content->resizeToWidth(st::boxWidth);
 | 
						|
	_content->heightValue(
 | 
						|
	) | rpl::start_with_next([=](int height) {
 | 
						|
		setDimensions(st::boxWidth, height);
 | 
						|
	}, _content->lifetime());
 | 
						|
}
 | 
						|
 | 
						|
} // namespace
 | 
						|
 | 
						|
EditContactScheme::EditContactScheme(ValueType type) : type(type) {
 | 
						|
}
 | 
						|
 | 
						|
PanelEditContact::PanelEditContact(
 | 
						|
	QWidget*,
 | 
						|
	not_null<PanelController*> controller,
 | 
						|
	Scheme scheme,
 | 
						|
	const QString &data,
 | 
						|
	const QString &existing)
 | 
						|
: _controller(controller)
 | 
						|
, _scheme(std::move(scheme))
 | 
						|
, _content(this)
 | 
						|
, _bottomShadow(this)
 | 
						|
, _done(
 | 
						|
		this,
 | 
						|
		tr::lng_passport_save_value(),
 | 
						|
		st::passportPanelSaveValue) {
 | 
						|
	setupControls(data, existing);
 | 
						|
}
 | 
						|
 | 
						|
void PanelEditContact::setupControls(
 | 
						|
		const QString &data,
 | 
						|
		const QString &existing) {
 | 
						|
	widthValue(
 | 
						|
	) | rpl::start_with_next([=](int width) {
 | 
						|
		_content->resizeToWidth(width);
 | 
						|
	}, _content->lifetime());
 | 
						|
 | 
						|
	_content->add(object_ptr<Ui::BoxContentDivider>(
 | 
						|
		_content,
 | 
						|
		st::passportFormDividerHeight));
 | 
						|
	if (!existing.isEmpty()) {
 | 
						|
		_content->add(
 | 
						|
			object_ptr<Ui::SettingsButton>(
 | 
						|
				_content,
 | 
						|
				tr::lng_passport_use_existing(
 | 
						|
					lt_existing,
 | 
						|
					rpl::single(_scheme.format
 | 
						|
						? _scheme.format(existing)
 | 
						|
						: existing)),
 | 
						|
				st::passportUploadButton),
 | 
						|
			st::passportUploadButtonPadding
 | 
						|
		)->addClickHandler([=] {
 | 
						|
			save(existing);
 | 
						|
		});
 | 
						|
		_content->add(
 | 
						|
			object_ptr<Ui::DividerLabel>(
 | 
						|
				_content,
 | 
						|
				object_ptr<Ui::FlatLabel>(
 | 
						|
					_content,
 | 
						|
					_scheme.aboutExisting,
 | 
						|
					st::boxDividerLabel),
 | 
						|
				st::passportFormLabelPadding));
 | 
						|
		_content->add(
 | 
						|
			object_ptr<Ui::FlatLabel>(
 | 
						|
				_content,
 | 
						|
				_scheme.newHeader,
 | 
						|
				st::passportFormHeader),
 | 
						|
			st::passportDetailsHeaderPadding);
 | 
						|
	}
 | 
						|
	const auto &fieldStyle = existing.isEmpty()
 | 
						|
		? st::passportContactField
 | 
						|
		: st::passportDetailsField;
 | 
						|
	const auto fieldPadding = existing.isEmpty()
 | 
						|
		? st::passportContactFieldPadding
 | 
						|
		: st::passportContactNewFieldPadding;
 | 
						|
	auto fieldPlaceholder = existing.isEmpty()
 | 
						|
		? rpl::duplicate(_scheme.newPlaceholder)
 | 
						|
		: nullptr;
 | 
						|
	auto wrap = object_ptr<Ui::RpWidget>(_content);
 | 
						|
	if (_scheme.type == Scheme::ValueType::Phone) {
 | 
						|
		_field = Ui::CreateChild<Ui::PhoneInput>(
 | 
						|
			wrap.data(),
 | 
						|
			fieldStyle,
 | 
						|
			std::move(fieldPlaceholder),
 | 
						|
			ExtractPhonePrefix(_controller->bot()->session().user()->phone()),
 | 
						|
			data);
 | 
						|
	} else {
 | 
						|
		_field = Ui::CreateChild<Ui::MaskedInputField>(
 | 
						|
			wrap.data(),
 | 
						|
			fieldStyle,
 | 
						|
			std::move(fieldPlaceholder),
 | 
						|
			data);
 | 
						|
	}
 | 
						|
 | 
						|
	_field->move(0, 0);
 | 
						|
	_field->heightValue(
 | 
						|
	) | rpl::start_with_next([=, pointer = wrap.data()](int height) {
 | 
						|
		pointer->resize(pointer->width(), height);
 | 
						|
	}, _field->lifetime());
 | 
						|
	wrap->widthValue(
 | 
						|
	) | rpl::start_with_next([=](int width) {
 | 
						|
		_field->resize(width, _field->height());
 | 
						|
	}, _field->lifetime());
 | 
						|
 | 
						|
	_content->add(std::move(wrap), fieldPadding);
 | 
						|
	const auto errorWrap = _content->add(
 | 
						|
		object_ptr<Ui::SlideWrap<Ui::FlatLabel>>(
 | 
						|
			_content,
 | 
						|
			object_ptr<Ui::FlatLabel>(
 | 
						|
				_content,
 | 
						|
				QString(),
 | 
						|
				st::passportVerifyErrorLabel),
 | 
						|
			st::passportContactErrorPadding),
 | 
						|
		st::passportContactErrorMargin);
 | 
						|
	errorWrap->hide(anim::type::instant);
 | 
						|
 | 
						|
	_content->add(
 | 
						|
		object_ptr<Ui::DividerLabel>(
 | 
						|
			_content,
 | 
						|
			object_ptr<Ui::FlatLabel>(
 | 
						|
				_content,
 | 
						|
				_scheme.aboutNew,
 | 
						|
				st::boxDividerLabel),
 | 
						|
			st::passportFormLabelPadding));
 | 
						|
 | 
						|
	if (auto text = _controller->deleteValueLabel()) {
 | 
						|
		_content->add(
 | 
						|
			object_ptr<Ui::SettingsButton>(
 | 
						|
				_content,
 | 
						|
				std::move(*text) | Ui::Text::ToUpper(),
 | 
						|
				st::passportDeleteButton),
 | 
						|
			st::passportUploadButtonPadding
 | 
						|
		)->addClickHandler([=] {
 | 
						|
			_controller->deleteValue();
 | 
						|
		});
 | 
						|
	}
 | 
						|
 | 
						|
	_controller->saveErrors(
 | 
						|
	) | rpl::start_with_next([=](const ScopeError &error) {
 | 
						|
		if (error.key == QString("value")) {
 | 
						|
			_field->showError();
 | 
						|
			errorWrap->entity()->setText(error.text);
 | 
						|
			_content->resizeToWidth(width());
 | 
						|
			errorWrap->show(anim::type::normal);
 | 
						|
		}
 | 
						|
	}, lifetime());
 | 
						|
 | 
						|
	const auto submit = [=] {
 | 
						|
		crl::on_main(this, [=] {
 | 
						|
			save();
 | 
						|
		});
 | 
						|
	};
 | 
						|
	connect(_field, &Ui::MaskedInputField::submitted, submit);
 | 
						|
	connect(_field, &Ui::MaskedInputField::changed, [=] {
 | 
						|
		errorWrap->hide(anim::type::normal);
 | 
						|
	});
 | 
						|
	_done->addClickHandler(submit);
 | 
						|
}
 | 
						|
 | 
						|
void PanelEditContact::focusInEvent(QFocusEvent *e) {
 | 
						|
	crl::on_main(this, [=] {
 | 
						|
		_field->setFocusFast();
 | 
						|
	});
 | 
						|
}
 | 
						|
 | 
						|
void PanelEditContact::resizeEvent(QResizeEvent *e) {
 | 
						|
	updateControlsGeometry();
 | 
						|
}
 | 
						|
 | 
						|
void PanelEditContact::updateControlsGeometry() {
 | 
						|
	const auto submitTop = height() - _done->height();
 | 
						|
	_bottomShadow->resizeToWidth(width());
 | 
						|
	_bottomShadow->moveToLeft(0, submitTop - st::lineWidth);
 | 
						|
	_done->resizeToWidth(width());
 | 
						|
	_done->moveToLeft(0, submitTop);
 | 
						|
}
 | 
						|
 | 
						|
void PanelEditContact::save() {
 | 
						|
	const auto result = _field->getLastText();
 | 
						|
	const auto processed = _scheme.postprocess
 | 
						|
		? _scheme.postprocess(result)
 | 
						|
		: result;
 | 
						|
	if (_scheme.validate && !_scheme.validate(processed)) {
 | 
						|
		_field->showError();
 | 
						|
		return;
 | 
						|
	}
 | 
						|
	save(processed);
 | 
						|
}
 | 
						|
 | 
						|
void PanelEditContact::save(const QString &value) {
 | 
						|
	auto data = ValueMap();
 | 
						|
	data.fields["value"].text = value;
 | 
						|
	_controller->saveScope(std::move(data), {});
 | 
						|
}
 | 
						|
 | 
						|
object_ptr<Ui::BoxContent> VerifyPhoneBox(
 | 
						|
		const QString &phone,
 | 
						|
		int codeLength,
 | 
						|
		Fn<void(QString code)> submit,
 | 
						|
		rpl::producer<QString> call,
 | 
						|
		rpl::producer<QString> error) {
 | 
						|
	return Box<VerifyBox>(
 | 
						|
		tr::lng_passport_phone_title(),
 | 
						|
		tr::lng_passport_confirm_phone(
 | 
						|
			tr::now,
 | 
						|
			lt_phone,
 | 
						|
			App::formatPhone(phone)),
 | 
						|
		codeLength,
 | 
						|
		submit,
 | 
						|
		nullptr,
 | 
						|
		std::move(call),
 | 
						|
		std::move(error),
 | 
						|
		nullptr);
 | 
						|
}
 | 
						|
 | 
						|
object_ptr<Ui::BoxContent> VerifyEmailBox(
 | 
						|
		const QString &email,
 | 
						|
		int codeLength,
 | 
						|
		Fn<void(QString code)> submit,
 | 
						|
		Fn<void()> resend,
 | 
						|
		rpl::producer<QString> error,
 | 
						|
		rpl::producer<QString> resent) {
 | 
						|
	return Box<VerifyBox>(
 | 
						|
		tr::lng_passport_email_title(),
 | 
						|
		tr::lng_passport_confirm_email(tr::now, lt_email, email),
 | 
						|
		codeLength,
 | 
						|
		submit,
 | 
						|
		resend,
 | 
						|
		rpl::single(QString()),
 | 
						|
		std::move(error),
 | 
						|
		std::move(resent));
 | 
						|
}
 | 
						|
 | 
						|
} // namespace Passport
 |