/* 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 title, const QString &text, int codeLength, Fn submit, Fn resend, rpl::producer call, rpl::producer error, rpl::producer resent); void setInnerFocus() override; protected: void prepare() override; private: void setupControls( const QString &text, int codeLength, Fn submit, Fn resend, rpl::producer call, rpl::producer error, rpl::producer resent); rpl::producer _title; Fn _submit; QPointer _code; QPointer _content; }; VerifyBox::VerifyBox( QWidget*, rpl::producer title, const QString &text, int codeLength, Fn submit, Fn resend, rpl::producer call, rpl::producer error, rpl::producer 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 submit, Fn resend, rpl::producer call, rpl::producer error, rpl::producer resent) { _content = Ui::CreateChild(this); const auto small = style::margins( st::boxPadding.left(), 0, st::boxPadding.right(), st::boxPadding.bottom()); const auto description = _content->add( object_ptr( _content, text, st::boxLabel), small); _code = _content->add( object_ptr( _content, st::defaultInputField, tr::lng_change_phone_code_title()), small); const auto problem = _content->add( object_ptr>( _content, object_ptr( _content, QString(), st::passportVerifyErrorLabel)), small); const auto waiter = _content->add( object_ptr( _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( _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 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( _content, st::passportFormDividerHeight)); if (!existing.isEmpty()) { _content->add( object_ptr( _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( _content, object_ptr( _content, _scheme.aboutExisting, st::boxDividerLabel), st::passportFormLabelPadding)); _content->add( object_ptr( _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(_content); if (_scheme.type == Scheme::ValueType::Phone) { _field = Ui::CreateChild( wrap.data(), fieldStyle, std::move(fieldPlaceholder), ExtractPhonePrefix(_controller->bot()->session().user()->phone()), data); } else { _field = Ui::CreateChild( 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>( _content, object_ptr( _content, QString(), st::passportVerifyErrorLabel), st::passportContactErrorPadding), st::passportContactErrorMargin); errorWrap->hide(anim::type::instant); _content->add( object_ptr( _content, object_ptr( _content, _scheme.aboutNew, st::boxDividerLabel), st::passportFormLabelPadding)); if (auto text = _controller->deleteValueLabel()) { _content->add( object_ptr( _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 VerifyPhoneBox( const QString &phone, int codeLength, Fn submit, rpl::producer call, rpl::producer error) { return Box( 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 VerifyEmailBox( const QString &email, int codeLength, Fn submit, Fn resend, rpl::producer error, rpl::producer resent) { return Box( 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