1 /*
2 This file is part of Telegram Desktop,
3 the official desktop application for the Telegram messaging service.
4
5 For license and copyright information please follow this link:
6 https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
7 */
8 #include "payments/ui/payments_edit_information.h"
9
10 #include "payments/ui/payments_panel_delegate.h"
11 #include "payments/ui/payments_field.h"
12 #include "ui/widgets/scroll_area.h"
13 #include "ui/widgets/buttons.h"
14 #include "ui/widgets/labels.h"
15 #include "ui/widgets/checkbox.h"
16 #include "ui/wrap/vertical_layout.h"
17 #include "ui/wrap/fade_wrap.h"
18 #include "lang/lang_keys.h"
19 #include "styles/style_payments.h"
20 #include "styles/style_passport.h"
21
22 namespace Payments::Ui {
23 namespace {
24
25 constexpr auto kMaxStreetSize = 64;
26 constexpr auto kMaxPostcodeSize = 10;
27 constexpr auto kMaxNameSize = 64;
28 constexpr auto kMaxEmailSize = 128;
29 constexpr auto kMaxPhoneSize = 16;
30 constexpr auto kMinCitySize = 2;
31 constexpr auto kMaxCitySize = 64;
32
33 } // namespace
34
EditInformation(QWidget * parent,const Invoice & invoice,const RequestedInformation & current,InformationField field,not_null<PanelDelegate * > delegate)35 EditInformation::EditInformation(
36 QWidget *parent,
37 const Invoice &invoice,
38 const RequestedInformation ¤t,
39 InformationField field,
40 not_null<PanelDelegate*> delegate)
41 : _delegate(delegate)
42 , _invoice(invoice)
43 , _information(current)
44 , _scroll(this, st::passportPanelScroll)
45 , _topShadow(this)
46 , _bottomShadow(this)
47 , _submit(
48 this,
49 tr::lng_settings_save(),
50 st::paymentsPanelButton)
51 , _cancel(
52 this,
53 tr::lng_cancel(),
54 st::paymentsPanelButton) {
55 setupControls();
56 }
57
58 EditInformation::~EditInformation() = default;
59
setFocus(InformationField field)60 void EditInformation::setFocus(InformationField field) {
61 _focusField = field;
62 if (const auto control = lookupField(field)) {
63 _scroll->ensureWidgetVisible(control->widget());
64 control->setFocus();
65 }
66 }
67
setFocusFast(InformationField field)68 void EditInformation::setFocusFast(InformationField field) {
69 _focusField = field;
70 if (const auto control = lookupField(field)) {
71 _scroll->ensureWidgetVisible(control->widget());
72 control->setFocusFast();
73 }
74 }
75
showError(InformationField field)76 void EditInformation::showError(InformationField field) {
77 if (const auto control = lookupField(field)) {
78 _scroll->ensureWidgetVisible(control->widget());
79 control->showError();
80 }
81 }
82
setupControls()83 void EditInformation::setupControls() {
84 const auto inner = setupContent();
85
86 _submit->addClickHandler([=] {
87 _delegate->panelValidateInformation(collect());
88 });
89 _cancel->addClickHandler([=] {
90 _delegate->panelCancelEdit();
91 });
92
93 using namespace rpl::mappers;
94
95 _topShadow->toggleOn(
96 _scroll->scrollTopValue() | rpl::map(_1 > 0));
97 _bottomShadow->toggleOn(rpl::combine(
98 _scroll->scrollTopValue(),
99 _scroll->heightValue(),
100 inner->heightValue(),
101 _1 + _2 < _3));
102 }
103
setupContent()104 not_null<RpWidget*> EditInformation::setupContent() {
105 const auto inner = _scroll->setOwnedWidget(
106 object_ptr<VerticalLayout>(this));
107
108 _scroll->widthValue(
109 ) | rpl::start_with_next([=](int width) {
110 inner->resizeToWidth(width);
111 }, inner->lifetime());
112
113 const auto showBox = [=](object_ptr<BoxContent> box) {
114 _delegate->panelShowBox(std::move(box));
115 };
116 auto last = (Field*)nullptr;
117 const auto add = [&](FieldConfig &&config) {
118 auto result = std::make_unique<Field>(inner, std::move(config));
119 inner->add(result->ownedWidget(), st::paymentsFieldPadding);
120 if (last) {
121 last->setNextField(result.get());
122 result->setPreviousField(last);
123 }
124 last = result.get();
125 return result;
126 };
127 if (_invoice.isShippingAddressRequested) {
128 _street1 = add({
129 .placeholder = tr::lng_payments_address_street1(),
130 .value = _information.shippingAddress.address1,
131 .validator = RangeLengthValidator(1, kMaxStreetSize),
132 });
133 _street2 = add({
134 .placeholder = tr::lng_payments_address_street2(),
135 .value = _information.shippingAddress.address2,
136 .validator = MaxLengthValidator(kMaxStreetSize),
137 });
138 _city = add({
139 .placeholder = tr::lng_payments_address_city(),
140 .value = _information.shippingAddress.city,
141 .validator = RangeLengthValidator(kMinCitySize, kMaxCitySize),
142 });
143 _state = add({
144 .placeholder = tr::lng_payments_address_state(),
145 .value = _information.shippingAddress.state,
146 });
147 _country = add({
148 .type = FieldType::Country,
149 .placeholder = tr::lng_payments_address_country(),
150 .value = _information.shippingAddress.countryIso2,
151 .validator = RequiredFinishedValidator(),
152 .showBox = showBox,
153 .defaultCountry = _information.defaultCountry,
154 });
155 _postcode = add({
156 .placeholder = tr::lng_payments_address_postcode(),
157 .value = _information.shippingAddress.postcode,
158 .validator = RangeLengthValidator(1, kMaxPostcodeSize),
159 });
160 }
161 if (_invoice.isNameRequested) {
162 _name = add({
163 .placeholder = tr::lng_payments_info_name(),
164 .value = _information.name,
165 .validator = RangeLengthValidator(1, kMaxNameSize),
166 });
167 }
168 if (_invoice.isEmailRequested) {
169 _email = add({
170 .type = FieldType::Email,
171 .placeholder = tr::lng_payments_info_email(),
172 .value = _information.email,
173 .validator = RangeLengthValidator(1, kMaxEmailSize),
174 });
175 }
176 if (_invoice.isPhoneRequested) {
177 _phone = add({
178 .type = FieldType::Phone,
179 .placeholder = tr::lng_payments_info_phone(),
180 .value = _information.phone,
181 .validator = RangeLengthValidator(1, kMaxPhoneSize),
182 .defaultPhone = _information.defaultPhone,
183 });
184 }
185 const auto emailToProvider = _invoice.isEmailRequested
186 && _invoice.emailSentToProvider;
187 const auto phoneToProvider = _invoice.isPhoneRequested
188 && _invoice.phoneSentToProvider;
189 if (emailToProvider || phoneToProvider) {
190 inner->add(
191 object_ptr<Ui::FlatLabel>(
192 inner,
193 ((emailToProvider && phoneToProvider)
194 ? tr::lng_payments_to_provider_phone_email
195 : emailToProvider
196 ? tr::lng_payments_to_provider_email
197 : tr::lng_payments_to_provider_phone)(
198 lt_provider,
199 rpl::single(_invoice.provider)),
200 st::paymentsToProviderLabel),
201 st::paymentsToProviderPadding);
202 }
203 _save = inner->add(
204 object_ptr<Ui::Checkbox>(
205 inner,
206 tr::lng_payments_save_information(tr::now),
207 true),
208 st::paymentsSaveCheckboxPadding);
209
210 if (last) {
211 last->submitted(
212 ) | rpl::start_with_next([=] {
213 _delegate->panelValidateInformation(collect());
214 }, lifetime());
215 }
216
217 return inner;
218 }
219
resizeEvent(QResizeEvent * e)220 void EditInformation::resizeEvent(QResizeEvent *e) {
221 updateControlsGeometry();
222 }
223
focusInEvent(QFocusEvent * e)224 void EditInformation::focusInEvent(QFocusEvent *e) {
225 if (const auto control = lookupField(_focusField)) {
226 control->setFocus();
227 }
228 }
229
updateControlsGeometry()230 void EditInformation::updateControlsGeometry() {
231 const auto &padding = st::paymentsPanelPadding;
232 const auto buttonsHeight = padding.top()
233 + _cancel->height()
234 + padding.bottom();
235 const auto buttonsTop = height() - buttonsHeight;
236 _scroll->setGeometry(0, 0, width(), buttonsTop);
237 _topShadow->resizeToWidth(width());
238 _topShadow->moveToLeft(0, 0);
239 _bottomShadow->resizeToWidth(width());
240 _bottomShadow->moveToLeft(0, buttonsTop - st::lineWidth);
241 auto right = padding.right();
242 _submit->moveToRight(right, buttonsTop + padding.top());
243 right += _submit->width() + padding.left();
244 _cancel->moveToRight(right, buttonsTop + padding.top());
245
246 _scroll->updateBars();
247 }
248
lookupField(InformationField field) const249 auto EditInformation::lookupField(InformationField field) const -> Field* {
250 switch (field) {
251 case InformationField::ShippingStreet: return _street1.get();
252 case InformationField::ShippingCity: return _city.get();
253 case InformationField::ShippingState: return _state.get();
254 case InformationField::ShippingCountry: return _country.get();
255 case InformationField::ShippingPostcode: return _postcode.get();
256 case InformationField::Name: return _name.get();
257 case InformationField::Email: return _email.get();
258 case InformationField::Phone: return _phone.get();
259 }
260 Unexpected("Unknown field in EditInformation::lookupField.");
261 }
262
collect() const263 RequestedInformation EditInformation::collect() const {
264 return {
265 .defaultPhone = _information.defaultPhone,
266 .defaultCountry = _information.defaultCountry,
267 .save = _save->checked(),
268 .name = _name ? _name->value() : QString(),
269 .phone = _phone ? _phone->value() : QString(),
270 .email = _email ? _email->value() : QString(),
271 .shippingAddress = {
272 .address1 = _street1 ? _street1->value() : QString(),
273 .address2 = _street2 ? _street2->value() : QString(),
274 .city = _city ? _city->value() : QString(),
275 .state = _state ? _state->value() : QString(),
276 .countryIso2 = _country ? _country->value() : QString(),
277 .postcode = _postcode ? _postcode->value() : QString(),
278 },
279 };
280 }
281
282 } // namespace Payments::Ui
283