mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-06-05 06:33:57 +02:00
First full-featured version of payments, no design.
This commit is contained in:
parent
4c707bff29
commit
0d44736575
42 changed files with 1756 additions and 736 deletions
|
@ -256,8 +256,6 @@ PRIVATE
|
||||||
boxes/sessions_box.h
|
boxes/sessions_box.h
|
||||||
boxes/share_box.cpp
|
boxes/share_box.cpp
|
||||||
boxes/share_box.h
|
boxes/share_box.h
|
||||||
boxes/single_choice_box.cpp
|
|
||||||
boxes/single_choice_box.h
|
|
||||||
boxes/sticker_set_box.cpp
|
boxes/sticker_set_box.cpp
|
||||||
boxes/sticker_set_box.h
|
boxes/sticker_set_box.h
|
||||||
boxes/stickers_box.cpp
|
boxes/stickers_box.cpp
|
||||||
|
@ -390,8 +388,6 @@ PRIVATE
|
||||||
data/data_cloud_file.h
|
data/data_cloud_file.h
|
||||||
data/data_cloud_themes.cpp
|
data/data_cloud_themes.cpp
|
||||||
data/data_cloud_themes.h
|
data/data_cloud_themes.h
|
||||||
data/data_countries.cpp
|
|
||||||
data/data_countries.h
|
|
||||||
data/data_document.cpp
|
data/data_document.cpp
|
||||||
data/data_document.h
|
data/data_document.h
|
||||||
data/data_document_media.cpp
|
data/data_document_media.cpp
|
||||||
|
@ -803,8 +799,6 @@ PRIVATE
|
||||||
passport/passport_panel.h
|
passport/passport_panel.h
|
||||||
passport/passport_panel_controller.cpp
|
passport/passport_panel_controller.cpp
|
||||||
passport/passport_panel_controller.h
|
passport/passport_panel_controller.h
|
||||||
passport/passport_panel_details_row.cpp
|
|
||||||
passport/passport_panel_details_row.h
|
|
||||||
passport/passport_panel_edit_contact.cpp
|
passport/passport_panel_edit_contact.cpp
|
||||||
passport/passport_panel_edit_contact.h
|
passport/passport_panel_edit_contact.h
|
||||||
passport/passport_panel_edit_document.cpp
|
passport/passport_panel_edit_document.cpp
|
||||||
|
|
|
@ -11,7 +11,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "main/main_session.h"
|
#include "main/main_session.h"
|
||||||
#include "boxes/add_contact_box.h"
|
#include "boxes/add_contact_box.h"
|
||||||
#include "boxes/confirm_box.h"
|
#include "boxes/confirm_box.h"
|
||||||
#include "boxes/single_choice_box.h"
|
|
||||||
#include "boxes/peer_list_controllers.h"
|
#include "boxes/peer_list_controllers.h"
|
||||||
#include "boxes/peers/edit_participants_box.h"
|
#include "boxes/peers/edit_participants_box.h"
|
||||||
#include "boxes/peers/edit_peer_type_box.h"
|
#include "boxes/peers/edit_peer_type_box.h"
|
||||||
|
@ -20,6 +19,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "boxes/peers/edit_peer_invite_links.h"
|
#include "boxes/peers/edit_peer_invite_links.h"
|
||||||
#include "boxes/peers/edit_linked_chat_box.h"
|
#include "boxes/peers/edit_linked_chat_box.h"
|
||||||
#include "boxes/stickers_box.h"
|
#include "boxes/stickers_box.h"
|
||||||
|
#include "ui/boxes/single_choice_box.h"
|
||||||
#include "chat_helpers/emoji_suggestions_widget.h"
|
#include "chat_helpers/emoji_suggestions_widget.h"
|
||||||
#include "core/application.h"
|
#include "core/application.h"
|
||||||
#include "core/core_settings.h"
|
#include "core/core_settings.h"
|
||||||
|
|
|
@ -35,7 +35,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "data/data_group_call.h"
|
#include "data/data_group_call.h"
|
||||||
#include "data/data_changes.h"
|
#include "data/data_changes.h"
|
||||||
#include "core/application.h"
|
#include "core/application.h"
|
||||||
#include "boxes/single_choice_box.h"
|
#include "ui/boxes/single_choice_box.h"
|
||||||
#include "webrtc/webrtc_audio_input_tester.h"
|
#include "webrtc/webrtc_audio_input_tester.h"
|
||||||
#include "webrtc/webrtc_media_devices.h"
|
#include "webrtc/webrtc_media_devices.h"
|
||||||
#include "settings/settings_common.h"
|
#include "settings/settings_common.h"
|
||||||
|
|
|
@ -1820,7 +1820,7 @@ Utf8String FormatDateTime(
|
||||||
).toUtf8();
|
).toUtf8();
|
||||||
}
|
}
|
||||||
|
|
||||||
Utf8String FormatMoneyAmount(uint64 amount, const Utf8String ¤cy) {
|
Utf8String FormatMoneyAmount(int64 amount, const Utf8String ¤cy) {
|
||||||
return Ui::FillAmountAndCurrency(
|
return Ui::FillAmountAndCurrency(
|
||||||
amount,
|
amount,
|
||||||
QString::fromUtf8(currency)).toUtf8();
|
QString::fromUtf8(currency)).toUtf8();
|
||||||
|
|
|
@ -660,7 +660,7 @@ Utf8String FormatDateTime(
|
||||||
QChar dateSeparator = QChar('.'),
|
QChar dateSeparator = QChar('.'),
|
||||||
QChar timeSeparator = QChar(':'),
|
QChar timeSeparator = QChar(':'),
|
||||||
QChar separator = QChar(' '));
|
QChar separator = QChar(' '));
|
||||||
Utf8String FormatMoneyAmount(uint64 amount, const Utf8String ¤cy);
|
Utf8String FormatMoneyAmount(int64 amount, const Utf8String ¤cy);
|
||||||
Utf8String FormatFileSize(int64 size);
|
Utf8String FormatFileSize(int64 size);
|
||||||
Utf8String FormatDuration(int64 seconds);
|
Utf8String FormatDuration(int64 seconds);
|
||||||
|
|
||||||
|
|
|
@ -958,7 +958,9 @@ void HistoryService::createFromMtp(const MTPDmessageService &message) {
|
||||||
UpdateComponents(HistoryServicePayment::Bit());
|
UpdateComponents(HistoryServicePayment::Bit());
|
||||||
const auto amount = data.vtotal_amount().v;
|
const auto amount = data.vtotal_amount().v;
|
||||||
const auto currency = qs(data.vcurrency());
|
const auto currency = qs(data.vcurrency());
|
||||||
Get<HistoryServicePayment>()->amount = Ui::FillAmountAndCurrency(amount, currency);
|
Get<HistoryServicePayment>()->amount = Ui::FillAmountAndCurrency(
|
||||||
|
amount,
|
||||||
|
currency);
|
||||||
} else if (message.vaction().type() == mtpc_messageActionGroupCall) {
|
} else if (message.vaction().type() == mtpc_messageActionGroupCall) {
|
||||||
const auto &data = message.vaction().c_messageActionGroupCall();
|
const auto &data = message.vaction().c_messageActionGroupCall();
|
||||||
if (data.vduration()) {
|
if (data.vduration()) {
|
||||||
|
|
|
@ -61,8 +61,8 @@ PhoneWidget::PhoneWidget(
|
||||||
setErrorCentered(true);
|
setErrorCentered(true);
|
||||||
setupQrLogin();
|
setupQrLogin();
|
||||||
|
|
||||||
if (!_country->onChooseCountry(getData()->country)) {
|
if (!_country->chooseCountry(getData()->country)) {
|
||||||
_country->onChooseCountry(qsl("US"));
|
_country->chooseCountry(qsl("US"));
|
||||||
}
|
}
|
||||||
_changed = false;
|
_changed = false;
|
||||||
}
|
}
|
||||||
|
@ -251,7 +251,7 @@ QString PhoneWidget::fullNumber() const {
|
||||||
}
|
}
|
||||||
|
|
||||||
void PhoneWidget::selectCountry(const QString &country) {
|
void PhoneWidget::selectCountry(const QString &country) {
|
||||||
_country->onChooseCountry(country);
|
_country->chooseCountry(country);
|
||||||
}
|
}
|
||||||
|
|
||||||
void PhoneWidget::setInnerFocus() {
|
void PhoneWidget::setInnerFocus() {
|
||||||
|
|
|
@ -9,10 +9,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
|
|
||||||
#include "lang/lang_keys.h"
|
#include "lang/lang_keys.h"
|
||||||
#include "passport/passport_panel_edit_document.h"
|
#include "passport/passport_panel_edit_document.h"
|
||||||
#include "passport/passport_panel_details_row.h"
|
|
||||||
#include "passport/passport_panel_edit_contact.h"
|
#include "passport/passport_panel_edit_contact.h"
|
||||||
#include "passport/passport_panel_edit_scans.h"
|
#include "passport/passport_panel_edit_scans.h"
|
||||||
#include "passport/passport_panel.h"
|
#include "passport/passport_panel.h"
|
||||||
|
#include "passport/ui/passport_details_row.h"
|
||||||
#include "base/openssl_help.h"
|
#include "base/openssl_help.h"
|
||||||
#include "base/unixtime.h"
|
#include "base/unixtime.h"
|
||||||
#include "boxes/passcode_box.h"
|
#include "boxes/passcode_box.h"
|
||||||
|
@ -212,7 +212,7 @@ EditDocumentScheme GetDocumentScheme(
|
||||||
result.rows = {
|
result.rows = {
|
||||||
{
|
{
|
||||||
ValueClass::Fields,
|
ValueClass::Fields,
|
||||||
PanelDetailsType::Text,
|
Ui::PanelDetailsType::Text,
|
||||||
qsl("first_name"),
|
qsl("first_name"),
|
||||||
tr::lng_passport_first_name(tr::now),
|
tr::lng_passport_first_name(tr::now),
|
||||||
NameValidate,
|
NameValidate,
|
||||||
|
@ -221,7 +221,7 @@ EditDocumentScheme GetDocumentScheme(
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ValueClass::Fields,
|
ValueClass::Fields,
|
||||||
PanelDetailsType::Text,
|
Ui::PanelDetailsType::Text,
|
||||||
qsl("middle_name"),
|
qsl("middle_name"),
|
||||||
tr::lng_passport_middle_name(tr::now),
|
tr::lng_passport_middle_name(tr::now),
|
||||||
NameOrEmptyValidate,
|
NameOrEmptyValidate,
|
||||||
|
@ -231,7 +231,7 @@ EditDocumentScheme GetDocumentScheme(
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ValueClass::Fields,
|
ValueClass::Fields,
|
||||||
PanelDetailsType::Text,
|
Ui::PanelDetailsType::Text,
|
||||||
qsl("last_name"),
|
qsl("last_name"),
|
||||||
tr::lng_passport_last_name(tr::now),
|
tr::lng_passport_last_name(tr::now),
|
||||||
NameValidate,
|
NameValidate,
|
||||||
|
@ -241,7 +241,7 @@ EditDocumentScheme GetDocumentScheme(
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ValueClass::Fields,
|
ValueClass::Fields,
|
||||||
PanelDetailsType::Date,
|
Ui::PanelDetailsType::Date,
|
||||||
qsl("birth_date"),
|
qsl("birth_date"),
|
||||||
tr::lng_passport_birth_date(tr::now),
|
tr::lng_passport_birth_date(tr::now),
|
||||||
DateValidate,
|
DateValidate,
|
||||||
|
@ -249,7 +249,7 @@ EditDocumentScheme GetDocumentScheme(
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ValueClass::Fields,
|
ValueClass::Fields,
|
||||||
PanelDetailsType::Gender,
|
Ui::PanelDetailsType::Gender,
|
||||||
qsl("gender"),
|
qsl("gender"),
|
||||||
tr::lng_passport_gender(tr::now),
|
tr::lng_passport_gender(tr::now),
|
||||||
GenderValidate,
|
GenderValidate,
|
||||||
|
@ -257,7 +257,7 @@ EditDocumentScheme GetDocumentScheme(
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ValueClass::Fields,
|
ValueClass::Fields,
|
||||||
PanelDetailsType::Country,
|
Ui::PanelDetailsType::Country,
|
||||||
qsl("country_code"),
|
qsl("country_code"),
|
||||||
tr::lng_passport_country(tr::now),
|
tr::lng_passport_country(tr::now),
|
||||||
CountryValidate,
|
CountryValidate,
|
||||||
|
@ -265,7 +265,7 @@ EditDocumentScheme GetDocumentScheme(
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ValueClass::Fields,
|
ValueClass::Fields,
|
||||||
PanelDetailsType::Country,
|
Ui::PanelDetailsType::Country,
|
||||||
qsl("residence_country_code"),
|
qsl("residence_country_code"),
|
||||||
tr::lng_passport_residence_country(tr::now),
|
tr::lng_passport_residence_country(tr::now),
|
||||||
CountryValidate,
|
CountryValidate,
|
||||||
|
@ -273,7 +273,7 @@ EditDocumentScheme GetDocumentScheme(
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ValueClass::Scans,
|
ValueClass::Scans,
|
||||||
PanelDetailsType::Text,
|
Ui::PanelDetailsType::Text,
|
||||||
qsl("document_no"),
|
qsl("document_no"),
|
||||||
tr::lng_passport_document_number(tr::now),
|
tr::lng_passport_document_number(tr::now),
|
||||||
DocumentValidate,
|
DocumentValidate,
|
||||||
|
@ -282,7 +282,7 @@ EditDocumentScheme GetDocumentScheme(
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ValueClass::Scans,
|
ValueClass::Scans,
|
||||||
PanelDetailsType::Date,
|
Ui::PanelDetailsType::Date,
|
||||||
qsl("expiry_date"),
|
qsl("expiry_date"),
|
||||||
tr::lng_passport_expiry_date(tr::now),
|
tr::lng_passport_expiry_date(tr::now),
|
||||||
DateOrEmptyValidate,
|
DateOrEmptyValidate,
|
||||||
|
@ -344,7 +344,7 @@ EditDocumentScheme GetDocumentScheme(
|
||||||
auto additional = std::initializer_list<Row>{
|
auto additional = std::initializer_list<Row>{
|
||||||
{
|
{
|
||||||
ValueClass::Additional,
|
ValueClass::Additional,
|
||||||
PanelDetailsType::Text,
|
Ui::PanelDetailsType::Text,
|
||||||
qsl("first_name_native"),
|
qsl("first_name_native"),
|
||||||
tr::lng_passport_first_name(tr::now),
|
tr::lng_passport_first_name(tr::now),
|
||||||
NativeNameValidate,
|
NativeNameValidate,
|
||||||
|
@ -355,7 +355,7 @@ EditDocumentScheme GetDocumentScheme(
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ValueClass::Additional,
|
ValueClass::Additional,
|
||||||
PanelDetailsType::Text,
|
Ui::PanelDetailsType::Text,
|
||||||
qsl("middle_name_native"),
|
qsl("middle_name_native"),
|
||||||
tr::lng_passport_middle_name(tr::now),
|
tr::lng_passport_middle_name(tr::now),
|
||||||
NativeNameOrEmptyValidate,
|
NativeNameOrEmptyValidate,
|
||||||
|
@ -366,7 +366,7 @@ EditDocumentScheme GetDocumentScheme(
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ValueClass::Additional,
|
ValueClass::Additional,
|
||||||
PanelDetailsType::Text,
|
Ui::PanelDetailsType::Text,
|
||||||
qsl("last_name_native"),
|
qsl("last_name_native"),
|
||||||
tr::lng_passport_last_name(tr::now),
|
tr::lng_passport_last_name(tr::now),
|
||||||
NativeNameValidate,
|
NativeNameValidate,
|
||||||
|
@ -411,7 +411,7 @@ EditDocumentScheme GetDocumentScheme(
|
||||||
result.rows = {
|
result.rows = {
|
||||||
{
|
{
|
||||||
ValueClass::Fields,
|
ValueClass::Fields,
|
||||||
PanelDetailsType::Text,
|
Ui::PanelDetailsType::Text,
|
||||||
qsl("street_line1"),
|
qsl("street_line1"),
|
||||||
tr::lng_passport_street(tr::now),
|
tr::lng_passport_street(tr::now),
|
||||||
StreetValidate,
|
StreetValidate,
|
||||||
|
@ -420,7 +420,7 @@ EditDocumentScheme GetDocumentScheme(
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ValueClass::Fields,
|
ValueClass::Fields,
|
||||||
PanelDetailsType::Text,
|
Ui::PanelDetailsType::Text,
|
||||||
qsl("street_line2"),
|
qsl("street_line2"),
|
||||||
tr::lng_passport_street(tr::now),
|
tr::lng_passport_street(tr::now),
|
||||||
DontValidate,
|
DontValidate,
|
||||||
|
@ -429,7 +429,7 @@ EditDocumentScheme GetDocumentScheme(
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ValueClass::Fields,
|
ValueClass::Fields,
|
||||||
PanelDetailsType::Text,
|
Ui::PanelDetailsType::Text,
|
||||||
qsl("city"),
|
qsl("city"),
|
||||||
tr::lng_passport_city(tr::now),
|
tr::lng_passport_city(tr::now),
|
||||||
CityValidate,
|
CityValidate,
|
||||||
|
@ -438,7 +438,7 @@ EditDocumentScheme GetDocumentScheme(
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ValueClass::Fields,
|
ValueClass::Fields,
|
||||||
PanelDetailsType::Text,
|
Ui::PanelDetailsType::Text,
|
||||||
qsl("state"),
|
qsl("state"),
|
||||||
tr::lng_passport_state(tr::now),
|
tr::lng_passport_state(tr::now),
|
||||||
DontValidate,
|
DontValidate,
|
||||||
|
@ -447,7 +447,7 @@ EditDocumentScheme GetDocumentScheme(
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ValueClass::Fields,
|
ValueClass::Fields,
|
||||||
PanelDetailsType::Country,
|
Ui::PanelDetailsType::Country,
|
||||||
qsl("country_code"),
|
qsl("country_code"),
|
||||||
tr::lng_passport_country(tr::now),
|
tr::lng_passport_country(tr::now),
|
||||||
CountryValidate,
|
CountryValidate,
|
||||||
|
@ -455,7 +455,7 @@ EditDocumentScheme GetDocumentScheme(
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
ValueClass::Fields,
|
ValueClass::Fields,
|
||||||
PanelDetailsType::Postcode,
|
Ui::PanelDetailsType::Postcode,
|
||||||
qsl("post_code"),
|
qsl("post_code"),
|
||||||
tr::lng_passport_postcode(tr::now),
|
tr::lng_passport_postcode(tr::now),
|
||||||
PostcodeValidate,
|
PostcodeValidate,
|
||||||
|
|
|
@ -8,7 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "passport/passport_panel_edit_contact.h"
|
#include "passport/passport_panel_edit_contact.h"
|
||||||
|
|
||||||
#include "passport/passport_panel_controller.h"
|
#include "passport/passport_panel_controller.h"
|
||||||
#include "passport/passport_panel_details_row.h"
|
#include "passport/ui/passport_details_row.h"
|
||||||
#include "ui/widgets/input_fields.h"
|
#include "ui/widgets/input_fields.h"
|
||||||
#include "ui/widgets/labels.h"
|
#include "ui/widgets/labels.h"
|
||||||
#include "ui/widgets/buttons.h"
|
#include "ui/widgets/buttons.h"
|
||||||
|
|
|
@ -8,8 +8,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "passport/passport_panel_edit_document.h"
|
#include "passport/passport_panel_edit_document.h"
|
||||||
|
|
||||||
#include "passport/passport_panel_controller.h"
|
#include "passport/passport_panel_controller.h"
|
||||||
#include "passport/passport_panel_details_row.h"
|
|
||||||
#include "passport/passport_panel_edit_scans.h"
|
#include "passport/passport_panel_edit_scans.h"
|
||||||
|
#include "passport/ui/passport_details_row.h"
|
||||||
#include "ui/widgets/input_fields.h"
|
#include "ui/widgets/input_fields.h"
|
||||||
#include "ui/widgets/scroll_area.h"
|
#include "ui/widgets/scroll_area.h"
|
||||||
#include "ui/widgets/labels.h"
|
#include "ui/widgets/labels.h"
|
||||||
|
@ -19,6 +19,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "ui/wrap/vertical_layout.h"
|
#include "ui/wrap/vertical_layout.h"
|
||||||
#include "ui/wrap/fade_wrap.h"
|
#include "ui/wrap/fade_wrap.h"
|
||||||
#include "ui/wrap/slide_wrap.h"
|
#include "ui/wrap/slide_wrap.h"
|
||||||
|
#include "data/data_countries.h"
|
||||||
|
#include "data/data_user.h" // ->bot()->session()
|
||||||
|
#include "main/main_session.h" // ->session().user()
|
||||||
#include "ui/text/text_utilities.h" // Ui::Text::ToUpper
|
#include "ui/text/text_utilities.h" // Ui::Text::ToUpper
|
||||||
#include "boxes/abstract_box.h"
|
#include "boxes/abstract_box.h"
|
||||||
#include "boxes/confirm_box.h"
|
#include "boxes/confirm_box.h"
|
||||||
|
@ -363,7 +366,7 @@ not_null<Ui::RpWidget*> PanelEditDocument::setupContent(
|
||||||
const ValueMap &fields) {
|
const ValueMap &fields) {
|
||||||
accumulate_max(
|
accumulate_max(
|
||||||
maxLabelWidth,
|
maxLabelWidth,
|
||||||
PanelDetailsRow::LabelWidth(row.label));
|
Ui::PanelDetailsRow::LabelWidth(row.label));
|
||||||
});
|
});
|
||||||
if (maxLabelWidth > 0) {
|
if (maxLabelWidth > 0) {
|
||||||
if (error && !error->isEmpty()) {
|
if (error && !error->isEmpty()) {
|
||||||
|
@ -513,12 +516,20 @@ void PanelEditDocument::createDetailsRow(
|
||||||
};
|
};
|
||||||
|
|
||||||
const auto current = valueOrEmpty(fields, row.key);
|
const auto current = valueOrEmpty(fields, row.key);
|
||||||
|
const auto showBox = [controller = _controller](
|
||||||
|
object_ptr<Ui::BoxContent> box) {
|
||||||
|
controller->show(std::move(box));
|
||||||
|
};
|
||||||
|
const auto isoByPhone = Data::CountryISO2ByPhone(
|
||||||
|
_controller->bot()->session().user()->phone());
|
||||||
|
|
||||||
const auto [it, ok] = _details.emplace(
|
const auto [it, ok] = _details.emplace(
|
||||||
i,
|
i,
|
||||||
container->add(PanelDetailsRow::Create(
|
container->add(Ui::PanelDetailsRow::Create(
|
||||||
container,
|
container,
|
||||||
|
showBox,
|
||||||
|
isoByPhone,
|
||||||
row.inputType,
|
row.inputType,
|
||||||
_controller,
|
|
||||||
row.label,
|
row.label,
|
||||||
maxLabelWidth,
|
maxLabelWidth,
|
||||||
current.text,
|
current.text,
|
||||||
|
@ -537,7 +548,7 @@ void PanelEditDocument::createDetailsRow(
|
||||||
}, it->second->lifetime());
|
}, it->second->lifetime());
|
||||||
}
|
}
|
||||||
|
|
||||||
not_null<PanelDetailsRow*> PanelEditDocument::findRow(
|
not_null<Ui::PanelDetailsRow*> PanelEditDocument::findRow(
|
||||||
const QString &key) const {
|
const QString &key) const {
|
||||||
for (auto i = 0, count = int(_scheme.rows.size()); i != count; ++i) {
|
for (auto i = 0, count = int(_scheme.rows.size()); i != count; ++i) {
|
||||||
const auto &row = _scheme.rows[i];
|
const auto &row = _scheme.rows[i];
|
||||||
|
@ -636,7 +647,7 @@ bool PanelEditDocument::validate() {
|
||||||
_scroll->scrollToY(_scroll->scrollTop() + scrolldelta);
|
_scroll->scrollToY(_scroll->scrollTop() + scrolldelta);
|
||||||
error = firsttop.y();
|
error = firsttop.y();
|
||||||
}
|
}
|
||||||
auto first = QPointer<PanelDetailsRow>();
|
auto first = QPointer<Ui::PanelDetailsRow>();
|
||||||
for (const auto &[i, field] : ranges::views::reverse(_details)) {
|
for (const auto &[i, field] : ranges::views::reverse(_details)) {
|
||||||
const auto &row = _scheme.rows[i];
|
const auto &row = _scheme.rows[i];
|
||||||
if (row.valueClass == Scheme::ValueClass::Additional
|
if (row.valueClass == Scheme::ValueClass::Additional
|
||||||
|
|
|
@ -24,15 +24,19 @@ template <typename Widget>
|
||||||
class SlideWrap;
|
class SlideWrap;
|
||||||
} // namespace Ui
|
} // namespace Ui
|
||||||
|
|
||||||
|
namespace Passport::Ui {
|
||||||
|
using namespace ::Ui;
|
||||||
|
enum class PanelDetailsType;
|
||||||
|
class PanelDetailsRow;
|
||||||
|
} // namespace Passport::Ui
|
||||||
|
|
||||||
namespace Passport {
|
namespace Passport {
|
||||||
|
|
||||||
class PanelController;
|
class PanelController;
|
||||||
struct ValueMap;
|
struct ValueMap;
|
||||||
struct ScanInfo;
|
struct ScanInfo;
|
||||||
class EditScans;
|
class EditScans;
|
||||||
class PanelDetailsRow;
|
|
||||||
enum class FileType;
|
enum class FileType;
|
||||||
enum class PanelDetailsType;
|
|
||||||
struct ScanListData;
|
struct ScanListData;
|
||||||
|
|
||||||
struct EditDocumentScheme {
|
struct EditDocumentScheme {
|
||||||
|
@ -50,7 +54,7 @@ struct EditDocumentScheme {
|
||||||
using Validator = Fn<std::optional<QString>(const QString &value)>;
|
using Validator = Fn<std::optional<QString>(const QString &value)>;
|
||||||
using Formatter = Fn<QString(const QString &value)>;
|
using Formatter = Fn<QString(const QString &value)>;
|
||||||
ValueClass valueClass = ValueClass::Fields;
|
ValueClass valueClass = ValueClass::Fields;
|
||||||
PanelDetailsType inputType = PanelDetailsType();
|
Ui::PanelDetailsType inputType = Ui::PanelDetailsType();
|
||||||
QString key;
|
QString key;
|
||||||
QString label;
|
QString label;
|
||||||
Validator error;
|
Validator error;
|
||||||
|
@ -140,7 +144,7 @@ private:
|
||||||
const Scheme::Row &row,
|
const Scheme::Row &row,
|
||||||
const ValueMap &fields,
|
const ValueMap &fields,
|
||||||
int maxLabelWidth);
|
int maxLabelWidth);
|
||||||
not_null<PanelDetailsRow*> findRow(const QString &key) const;
|
not_null<Ui::PanelDetailsRow*> findRow(const QString &key) const;
|
||||||
|
|
||||||
not_null<PanelController*> _controller;
|
not_null<PanelController*> _controller;
|
||||||
Scheme _scheme;
|
Scheme _scheme;
|
||||||
|
@ -151,7 +155,7 @@ private:
|
||||||
|
|
||||||
QPointer<EditScans> _editScans;
|
QPointer<EditScans> _editScans;
|
||||||
QPointer<Ui::SlideWrap<Ui::FlatLabel>> _commonError;
|
QPointer<Ui::SlideWrap<Ui::FlatLabel>> _commonError;
|
||||||
std::map<int, QPointer<PanelDetailsRow>> _details;
|
std::map<int, QPointer<Ui::PanelDetailsRow>> _details;
|
||||||
bool _fieldsChanged = false;
|
bool _fieldsChanged = false;
|
||||||
bool _additionalShown = false;
|
bool _additionalShown = false;
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "passport/passport_panel_edit_scans.h"
|
#include "passport/passport_panel_edit_scans.h"
|
||||||
|
|
||||||
#include "passport/passport_panel_controller.h"
|
#include "passport/passport_panel_controller.h"
|
||||||
#include "passport/passport_panel_details_row.h"
|
#include "passport/ui/passport_details_row.h"
|
||||||
#include "ui/widgets/buttons.h"
|
#include "ui/widgets/buttons.h"
|
||||||
#include "ui/widgets/labels.h"
|
#include "ui/widgets/labels.h"
|
||||||
#include "ui/widgets/box_content_divider.h"
|
#include "ui/widgets/box_content_divider.h"
|
||||||
|
|
|
@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "passport/passport_panel_form.h"
|
#include "passport/passport_panel_form.h"
|
||||||
|
|
||||||
#include "passport/passport_panel_controller.h"
|
#include "passport/passport_panel_controller.h"
|
||||||
|
#include "passport/ui/passport_form_row.h"
|
||||||
#include "lang/lang_keys.h"
|
#include "lang/lang_keys.h"
|
||||||
#include "boxes/abstract_box.h"
|
#include "boxes/abstract_box.h"
|
||||||
#include "core/click_handler_types.h"
|
#include "core/click_handler_types.h"
|
||||||
|
@ -30,145 +31,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
|
|
||||||
namespace Passport {
|
namespace Passport {
|
||||||
|
|
||||||
class PanelForm::Row : public Ui::RippleButton {
|
|
||||||
public:
|
|
||||||
explicit Row(QWidget *parent);
|
|
||||||
|
|
||||||
void updateContent(
|
|
||||||
const QString &title,
|
|
||||||
const QString &description,
|
|
||||||
bool ready,
|
|
||||||
bool error,
|
|
||||||
anim::type animated);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
int resizeGetHeight(int newWidth) override;
|
|
||||||
|
|
||||||
void paintEvent(QPaintEvent *e) override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
int countAvailableWidth() const;
|
|
||||||
int countAvailableWidth(int newWidth) const;
|
|
||||||
|
|
||||||
Ui::Text::String _title;
|
|
||||||
Ui::Text::String _description;
|
|
||||||
int _titleHeight = 0;
|
|
||||||
int _descriptionHeight = 0;
|
|
||||||
bool _ready = false;
|
|
||||||
bool _error = false;
|
|
||||||
Ui::Animations::Simple _errorAnimation;
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
PanelForm::Row::Row(QWidget *parent)
|
|
||||||
: RippleButton(parent, st::passportRowRipple)
|
|
||||||
, _title(st::boxWideWidth / 2)
|
|
||||||
, _description(st::boxWideWidth / 2) {
|
|
||||||
}
|
|
||||||
|
|
||||||
void PanelForm::Row::updateContent(
|
|
||||||
const QString &title,
|
|
||||||
const QString &description,
|
|
||||||
bool ready,
|
|
||||||
bool error,
|
|
||||||
anim::type animated) {
|
|
||||||
_title.setText(
|
|
||||||
st::semiboldTextStyle,
|
|
||||||
title,
|
|
||||||
Ui::NameTextOptions());
|
|
||||||
_description.setText(
|
|
||||||
st::defaultTextStyle,
|
|
||||||
description,
|
|
||||||
TextParseOptions {
|
|
||||||
TextParseMultiline,
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
Qt::LayoutDirectionAuto
|
|
||||||
});
|
|
||||||
_ready = ready && !error;
|
|
||||||
if (_error != error) {
|
|
||||||
_error = error;
|
|
||||||
if (animated == anim::type::instant) {
|
|
||||||
_errorAnimation.stop();
|
|
||||||
} else {
|
|
||||||
_errorAnimation.start(
|
|
||||||
[=] { update(); },
|
|
||||||
_error ? 0. : 1.,
|
|
||||||
_error ? 1. : 0.,
|
|
||||||
st::fadeWrapDuration);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
resizeToWidth(width());
|
|
||||||
update();
|
|
||||||
}
|
|
||||||
|
|
||||||
int PanelForm::Row::resizeGetHeight(int newWidth) {
|
|
||||||
const auto availableWidth = countAvailableWidth(newWidth);
|
|
||||||
_titleHeight = _title.countHeight(availableWidth);
|
|
||||||
_descriptionHeight = _description.countHeight(availableWidth);
|
|
||||||
const auto result = st::passportRowPadding.top()
|
|
||||||
+ _titleHeight
|
|
||||||
+ st::passportRowSkip
|
|
||||||
+ _descriptionHeight
|
|
||||||
+ st::passportRowPadding.bottom();
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
int PanelForm::Row::countAvailableWidth(int newWidth) const {
|
|
||||||
return newWidth
|
|
||||||
- st::passportRowPadding.left()
|
|
||||||
- st::passportRowPadding.right()
|
|
||||||
- (_ready
|
|
||||||
? st::passportRowReadyIcon
|
|
||||||
: st::passportRowEmptyIcon).width()
|
|
||||||
- st::passportRowIconSkip;
|
|
||||||
}
|
|
||||||
|
|
||||||
int PanelForm::Row::countAvailableWidth() const {
|
|
||||||
return countAvailableWidth(width());
|
|
||||||
}
|
|
||||||
|
|
||||||
void PanelForm::Row::paintEvent(QPaintEvent *e) {
|
|
||||||
Painter p(this);
|
|
||||||
|
|
||||||
paintRipple(p, 0, 0);
|
|
||||||
|
|
||||||
const auto left = st::passportRowPadding.left();
|
|
||||||
const auto availableWidth = countAvailableWidth();
|
|
||||||
auto top = st::passportRowPadding.top();
|
|
||||||
|
|
||||||
const auto error = _errorAnimation.value(_error ? 1. : 0.);
|
|
||||||
|
|
||||||
p.setPen(st::passportRowTitleFg);
|
|
||||||
_title.drawLeft(p, left, top, availableWidth, width());
|
|
||||||
top += _titleHeight + st::passportRowSkip;
|
|
||||||
|
|
||||||
p.setPen(anim::pen(
|
|
||||||
st::passportRowDescriptionFg,
|
|
||||||
st::boxTextFgError,
|
|
||||||
error));
|
|
||||||
_description.drawLeft(p, left, top, availableWidth, width());
|
|
||||||
top += _descriptionHeight + st::passportRowPadding.bottom();
|
|
||||||
|
|
||||||
const auto &icon = _ready
|
|
||||||
? st::passportRowReadyIcon
|
|
||||||
: st::passportRowEmptyIcon;
|
|
||||||
if (error > 0. && !_ready) {
|
|
||||||
icon.paint(
|
|
||||||
p,
|
|
||||||
width() - st::passportRowPadding.right() - icon.width(),
|
|
||||||
(height() - icon.height()) / 2,
|
|
||||||
width(),
|
|
||||||
anim::color(st::menuIconFgOver, st::boxTextFgError, error));
|
|
||||||
} else {
|
|
||||||
icon.paint(
|
|
||||||
p,
|
|
||||||
width() - st::passportRowPadding.right() - icon.width(),
|
|
||||||
(height() - icon.height()) / 2,
|
|
||||||
width());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
PanelForm::PanelForm(
|
PanelForm::PanelForm(
|
||||||
QWidget *parent,
|
QWidget *parent,
|
||||||
not_null<PanelController*> controller)
|
not_null<PanelController*> controller)
|
||||||
|
|
|
@ -19,6 +19,11 @@ class FlatLabel;
|
||||||
class UserpicButton;
|
class UserpicButton;
|
||||||
} // namespace Ui
|
} // namespace Ui
|
||||||
|
|
||||||
|
namespace Passport::Ui {
|
||||||
|
using namespace ::Ui;
|
||||||
|
class FormRow;
|
||||||
|
} // namespace Passport::Ui
|
||||||
|
|
||||||
namespace Passport {
|
namespace Passport {
|
||||||
|
|
||||||
class PanelController;
|
class PanelController;
|
||||||
|
@ -33,7 +38,7 @@ protected:
|
||||||
void resizeEvent(QResizeEvent *e) override;
|
void resizeEvent(QResizeEvent *e) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
class Row;
|
using Row = Ui::FormRow;
|
||||||
|
|
||||||
void setupControls();
|
void setupControls();
|
||||||
not_null<Ui::RpWidget*> setupContent();
|
not_null<Ui::RpWidget*> setupContent();
|
||||||
|
|
|
@ -5,9 +5,8 @@ the official desktop application for the Telegram messaging service.
|
||||||
For license and copyright information please follow this link:
|
For license and copyright information please follow this link:
|
||||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
*/
|
*/
|
||||||
#include "passport/passport_panel_details_row.h"
|
#include "passport/ui/passport_details_row.h"
|
||||||
|
|
||||||
#include "passport/passport_panel_controller.h"
|
|
||||||
#include "lang/lang_keys.h"
|
#include "lang/lang_keys.h"
|
||||||
#include "base/platform/base_platform_info.h"
|
#include "base/platform/base_platform_info.h"
|
||||||
#include "ui/widgets/input_fields.h"
|
#include "ui/widgets/input_fields.h"
|
||||||
|
@ -15,17 +14,18 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "ui/widgets/buttons.h"
|
#include "ui/widgets/buttons.h"
|
||||||
#include "ui/widgets/checkbox.h"
|
#include "ui/widgets/checkbox.h"
|
||||||
#include "ui/wrap/slide_wrap.h"
|
#include "ui/wrap/slide_wrap.h"
|
||||||
#include "ui/countryinput.h"
|
#include "ui/layers/box_content.h"
|
||||||
#include "main/main_session.h"
|
#include "ui/boxes/country_select_box.h"
|
||||||
#include "data/data_user.h"
|
|
||||||
#include "data/data_countries.h"
|
#include "data/data_countries.h"
|
||||||
#include "styles/style_layers.h"
|
#include "styles/style_layers.h"
|
||||||
#include "styles/style_passport.h"
|
#include "styles/style_passport.h"
|
||||||
|
|
||||||
namespace Passport {
|
#include <QtCore/QRegularExpression>
|
||||||
|
|
||||||
|
namespace Passport::Ui {
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
class PostcodeInput : public Ui::MaskedInputField {
|
class PostcodeInput : public MaskedInputField {
|
||||||
public:
|
public:
|
||||||
PostcodeInput(
|
PostcodeInput(
|
||||||
QWidget *parent,
|
QWidget *parent,
|
||||||
|
@ -103,7 +103,8 @@ class CountryRow : public PanelDetailsRow {
|
||||||
public:
|
public:
|
||||||
CountryRow(
|
CountryRow(
|
||||||
QWidget *parent,
|
QWidget *parent,
|
||||||
not_null<PanelController*> controller,
|
Fn<void(object_ptr<BoxContent>)> showBox,
|
||||||
|
const QString &defaultCountry,
|
||||||
const QString &label,
|
const QString &label,
|
||||||
int maxLabelWidth,
|
int maxLabelWidth,
|
||||||
const QString &value);
|
const QString &value);
|
||||||
|
@ -121,15 +122,16 @@ private:
|
||||||
void toggleError(bool shown);
|
void toggleError(bool shown);
|
||||||
void errorAnimationCallback();
|
void errorAnimationCallback();
|
||||||
|
|
||||||
not_null<PanelController*> _controller;
|
QString _defaultCountry;
|
||||||
object_ptr<Ui::LinkButton> _link;
|
Fn<void(object_ptr<BoxContent>)> _showBox;
|
||||||
|
object_ptr<LinkButton> _link;
|
||||||
rpl::variable<QString> _value;
|
rpl::variable<QString> _value;
|
||||||
bool _errorShown = false;
|
bool _errorShown = false;
|
||||||
Ui::Animations::Simple _errorAnimation;
|
Animations::Simple _errorAnimation;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class DateInput final : public Ui::MaskedInputField {
|
class DateInput final : public MaskedInputField {
|
||||||
public:
|
public:
|
||||||
using MaskedInputField::MaskedInputField;
|
using MaskedInputField::MaskedInputField;
|
||||||
|
|
||||||
|
@ -191,21 +193,21 @@ private:
|
||||||
int number(const object_ptr<DateInput> &field) const;
|
int number(const object_ptr<DateInput> &field) const;
|
||||||
|
|
||||||
object_ptr<DateInput> _day;
|
object_ptr<DateInput> _day;
|
||||||
object_ptr<Ui::PaddingWrap<Ui::FlatLabel>> _separator1;
|
object_ptr<PaddingWrap<FlatLabel>> _separator1;
|
||||||
object_ptr<DateInput> _month;
|
object_ptr<DateInput> _month;
|
||||||
object_ptr<Ui::PaddingWrap<Ui::FlatLabel>> _separator2;
|
object_ptr<PaddingWrap<FlatLabel>> _separator2;
|
||||||
object_ptr<DateInput> _year;
|
object_ptr<DateInput> _year;
|
||||||
rpl::variable<QString> _value;
|
rpl::variable<QString> _value;
|
||||||
|
|
||||||
style::cursor _cursor = style::cur_default;
|
style::cursor _cursor = style::cur_default;
|
||||||
Ui::Animations::Simple _a_borderShown;
|
Animations::Simple _a_borderShown;
|
||||||
int _borderAnimationStart = 0;
|
int _borderAnimationStart = 0;
|
||||||
Ui::Animations::Simple _a_borderOpacity;
|
Animations::Simple _a_borderOpacity;
|
||||||
bool _borderVisible = false;
|
bool _borderVisible = false;
|
||||||
|
|
||||||
Ui::Animations::Simple _a_error;
|
Animations::Simple _a_error;
|
||||||
bool _error = false;
|
bool _error = false;
|
||||||
Ui::Animations::Simple _a_focused;
|
Animations::Simple _a_focused;
|
||||||
bool _focused = false;
|
bool _focused = false;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
@ -238,18 +240,18 @@ private:
|
||||||
void hideGenderError();
|
void hideGenderError();
|
||||||
void errorAnimationCallback();
|
void errorAnimationCallback();
|
||||||
|
|
||||||
std::unique_ptr<Ui::AbstractCheckView> createRadioView(
|
std::unique_ptr<AbstractCheckView> createRadioView(
|
||||||
Ui::RadioView* &weak) const;
|
RadioView* &weak) const;
|
||||||
|
|
||||||
std::shared_ptr<Ui::RadioenumGroup<Gender>> _group;
|
std::shared_ptr<RadioenumGroup<Gender>> _group;
|
||||||
Ui::RadioView *_maleRadio = nullptr;
|
RadioView *_maleRadio = nullptr;
|
||||||
Ui::RadioView *_femaleRadio = nullptr;
|
RadioView *_femaleRadio = nullptr;
|
||||||
object_ptr<Ui::Radioenum<Gender>> _male;
|
object_ptr<Radioenum<Gender>> _male;
|
||||||
object_ptr<Ui::Radioenum<Gender>> _female;
|
object_ptr<Radioenum<Gender>> _female;
|
||||||
rpl::variable<QString> _value;
|
rpl::variable<QString> _value;
|
||||||
|
|
||||||
bool _errorShown = false;
|
bool _errorShown = false;
|
||||||
Ui::Animations::Simple _errorAnimation;
|
Animations::Simple _errorAnimation;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -308,12 +310,14 @@ QString CountryString(const QString &code) {
|
||||||
|
|
||||||
CountryRow::CountryRow(
|
CountryRow::CountryRow(
|
||||||
QWidget *parent,
|
QWidget *parent,
|
||||||
not_null<PanelController*> controller,
|
Fn<void(object_ptr<BoxContent>)> showBox,
|
||||||
|
const QString &defaultCountry,
|
||||||
const QString &label,
|
const QString &label,
|
||||||
int maxLabelWidth,
|
int maxLabelWidth,
|
||||||
const QString &value)
|
const QString &value)
|
||||||
: PanelDetailsRow(parent, label, maxLabelWidth)
|
: PanelDetailsRow(parent, label, maxLabelWidth)
|
||||||
, _controller(controller)
|
, _defaultCountry(defaultCountry)
|
||||||
|
, _showBox(std::move(showBox))
|
||||||
, _link(this, CountryString(value), st::boxLinkButton)
|
, _link(this, CountryString(value), st::boxLinkButton)
|
||||||
, _value(value) {
|
, _value(value) {
|
||||||
_value.changes(
|
_value.changes(
|
||||||
|
@ -380,20 +384,23 @@ void CountryRow::errorAnimationCallback() {
|
||||||
void CountryRow::chooseCountry() {
|
void CountryRow::chooseCountry() {
|
||||||
const auto top = _value.current();
|
const auto top = _value.current();
|
||||||
const auto name = Data::CountryNameByISO2(top);
|
const auto name = Data::CountryNameByISO2(top);
|
||||||
const auto isoByPhone = Data::CountryISO2ByPhone(
|
const auto country = !name.isEmpty()
|
||||||
_controller->bot()->session().user()->phone());
|
|
||||||
const auto box = _controller->show(Box<CountrySelectBox>(!name.isEmpty()
|
|
||||||
? top
|
? top
|
||||||
: !isoByPhone.isEmpty()
|
: !_defaultCountry.isEmpty()
|
||||||
? isoByPhone
|
? _defaultCountry
|
||||||
: Platform::SystemCountry(),
|
: Platform::SystemCountry();
|
||||||
CountrySelectBox::Type::Countries));
|
auto box = Box<CountrySelectBox>(
|
||||||
connect(box, &CountrySelectBox::countryChosen, this, [=](QString iso) {
|
country,
|
||||||
|
CountrySelectBox::Type::Countries);
|
||||||
|
const auto raw = box.data();
|
||||||
|
raw->countryChosen(
|
||||||
|
) | rpl::start_with_next([=](QString iso) {
|
||||||
_value = iso;
|
_value = iso;
|
||||||
_link->setText(CountryString(iso));
|
_link->setText(CountryString(iso));
|
||||||
hideCountryError();
|
hideCountryError();
|
||||||
box->closeBox();
|
raw->closeBox();
|
||||||
});
|
}, lifetime());
|
||||||
|
_showBox(std::move(box));
|
||||||
}
|
}
|
||||||
|
|
||||||
QDate ValidateDate(const QString &value) {
|
QDate ValidateDate(const QString &value) {
|
||||||
|
@ -528,7 +535,7 @@ DateRow::DateRow(
|
||||||
GetDay(value))
|
GetDay(value))
|
||||||
, _separator1(
|
, _separator1(
|
||||||
this,
|
this,
|
||||||
object_ptr<Ui::FlatLabel>(
|
object_ptr<FlatLabel>(
|
||||||
this,
|
this,
|
||||||
QString(" / "),
|
QString(" / "),
|
||||||
st::passportDetailsSeparator),
|
st::passportDetailsSeparator),
|
||||||
|
@ -540,7 +547,7 @@ DateRow::DateRow(
|
||||||
GetMonth(value))
|
GetMonth(value))
|
||||||
, _separator2(
|
, _separator2(
|
||||||
this,
|
this,
|
||||||
object_ptr<Ui::FlatLabel>(
|
object_ptr<FlatLabel>(
|
||||||
this,
|
this,
|
||||||
QString(" / "),
|
QString(" / "),
|
||||||
st::passportDetailsSeparator),
|
st::passportDetailsSeparator),
|
||||||
|
@ -552,7 +559,7 @@ DateRow::DateRow(
|
||||||
GetYear(value))
|
GetYear(value))
|
||||||
, _value(valueCurrent()) {
|
, _value(valueCurrent()) {
|
||||||
const auto focused = [=](const object_ptr<DateInput> &field) {
|
const auto focused = [=](const object_ptr<DateInput> &field) {
|
||||||
return [this, pointer = Ui::MakeWeak(field.data())]{
|
return [this, pointer = MakeWeak(field.data())]{
|
||||||
_borderAnimationStart = pointer->borderAnimationStart()
|
_borderAnimationStart = pointer->borderAnimationStart()
|
||||||
+ pointer->x()
|
+ pointer->x()
|
||||||
- _day->x();
|
- _day->x();
|
||||||
|
@ -565,15 +572,15 @@ DateRow::DateRow(
|
||||||
const auto changed = [=] {
|
const auto changed = [=] {
|
||||||
_value = valueCurrent();
|
_value = valueCurrent();
|
||||||
};
|
};
|
||||||
connect(_day, &Ui::MaskedInputField::focused, focused(_day));
|
connect(_day, &MaskedInputField::focused, focused(_day));
|
||||||
connect(_month, &Ui::MaskedInputField::focused, focused(_month));
|
connect(_month, &MaskedInputField::focused, focused(_month));
|
||||||
connect(_year, &Ui::MaskedInputField::focused, focused(_year));
|
connect(_year, &MaskedInputField::focused, focused(_year));
|
||||||
connect(_day, &Ui::MaskedInputField::blurred, blurred);
|
connect(_day, &MaskedInputField::blurred, blurred);
|
||||||
connect(_month, &Ui::MaskedInputField::blurred, blurred);
|
connect(_month, &MaskedInputField::blurred, blurred);
|
||||||
connect(_year, &Ui::MaskedInputField::blurred, blurred);
|
connect(_year, &MaskedInputField::blurred, blurred);
|
||||||
connect(_day, &Ui::MaskedInputField::changed, changed);
|
connect(_day, &MaskedInputField::changed, changed);
|
||||||
connect(_month, &Ui::MaskedInputField::changed, changed);
|
connect(_month, &MaskedInputField::changed, changed);
|
||||||
connect(_year, &Ui::MaskedInputField::changed, changed);
|
connect(_year, &MaskedInputField::changed, changed);
|
||||||
_day->setMaxValue(31);
|
_day->setMaxValue(31);
|
||||||
_day->putNext() | rpl::start_with_next([=](QChar ch) {
|
_day->putNext() | rpl::start_with_next([=](QChar ch) {
|
||||||
putNext(_month, ch);
|
putNext(_month, ch);
|
||||||
|
@ -845,8 +852,8 @@ GenderRow::GenderRow(
|
||||||
const QString &value)
|
const QString &value)
|
||||||
: PanelDetailsRow(parent, label, maxLabelWidth)
|
: PanelDetailsRow(parent, label, maxLabelWidth)
|
||||||
, _group(StringToGender(value).has_value()
|
, _group(StringToGender(value).has_value()
|
||||||
? std::make_shared<Ui::RadioenumGroup<Gender>>(*StringToGender(value))
|
? std::make_shared<RadioenumGroup<Gender>>(*StringToGender(value))
|
||||||
: std::make_shared<Ui::RadioenumGroup<Gender>>())
|
: std::make_shared<RadioenumGroup<Gender>>())
|
||||||
, _male(
|
, _male(
|
||||||
this,
|
this,
|
||||||
_group,
|
_group,
|
||||||
|
@ -868,9 +875,9 @@ GenderRow::GenderRow(
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<Ui::AbstractCheckView> GenderRow::createRadioView(
|
std::unique_ptr<AbstractCheckView> GenderRow::createRadioView(
|
||||||
Ui::RadioView* &weak) const {
|
RadioView* &weak) const {
|
||||||
auto result = std::make_unique<Ui::RadioView>(st::defaultRadio, false);
|
auto result = std::make_unique<RadioView>(st::defaultRadio, false);
|
||||||
weak = result.get();
|
weak = result.get();
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
@ -959,8 +966,9 @@ PanelDetailsRow::PanelDetailsRow(
|
||||||
|
|
||||||
object_ptr<PanelDetailsRow> PanelDetailsRow::Create(
|
object_ptr<PanelDetailsRow> PanelDetailsRow::Create(
|
||||||
QWidget *parent,
|
QWidget *parent,
|
||||||
|
Fn<void(object_ptr<BoxContent>)> showBox,
|
||||||
|
const QString &defaultCountry,
|
||||||
Type type,
|
Type type,
|
||||||
not_null<PanelController*> controller,
|
|
||||||
const QString &label,
|
const QString &label,
|
||||||
int maxLabelWidth,
|
int maxLabelWidth,
|
||||||
const QString &value,
|
const QString &value,
|
||||||
|
@ -969,7 +977,7 @@ object_ptr<PanelDetailsRow> PanelDetailsRow::Create(
|
||||||
auto result = [&]() -> object_ptr<PanelDetailsRow> {
|
auto result = [&]() -> object_ptr<PanelDetailsRow> {
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case Type::Text:
|
case Type::Text:
|
||||||
return object_ptr<AbstractTextRow<Ui::InputField>>(
|
return object_ptr<AbstractTextRow<InputField>>(
|
||||||
parent,
|
parent,
|
||||||
label,
|
label,
|
||||||
maxLabelWidth,
|
maxLabelWidth,
|
||||||
|
@ -985,7 +993,8 @@ object_ptr<PanelDetailsRow> PanelDetailsRow::Create(
|
||||||
case Type::Country:
|
case Type::Country:
|
||||||
return object_ptr<CountryRow>(
|
return object_ptr<CountryRow>(
|
||||||
parent,
|
parent,
|
||||||
controller,
|
showBox,
|
||||||
|
defaultCountry,
|
||||||
label,
|
label,
|
||||||
maxLabelWidth,
|
maxLabelWidth,
|
||||||
value);
|
value);
|
||||||
|
@ -1062,7 +1071,7 @@ void PanelDetailsRow::showError(std::optional<QString> error) {
|
||||||
if (!_error) {
|
if (!_error) {
|
||||||
_error.create(
|
_error.create(
|
||||||
this,
|
this,
|
||||||
object_ptr<Ui::FlatLabel>(
|
object_ptr<FlatLabel>(
|
||||||
this,
|
this,
|
||||||
*error,
|
*error,
|
||||||
st::passportVerifyErrorLabel));
|
st::passportVerifyErrorLabel));
|
||||||
|
@ -1122,4 +1131,4 @@ void PanelDetailsRow::paintEvent(QPaintEvent *e) {
|
||||||
p.drawTextLeft(padding.left(), padding.top(), width(), _label);
|
p.drawTextLeft(padding.left(), padding.top(), width(), _label);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Passport
|
} // namespace Passport::Ui
|
|
@ -11,18 +11,18 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "ui/effects/animations.h"
|
#include "ui/effects/animations.h"
|
||||||
#include "ui/wrap/padding_wrap.h"
|
#include "ui/wrap/padding_wrap.h"
|
||||||
#include "ui/widgets/labels.h"
|
#include "ui/widgets/labels.h"
|
||||||
#include "boxes/abstract_box.h"
|
|
||||||
|
|
||||||
namespace Ui {
|
namespace Ui {
|
||||||
|
class BoxContent;
|
||||||
class InputField;
|
class InputField;
|
||||||
class FlatLabel;
|
class FlatLabel;
|
||||||
template <typename Widget>
|
template <typename Widget>
|
||||||
class SlideWrap;
|
class SlideWrap;
|
||||||
} // namespace Ui
|
} // namespace Ui
|
||||||
|
|
||||||
namespace Passport {
|
namespace Passport::Ui {
|
||||||
|
|
||||||
class PanelController;
|
using namespace ::Ui;
|
||||||
|
|
||||||
enum class PanelDetailsType {
|
enum class PanelDetailsType {
|
||||||
Text,
|
Text,
|
||||||
|
@ -32,7 +32,7 @@ enum class PanelDetailsType {
|
||||||
Gender,
|
Gender,
|
||||||
};
|
};
|
||||||
|
|
||||||
class PanelDetailsRow : public Ui::RpWidget {
|
class PanelDetailsRow : public RpWidget {
|
||||||
public:
|
public:
|
||||||
using Type = PanelDetailsType;
|
using Type = PanelDetailsType;
|
||||||
|
|
||||||
|
@ -43,8 +43,9 @@ public:
|
||||||
|
|
||||||
static object_ptr<PanelDetailsRow> Create(
|
static object_ptr<PanelDetailsRow> Create(
|
||||||
QWidget *parent,
|
QWidget *parent,
|
||||||
|
Fn<void(object_ptr<BoxContent>)> showBox,
|
||||||
|
const QString &defaultCountry,
|
||||||
Type type,
|
Type type,
|
||||||
not_null<PanelController*> controller,
|
|
||||||
const QString &label,
|
const QString &label,
|
||||||
int maxLabelWidth,
|
int maxLabelWidth,
|
||||||
const QString &value,
|
const QString &value,
|
||||||
|
@ -74,11 +75,11 @@ private:
|
||||||
|
|
||||||
QString _label;
|
QString _label;
|
||||||
int _maxLabelWidth = 0;
|
int _maxLabelWidth = 0;
|
||||||
object_ptr<Ui::SlideWrap<Ui::FlatLabel>> _error = { nullptr };
|
object_ptr<SlideWrap<FlatLabel>> _error = { nullptr };
|
||||||
bool _errorShown = false;
|
bool _errorShown = false;
|
||||||
bool _errorHideSubscription = false;
|
bool _errorHideSubscription = false;
|
||||||
Ui::Animations::Simple _errorAnimation;
|
Animations::Simple _errorAnimation;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Passport
|
} // namespace Passport::Ui
|
125
Telegram/SourceFiles/passport/ui/passport_form_row.cpp
Normal file
125
Telegram/SourceFiles/passport/ui/passport_form_row.cpp
Normal file
|
@ -0,0 +1,125 @@
|
||||||
|
/*
|
||||||
|
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/ui/passport_form_row.h"
|
||||||
|
|
||||||
|
#include "ui/text/text_options.h"
|
||||||
|
#include "styles/style_passport.h"
|
||||||
|
#include "styles/style_layers.h"
|
||||||
|
|
||||||
|
namespace Passport::Ui {
|
||||||
|
|
||||||
|
FormRow::FormRow(QWidget *parent)
|
||||||
|
: RippleButton(parent, st::passportRowRipple)
|
||||||
|
, _title(st::boxWideWidth / 2)
|
||||||
|
, _description(st::boxWideWidth / 2) {
|
||||||
|
}
|
||||||
|
|
||||||
|
void FormRow::updateContent(
|
||||||
|
const QString &title,
|
||||||
|
const QString &description,
|
||||||
|
bool ready,
|
||||||
|
bool error,
|
||||||
|
anim::type animated) {
|
||||||
|
_title.setText(
|
||||||
|
st::semiboldTextStyle,
|
||||||
|
title,
|
||||||
|
NameTextOptions());
|
||||||
|
_description.setText(
|
||||||
|
st::defaultTextStyle,
|
||||||
|
description,
|
||||||
|
TextParseOptions {
|
||||||
|
TextParseMultiline,
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
Qt::LayoutDirectionAuto
|
||||||
|
});
|
||||||
|
_ready = ready && !error;
|
||||||
|
if (_error != error) {
|
||||||
|
_error = error;
|
||||||
|
if (animated == anim::type::instant) {
|
||||||
|
_errorAnimation.stop();
|
||||||
|
} else {
|
||||||
|
_errorAnimation.start(
|
||||||
|
[=] { update(); },
|
||||||
|
_error ? 0. : 1.,
|
||||||
|
_error ? 1. : 0.,
|
||||||
|
st::fadeWrapDuration);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
resizeToWidth(width());
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
int FormRow::resizeGetHeight(int newWidth) {
|
||||||
|
const auto availableWidth = countAvailableWidth(newWidth);
|
||||||
|
_titleHeight = _title.countHeight(availableWidth);
|
||||||
|
_descriptionHeight = _description.countHeight(availableWidth);
|
||||||
|
const auto result = st::passportRowPadding.top()
|
||||||
|
+ _titleHeight
|
||||||
|
+ st::passportRowSkip
|
||||||
|
+ _descriptionHeight
|
||||||
|
+ st::passportRowPadding.bottom();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int FormRow::countAvailableWidth(int newWidth) const {
|
||||||
|
return newWidth
|
||||||
|
- st::passportRowPadding.left()
|
||||||
|
- st::passportRowPadding.right()
|
||||||
|
- (_ready
|
||||||
|
? st::passportRowReadyIcon
|
||||||
|
: st::passportRowEmptyIcon).width()
|
||||||
|
- st::passportRowIconSkip;
|
||||||
|
}
|
||||||
|
|
||||||
|
int FormRow::countAvailableWidth() const {
|
||||||
|
return countAvailableWidth(width());
|
||||||
|
}
|
||||||
|
|
||||||
|
void FormRow::paintEvent(QPaintEvent *e) {
|
||||||
|
Painter p(this);
|
||||||
|
|
||||||
|
paintRipple(p, 0, 0);
|
||||||
|
|
||||||
|
const auto left = st::passportRowPadding.left();
|
||||||
|
const auto availableWidth = countAvailableWidth();
|
||||||
|
auto top = st::passportRowPadding.top();
|
||||||
|
|
||||||
|
const auto error = _errorAnimation.value(_error ? 1. : 0.);
|
||||||
|
|
||||||
|
p.setPen(st::passportRowTitleFg);
|
||||||
|
_title.drawLeft(p, left, top, availableWidth, width());
|
||||||
|
top += _titleHeight + st::passportRowSkip;
|
||||||
|
|
||||||
|
p.setPen(anim::pen(
|
||||||
|
st::passportRowDescriptionFg,
|
||||||
|
st::boxTextFgError,
|
||||||
|
error));
|
||||||
|
_description.drawLeft(p, left, top, availableWidth, width());
|
||||||
|
top += _descriptionHeight + st::passportRowPadding.bottom();
|
||||||
|
|
||||||
|
const auto &icon = _ready
|
||||||
|
? st::passportRowReadyIcon
|
||||||
|
: st::passportRowEmptyIcon;
|
||||||
|
if (error > 0. && !_ready) {
|
||||||
|
icon.paint(
|
||||||
|
p,
|
||||||
|
width() - st::passportRowPadding.right() - icon.width(),
|
||||||
|
(height() - icon.height()) / 2,
|
||||||
|
width(),
|
||||||
|
anim::color(st::menuIconFgOver, st::boxTextFgError, error));
|
||||||
|
} else {
|
||||||
|
icon.paint(
|
||||||
|
p,
|
||||||
|
width() - st::passportRowPadding.right() - icon.width(),
|
||||||
|
(height() - icon.height()) / 2,
|
||||||
|
width());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Passport::Ui
|
48
Telegram/SourceFiles/passport/ui/passport_form_row.h
Normal file
48
Telegram/SourceFiles/passport/ui/passport_form_row.h
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
/*
|
||||||
|
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
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "ui/text/text.h"
|
||||||
|
#include "ui/effects/animations.h"
|
||||||
|
#include "ui/widgets/buttons.h"
|
||||||
|
|
||||||
|
namespace Passport::Ui {
|
||||||
|
|
||||||
|
using namespace ::Ui;
|
||||||
|
|
||||||
|
class FormRow : public RippleButton {
|
||||||
|
public:
|
||||||
|
explicit FormRow(QWidget *parent);
|
||||||
|
|
||||||
|
void updateContent(
|
||||||
|
const QString &title,
|
||||||
|
const QString &description,
|
||||||
|
bool ready,
|
||||||
|
bool error,
|
||||||
|
anim::type animated);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
int resizeGetHeight(int newWidth) override;
|
||||||
|
|
||||||
|
void paintEvent(QPaintEvent *e) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
int countAvailableWidth() const;
|
||||||
|
int countAvailableWidth(int newWidth) const;
|
||||||
|
|
||||||
|
Text::String _title;
|
||||||
|
Text::String _description;
|
||||||
|
int _titleHeight = 0;
|
||||||
|
int _descriptionHeight = 0;
|
||||||
|
bool _ready = false;
|
||||||
|
bool _error = false;
|
||||||
|
Animations::Simple _errorAnimation;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Passport::Ui
|
|
@ -95,11 +95,19 @@ not_null<Ui::PanelDelegate*> CheckoutProcess::panelDelegate() {
|
||||||
|
|
||||||
void CheckoutProcess::handleFormUpdate(const FormUpdate &update) {
|
void CheckoutProcess::handleFormUpdate(const FormUpdate &update) {
|
||||||
v::match(update.data, [&](const FormReady &) {
|
v::match(update.data, [&](const FormReady &) {
|
||||||
_panel->showForm(_form->invoice());
|
showForm();
|
||||||
}, [&](const FormError &error) {
|
}, [&](const FormError &error) { // #TODO payments refactor errors
|
||||||
handleFormError(error);
|
handleFormError(error);
|
||||||
|
}, [&](const ValidateError &error) {
|
||||||
|
handleValidateError(error);
|
||||||
}, [&](const SendError &error) {
|
}, [&](const SendError &error) {
|
||||||
handleSendError(error);
|
handleSendError(error);
|
||||||
|
}, [&](const ValidateFinished &) {
|
||||||
|
showForm();
|
||||||
|
if (_submitState == SubmitState::Validation) {
|
||||||
|
_submitState = SubmitState::Validated;
|
||||||
|
panelSubmit();
|
||||||
|
}
|
||||||
}, [&](const VerificationNeeded &info) {
|
}, [&](const VerificationNeeded &info) {
|
||||||
if (_webviewWindow) {
|
if (_webviewWindow) {
|
||||||
_webviewWindow->navigate(info.url);
|
_webviewWindow->navigate(info.url);
|
||||||
|
@ -130,8 +138,44 @@ void CheckoutProcess::handleFormError(const FormError &error) {
|
||||||
} else if (type == u"INVOICE_ALREADY_PAID"_q) {
|
} else if (type == u"INVOICE_ALREADY_PAID"_q) {
|
||||||
|
|
||||||
}
|
}
|
||||||
App::wnd()->activate();
|
if (_panel) {
|
||||||
Ui::Toast::Show("payments.getPaymentForm: " + type);
|
_panel->showToast("payments.getPaymentForm: " + type);
|
||||||
|
} else {
|
||||||
|
App::wnd()->activate();
|
||||||
|
Ui::Toast::Show("payments.getPaymentForm: " + type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CheckoutProcess::handleValidateError(const ValidateError &error) {
|
||||||
|
// #TODO payments errors
|
||||||
|
const auto &type = error.type;
|
||||||
|
if (type == u"REQ_INFO_NAME_INVALID"_q) {
|
||||||
|
|
||||||
|
} else if (type == u"REQ_INFO_EMAIL_INVALID"_q) {
|
||||||
|
|
||||||
|
} else if (type == u"REQ_INFO_PHONE_INVALID"_q) {
|
||||||
|
|
||||||
|
} else if (type == u"ADDRESS_STREET_LINE1_INVALID"_q) {
|
||||||
|
|
||||||
|
} else if (type == u"ADDRESS_CITY_INVALID"_q) {
|
||||||
|
|
||||||
|
} else if (type == u"ADDRESS_STATE_INVALID"_q) {
|
||||||
|
|
||||||
|
} else if (type == u"ADDRESS_COUNTRY_INVALID"_q) {
|
||||||
|
|
||||||
|
} else if (type == u"ADDRESS_POSTCODE_INVALID"_q) {
|
||||||
|
|
||||||
|
} else if (type == u"SHIPPING_BOT_TIMEOUT"_q) {
|
||||||
|
|
||||||
|
} else if (type == u"SHIPPING_NOT_AVAILABLE"_q) {
|
||||||
|
|
||||||
|
}
|
||||||
|
if (_panel) {
|
||||||
|
_panel->showToast("payments.validateRequestedInfo: " + type);
|
||||||
|
} else {
|
||||||
|
App::wnd()->activate();
|
||||||
|
Ui::Toast::Show("payments.validateRequestedInfo: " + type);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CheckoutProcess::handleSendError(const SendError &error) {
|
void CheckoutProcess::handleSendError(const SendError &error) {
|
||||||
|
@ -150,8 +194,12 @@ void CheckoutProcess::handleSendError(const SendError &error) {
|
||||||
} else if (type == u"BOT_PRECHECKOUT_FAILED"_q) {
|
} else if (type == u"BOT_PRECHECKOUT_FAILED"_q) {
|
||||||
|
|
||||||
}
|
}
|
||||||
App::wnd()->activate();
|
if (_panel) {
|
||||||
Ui::Toast::Show("payments.sendPaymentForm: " + type);
|
_panel->showToast("payments.sendPaymentForm: " + type);
|
||||||
|
} else {
|
||||||
|
App::wnd()->activate();
|
||||||
|
Ui::Toast::Show("payments.sendPaymentForm: " + type);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CheckoutProcess::panelRequestClose() {
|
void CheckoutProcess::panelRequestClose() {
|
||||||
|
@ -176,6 +224,26 @@ void CheckoutProcess::panelCloseSure() {
|
||||||
}
|
}
|
||||||
|
|
||||||
void CheckoutProcess::panelSubmit() {
|
void CheckoutProcess::panelSubmit() {
|
||||||
|
if (_submitState == SubmitState::Validation
|
||||||
|
|| _submitState == SubmitState::Finishing) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto &invoice = _form->invoice();
|
||||||
|
const auto &options = _form->shippingOptions();
|
||||||
|
if (!options.list.empty() && options.selectedId.isEmpty()) {
|
||||||
|
chooseShippingOption();
|
||||||
|
return;
|
||||||
|
} else if (_submitState != SubmitState::Validated
|
||||||
|
&& options.list.empty()
|
||||||
|
&& (invoice.isShippingAddressRequested
|
||||||
|
|| invoice.isNameRequested
|
||||||
|
|| invoice.isEmailRequested
|
||||||
|
|| invoice.isPhoneRequested)) {
|
||||||
|
_submitState = SubmitState::Validation;
|
||||||
|
_form->validateInformation(_form->savedInformation());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_submitState = SubmitState::Finishing;
|
||||||
_webviewWindow = std::make_unique<Ui::WebviewWindow>(
|
_webviewWindow = std::make_unique<Ui::WebviewWindow>(
|
||||||
_form->details().url,
|
_form->details().url,
|
||||||
panelDelegate());
|
panelDelegate());
|
||||||
|
@ -237,4 +305,62 @@ bool CheckoutProcess::panelWebviewNavigationAttempt(const QString &uri) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CheckoutProcess::panelEditShippingInformation() {
|
||||||
|
showEditInformation(Ui::EditField::ShippingInformation);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CheckoutProcess::panelEditName() {
|
||||||
|
showEditInformation(Ui::EditField::Name);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CheckoutProcess::panelEditEmail() {
|
||||||
|
showEditInformation(Ui::EditField::Email);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CheckoutProcess::panelEditPhone() {
|
||||||
|
showEditInformation(Ui::EditField::Phone);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CheckoutProcess::showForm() {
|
||||||
|
_panel->showForm(
|
||||||
|
_form->invoice(),
|
||||||
|
_form->savedInformation(),
|
||||||
|
_form->shippingOptions());
|
||||||
|
}
|
||||||
|
|
||||||
|
void CheckoutProcess::showEditInformation(Ui::EditField field) {
|
||||||
|
if (_submitState != SubmitState::None) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_panel->showEditInformation(
|
||||||
|
_form->invoice(),
|
||||||
|
_form->savedInformation(),
|
||||||
|
field);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CheckoutProcess::chooseShippingOption() {
|
||||||
|
_panel->chooseShippingOption(_form->shippingOptions());
|
||||||
|
}
|
||||||
|
|
||||||
|
void CheckoutProcess::panelChooseShippingOption() {
|
||||||
|
if (_submitState != SubmitState::None) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
chooseShippingOption();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CheckoutProcess::panelChangeShippingOption(const QString &id) {
|
||||||
|
_form->setShippingOption(id);
|
||||||
|
showForm();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CheckoutProcess::panelValidateInformation(
|
||||||
|
Ui::RequestedInformation data) {
|
||||||
|
_form->validateInformation(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CheckoutProcess::panelShowBox(object_ptr<Ui::BoxContent> box) {
|
||||||
|
_panel->showBox(std::move(box));
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Payments
|
} // namespace Payments
|
||||||
|
|
|
@ -19,6 +19,7 @@ class Session;
|
||||||
namespace Payments::Ui {
|
namespace Payments::Ui {
|
||||||
class Panel;
|
class Panel;
|
||||||
class WebviewWindow;
|
class WebviewWindow;
|
||||||
|
enum class EditField;
|
||||||
} // namespace Payments::Ui
|
} // namespace Payments::Ui
|
||||||
|
|
||||||
namespace Payments {
|
namespace Payments {
|
||||||
|
@ -27,6 +28,7 @@ class Form;
|
||||||
struct FormUpdate;
|
struct FormUpdate;
|
||||||
struct FormError;
|
struct FormError;
|
||||||
struct SendError;
|
struct SendError;
|
||||||
|
struct ValidateError;
|
||||||
|
|
||||||
class CheckoutProcess final
|
class CheckoutProcess final
|
||||||
: public base::has_weak_ptr
|
: public base::has_weak_ptr
|
||||||
|
@ -45,22 +47,44 @@ public:
|
||||||
void requestActivate();
|
void requestActivate();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
enum class SubmitState {
|
||||||
|
None,
|
||||||
|
Validation,
|
||||||
|
Validated,
|
||||||
|
Finishing,
|
||||||
|
};
|
||||||
[[nodiscard]] not_null<PanelDelegate*> panelDelegate();
|
[[nodiscard]] not_null<PanelDelegate*> panelDelegate();
|
||||||
|
|
||||||
void handleFormUpdate(const FormUpdate &update);
|
void handleFormUpdate(const FormUpdate &update);
|
||||||
void handleFormError(const FormError &error);
|
void handleFormError(const FormError &error);
|
||||||
|
void handleValidateError(const ValidateError &error);
|
||||||
void handleSendError(const SendError &error);
|
void handleSendError(const SendError &error);
|
||||||
|
|
||||||
|
void showForm();
|
||||||
|
void showEditInformation(Ui::EditField field);
|
||||||
|
void chooseShippingOption();
|
||||||
|
|
||||||
void panelRequestClose() override;
|
void panelRequestClose() override;
|
||||||
void panelCloseSure() override;
|
void panelCloseSure() override;
|
||||||
void panelSubmit() override;
|
void panelSubmit() override;
|
||||||
void panelWebviewMessage(const QJsonDocument &message) override;
|
void panelWebviewMessage(const QJsonDocument &message) override;
|
||||||
bool panelWebviewNavigationAttempt(const QString &uri) override;
|
bool panelWebviewNavigationAttempt(const QString &uri) override;
|
||||||
|
|
||||||
|
void panelEditShippingInformation() override;
|
||||||
|
void panelEditName() override;
|
||||||
|
void panelEditEmail() override;
|
||||||
|
void panelEditPhone() override;
|
||||||
|
void panelChooseShippingOption() override;
|
||||||
|
void panelChangeShippingOption(const QString &id) override;
|
||||||
|
|
||||||
|
void panelValidateInformation(Ui::RequestedInformation data) override;
|
||||||
|
void panelShowBox(object_ptr<Ui::BoxContent> box) override;
|
||||||
|
|
||||||
const not_null<Main::Session*> _session;
|
const not_null<Main::Session*> _session;
|
||||||
const std::unique_ptr<Form> _form;
|
const std::unique_ptr<Form> _form;
|
||||||
const std::unique_ptr<Ui::Panel> _panel;
|
const std::unique_ptr<Ui::Panel> _panel;
|
||||||
std::unique_ptr<Ui::WebviewWindow> _webviewWindow;
|
std::unique_ptr<Ui::WebviewWindow> _webviewWindow;
|
||||||
|
SubmitState _submitState = SubmitState::None;
|
||||||
|
|
||||||
rpl::lifetime _lifetime;
|
rpl::lifetime _lifetime;
|
||||||
|
|
||||||
|
|
|
@ -27,16 +27,53 @@ namespace {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] std::vector<Ui::LabeledPrice> ParsePrices(
|
||||||
|
const MTPVector<MTPLabeledPrice> &data) {
|
||||||
|
return ranges::views::all(
|
||||||
|
data.v
|
||||||
|
) | ranges::views::transform([](const MTPLabeledPrice &price) {
|
||||||
|
return price.match([&](const MTPDlabeledPrice &data) {
|
||||||
|
return Ui::LabeledPrice{
|
||||||
|
.label = qs(data.vlabel()),
|
||||||
|
.price = *reinterpret_cast<const int64*>(&data.vamount().v),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}) | ranges::to_vector;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] MTPPaymentRequestedInfo Serialize(
|
||||||
|
const Ui::RequestedInformation &information) {
|
||||||
|
using Flag = MTPDpaymentRequestedInfo::Flag;
|
||||||
|
return MTP_paymentRequestedInfo(
|
||||||
|
MTP_flags((information.name.isEmpty() ? Flag(0) : Flag::f_name)
|
||||||
|
| (information.email.isEmpty() ? Flag(0) : Flag::f_email)
|
||||||
|
| (information.phone.isEmpty() ? Flag(0) : Flag::f_phone)
|
||||||
|
| (information.shippingAddress
|
||||||
|
? Flag::f_shipping_address
|
||||||
|
: Flag(0))),
|
||||||
|
MTP_string(information.name),
|
||||||
|
MTP_string(information.phone),
|
||||||
|
MTP_string(information.email),
|
||||||
|
MTP_postAddress(
|
||||||
|
MTP_string(information.shippingAddress.address1),
|
||||||
|
MTP_string(information.shippingAddress.address2),
|
||||||
|
MTP_string(information.shippingAddress.city),
|
||||||
|
MTP_string(information.shippingAddress.state),
|
||||||
|
MTP_string(information.shippingAddress.countryIso2),
|
||||||
|
MTP_string(information.shippingAddress.postCode)));
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
Form::Form(not_null<Main::Session*> session, FullMsgId itemId)
|
Form::Form(not_null<Main::Session*> session, FullMsgId itemId)
|
||||||
: _session(session)
|
: _session(session)
|
||||||
|
, _api(&_session->mtp())
|
||||||
, _msgId(itemId.msg) {
|
, _msgId(itemId.msg) {
|
||||||
requestForm();
|
requestForm();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Form::requestForm() {
|
void Form::requestForm() {
|
||||||
_session->api().request(MTPpayments_GetPaymentForm(
|
_api.request(MTPpayments_GetPaymentForm(
|
||||||
MTP_int(_msgId)
|
MTP_int(_msgId)
|
||||||
)).done([=](const MTPpayments_PaymentForm &result) {
|
)).done([=](const MTPpayments_PaymentForm &result) {
|
||||||
result.match([&](const auto &data) {
|
result.match([&](const auto &data) {
|
||||||
|
@ -69,18 +106,8 @@ void Form::processForm(const MTPDpayments_paymentForm &data) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Form::processInvoice(const MTPDinvoice &data) {
|
void Form::processInvoice(const MTPDinvoice &data) {
|
||||||
auto &&prices = ranges::views::all(
|
|
||||||
data.vprices().v
|
|
||||||
) | ranges::views::transform([](const MTPLabeledPrice &price) {
|
|
||||||
return price.match([&](const MTPDlabeledPrice &data) {
|
|
||||||
return Ui::LabeledPrice{
|
|
||||||
.label = qs(data.vlabel()),
|
|
||||||
.price = data.vamount().v,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
});
|
|
||||||
_invoice = Ui::Invoice{
|
_invoice = Ui::Invoice{
|
||||||
.prices = prices | ranges::to_vector,
|
.prices = ParsePrices(data.vprices()),
|
||||||
.currency = qs(data.vcurrency()),
|
.currency = qs(data.vcurrency()),
|
||||||
|
|
||||||
.isNameRequested = data.is_name_requested(),
|
.isNameRequested = data.is_name_requested(),
|
||||||
|
@ -115,7 +142,7 @@ void Form::processDetails(const MTPDpayments_paymentForm &data) {
|
||||||
|
|
||||||
void Form::processSavedInformation(const MTPDpaymentRequestedInfo &data) {
|
void Form::processSavedInformation(const MTPDpaymentRequestedInfo &data) {
|
||||||
const auto address = data.vshipping_address();
|
const auto address = data.vshipping_address();
|
||||||
_savedInformation = Ui::SavedInformation{
|
_savedInformation = Ui::RequestedInformation{
|
||||||
.name = qs(data.vname().value_or_empty()),
|
.name = qs(data.vname().value_or_empty()),
|
||||||
.phone = qs(data.vphone().value_or_empty()),
|
.phone = qs(data.vphone().value_or_empty()),
|
||||||
.email = qs(data.vemail().value_or_empty()),
|
.email = qs(data.vemail().value_or_empty()),
|
||||||
|
@ -132,11 +159,17 @@ void Form::processSavedCredentials(
|
||||||
}
|
}
|
||||||
|
|
||||||
void Form::send(const QByteArray &serializedCredentials) {
|
void Form::send(const QByteArray &serializedCredentials) {
|
||||||
_session->api().request(MTPpayments_SendPaymentForm(
|
using Flag = MTPpayments_SendPaymentForm::Flag;
|
||||||
MTP_flags(0),
|
_api.request(MTPpayments_SendPaymentForm(
|
||||||
|
MTP_flags((_requestedInformationId.isEmpty()
|
||||||
|
? Flag(0)
|
||||||
|
: Flag::f_requested_info_id)
|
||||||
|
| (_shippingOptions.selectedId.isEmpty()
|
||||||
|
? Flag(0)
|
||||||
|
: Flag::f_shipping_option_id)),
|
||||||
MTP_int(_msgId),
|
MTP_int(_msgId),
|
||||||
MTPstring(), // requested_info_id
|
MTP_string(_requestedInformationId),
|
||||||
MTPstring(), // shipping_option_id,
|
MTP_string(_shippingOptions.selectedId),
|
||||||
MTP_inputPaymentCredentials(
|
MTP_inputPaymentCredentials(
|
||||||
MTP_flags(0),
|
MTP_flags(0),
|
||||||
MTP_dataJSON(MTP_bytes(serializedCredentials)))
|
MTP_dataJSON(MTP_bytes(serializedCredentials)))
|
||||||
|
@ -151,4 +184,59 @@ void Form::send(const QByteArray &serializedCredentials) {
|
||||||
}).send();
|
}).send();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Form::validateInformation(const Ui::RequestedInformation &information) {
|
||||||
|
if (_validateRequestId) {
|
||||||
|
if (_validatedInformation == information) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_api.request(base::take(_validateRequestId)).cancel();
|
||||||
|
}
|
||||||
|
_validatedInformation = information;
|
||||||
|
_validateRequestId = _api.request(MTPpayments_ValidateRequestedInfo(
|
||||||
|
MTP_flags(0), // #TODO payments save information
|
||||||
|
MTP_int(_msgId),
|
||||||
|
Serialize(information)
|
||||||
|
)).done([=](const MTPpayments_ValidatedRequestedInfo &result) {
|
||||||
|
_validateRequestId = 0;
|
||||||
|
const auto oldSelectedId = _shippingOptions.selectedId;
|
||||||
|
result.match([&](const MTPDpayments_validatedRequestedInfo &data) {
|
||||||
|
_requestedInformationId = data.vid().value_or_empty();
|
||||||
|
processShippingOptions(
|
||||||
|
data.vshipping_options().value_or_empty());
|
||||||
|
});
|
||||||
|
_shippingOptions.selectedId = ranges::contains(
|
||||||
|
_shippingOptions.list,
|
||||||
|
oldSelectedId,
|
||||||
|
&Ui::ShippingOption::id
|
||||||
|
) ? oldSelectedId : QString();
|
||||||
|
if (_shippingOptions.selectedId.isEmpty()
|
||||||
|
&& _shippingOptions.list.size() == 1) {
|
||||||
|
_shippingOptions.selectedId = _shippingOptions.list.front().id;
|
||||||
|
}
|
||||||
|
_savedInformation = _validatedInformation;
|
||||||
|
_updates.fire({ ValidateFinished{} });
|
||||||
|
}).fail([=](const MTP::Error &error) {
|
||||||
|
_validateRequestId = 0;
|
||||||
|
_updates.fire({ ValidateError{ error.type() } });
|
||||||
|
}).send();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Form::setShippingOption(const QString &id) {
|
||||||
|
_shippingOptions.selectedId = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Form::processShippingOptions(const QVector<MTPShippingOption> &data) {
|
||||||
|
_shippingOptions = Ui::ShippingOptions{ ranges::views::all(
|
||||||
|
data
|
||||||
|
) | ranges::views::transform([](const MTPShippingOption &option) {
|
||||||
|
return option.match([](const MTPDshippingOption &data) {
|
||||||
|
return Ui::ShippingOption{
|
||||||
|
.id = qs(data.vid()),
|
||||||
|
.title = qs(data.vtitle()),
|
||||||
|
.prices = ParsePrices(data.vprices()),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}) | ranges::to_vector };
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Payments
|
} // namespace Payments
|
||||||
|
|
|
@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "payments/ui/payments_panel_data.h"
|
#include "payments/ui/payments_panel_data.h"
|
||||||
|
#include "mtproto/sender.h"
|
||||||
|
|
||||||
namespace Main {
|
namespace Main {
|
||||||
class Session;
|
class Session;
|
||||||
|
@ -34,10 +35,16 @@ struct FormDetails {
|
||||||
|
|
||||||
struct FormReady {};
|
struct FormReady {};
|
||||||
|
|
||||||
|
struct ValidateFinished {};
|
||||||
|
|
||||||
struct FormError {
|
struct FormError {
|
||||||
QString type;
|
QString type;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct ValidateError {
|
||||||
|
QString type;
|
||||||
|
};
|
||||||
|
|
||||||
struct SendError {
|
struct SendError {
|
||||||
QString type;
|
QString type;
|
||||||
};
|
};
|
||||||
|
@ -54,8 +61,10 @@ struct FormUpdate {
|
||||||
std::variant<
|
std::variant<
|
||||||
FormReady,
|
FormReady,
|
||||||
FormError,
|
FormError,
|
||||||
|
ValidateError,
|
||||||
SendError,
|
SendError,
|
||||||
VerificationNeeded,
|
VerificationNeeded,
|
||||||
|
ValidateFinished,
|
||||||
PaymentFinished> data;
|
PaymentFinished> data;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -69,17 +78,22 @@ public:
|
||||||
[[nodiscard]] const FormDetails &details() const {
|
[[nodiscard]] const FormDetails &details() const {
|
||||||
return _details;
|
return _details;
|
||||||
}
|
}
|
||||||
[[nodiscard]] const Ui::SavedInformation &savedInformation() const {
|
[[nodiscard]] const Ui::RequestedInformation &savedInformation() const {
|
||||||
return _savedInformation;
|
return _savedInformation;
|
||||||
}
|
}
|
||||||
[[nodiscard]] const Ui::SavedCredentials &savedCredentials() const {
|
[[nodiscard]] const Ui::SavedCredentials &savedCredentials() const {
|
||||||
return _savedCredentials;
|
return _savedCredentials;
|
||||||
}
|
}
|
||||||
|
[[nodiscard]] const Ui::ShippingOptions &shippingOptions() const {
|
||||||
|
return _shippingOptions;
|
||||||
|
}
|
||||||
|
|
||||||
[[nodiscard]] rpl::producer<FormUpdate> updates() const {
|
[[nodiscard]] rpl::producer<FormUpdate> updates() const {
|
||||||
return _updates.events();
|
return _updates.events();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void validateInformation(const Ui::RequestedInformation &information);
|
||||||
|
void setShippingOption(const QString &id);
|
||||||
void send(const QByteArray &serializedCredentials);
|
void send(const QByteArray &serializedCredentials);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -90,15 +104,23 @@ private:
|
||||||
void processSavedInformation(const MTPDpaymentRequestedInfo &data);
|
void processSavedInformation(const MTPDpaymentRequestedInfo &data);
|
||||||
void processSavedCredentials(
|
void processSavedCredentials(
|
||||||
const MTPDpaymentSavedCredentialsCard &data);
|
const MTPDpaymentSavedCredentialsCard &data);
|
||||||
|
void processShippingOptions(const QVector<MTPShippingOption> &data);
|
||||||
|
|
||||||
const not_null<Main::Session*> _session;
|
const not_null<Main::Session*> _session;
|
||||||
|
MTP::Sender _api;
|
||||||
MsgId _msgId = 0;
|
MsgId _msgId = 0;
|
||||||
|
|
||||||
Ui::Invoice _invoice;
|
Ui::Invoice _invoice;
|
||||||
FormDetails _details;
|
FormDetails _details;
|
||||||
Ui::SavedInformation _savedInformation;
|
Ui::RequestedInformation _savedInformation;
|
||||||
Ui::SavedCredentials _savedCredentials;
|
Ui::SavedCredentials _savedCredentials;
|
||||||
|
|
||||||
|
Ui::RequestedInformation _validatedInformation;
|
||||||
|
mtpRequestId _validateRequestId = 0;
|
||||||
|
|
||||||
|
Ui::ShippingOptions _shippingOptions;
|
||||||
|
QString _requestedInformationId;
|
||||||
|
|
||||||
rpl::event_stream<FormUpdate> _updates;
|
rpl::event_stream<FormUpdate> _updates;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -8,3 +8,5 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
using "ui/basic.style";
|
using "ui/basic.style";
|
||||||
|
|
||||||
using "passport/passport.style";
|
using "passport/passport.style";
|
||||||
|
|
||||||
|
paymentsFormPricePadding: margins(22px, 7px, 22px, 6px);
|
||||||
|
|
262
Telegram/SourceFiles/payments/ui/payments_edit_information.cpp
Normal file
262
Telegram/SourceFiles/payments/ui/payments_edit_information.cpp
Normal file
|
@ -0,0 +1,262 @@
|
||||||
|
/*
|
||||||
|
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_information.h"
|
||||||
|
|
||||||
|
#include "payments/ui/payments_panel_delegate.h"
|
||||||
|
#include "passport/ui/passport_details_row.h"
|
||||||
|
#include "ui/widgets/scroll_area.h"
|
||||||
|
#include "ui/widgets/buttons.h"
|
||||||
|
#include "ui/widgets/labels.h"
|
||||||
|
#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"
|
||||||
|
|
||||||
|
namespace Payments::Ui {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
constexpr auto kMaxStreetSize = 64;
|
||||||
|
constexpr auto kMaxPostcodeSize = 10;
|
||||||
|
constexpr auto kMaxNameSize = 64;
|
||||||
|
constexpr auto kMaxEmailSize = 128;
|
||||||
|
constexpr auto kMaxPhoneSize = 16;
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
EditInformation::EditInformation(
|
||||||
|
QWidget *parent,
|
||||||
|
const Invoice &invoice,
|
||||||
|
const RequestedInformation ¤t,
|
||||||
|
EditField field,
|
||||||
|
not_null<PanelDelegate*> delegate)
|
||||||
|
: _delegate(delegate)
|
||||||
|
, _invoice(invoice)
|
||||||
|
, _information(current)
|
||||||
|
, _scroll(this, st::passportPanelScroll)
|
||||||
|
, _topShadow(this)
|
||||||
|
, _bottomShadow(this)
|
||||||
|
, _done(
|
||||||
|
this,
|
||||||
|
tr::lng_about_done(),
|
||||||
|
st::passportPanelSaveValue) {
|
||||||
|
setupControls();
|
||||||
|
}
|
||||||
|
|
||||||
|
void EditInformation::setupControls() {
|
||||||
|
const auto inner = setupContent();
|
||||||
|
|
||||||
|
_done->addClickHandler([=] {
|
||||||
|
_delegate->panelValidateInformation(collect());
|
||||||
|
});
|
||||||
|
|
||||||
|
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*> EditInformation::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));
|
||||||
|
};
|
||||||
|
using Type = Passport::Ui::PanelDetailsType;
|
||||||
|
auto maxLabelWidth = 0;
|
||||||
|
if (_invoice.isShippingAddressRequested) {
|
||||||
|
accumulate_max(
|
||||||
|
maxLabelWidth,
|
||||||
|
Row::LabelWidth(tr::lng_passport_street(tr::now)));
|
||||||
|
accumulate_max(
|
||||||
|
maxLabelWidth,
|
||||||
|
Row::LabelWidth(tr::lng_passport_city(tr::now)));
|
||||||
|
accumulate_max(
|
||||||
|
maxLabelWidth,
|
||||||
|
Row::LabelWidth(tr::lng_passport_state(tr::now)));
|
||||||
|
accumulate_max(
|
||||||
|
maxLabelWidth,
|
||||||
|
Row::LabelWidth(tr::lng_passport_country(tr::now)));
|
||||||
|
accumulate_max(
|
||||||
|
maxLabelWidth,
|
||||||
|
Row::LabelWidth(tr::lng_passport_postcode(tr::now)));
|
||||||
|
}
|
||||||
|
if (_invoice.isNameRequested) {
|
||||||
|
accumulate_max(
|
||||||
|
maxLabelWidth,
|
||||||
|
Row::LabelWidth(tr::lng_payments_info_name(tr::now)));
|
||||||
|
}
|
||||||
|
if (_invoice.isEmailRequested) {
|
||||||
|
accumulate_max(
|
||||||
|
maxLabelWidth,
|
||||||
|
Row::LabelWidth(tr::lng_payments_info_email(tr::now)));
|
||||||
|
}
|
||||||
|
if (_invoice.isPhoneRequested) {
|
||||||
|
accumulate_max(
|
||||||
|
maxLabelWidth,
|
||||||
|
Row::LabelWidth(tr::lng_payments_info_phone(tr::now)));
|
||||||
|
}
|
||||||
|
if (_invoice.isShippingAddressRequested) {
|
||||||
|
_street1 = inner->add(
|
||||||
|
Row::Create(
|
||||||
|
inner,
|
||||||
|
showBox,
|
||||||
|
QString(),
|
||||||
|
Type::Text,
|
||||||
|
tr::lng_passport_street(tr::now),
|
||||||
|
maxLabelWidth,
|
||||||
|
_information.shippingAddress.address1,
|
||||||
|
QString(),
|
||||||
|
kMaxStreetSize));
|
||||||
|
_street2 = inner->add(
|
||||||
|
Row::Create(
|
||||||
|
inner,
|
||||||
|
showBox,
|
||||||
|
QString(),
|
||||||
|
Type::Text,
|
||||||
|
tr::lng_passport_street(tr::now),
|
||||||
|
maxLabelWidth,
|
||||||
|
_information.shippingAddress.address2,
|
||||||
|
QString(),
|
||||||
|
kMaxStreetSize));
|
||||||
|
_city = inner->add(
|
||||||
|
Row::Create(
|
||||||
|
inner,
|
||||||
|
showBox,
|
||||||
|
QString(),
|
||||||
|
Type::Text,
|
||||||
|
tr::lng_passport_city(tr::now),
|
||||||
|
maxLabelWidth,
|
||||||
|
_information.shippingAddress.city,
|
||||||
|
QString(),
|
||||||
|
kMaxStreetSize));
|
||||||
|
_state = inner->add(
|
||||||
|
Row::Create(
|
||||||
|
inner,
|
||||||
|
showBox,
|
||||||
|
QString(),
|
||||||
|
Type::Text,
|
||||||
|
tr::lng_passport_state(tr::now),
|
||||||
|
maxLabelWidth,
|
||||||
|
_information.shippingAddress.state,
|
||||||
|
QString(),
|
||||||
|
kMaxStreetSize));
|
||||||
|
_country = inner->add(
|
||||||
|
Row::Create(
|
||||||
|
inner,
|
||||||
|
showBox,
|
||||||
|
QString(),
|
||||||
|
Type::Country,
|
||||||
|
tr::lng_passport_country(tr::now),
|
||||||
|
maxLabelWidth,
|
||||||
|
_information.shippingAddress.countryIso2,
|
||||||
|
QString()));
|
||||||
|
_postcode = inner->add(
|
||||||
|
Row::Create(
|
||||||
|
inner,
|
||||||
|
showBox,
|
||||||
|
QString(),
|
||||||
|
Type::Postcode,
|
||||||
|
tr::lng_passport_postcode(tr::now),
|
||||||
|
maxLabelWidth,
|
||||||
|
_information.shippingAddress.postCode,
|
||||||
|
QString(),
|
||||||
|
kMaxPostcodeSize));
|
||||||
|
//StreetValidate, // #TODO payments
|
||||||
|
//CityValidate,
|
||||||
|
//CountryValidate,
|
||||||
|
//CountryFormat,
|
||||||
|
//PostcodeValidate,
|
||||||
|
}
|
||||||
|
if (_invoice.isNameRequested) {
|
||||||
|
_name = inner->add(
|
||||||
|
Row::Create(
|
||||||
|
inner,
|
||||||
|
showBox,
|
||||||
|
QString(),
|
||||||
|
Type::Text,
|
||||||
|
tr::lng_payments_info_name(tr::now),
|
||||||
|
maxLabelWidth,
|
||||||
|
_information.name,
|
||||||
|
QString(),
|
||||||
|
kMaxNameSize));
|
||||||
|
}
|
||||||
|
if (_invoice.isEmailRequested) {
|
||||||
|
_email = inner->add(
|
||||||
|
Row::Create(
|
||||||
|
inner,
|
||||||
|
showBox,
|
||||||
|
QString(),
|
||||||
|
Type::Text,
|
||||||
|
tr::lng_payments_info_email(tr::now),
|
||||||
|
maxLabelWidth,
|
||||||
|
_information.email,
|
||||||
|
QString(),
|
||||||
|
kMaxEmailSize));
|
||||||
|
}
|
||||||
|
if (_invoice.isPhoneRequested) {
|
||||||
|
_phone = inner->add(
|
||||||
|
Row::Create(
|
||||||
|
inner,
|
||||||
|
showBox,
|
||||||
|
QString(),
|
||||||
|
Type::Text,
|
||||||
|
tr::lng_payments_info_phone(tr::now),
|
||||||
|
maxLabelWidth,
|
||||||
|
_information.phone,
|
||||||
|
QString(),
|
||||||
|
kMaxPhoneSize));
|
||||||
|
}
|
||||||
|
return inner;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EditInformation::resizeEvent(QResizeEvent *e) {
|
||||||
|
updateControlsGeometry();
|
||||||
|
}
|
||||||
|
|
||||||
|
void EditInformation::updateControlsGeometry() {
|
||||||
|
const auto submitTop = height() - _done->height();
|
||||||
|
_scroll->setGeometry(0, 0, width(), submitTop);
|
||||||
|
_topShadow->resizeToWidth(width());
|
||||||
|
_topShadow->moveToLeft(0, 0);
|
||||||
|
_bottomShadow->resizeToWidth(width());
|
||||||
|
_bottomShadow->moveToLeft(0, submitTop - st::lineWidth);
|
||||||
|
_done->setFullWidth(width());
|
||||||
|
_done->moveToLeft(0, submitTop);
|
||||||
|
|
||||||
|
_scroll->updateBars();
|
||||||
|
}
|
||||||
|
|
||||||
|
RequestedInformation EditInformation::collect() const {
|
||||||
|
return {
|
||||||
|
.name = _name ? _name->valueCurrent() : QString(),
|
||||||
|
.phone = _phone ? _phone->valueCurrent() : QString(),
|
||||||
|
.email = _email ? _email->valueCurrent() : QString(),
|
||||||
|
.shippingAddress = {
|
||||||
|
.address1 = _street1 ? _street1->valueCurrent() : QString(),
|
||||||
|
.address2 = _street2 ? _street2->valueCurrent() : QString(),
|
||||||
|
.city = _city ? _city->valueCurrent() : QString(),
|
||||||
|
.state = _state ? _state->valueCurrent() : QString(),
|
||||||
|
.countryIso2 = _country ? _country->valueCurrent() : QString(),
|
||||||
|
.postCode = _postcode ? _postcode->valueCurrent() : QString(),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Payments::Ui
|
71
Telegram/SourceFiles/payments/ui/payments_edit_information.h
Normal file
71
Telegram/SourceFiles/payments/ui/payments_edit_information.h
Normal file
|
@ -0,0 +1,71 @@
|
||||||
|
/*
|
||||||
|
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
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "ui/rp_widget.h"
|
||||||
|
#include "payments/ui/payments_panel_data.h"
|
||||||
|
#include "base/object_ptr.h"
|
||||||
|
|
||||||
|
namespace Ui {
|
||||||
|
class ScrollArea;
|
||||||
|
class FadeShadow;
|
||||||
|
class RoundButton;
|
||||||
|
} // namespace Ui
|
||||||
|
|
||||||
|
namespace Passport::Ui {
|
||||||
|
class PanelDetailsRow;
|
||||||
|
} // namespace Passport::Ui
|
||||||
|
|
||||||
|
namespace Payments::Ui {
|
||||||
|
|
||||||
|
using namespace ::Ui;
|
||||||
|
|
||||||
|
class PanelDelegate;
|
||||||
|
|
||||||
|
class EditInformation final : public RpWidget {
|
||||||
|
public:
|
||||||
|
EditInformation(
|
||||||
|
QWidget *parent,
|
||||||
|
const Invoice &invoice,
|
||||||
|
const RequestedInformation ¤t,
|
||||||
|
EditField field,
|
||||||
|
not_null<PanelDelegate*> delegate);
|
||||||
|
|
||||||
|
private:
|
||||||
|
using Row = Passport::Ui::PanelDetailsRow;
|
||||||
|
|
||||||
|
void resizeEvent(QResizeEvent *e) override;
|
||||||
|
|
||||||
|
void setupControls();
|
||||||
|
[[nodiscard]] not_null<Ui::RpWidget*> setupContent();
|
||||||
|
void updateControlsGeometry();
|
||||||
|
|
||||||
|
[[nodiscard]] RequestedInformation collect() const;
|
||||||
|
|
||||||
|
const not_null<PanelDelegate*> _delegate;
|
||||||
|
Invoice _invoice;
|
||||||
|
RequestedInformation _information;
|
||||||
|
|
||||||
|
object_ptr<ScrollArea> _scroll;
|
||||||
|
object_ptr<FadeShadow> _topShadow;
|
||||||
|
object_ptr<FadeShadow> _bottomShadow;
|
||||||
|
object_ptr<RoundButton> _done;
|
||||||
|
|
||||||
|
Row *_street1 = nullptr;
|
||||||
|
Row *_street2 = nullptr;
|
||||||
|
Row *_city = nullptr;
|
||||||
|
Row *_state = nullptr;
|
||||||
|
Row *_country = nullptr;
|
||||||
|
Row *_postcode = nullptr;
|
||||||
|
Row *_name = nullptr;
|
||||||
|
Row *_email = nullptr;
|
||||||
|
Row *_phone = nullptr;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Payments::Ui
|
|
@ -8,10 +8,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "payments/ui/payments_form_summary.h"
|
#include "payments/ui/payments_form_summary.h"
|
||||||
|
|
||||||
#include "payments/ui/payments_panel_delegate.h"
|
#include "payments/ui/payments_panel_delegate.h"
|
||||||
|
#include "passport/ui/passport_form_row.h"
|
||||||
#include "ui/widgets/scroll_area.h"
|
#include "ui/widgets/scroll_area.h"
|
||||||
#include "ui/widgets/buttons.h"
|
#include "ui/widgets/buttons.h"
|
||||||
|
#include "ui/widgets/labels.h"
|
||||||
#include "ui/wrap/vertical_layout.h"
|
#include "ui/wrap/vertical_layout.h"
|
||||||
#include "ui/wrap/fade_wrap.h"
|
#include "ui/wrap/fade_wrap.h"
|
||||||
|
#include "ui/text/format_values.h"
|
||||||
#include "lang/lang_keys.h"
|
#include "lang/lang_keys.h"
|
||||||
#include "styles/style_payments.h"
|
#include "styles/style_payments.h"
|
||||||
#include "styles/style_passport.h"
|
#include "styles/style_passport.h"
|
||||||
|
@ -19,24 +22,56 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
namespace Payments::Ui {
|
namespace Payments::Ui {
|
||||||
|
|
||||||
using namespace ::Ui;
|
using namespace ::Ui;
|
||||||
|
using namespace Passport::Ui;
|
||||||
|
|
||||||
class PanelDelegate;
|
class PanelDelegate;
|
||||||
|
|
||||||
FormSummary::FormSummary(
|
FormSummary::FormSummary(
|
||||||
QWidget *parent,
|
QWidget *parent,
|
||||||
const Invoice &invoice,
|
const Invoice &invoice,
|
||||||
|
const RequestedInformation ¤t,
|
||||||
|
const ShippingOptions &options,
|
||||||
not_null<PanelDelegate*> delegate)
|
not_null<PanelDelegate*> delegate)
|
||||||
: _delegate(delegate)
|
: _delegate(delegate)
|
||||||
|
, _invoice(invoice)
|
||||||
|
, _options(options)
|
||||||
|
, _information(current)
|
||||||
, _scroll(this, st::passportPanelScroll)
|
, _scroll(this, st::passportPanelScroll)
|
||||||
, _topShadow(this)
|
, _topShadow(this)
|
||||||
, _bottomShadow(this)
|
, _bottomShadow(this)
|
||||||
, _submit(
|
, _submit(
|
||||||
this,
|
this,
|
||||||
tr::lng_payments_pay_amount(lt_amount, rpl::single(QString("much"))),
|
tr::lng_payments_pay_amount(
|
||||||
|
lt_amount,
|
||||||
|
rpl::single(computeTotalAmount())),
|
||||||
st::passportPanelAuthorize) {
|
st::passportPanelAuthorize) {
|
||||||
setupControls();
|
setupControls();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString FormSummary::computeAmount(int64 amount) const {
|
||||||
|
return FillAmountAndCurrency(amount, _invoice.currency);
|
||||||
|
}
|
||||||
|
|
||||||
|
QString FormSummary::computeTotalAmount() const {
|
||||||
|
const auto total = ranges::accumulate(
|
||||||
|
_invoice.prices,
|
||||||
|
int64(0),
|
||||||
|
std::plus<>(),
|
||||||
|
&LabeledPrice::price);
|
||||||
|
const auto selected = ranges::find(
|
||||||
|
_options.list,
|
||||||
|
_options.selectedId,
|
||||||
|
&ShippingOption::id);
|
||||||
|
const auto shipping = (selected != end(_options.list))
|
||||||
|
? ranges::accumulate(
|
||||||
|
selected->prices,
|
||||||
|
int64(0),
|
||||||
|
std::plus<>(),
|
||||||
|
&LabeledPrice::price)
|
||||||
|
: int64(0);
|
||||||
|
return computeAmount(total + shipping);
|
||||||
|
}
|
||||||
|
|
||||||
void FormSummary::setupControls() {
|
void FormSummary::setupControls() {
|
||||||
const auto inner = setupContent();
|
const auto inner = setupContent();
|
||||||
|
|
||||||
|
@ -64,6 +99,116 @@ not_null<Ui::RpWidget*> FormSummary::setupContent() {
|
||||||
inner->resizeToWidth(width);
|
inner->resizeToWidth(width);
|
||||||
}, inner->lifetime());
|
}, inner->lifetime());
|
||||||
|
|
||||||
|
for (const auto &price : _invoice.prices) {
|
||||||
|
inner->add(
|
||||||
|
object_ptr<Ui::FlatLabel>(
|
||||||
|
inner,
|
||||||
|
price.label + ": " + computeAmount(price.price),
|
||||||
|
st::passportFormPolicy),
|
||||||
|
st::paymentsFormPricePadding);
|
||||||
|
}
|
||||||
|
const auto selected = ranges::find(
|
||||||
|
_options.list,
|
||||||
|
_options.selectedId,
|
||||||
|
&ShippingOption::id);
|
||||||
|
if (selected != end(_options.list)) {
|
||||||
|
for (const auto &price : selected->prices) {
|
||||||
|
inner->add(
|
||||||
|
object_ptr<Ui::FlatLabel>(
|
||||||
|
inner,
|
||||||
|
price.label + ": " + computeAmount(price.price),
|
||||||
|
st::passportFormPolicy),
|
||||||
|
st::paymentsFormPricePadding);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
inner->add(
|
||||||
|
object_ptr<Ui::FlatLabel>(
|
||||||
|
inner,
|
||||||
|
"Total: " + computeTotalAmount(),
|
||||||
|
st::passportFormHeader),
|
||||||
|
st::passportFormHeaderPadding);
|
||||||
|
|
||||||
|
inner->add(
|
||||||
|
object_ptr<Ui::BoxContentDivider>(
|
||||||
|
inner,
|
||||||
|
st::passportFormDividerHeight),
|
||||||
|
{ 0, 0, 0, st::passportFormHeaderPadding.top() });
|
||||||
|
|
||||||
|
if (_invoice.isShippingAddressRequested) {
|
||||||
|
const auto info = inner->add(object_ptr<FormRow>(inner));
|
||||||
|
info->addClickHandler([=] {
|
||||||
|
_delegate->panelEditShippingInformation();
|
||||||
|
});
|
||||||
|
auto list = QStringList();
|
||||||
|
const auto push = [&](const QString &value) {
|
||||||
|
if (!value.isEmpty()) {
|
||||||
|
list.push_back(value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
push(_information.shippingAddress.address1);
|
||||||
|
push(_information.shippingAddress.address2);
|
||||||
|
push(_information.shippingAddress.city);
|
||||||
|
push(_information.shippingAddress.state);
|
||||||
|
push(_information.shippingAddress.countryIso2);
|
||||||
|
push(_information.shippingAddress.postCode);
|
||||||
|
info->updateContent(
|
||||||
|
tr::lng_payments_shipping_address(tr::now),
|
||||||
|
(list.isEmpty() ? "enter pls" : list.join(", ")),
|
||||||
|
!list.isEmpty(),
|
||||||
|
false,
|
||||||
|
anim::type::instant);
|
||||||
|
}
|
||||||
|
if (!_options.list.empty()) {
|
||||||
|
const auto options = inner->add(object_ptr<FormRow>(inner));
|
||||||
|
options->addClickHandler([=] {
|
||||||
|
_delegate->panelChooseShippingOption();
|
||||||
|
});
|
||||||
|
options->updateContent(
|
||||||
|
tr::lng_payments_shipping_method(tr::now),
|
||||||
|
(selected != end(_options.list)
|
||||||
|
? selected->title
|
||||||
|
: "enter pls"),
|
||||||
|
(selected != end(_options.list)),
|
||||||
|
false,
|
||||||
|
anim::type::instant);
|
||||||
|
}
|
||||||
|
if (_invoice.isNameRequested) {
|
||||||
|
const auto name = inner->add(object_ptr<FormRow>(inner));
|
||||||
|
name->addClickHandler([=] { _delegate->panelEditName(); });
|
||||||
|
name->updateContent(
|
||||||
|
tr::lng_payments_info_name(tr::now),
|
||||||
|
(_information.name.isEmpty()
|
||||||
|
? "enter pls"
|
||||||
|
: _information.name),
|
||||||
|
!_information.name.isEmpty(),
|
||||||
|
false,
|
||||||
|
anim::type::instant);
|
||||||
|
}
|
||||||
|
if (_invoice.isEmailRequested) {
|
||||||
|
const auto email = inner->add(object_ptr<FormRow>(inner));
|
||||||
|
email->addClickHandler([=] { _delegate->panelEditEmail(); });
|
||||||
|
email->updateContent(
|
||||||
|
tr::lng_payments_info_email(tr::now),
|
||||||
|
(_information.email.isEmpty()
|
||||||
|
? "enter pls"
|
||||||
|
: _information.email),
|
||||||
|
!_information.email.isEmpty(),
|
||||||
|
false,
|
||||||
|
anim::type::instant);
|
||||||
|
}
|
||||||
|
if (_invoice.isPhoneRequested) {
|
||||||
|
const auto phone = inner->add(object_ptr<FormRow>(inner));
|
||||||
|
phone->addClickHandler([=] { _delegate->panelEditPhone(); });
|
||||||
|
phone->updateContent(
|
||||||
|
tr::lng_payments_info_email(tr::now),
|
||||||
|
(_information.phone.isEmpty()
|
||||||
|
? "enter pls"
|
||||||
|
: _information.phone),
|
||||||
|
!_information.phone.isEmpty(),
|
||||||
|
false,
|
||||||
|
anim::type::instant);
|
||||||
|
}
|
||||||
|
|
||||||
return inner;
|
return inner;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -28,6 +28,8 @@ public:
|
||||||
FormSummary(
|
FormSummary(
|
||||||
QWidget *parent,
|
QWidget *parent,
|
||||||
const Invoice &invoice,
|
const Invoice &invoice,
|
||||||
|
const RequestedInformation ¤t,
|
||||||
|
const ShippingOptions &options,
|
||||||
not_null<PanelDelegate*> delegate);
|
not_null<PanelDelegate*> delegate);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -37,7 +39,13 @@ private:
|
||||||
[[nodiscard]] not_null<Ui::RpWidget*> setupContent();
|
[[nodiscard]] not_null<Ui::RpWidget*> setupContent();
|
||||||
void updateControlsGeometry();
|
void updateControlsGeometry();
|
||||||
|
|
||||||
|
[[nodiscard]] QString computeAmount(int64 amount) const;
|
||||||
|
[[nodiscard]] QString computeTotalAmount() const;
|
||||||
|
|
||||||
const not_null<PanelDelegate*> _delegate;
|
const not_null<PanelDelegate*> _delegate;
|
||||||
|
Invoice _invoice;
|
||||||
|
ShippingOptions _options;
|
||||||
|
RequestedInformation _information;
|
||||||
object_ptr<ScrollArea> _scroll;
|
object_ptr<ScrollArea> _scroll;
|
||||||
object_ptr<FadeShadow> _topShadow;
|
object_ptr<FadeShadow> _topShadow;
|
||||||
object_ptr<FadeShadow> _bottomShadow;
|
object_ptr<FadeShadow> _bottomShadow;
|
||||||
|
|
|
@ -8,8 +8,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "payments/ui/payments_panel.h"
|
#include "payments/ui/payments_panel.h"
|
||||||
|
|
||||||
#include "payments/ui/payments_form_summary.h"
|
#include "payments/ui/payments_form_summary.h"
|
||||||
|
#include "payments/ui/payments_edit_information.h"
|
||||||
#include "payments/ui/payments_panel_delegate.h"
|
#include "payments/ui/payments_panel_delegate.h"
|
||||||
#include "ui/widgets/separate_panel.h"
|
#include "ui/widgets/separate_panel.h"
|
||||||
|
#include "ui/boxes/single_choice_box.h"
|
||||||
#include "lang/lang_keys.h"
|
#include "lang/lang_keys.h"
|
||||||
#include "styles/style_payments.h"
|
#include "styles/style_payments.h"
|
||||||
#include "styles/style_passport.h"
|
#include "styles/style_passport.h"
|
||||||
|
@ -39,9 +41,63 @@ void Panel::requestActivate() {
|
||||||
_widget->showAndActivate();
|
_widget->showAndActivate();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Panel::showForm(const Invoice &invoice) {
|
void Panel::showForm(
|
||||||
|
const Invoice &invoice,
|
||||||
|
const RequestedInformation ¤t,
|
||||||
|
const ShippingOptions &options) {
|
||||||
_widget->showInner(
|
_widget->showInner(
|
||||||
base::make_unique_q<FormSummary>(_widget.get(), invoice, _delegate));
|
base::make_unique_q<FormSummary>(
|
||||||
|
_widget.get(),
|
||||||
|
invoice,
|
||||||
|
current,
|
||||||
|
options,
|
||||||
|
_delegate));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Panel::showEditInformation(
|
||||||
|
const Invoice &invoice,
|
||||||
|
const RequestedInformation ¤t,
|
||||||
|
EditField field) {
|
||||||
|
_widget->showInner(base::make_unique_q<EditInformation>(
|
||||||
|
_widget.get(),
|
||||||
|
invoice,
|
||||||
|
current,
|
||||||
|
field,
|
||||||
|
_delegate));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Panel::chooseShippingOption(const ShippingOptions &options) {
|
||||||
|
showBox(Box([=](not_null<Ui::GenericBox*> box) {
|
||||||
|
auto list = options.list | ranges::views::transform(
|
||||||
|
&ShippingOption::title
|
||||||
|
) | ranges::to_vector;
|
||||||
|
const auto i = ranges::find(
|
||||||
|
options.list,
|
||||||
|
options.selectedId,
|
||||||
|
&ShippingOption::id);
|
||||||
|
const auto save = [=](int option) {
|
||||||
|
_delegate->panelChangeShippingOption(options.list[option].id);
|
||||||
|
};
|
||||||
|
SingleChoiceBox(box, {
|
||||||
|
.title = tr::lng_payments_shipping_method(),
|
||||||
|
.options = list,
|
||||||
|
.initialSelection = (i != end(options.list)
|
||||||
|
? (i - begin(options.list))
|
||||||
|
: -1),
|
||||||
|
.callback = save,
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Panel::showBox(object_ptr<Ui::BoxContent> box) {
|
||||||
|
_widget->showBox(
|
||||||
|
std::move(box),
|
||||||
|
Ui::LayerOption::KeepOther,
|
||||||
|
anim::type::normal);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Panel::showToast(const QString &text) {
|
||||||
|
_widget->showToast(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Payments::Ui
|
} // namespace Payments::Ui
|
||||||
|
|
|
@ -7,8 +7,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "base/object_ptr.h"
|
||||||
|
|
||||||
namespace Ui {
|
namespace Ui {
|
||||||
class SeparatePanel;
|
class SeparatePanel;
|
||||||
|
class BoxContent;
|
||||||
} // namespace Ui
|
} // namespace Ui
|
||||||
|
|
||||||
namespace Payments::Ui {
|
namespace Payments::Ui {
|
||||||
|
@ -17,6 +20,9 @@ using namespace ::Ui;
|
||||||
|
|
||||||
class PanelDelegate;
|
class PanelDelegate;
|
||||||
struct Invoice;
|
struct Invoice;
|
||||||
|
struct RequestedInformation;
|
||||||
|
struct ShippingOptions;
|
||||||
|
enum class EditField;
|
||||||
|
|
||||||
class Panel final {
|
class Panel final {
|
||||||
public:
|
public:
|
||||||
|
@ -25,7 +31,18 @@ public:
|
||||||
|
|
||||||
void requestActivate();
|
void requestActivate();
|
||||||
|
|
||||||
void showForm(const Invoice &invoice);
|
void showForm(
|
||||||
|
const Invoice &invoice,
|
||||||
|
const RequestedInformation ¤t,
|
||||||
|
const ShippingOptions &options);
|
||||||
|
void showEditInformation(
|
||||||
|
const Invoice &invoice,
|
||||||
|
const RequestedInformation ¤t,
|
||||||
|
EditField field);
|
||||||
|
void chooseShippingOption(const ShippingOptions &options);
|
||||||
|
|
||||||
|
void showBox(object_ptr<Ui::BoxContent> box);
|
||||||
|
void showToast(const QString &text);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const not_null<PanelDelegate*> _delegate;
|
const not_null<PanelDelegate*> _delegate;
|
||||||
|
|
|
@ -11,7 +11,7 @@ namespace Payments::Ui {
|
||||||
|
|
||||||
struct LabeledPrice {
|
struct LabeledPrice {
|
||||||
QString label;
|
QString label;
|
||||||
uint64 price = 0;
|
int64 price = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Invoice {
|
struct Invoice {
|
||||||
|
@ -36,6 +36,17 @@ struct Invoice {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct ShippingOption {
|
||||||
|
QString id;
|
||||||
|
QString title;
|
||||||
|
std::vector<LabeledPrice> prices;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ShippingOptions {
|
||||||
|
std::vector<ShippingOption> list;
|
||||||
|
QString selectedId;
|
||||||
|
};
|
||||||
|
|
||||||
struct Address {
|
struct Address {
|
||||||
QString address1;
|
QString address1;
|
||||||
QString address2;
|
QString address2;
|
||||||
|
@ -52,9 +63,21 @@ struct Address {
|
||||||
[[nodiscard]] explicit operator bool() const {
|
[[nodiscard]] explicit operator bool() const {
|
||||||
return valid();
|
return valid();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline bool operator==(const Address &other) const {
|
||||||
|
return (address1 == other.address1)
|
||||||
|
&& (address2 == other.address2)
|
||||||
|
&& (city == other.city)
|
||||||
|
&& (state == other.state)
|
||||||
|
&& (countryIso2 == other.countryIso2)
|
||||||
|
&& (postCode == other.postCode);
|
||||||
|
}
|
||||||
|
inline bool operator!=(const Address &other) const {
|
||||||
|
return !(*this == other);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct SavedInformation {
|
struct RequestedInformation {
|
||||||
QString name;
|
QString name;
|
||||||
QString phone;
|
QString phone;
|
||||||
QString email;
|
QString email;
|
||||||
|
@ -69,6 +92,16 @@ struct SavedInformation {
|
||||||
[[nodiscard]] explicit operator bool() const {
|
[[nodiscard]] explicit operator bool() const {
|
||||||
return !empty();
|
return !empty();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline bool operator==(const RequestedInformation &other) const {
|
||||||
|
return (name == other.name)
|
||||||
|
&& (phone == other.phone)
|
||||||
|
&& (email == other.email)
|
||||||
|
&& (shippingAddress == other.shippingAddress);
|
||||||
|
}
|
||||||
|
inline bool operator!=(const RequestedInformation &other) const {
|
||||||
|
return !(*this == other);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct SavedCredentials {
|
struct SavedCredentials {
|
||||||
|
@ -83,4 +116,11 @@ struct SavedCredentials {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class EditField {
|
||||||
|
ShippingInformation,
|
||||||
|
Name,
|
||||||
|
Email,
|
||||||
|
Phone,
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace Payments::Ui
|
} // namespace Payments::Ui
|
||||||
|
|
|
@ -7,11 +7,21 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "base/object_ptr.h"
|
||||||
|
|
||||||
class QJsonDocument;
|
class QJsonDocument;
|
||||||
class QString;
|
class QString;
|
||||||
|
|
||||||
|
namespace Ui {
|
||||||
|
class BoxContent;
|
||||||
|
} // namespace Ui
|
||||||
|
|
||||||
namespace Payments::Ui {
|
namespace Payments::Ui {
|
||||||
|
|
||||||
|
using namespace ::Ui;
|
||||||
|
|
||||||
|
struct RequestedInformation;
|
||||||
|
|
||||||
class PanelDelegate {
|
class PanelDelegate {
|
||||||
public:
|
public:
|
||||||
virtual void panelRequestClose() = 0;
|
virtual void panelRequestClose() = 0;
|
||||||
|
@ -19,6 +29,16 @@ public:
|
||||||
virtual void panelSubmit() = 0;
|
virtual void panelSubmit() = 0;
|
||||||
virtual void panelWebviewMessage(const QJsonDocument &message) = 0;
|
virtual void panelWebviewMessage(const QJsonDocument &message) = 0;
|
||||||
virtual bool panelWebviewNavigationAttempt(const QString &uri) = 0;
|
virtual bool panelWebviewNavigationAttempt(const QString &uri) = 0;
|
||||||
|
|
||||||
|
virtual void panelEditShippingInformation() = 0;
|
||||||
|
virtual void panelEditName() = 0;
|
||||||
|
virtual void panelEditEmail() = 0;
|
||||||
|
virtual void panelEditPhone() = 0;
|
||||||
|
virtual void panelChooseShippingOption() = 0;
|
||||||
|
virtual void panelChangeShippingOption(const QString &id) = 0;
|
||||||
|
|
||||||
|
virtual void panelValidateInformation(RequestedInformation data) = 0;
|
||||||
|
virtual void panelShowBox(object_ptr<BoxContent> box) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Payments::Ui
|
} // namespace Payments::Ui
|
||||||
|
|
|
@ -14,7 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "ui/widgets/checkbox.h"
|
#include "ui/widgets/checkbox.h"
|
||||||
#include "ui/widgets/level_meter.h"
|
#include "ui/widgets/level_meter.h"
|
||||||
#include "ui/widgets/buttons.h"
|
#include "ui/widgets/buttons.h"
|
||||||
#include "boxes/single_choice_box.h"
|
#include "ui/boxes/single_choice_box.h"
|
||||||
#include "boxes/confirm_box.h"
|
#include "boxes/confirm_box.h"
|
||||||
#include "platform/platform_specific.h"
|
#include "platform/platform_specific.h"
|
||||||
#include "main/main_session.h"
|
#include "main/main_session.h"
|
||||||
|
|
443
Telegram/SourceFiles/ui/boxes/country_select_box.cpp
Normal file
443
Telegram/SourceFiles/ui/boxes/country_select_box.cpp
Normal file
|
@ -0,0 +1,443 @@
|
||||||
|
/*
|
||||||
|
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 "ui/boxes/country_select_box.h"
|
||||||
|
|
||||||
|
#include "lang/lang_keys.h"
|
||||||
|
#include "ui/widgets/scroll_area.h"
|
||||||
|
#include "ui/widgets/multi_select.h"
|
||||||
|
#include "ui/effects/ripple_animation.h"
|
||||||
|
#include "data/data_countries.h"
|
||||||
|
#include "base/qt_adapters.h"
|
||||||
|
#include "styles/style_layers.h"
|
||||||
|
#include "styles/style_boxes.h"
|
||||||
|
#include "styles/style_intro.h"
|
||||||
|
|
||||||
|
#include <QtCore/QRegularExpression>
|
||||||
|
|
||||||
|
namespace Ui {
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
QString LastValidISO;
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
class CountrySelectBox::Inner : public TWidget {
|
||||||
|
public:
|
||||||
|
Inner(QWidget *parent, Type type);
|
||||||
|
~Inner();
|
||||||
|
|
||||||
|
void updateFilter(QString filter = QString());
|
||||||
|
|
||||||
|
void selectSkip(int32 dir);
|
||||||
|
void selectSkipPage(int32 h, int32 dir);
|
||||||
|
|
||||||
|
void chooseCountry();
|
||||||
|
|
||||||
|
void refresh();
|
||||||
|
|
||||||
|
[[nodiscard]] rpl::producer<QString> countryChosen() const {
|
||||||
|
return _countryChosen.events();
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] rpl::producer<ScrollToRequest> mustScrollTo() const {
|
||||||
|
return _mustScrollTo.events();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void paintEvent(QPaintEvent *e) override;
|
||||||
|
void enterEventHook(QEvent *e) override;
|
||||||
|
void leaveEventHook(QEvent *e) override;
|
||||||
|
void mouseMoveEvent(QMouseEvent *e) override;
|
||||||
|
void mousePressEvent(QMouseEvent *e) override;
|
||||||
|
void mouseReleaseEvent(QMouseEvent *e) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void updateSelected() {
|
||||||
|
updateSelected(mapFromGlobal(QCursor::pos()));
|
||||||
|
}
|
||||||
|
void updateSelected(QPoint localPos);
|
||||||
|
void updateSelectedRow();
|
||||||
|
void updateRow(int index);
|
||||||
|
void setPressed(int pressed);
|
||||||
|
const std::vector<not_null<const Data::CountryInfo*>> ¤t() const;
|
||||||
|
|
||||||
|
Type _type = Type::Phones;
|
||||||
|
int _rowHeight = 0;
|
||||||
|
|
||||||
|
int _selected = -1;
|
||||||
|
int _pressed = -1;
|
||||||
|
QString _filter;
|
||||||
|
bool _mouseSelection = false;
|
||||||
|
|
||||||
|
std::vector<std::unique_ptr<RippleAnimation>> _ripples;
|
||||||
|
|
||||||
|
std::vector<not_null<const Data::CountryInfo*>> _list;
|
||||||
|
std::vector<not_null<const Data::CountryInfo*>> _filtered;
|
||||||
|
base::flat_map<QChar, std::vector<int>> _byLetter;
|
||||||
|
std::vector<std::vector<QString>> _namesList;
|
||||||
|
|
||||||
|
rpl::event_stream<QString> _countryChosen;
|
||||||
|
rpl::event_stream<ScrollToRequest> _mustScrollTo;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
CountrySelectBox::CountrySelectBox(QWidget*)
|
||||||
|
: _select(this, st::defaultMultiSelect, tr::lng_country_ph()) {
|
||||||
|
}
|
||||||
|
|
||||||
|
CountrySelectBox::CountrySelectBox(QWidget*, const QString &iso, Type type)
|
||||||
|
: _type(type)
|
||||||
|
, _select(this, st::defaultMultiSelect, tr::lng_country_ph()) {
|
||||||
|
if (Data::CountriesByISO2().contains(iso)) {
|
||||||
|
LastValidISO = iso;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rpl::producer<QString> CountrySelectBox::countryChosen() const {
|
||||||
|
return _inner->countryChosen();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CountrySelectBox::prepare() {
|
||||||
|
setTitle(tr::lng_country_select());
|
||||||
|
|
||||||
|
_select->resizeToWidth(st::boxWidth);
|
||||||
|
_select->setQueryChangedCallback([=](const QString &query) {
|
||||||
|
applyFilterUpdate(query);
|
||||||
|
});
|
||||||
|
_select->setSubmittedCallback([=](Qt::KeyboardModifiers) {
|
||||||
|
submit();
|
||||||
|
});
|
||||||
|
|
||||||
|
_inner = setInnerWidget(
|
||||||
|
object_ptr<Inner>(this, _type),
|
||||||
|
st::countriesScroll,
|
||||||
|
_select->height());
|
||||||
|
|
||||||
|
addButton(tr::lng_close(), [=] { closeBox(); });
|
||||||
|
|
||||||
|
setDimensions(st::boxWidth, st::boxMaxListHeight);
|
||||||
|
|
||||||
|
_inner->mustScrollTo(
|
||||||
|
) | rpl::start_with_next([=](ScrollToRequest request) {
|
||||||
|
onScrollToY(request.ymin, request.ymax);
|
||||||
|
}, lifetime());
|
||||||
|
}
|
||||||
|
|
||||||
|
void CountrySelectBox::submit() {
|
||||||
|
_inner->chooseCountry();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CountrySelectBox::keyPressEvent(QKeyEvent *e) {
|
||||||
|
if (e->key() == Qt::Key_Down) {
|
||||||
|
_inner->selectSkip(1);
|
||||||
|
} else if (e->key() == Qt::Key_Up) {
|
||||||
|
_inner->selectSkip(-1);
|
||||||
|
} else if (e->key() == Qt::Key_PageDown) {
|
||||||
|
_inner->selectSkipPage(height() - _select->height(), 1);
|
||||||
|
} else if (e->key() == Qt::Key_PageUp) {
|
||||||
|
_inner->selectSkipPage(height() - _select->height(), -1);
|
||||||
|
} else {
|
||||||
|
BoxContent::keyPressEvent(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CountrySelectBox::resizeEvent(QResizeEvent *e) {
|
||||||
|
BoxContent::resizeEvent(e);
|
||||||
|
|
||||||
|
_select->resizeToWidth(width());
|
||||||
|
_select->moveToLeft(0, 0);
|
||||||
|
|
||||||
|
_inner->resizeToWidth(width());
|
||||||
|
}
|
||||||
|
|
||||||
|
void CountrySelectBox::applyFilterUpdate(const QString &query) {
|
||||||
|
onScrollToY(0);
|
||||||
|
_inner->updateFilter(query);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CountrySelectBox::setInnerFocus() {
|
||||||
|
_select->setInnerFocus();
|
||||||
|
}
|
||||||
|
|
||||||
|
CountrySelectBox::Inner::Inner(QWidget *parent, Type type)
|
||||||
|
: TWidget(parent)
|
||||||
|
, _type(type)
|
||||||
|
, _rowHeight(st::countryRowHeight) {
|
||||||
|
setAttribute(Qt::WA_OpaquePaintEvent);
|
||||||
|
|
||||||
|
const auto &byISO2 = Data::CountriesByISO2();
|
||||||
|
|
||||||
|
_list.reserve(byISO2.size());
|
||||||
|
_namesList.reserve(byISO2.size());
|
||||||
|
|
||||||
|
const auto l = byISO2.constFind(LastValidISO);
|
||||||
|
const auto lastValid = (l != byISO2.cend()) ? (*l) : nullptr;
|
||||||
|
if (lastValid) {
|
||||||
|
_list.emplace_back(lastValid);
|
||||||
|
}
|
||||||
|
for (const auto &entry : Data::Countries()) {
|
||||||
|
if (&entry != lastValid) {
|
||||||
|
_list.emplace_back(&entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
auto index = 0;
|
||||||
|
for (const auto info : _list) {
|
||||||
|
auto full = QString::fromUtf8(info->name)
|
||||||
|
+ ' '
|
||||||
|
+ (info->alternativeName
|
||||||
|
? QString::fromUtf8(info->alternativeName)
|
||||||
|
: QString());
|
||||||
|
const auto namesList = std::move(full).toLower().split(
|
||||||
|
QRegularExpression("[\\s\\-]"),
|
||||||
|
base::QStringSkipEmptyParts);
|
||||||
|
auto &names = _namesList.emplace_back();
|
||||||
|
names.reserve(namesList.size());
|
||||||
|
for (const auto &name : namesList) {
|
||||||
|
const auto part = name.trimmed();
|
||||||
|
if (part.isEmpty()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto ch = part[0];
|
||||||
|
auto &byLetter = _byLetter[ch];
|
||||||
|
if (byLetter.empty() || byLetter.back() != index) {
|
||||||
|
byLetter.push_back(index);
|
||||||
|
}
|
||||||
|
names.push_back(part);
|
||||||
|
}
|
||||||
|
++index;
|
||||||
|
}
|
||||||
|
|
||||||
|
_filter = u"a"_q;
|
||||||
|
updateFilter();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CountrySelectBox::Inner::paintEvent(QPaintEvent *e) {
|
||||||
|
Painter p(this);
|
||||||
|
QRect r(e->rect());
|
||||||
|
p.setClipRect(r);
|
||||||
|
|
||||||
|
const auto &list = current();
|
||||||
|
if (list.empty()) {
|
||||||
|
p.fillRect(r, st::boxBg);
|
||||||
|
p.setFont(st::noContactsFont);
|
||||||
|
p.setPen(st::noContactsColor);
|
||||||
|
p.drawText(QRect(0, 0, width(), st::noContactsHeight), tr::lng_country_none(tr::now), style::al_center);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const auto l = int(list.size());
|
||||||
|
if (r.intersects(QRect(0, 0, width(), st::countriesSkip))) {
|
||||||
|
p.fillRect(r.intersected(QRect(0, 0, width(), st::countriesSkip)), st::countryRowBg);
|
||||||
|
}
|
||||||
|
int32 from = std::clamp((r.y() - st::countriesSkip) / _rowHeight, 0, l);
|
||||||
|
int32 to = std::clamp((r.y() + r.height() - st::countriesSkip + _rowHeight - 1) / _rowHeight, 0, l);
|
||||||
|
for (int32 i = from; i < to; ++i) {
|
||||||
|
auto selected = (i == (_pressed >= 0 ? _pressed : _selected));
|
||||||
|
auto y = st::countriesSkip + i * _rowHeight;
|
||||||
|
|
||||||
|
p.fillRect(0, y, width(), _rowHeight, selected ? st::countryRowBgOver : st::countryRowBg);
|
||||||
|
if (_ripples.size() > i && _ripples[i]) {
|
||||||
|
_ripples[i]->paint(p, 0, y, width());
|
||||||
|
if (_ripples[i]->empty()) {
|
||||||
|
_ripples[i].reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto code = QString("+") + list[i]->code;
|
||||||
|
auto codeWidth = st::countryRowCodeFont->width(code);
|
||||||
|
|
||||||
|
auto name = QString::fromUtf8(list[i]->name);
|
||||||
|
auto nameWidth = st::countryRowNameFont->width(name);
|
||||||
|
auto availWidth = width() - st::countryRowPadding.left() - st::countryRowPadding.right() - codeWidth - st::boxScroll.width;
|
||||||
|
if (nameWidth > availWidth) {
|
||||||
|
name = st::countryRowNameFont->elided(name, availWidth);
|
||||||
|
nameWidth = st::countryRowNameFont->width(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
p.setFont(st::countryRowNameFont);
|
||||||
|
p.setPen(st::countryRowNameFg);
|
||||||
|
p.drawTextLeft(st::countryRowPadding.left(), y + st::countryRowPadding.top(), width(), name);
|
||||||
|
|
||||||
|
if (_type == Type::Phones) {
|
||||||
|
p.setFont(st::countryRowCodeFont);
|
||||||
|
p.setPen(selected ? st::countryRowCodeFgOver : st::countryRowCodeFg);
|
||||||
|
p.drawTextLeft(st::countryRowPadding.left() + nameWidth + st::countryRowPadding.right(), y + st::countryRowPadding.top(), width(), code);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CountrySelectBox::Inner::enterEventHook(QEvent *e) {
|
||||||
|
setMouseTracking(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CountrySelectBox::Inner::leaveEventHook(QEvent *e) {
|
||||||
|
_mouseSelection = false;
|
||||||
|
setMouseTracking(false);
|
||||||
|
if (_selected >= 0) {
|
||||||
|
updateSelectedRow();
|
||||||
|
_selected = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CountrySelectBox::Inner::mouseMoveEvent(QMouseEvent *e) {
|
||||||
|
_mouseSelection = true;
|
||||||
|
updateSelected(e->pos());
|
||||||
|
}
|
||||||
|
|
||||||
|
void CountrySelectBox::Inner::mousePressEvent(QMouseEvent *e) {
|
||||||
|
_mouseSelection = true;
|
||||||
|
updateSelected(e->pos());
|
||||||
|
|
||||||
|
setPressed(_selected);
|
||||||
|
const auto &list = current();
|
||||||
|
if (_pressed >= 0 && _pressed < list.size()) {
|
||||||
|
if (_ripples.size() <= _pressed) {
|
||||||
|
_ripples.reserve(_pressed + 1);
|
||||||
|
while (_ripples.size() <= _pressed) {
|
||||||
|
_ripples.push_back(nullptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!_ripples[_pressed]) {
|
||||||
|
auto mask = RippleAnimation::rectMask(QSize(width(), _rowHeight));
|
||||||
|
_ripples[_pressed] = std::make_unique<RippleAnimation>(st::countryRipple, std::move(mask), [this, index = _pressed] {
|
||||||
|
updateRow(index);
|
||||||
|
});
|
||||||
|
_ripples[_pressed]->add(e->pos() - QPoint(0, st::countriesSkip + _pressed * _rowHeight));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CountrySelectBox::Inner::mouseReleaseEvent(QMouseEvent *e) {
|
||||||
|
auto pressed = _pressed;
|
||||||
|
setPressed(-1);
|
||||||
|
updateSelectedRow();
|
||||||
|
if (e->button() == Qt::LeftButton) {
|
||||||
|
if ((pressed >= 0) && pressed == _selected) {
|
||||||
|
chooseCountry();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CountrySelectBox::Inner::updateFilter(QString filter) {
|
||||||
|
const auto words = TextUtilities::PrepareSearchWords(filter);
|
||||||
|
filter = words.isEmpty() ? QString() : words.join(' ');
|
||||||
|
if (_filter == filter) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_filter = filter;
|
||||||
|
|
||||||
|
const auto findWord = [&](
|
||||||
|
const std::vector<QString> &names,
|
||||||
|
const QString &word) {
|
||||||
|
for (const auto &name : names) {
|
||||||
|
if (name.startsWith(word)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
const auto hasAllWords = [&](const std::vector<QString> &names) {
|
||||||
|
for (const auto &word : words) {
|
||||||
|
if (!findWord(names, word)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
if (!_filter.isEmpty()) {
|
||||||
|
_filtered.clear();
|
||||||
|
for (const auto index : _byLetter[_filter[0].toLower()]) {
|
||||||
|
const auto &names = _namesList[index];
|
||||||
|
if (hasAllWords(_namesList[index])) {
|
||||||
|
_filtered.push_back(_list[index]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
refresh();
|
||||||
|
_selected = current().empty() ? -1 : 0;
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CountrySelectBox::Inner::selectSkip(int32 dir) {
|
||||||
|
_mouseSelection = false;
|
||||||
|
|
||||||
|
const auto &list = current();
|
||||||
|
int cur = (_selected >= 0) ? _selected : -1;
|
||||||
|
cur += dir;
|
||||||
|
if (cur <= 0) {
|
||||||
|
_selected = list.empty() ? -1 : 0;
|
||||||
|
} else if (cur >= list.size()) {
|
||||||
|
_selected = -1;
|
||||||
|
} else {
|
||||||
|
_selected = cur;
|
||||||
|
}
|
||||||
|
if (_selected >= 0) {
|
||||||
|
_mustScrollTo.fire(ScrollToRequest(
|
||||||
|
st::countriesSkip + _selected * _rowHeight,
|
||||||
|
st::countriesSkip + (_selected + 1) * _rowHeight));
|
||||||
|
}
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CountrySelectBox::Inner::selectSkipPage(int32 h, int32 dir) {
|
||||||
|
int32 points = h / _rowHeight;
|
||||||
|
if (!points) return;
|
||||||
|
selectSkip(points * dir);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CountrySelectBox::Inner::chooseCountry() {
|
||||||
|
const auto &list = current();
|
||||||
|
_countryChosen.fire((_selected >= 0 && _selected < list.size())
|
||||||
|
? QString(list[_selected]->iso2)
|
||||||
|
: QString());
|
||||||
|
}
|
||||||
|
|
||||||
|
void CountrySelectBox::Inner::refresh() {
|
||||||
|
const auto &list = current();
|
||||||
|
resize(width(), list.empty() ? st::noContactsHeight : (list.size() * _rowHeight + st::countriesSkip));
|
||||||
|
}
|
||||||
|
|
||||||
|
void CountrySelectBox::Inner::updateSelected(QPoint localPos) {
|
||||||
|
if (!_mouseSelection) return;
|
||||||
|
|
||||||
|
auto in = parentWidget()->rect().contains(parentWidget()->mapFromGlobal(QCursor::pos()));
|
||||||
|
|
||||||
|
const auto &list = current();
|
||||||
|
auto selected = (in && localPos.y() >= st::countriesSkip && localPos.y() < st::countriesSkip + list.size() * _rowHeight) ? ((localPos.y() - st::countriesSkip) / _rowHeight) : -1;
|
||||||
|
if (_selected != selected) {
|
||||||
|
updateSelectedRow();
|
||||||
|
_selected = selected;
|
||||||
|
updateSelectedRow();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto CountrySelectBox::Inner::current() const
|
||||||
|
-> const std::vector<not_null<const Data::CountryInfo*>> & {
|
||||||
|
return _filter.isEmpty() ? _list : _filtered;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CountrySelectBox::Inner::updateSelectedRow() {
|
||||||
|
updateRow(_selected);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CountrySelectBox::Inner::updateRow(int index) {
|
||||||
|
if (index >= 0) {
|
||||||
|
update(0, st::countriesSkip + index * _rowHeight, width(), _rowHeight);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CountrySelectBox::Inner::setPressed(int pressed) {
|
||||||
|
if (_pressed >= 0 && _pressed < _ripples.size() && _ripples[_pressed]) {
|
||||||
|
_ripples[_pressed]->lastStop();
|
||||||
|
}
|
||||||
|
_pressed = pressed;
|
||||||
|
}
|
||||||
|
|
||||||
|
CountrySelectBox::Inner::~Inner() = default;
|
||||||
|
|
||||||
|
} // namespace Ui
|
53
Telegram/SourceFiles/ui/boxes/country_select_box.h
Normal file
53
Telegram/SourceFiles/ui/boxes/country_select_box.h
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
/*
|
||||||
|
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
|
||||||
|
*/
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "boxes/abstract_box.h"
|
||||||
|
#include "styles/style_widgets.h"
|
||||||
|
|
||||||
|
namespace Data {
|
||||||
|
struct CountryInfo;
|
||||||
|
} // namespace Data
|
||||||
|
|
||||||
|
namespace Ui {
|
||||||
|
|
||||||
|
class MultiSelect;
|
||||||
|
class RippleAnimation;
|
||||||
|
|
||||||
|
class CountrySelectBox : public BoxContent {
|
||||||
|
public:
|
||||||
|
enum class Type {
|
||||||
|
Phones,
|
||||||
|
Countries,
|
||||||
|
};
|
||||||
|
|
||||||
|
CountrySelectBox(QWidget*);
|
||||||
|
CountrySelectBox(QWidget*, const QString &iso, Type type);
|
||||||
|
|
||||||
|
[[nodiscard]] rpl::producer<QString> countryChosen() const;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void prepare() override;
|
||||||
|
void setInnerFocus() override;
|
||||||
|
|
||||||
|
void keyPressEvent(QKeyEvent *e) override;
|
||||||
|
void resizeEvent(QResizeEvent *e) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void submit();
|
||||||
|
void applyFilterUpdate(const QString &query);
|
||||||
|
|
||||||
|
Type _type = Type::Phones;
|
||||||
|
object_ptr<MultiSelect> _select;
|
||||||
|
|
||||||
|
class Inner;
|
||||||
|
QPointer<Inner> _inner;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Ui
|
|
@ -5,11 +5,9 @@ the official desktop application for the Telegram messaging service.
|
||||||
For license and copyright information please follow this link:
|
For license and copyright information please follow this link:
|
||||||
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
*/
|
*/
|
||||||
#include "boxes/single_choice_box.h"
|
#include "ui/boxes/single_choice_box.h"
|
||||||
|
|
||||||
#include "lang/lang_keys.h"
|
#include "lang/lang_keys.h"
|
||||||
#include "storage/localstorage.h"
|
|
||||||
#include "mainwindow.h"
|
|
||||||
#include "ui/widgets/checkbox.h"
|
#include "ui/widgets/checkbox.h"
|
||||||
#include "ui/wrap/vertical_layout.h"
|
#include "ui/wrap/vertical_layout.h"
|
||||||
#include "ui/wrap/padding_wrap.h"
|
#include "ui/wrap/padding_wrap.h"
|
|
@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "ui/widgets/scroll_area.h"
|
#include "ui/widgets/scroll_area.h"
|
||||||
#include "ui/widgets/multi_select.h"
|
#include "ui/widgets/multi_select.h"
|
||||||
#include "ui/effects/ripple_animation.h"
|
#include "ui/effects/ripple_animation.h"
|
||||||
|
#include "ui/boxes/country_select_box.h"
|
||||||
#include "data/data_countries.h"
|
#include "data/data_countries.h"
|
||||||
#include "base/qt_adapters.h"
|
#include "base/qt_adapters.h"
|
||||||
#include "styles/style_layers.h"
|
#include "styles/style_layers.h"
|
||||||
|
@ -23,7 +24,8 @@ QString LastValidISO;
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
CountryInput::CountryInput(QWidget *parent, const style::InputField &st) : TWidget(parent)
|
CountryInput::CountryInput(QWidget *parent, const style::InputField &st)
|
||||||
|
: RpWidget(parent)
|
||||||
, _st(st)
|
, _st(st)
|
||||||
, _text(tr::lng_country_code(tr::now)) {
|
, _text(tr::lng_country_code(tr::now)) {
|
||||||
resize(_st.width, _st.heightMin);
|
resize(_st.width, _st.heightMin);
|
||||||
|
@ -91,8 +93,11 @@ void CountryInput::mouseMoveEvent(QMouseEvent *e) {
|
||||||
void CountryInput::mousePressEvent(QMouseEvent *e) {
|
void CountryInput::mousePressEvent(QMouseEvent *e) {
|
||||||
mouseMoveEvent(e);
|
mouseMoveEvent(e);
|
||||||
if (_active) {
|
if (_active) {
|
||||||
auto box = Ui::show(Box<CountrySelectBox>());
|
auto box = Ui::show(Box<Ui::CountrySelectBox>());
|
||||||
connect(box, SIGNAL(countryChosen(const QString&)), this, SLOT(onChooseCountry(const QString&)));
|
box->countryChosen(
|
||||||
|
) | rpl::start_with_next([=](QString iso) {
|
||||||
|
chooseCountry(iso);
|
||||||
|
}, lifetime());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -125,7 +130,7 @@ void CountryInput::onChooseCode(const QString &code) {
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool CountryInput::onChooseCountry(const QString &iso) {
|
bool CountryInput::chooseCountry(const QString &iso) {
|
||||||
Ui::hideLayer();
|
Ui::hideLayer();
|
||||||
|
|
||||||
const auto &byISO2 = Data::CountriesByISO2();
|
const auto &byISO2 = Data::CountriesByISO2();
|
||||||
|
@ -146,349 +151,3 @@ bool CountryInput::onChooseCountry(const QString &iso) {
|
||||||
void CountryInput::setText(const QString &newText) {
|
void CountryInput::setText(const QString &newText) {
|
||||||
_text = _st.font->elided(newText, width() - _st.textMargins.left() - _st.textMargins.right());
|
_text = _st.font->elided(newText, width() - _st.textMargins.left() - _st.textMargins.right());
|
||||||
}
|
}
|
||||||
|
|
||||||
CountrySelectBox::CountrySelectBox(QWidget*)
|
|
||||||
: _select(this, st::defaultMultiSelect, tr::lng_country_ph()) {
|
|
||||||
}
|
|
||||||
|
|
||||||
CountrySelectBox::CountrySelectBox(QWidget*, const QString &iso, Type type)
|
|
||||||
: _type(type)
|
|
||||||
, _select(this, st::defaultMultiSelect, tr::lng_country_ph()) {
|
|
||||||
if (Data::CountriesByISO2().contains(iso)) {
|
|
||||||
LastValidISO = iso;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CountrySelectBox::prepare() {
|
|
||||||
setTitle(tr::lng_country_select());
|
|
||||||
|
|
||||||
_select->resizeToWidth(st::boxWidth);
|
|
||||||
_select->setQueryChangedCallback([=](const QString &query) {
|
|
||||||
applyFilterUpdate(query);
|
|
||||||
});
|
|
||||||
_select->setSubmittedCallback([=](Qt::KeyboardModifiers) {
|
|
||||||
submit();
|
|
||||||
});
|
|
||||||
|
|
||||||
_inner = setInnerWidget(
|
|
||||||
object_ptr<Inner>(this, _type),
|
|
||||||
st::countriesScroll,
|
|
||||||
_select->height());
|
|
||||||
|
|
||||||
addButton(tr::lng_close(), [=] { closeBox(); });
|
|
||||||
|
|
||||||
setDimensions(st::boxWidth, st::boxMaxListHeight);
|
|
||||||
|
|
||||||
connect(_inner, SIGNAL(mustScrollTo(int, int)), this, SLOT(onScrollToY(int, int)));
|
|
||||||
connect(_inner, SIGNAL(countryChosen(const QString&)), this, SIGNAL(countryChosen(const QString&)));
|
|
||||||
}
|
|
||||||
|
|
||||||
void CountrySelectBox::submit() {
|
|
||||||
_inner->chooseCountry();
|
|
||||||
}
|
|
||||||
|
|
||||||
void CountrySelectBox::keyPressEvent(QKeyEvent *e) {
|
|
||||||
if (e->key() == Qt::Key_Down) {
|
|
||||||
_inner->selectSkip(1);
|
|
||||||
} else if (e->key() == Qt::Key_Up) {
|
|
||||||
_inner->selectSkip(-1);
|
|
||||||
} else if (e->key() == Qt::Key_PageDown) {
|
|
||||||
_inner->selectSkipPage(height() - _select->height(), 1);
|
|
||||||
} else if (e->key() == Qt::Key_PageUp) {
|
|
||||||
_inner->selectSkipPage(height() - _select->height(), -1);
|
|
||||||
} else {
|
|
||||||
BoxContent::keyPressEvent(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CountrySelectBox::resizeEvent(QResizeEvent *e) {
|
|
||||||
BoxContent::resizeEvent(e);
|
|
||||||
|
|
||||||
_select->resizeToWidth(width());
|
|
||||||
_select->moveToLeft(0, 0);
|
|
||||||
|
|
||||||
_inner->resizeToWidth(width());
|
|
||||||
}
|
|
||||||
|
|
||||||
void CountrySelectBox::applyFilterUpdate(const QString &query) {
|
|
||||||
onScrollToY(0);
|
|
||||||
_inner->updateFilter(query);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CountrySelectBox::setInnerFocus() {
|
|
||||||
_select->setInnerFocus();
|
|
||||||
}
|
|
||||||
|
|
||||||
CountrySelectBox::Inner::Inner(QWidget *parent, Type type)
|
|
||||||
: TWidget(parent)
|
|
||||||
, _type(type)
|
|
||||||
, _rowHeight(st::countryRowHeight) {
|
|
||||||
setAttribute(Qt::WA_OpaquePaintEvent);
|
|
||||||
|
|
||||||
const auto &byISO2 = Data::CountriesByISO2();
|
|
||||||
|
|
||||||
_list.reserve(byISO2.size());
|
|
||||||
_namesList.reserve(byISO2.size());
|
|
||||||
|
|
||||||
const auto l = byISO2.constFind(LastValidISO);
|
|
||||||
const auto lastValid = (l != byISO2.cend()) ? (*l) : nullptr;
|
|
||||||
if (lastValid) {
|
|
||||||
_list.emplace_back(lastValid);
|
|
||||||
}
|
|
||||||
for (const auto &entry : Data::Countries()) {
|
|
||||||
if (&entry != lastValid) {
|
|
||||||
_list.emplace_back(&entry);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
auto index = 0;
|
|
||||||
for (const auto info : _list) {
|
|
||||||
auto full = QString::fromUtf8(info->name)
|
|
||||||
+ ' '
|
|
||||||
+ (info->alternativeName
|
|
||||||
? QString::fromUtf8(info->alternativeName)
|
|
||||||
: QString());
|
|
||||||
const auto namesList = std::move(full).toLower().split(
|
|
||||||
QRegularExpression("[\\s\\-]"),
|
|
||||||
base::QStringSkipEmptyParts);
|
|
||||||
auto &names = _namesList.emplace_back();
|
|
||||||
names.reserve(namesList.size());
|
|
||||||
for (const auto &name : namesList) {
|
|
||||||
const auto part = name.trimmed();
|
|
||||||
if (part.isEmpty()) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
const auto ch = part[0];
|
|
||||||
auto &byLetter = _byLetter[ch];
|
|
||||||
if (byLetter.empty() || byLetter.back() != index) {
|
|
||||||
byLetter.push_back(index);
|
|
||||||
}
|
|
||||||
names.push_back(part);
|
|
||||||
}
|
|
||||||
++index;
|
|
||||||
}
|
|
||||||
|
|
||||||
_filter = qsl("a");
|
|
||||||
updateFilter();
|
|
||||||
}
|
|
||||||
|
|
||||||
void CountrySelectBox::Inner::paintEvent(QPaintEvent *e) {
|
|
||||||
Painter p(this);
|
|
||||||
QRect r(e->rect());
|
|
||||||
p.setClipRect(r);
|
|
||||||
|
|
||||||
const auto &list = current();
|
|
||||||
if (list.empty()) {
|
|
||||||
p.fillRect(r, st::boxBg);
|
|
||||||
p.setFont(st::noContactsFont);
|
|
||||||
p.setPen(st::noContactsColor);
|
|
||||||
p.drawText(QRect(0, 0, width(), st::noContactsHeight), tr::lng_country_none(tr::now), style::al_center);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const auto l = list.size();
|
|
||||||
if (r.intersects(QRect(0, 0, width(), st::countriesSkip))) {
|
|
||||||
p.fillRect(r.intersected(QRect(0, 0, width(), st::countriesSkip)), st::countryRowBg);
|
|
||||||
}
|
|
||||||
int32 from = floorclamp(r.y() - st::countriesSkip, _rowHeight, 0, l);
|
|
||||||
int32 to = ceilclamp(r.y() + r.height() - st::countriesSkip, _rowHeight, 0, l);
|
|
||||||
for (int32 i = from; i < to; ++i) {
|
|
||||||
auto selected = (i == (_pressed >= 0 ? _pressed : _selected));
|
|
||||||
auto y = st::countriesSkip + i * _rowHeight;
|
|
||||||
|
|
||||||
p.fillRect(0, y, width(), _rowHeight, selected ? st::countryRowBgOver : st::countryRowBg);
|
|
||||||
if (_ripples.size() > i && _ripples[i]) {
|
|
||||||
_ripples[i]->paint(p, 0, y, width());
|
|
||||||
if (_ripples[i]->empty()) {
|
|
||||||
_ripples[i].reset();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
auto code = QString("+") + list[i]->code;
|
|
||||||
auto codeWidth = st::countryRowCodeFont->width(code);
|
|
||||||
|
|
||||||
auto name = QString::fromUtf8(list[i]->name);
|
|
||||||
auto nameWidth = st::countryRowNameFont->width(name);
|
|
||||||
auto availWidth = width() - st::countryRowPadding.left() - st::countryRowPadding.right() - codeWidth - st::boxScroll.width;
|
|
||||||
if (nameWidth > availWidth) {
|
|
||||||
name = st::countryRowNameFont->elided(name, availWidth);
|
|
||||||
nameWidth = st::countryRowNameFont->width(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
p.setFont(st::countryRowNameFont);
|
|
||||||
p.setPen(st::countryRowNameFg);
|
|
||||||
p.drawTextLeft(st::countryRowPadding.left(), y + st::countryRowPadding.top(), width(), name);
|
|
||||||
|
|
||||||
if (_type == Type::Phones) {
|
|
||||||
p.setFont(st::countryRowCodeFont);
|
|
||||||
p.setPen(selected ? st::countryRowCodeFgOver : st::countryRowCodeFg);
|
|
||||||
p.drawTextLeft(st::countryRowPadding.left() + nameWidth + st::countryRowPadding.right(), y + st::countryRowPadding.top(), width(), code);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CountrySelectBox::Inner::enterEventHook(QEvent *e) {
|
|
||||||
setMouseTracking(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CountrySelectBox::Inner::leaveEventHook(QEvent *e) {
|
|
||||||
_mouseSelection = false;
|
|
||||||
setMouseTracking(false);
|
|
||||||
if (_selected >= 0) {
|
|
||||||
updateSelectedRow();
|
|
||||||
_selected = -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CountrySelectBox::Inner::mouseMoveEvent(QMouseEvent *e) {
|
|
||||||
_mouseSelection = true;
|
|
||||||
updateSelected(e->pos());
|
|
||||||
}
|
|
||||||
|
|
||||||
void CountrySelectBox::Inner::mousePressEvent(QMouseEvent *e) {
|
|
||||||
_mouseSelection = true;
|
|
||||||
updateSelected(e->pos());
|
|
||||||
|
|
||||||
setPressed(_selected);
|
|
||||||
const auto &list = current();
|
|
||||||
if (_pressed >= 0 && _pressed < list.size()) {
|
|
||||||
if (_ripples.size() <= _pressed) {
|
|
||||||
_ripples.reserve(_pressed + 1);
|
|
||||||
while (_ripples.size() <= _pressed) {
|
|
||||||
_ripples.push_back(nullptr);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!_ripples[_pressed]) {
|
|
||||||
auto mask = Ui::RippleAnimation::rectMask(QSize(width(), _rowHeight));
|
|
||||||
_ripples[_pressed] = std::make_unique<Ui::RippleAnimation>(st::countryRipple, std::move(mask), [this, index = _pressed] {
|
|
||||||
updateRow(index);
|
|
||||||
});
|
|
||||||
_ripples[_pressed]->add(e->pos() - QPoint(0, st::countriesSkip + _pressed * _rowHeight));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CountrySelectBox::Inner::mouseReleaseEvent(QMouseEvent *e) {
|
|
||||||
auto pressed = _pressed;
|
|
||||||
setPressed(-1);
|
|
||||||
updateSelectedRow();
|
|
||||||
if (e->button() == Qt::LeftButton) {
|
|
||||||
if ((pressed >= 0) && pressed == _selected) {
|
|
||||||
chooseCountry();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CountrySelectBox::Inner::updateFilter(QString filter) {
|
|
||||||
const auto words = TextUtilities::PrepareSearchWords(filter);
|
|
||||||
filter = words.isEmpty() ? QString() : words.join(' ');
|
|
||||||
if (_filter == filter) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_filter = filter;
|
|
||||||
|
|
||||||
const auto findWord = [&](
|
|
||||||
const std::vector<QString> &names,
|
|
||||||
const QString &word) {
|
|
||||||
for (const auto &name : names) {
|
|
||||||
if (name.startsWith(word)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
const auto hasAllWords = [&](const std::vector<QString> &names) {
|
|
||||||
for (const auto &word : words) {
|
|
||||||
if (!findWord(names, word)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
if (!_filter.isEmpty()) {
|
|
||||||
_filtered.clear();
|
|
||||||
for (const auto index : _byLetter[_filter[0].toLower()]) {
|
|
||||||
const auto &names = _namesList[index];
|
|
||||||
if (hasAllWords(_namesList[index])) {
|
|
||||||
_filtered.push_back(_list[index]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
refresh();
|
|
||||||
_selected = current().empty() ? -1 : 0;
|
|
||||||
update();
|
|
||||||
}
|
|
||||||
|
|
||||||
void CountrySelectBox::Inner::selectSkip(int32 dir) {
|
|
||||||
_mouseSelection = false;
|
|
||||||
|
|
||||||
const auto &list = current();
|
|
||||||
int cur = (_selected >= 0) ? _selected : -1;
|
|
||||||
cur += dir;
|
|
||||||
if (cur <= 0) {
|
|
||||||
_selected = list.empty() ? -1 : 0;
|
|
||||||
} else if (cur >= list.size()) {
|
|
||||||
_selected = -1;
|
|
||||||
} else {
|
|
||||||
_selected = cur;
|
|
||||||
}
|
|
||||||
if (_selected >= 0) {
|
|
||||||
mustScrollTo(st::countriesSkip + _selected * _rowHeight, st::countriesSkip + (_selected + 1) * _rowHeight);
|
|
||||||
}
|
|
||||||
update();
|
|
||||||
}
|
|
||||||
|
|
||||||
void CountrySelectBox::Inner::selectSkipPage(int32 h, int32 dir) {
|
|
||||||
int32 points = h / _rowHeight;
|
|
||||||
if (!points) return;
|
|
||||||
selectSkip(points * dir);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CountrySelectBox::Inner::chooseCountry() {
|
|
||||||
const auto &list = current();
|
|
||||||
countryChosen((_selected >= 0 && _selected < list.size())
|
|
||||||
? QString(list[_selected]->iso2)
|
|
||||||
: QString());
|
|
||||||
}
|
|
||||||
|
|
||||||
void CountrySelectBox::Inner::refresh() {
|
|
||||||
const auto &list = current();
|
|
||||||
resize(width(), list.empty() ? st::noContactsHeight : (list.size() * _rowHeight + st::countriesSkip));
|
|
||||||
}
|
|
||||||
|
|
||||||
void CountrySelectBox::Inner::updateSelected(QPoint localPos) {
|
|
||||||
if (!_mouseSelection) return;
|
|
||||||
|
|
||||||
auto in = parentWidget()->rect().contains(parentWidget()->mapFromGlobal(QCursor::pos()));
|
|
||||||
|
|
||||||
const auto &list = current();
|
|
||||||
auto selected = (in && localPos.y() >= st::countriesSkip && localPos.y() < st::countriesSkip + list.size() * _rowHeight) ? ((localPos.y() - st::countriesSkip) / _rowHeight) : -1;
|
|
||||||
if (_selected != selected) {
|
|
||||||
updateSelectedRow();
|
|
||||||
_selected = selected;
|
|
||||||
updateSelectedRow();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
auto CountrySelectBox::Inner::current() const
|
|
||||||
-> const std::vector<not_null<const Data::CountryInfo*>> & {
|
|
||||||
return _filter.isEmpty() ? _list : _filtered;
|
|
||||||
}
|
|
||||||
|
|
||||||
void CountrySelectBox::Inner::updateSelectedRow() {
|
|
||||||
updateRow(_selected);
|
|
||||||
}
|
|
||||||
|
|
||||||
void CountrySelectBox::Inner::updateRow(int index) {
|
|
||||||
if (index >= 0) {
|
|
||||||
update(0, st::countriesSkip + index * _rowHeight, width(), _rowHeight);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CountrySelectBox::Inner::setPressed(int pressed) {
|
|
||||||
if (_pressed >= 0 && _pressed < _ripples.size() && _ripples[_pressed]) {
|
|
||||||
_ripples[_pressed]->lastStop();
|
|
||||||
}
|
|
||||||
_pressed = pressed;
|
|
||||||
}
|
|
||||||
|
|
||||||
CountrySelectBox::Inner::~Inner() = default;
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
*/
|
*/
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "boxes/abstract_box.h"
|
#include "ui/rp_widget.h"
|
||||||
#include "styles/style_widgets.h"
|
#include "styles/style_widgets.h"
|
||||||
|
|
||||||
namespace Data {
|
namespace Data {
|
||||||
|
@ -19,19 +19,19 @@ class MultiSelect;
|
||||||
class RippleAnimation;
|
class RippleAnimation;
|
||||||
} // namespace Ui
|
} // namespace Ui
|
||||||
|
|
||||||
class CountryInput : public TWidget {
|
class CountryInput : public Ui::RpWidget {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
CountryInput(QWidget *parent, const style::InputField &st);
|
CountryInput(QWidget *parent, const style::InputField &st);
|
||||||
|
|
||||||
QString iso() const {
|
[[nodiscard]] QString iso() const {
|
||||||
return _chosenIso;
|
return _chosenIso;
|
||||||
}
|
}
|
||||||
|
bool chooseCountry(const QString &country);
|
||||||
|
|
||||||
public Q_SLOTS:
|
public Q_SLOTS:
|
||||||
void onChooseCode(const QString &code);
|
void onChooseCode(const QString &code);
|
||||||
bool onChooseCountry(const QString &country);
|
|
||||||
|
|
||||||
Q_SIGNALS:
|
Q_SIGNALS:
|
||||||
void codeChanged(const QString &code);
|
void codeChanged(const QString &code);
|
||||||
|
@ -53,94 +53,3 @@ private:
|
||||||
QPainterPath _placeholderPath;
|
QPainterPath _placeholderPath;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
class CountrySelectBox : public Ui::BoxContent {
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
enum class Type {
|
|
||||||
Phones,
|
|
||||||
Countries,
|
|
||||||
};
|
|
||||||
|
|
||||||
CountrySelectBox(QWidget*);
|
|
||||||
CountrySelectBox(QWidget*, const QString &iso, Type type);
|
|
||||||
|
|
||||||
Q_SIGNALS:
|
|
||||||
void countryChosen(const QString &iso);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void prepare() override;
|
|
||||||
void setInnerFocus() override;
|
|
||||||
|
|
||||||
void keyPressEvent(QKeyEvent *e) override;
|
|
||||||
void resizeEvent(QResizeEvent *e) override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
void submit();
|
|
||||||
void applyFilterUpdate(const QString &query);
|
|
||||||
|
|
||||||
Type _type = Type::Phones;
|
|
||||||
object_ptr<Ui::MultiSelect> _select;
|
|
||||||
|
|
||||||
class Inner;
|
|
||||||
QPointer<Inner> _inner;
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
// This class is hold in header because it requires Qt preprocessing.
|
|
||||||
class CountrySelectBox::Inner : public TWidget {
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
public:
|
|
||||||
Inner(QWidget *parent, Type type);
|
|
||||||
|
|
||||||
void updateFilter(QString filter = QString());
|
|
||||||
|
|
||||||
void selectSkip(int32 dir);
|
|
||||||
void selectSkipPage(int32 h, int32 dir);
|
|
||||||
|
|
||||||
void chooseCountry();
|
|
||||||
|
|
||||||
void refresh();
|
|
||||||
|
|
||||||
~Inner();
|
|
||||||
|
|
||||||
Q_SIGNALS:
|
|
||||||
void countryChosen(const QString &iso);
|
|
||||||
void mustScrollTo(int ymin, int ymax);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
void paintEvent(QPaintEvent *e) override;
|
|
||||||
void enterEventHook(QEvent *e) override;
|
|
||||||
void leaveEventHook(QEvent *e) override;
|
|
||||||
void mouseMoveEvent(QMouseEvent *e) override;
|
|
||||||
void mousePressEvent(QMouseEvent *e) override;
|
|
||||||
void mouseReleaseEvent(QMouseEvent *e) override;
|
|
||||||
|
|
||||||
private:
|
|
||||||
void updateSelected() {
|
|
||||||
updateSelected(mapFromGlobal(QCursor::pos()));
|
|
||||||
}
|
|
||||||
void updateSelected(QPoint localPos);
|
|
||||||
void updateSelectedRow();
|
|
||||||
void updateRow(int index);
|
|
||||||
void setPressed(int pressed);
|
|
||||||
const std::vector<not_null<const Data::CountryInfo*>> ¤t() const;
|
|
||||||
|
|
||||||
Type _type = Type::Phones;
|
|
||||||
int _rowHeight = 0;
|
|
||||||
|
|
||||||
int _selected = -1;
|
|
||||||
int _pressed = -1;
|
|
||||||
QString _filter;
|
|
||||||
bool _mouseSelection = false;
|
|
||||||
|
|
||||||
std::vector<std::unique_ptr<Ui::RippleAnimation>> _ripples;
|
|
||||||
|
|
||||||
std::vector<not_null<const Data::CountryInfo*>> _list;
|
|
||||||
std::vector<not_null<const Data::CountryInfo*>> _filtered;
|
|
||||||
base::flat_map<QChar, std::vector<int>> _byLetter;
|
|
||||||
std::vector<std::vector<QString>> _namesList;
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
|
@ -125,7 +125,7 @@ QString FormatPlayedText(qint64 played, qint64 duration) {
|
||||||
return tr::lng_duration_played(tr::now, lt_played, FormatDurationText(played), lt_duration, FormatDurationText(duration));
|
return tr::lng_duration_played(tr::now, lt_played, FormatDurationText(played), lt_duration, FormatDurationText(duration));
|
||||||
}
|
}
|
||||||
|
|
||||||
QString FillAmountAndCurrency(uint64 amount, const QString ¤cy) {
|
QString FillAmountAndCurrency(int64 amount, const QString ¤cy) {
|
||||||
static const auto ShortCurrencyNames = QMap<QString, QString>{
|
static const auto ShortCurrencyNames = QMap<QString, QString>{
|
||||||
{ u"USD"_q, QString::fromUtf8("\x24") },
|
{ u"USD"_q, QString::fromUtf8("\x24") },
|
||||||
{ u"GBP"_q, QString::fromUtf8("\xC2\xA3") },
|
{ u"GBP"_q, QString::fromUtf8("\xC2\xA3") },
|
||||||
|
|
|
@ -24,7 +24,7 @@ inline constexpr auto FileStatusSizeFailed = 0x7FFFFFF2;
|
||||||
[[nodiscard]] QString FormatPlayedText(qint64 played, qint64 duration);
|
[[nodiscard]] QString FormatPlayedText(qint64 played, qint64 duration);
|
||||||
|
|
||||||
[[nodiscard]] QString FillAmountAndCurrency(
|
[[nodiscard]] QString FillAmountAndCurrency(
|
||||||
uint64 amount,
|
int64 amount,
|
||||||
const QString ¤cy);
|
const QString ¤cy);
|
||||||
|
|
||||||
[[nodiscard]] QString ComposeNameString(
|
[[nodiscard]] QString ComposeNameString(
|
||||||
|
|
|
@ -12,6 +12,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
||||||
#include "ui/effects/animations.h"
|
#include "ui/effects/animations.h"
|
||||||
#include "base/object_ptr.h"
|
#include "base/object_ptr.h"
|
||||||
|
|
||||||
|
#include <set>
|
||||||
|
|
||||||
namespace Ui {
|
namespace Ui {
|
||||||
|
|
||||||
class InputField;
|
class InputField;
|
||||||
|
|
|
@ -52,6 +52,9 @@ PRIVATE
|
||||||
core/mime_type.cpp
|
core/mime_type.cpp
|
||||||
core/mime_type.h
|
core/mime_type.h
|
||||||
|
|
||||||
|
data/data_countries.cpp
|
||||||
|
data/data_countries.h
|
||||||
|
|
||||||
media/clip/media_clip_check_streaming.cpp
|
media/clip/media_clip_check_streaming.cpp
|
||||||
media/clip/media_clip_check_streaming.h
|
media/clip/media_clip_check_streaming.h
|
||||||
media/clip/media_clip_ffmpeg.cpp
|
media/clip/media_clip_ffmpeg.cpp
|
||||||
|
@ -61,6 +64,13 @@ PRIVATE
|
||||||
media/clip/media_clip_reader.cpp
|
media/clip/media_clip_reader.cpp
|
||||||
media/clip/media_clip_reader.h
|
media/clip/media_clip_reader.h
|
||||||
|
|
||||||
|
passport/ui/passport_details_row.cpp
|
||||||
|
passport/ui/passport_details_row.h
|
||||||
|
passport/ui/passport_form_row.cpp
|
||||||
|
passport/ui/passport_form_row.h
|
||||||
|
|
||||||
|
payments/ui/payments_edit_information.cpp
|
||||||
|
payments/ui/payments_edit_information.h
|
||||||
payments/ui/payments_form_summary.cpp
|
payments/ui/payments_form_summary.cpp
|
||||||
payments/ui/payments_form_summary.h
|
payments/ui/payments_form_summary.h
|
||||||
payments/ui/payments_panel.cpp
|
payments/ui/payments_panel.cpp
|
||||||
|
@ -80,10 +90,14 @@ PRIVATE
|
||||||
ui/boxes/calendar_box.h
|
ui/boxes/calendar_box.h
|
||||||
ui/boxes/choose_date_time.cpp
|
ui/boxes/choose_date_time.cpp
|
||||||
ui/boxes/choose_date_time.h
|
ui/boxes/choose_date_time.h
|
||||||
|
ui/boxes/country_select_box.cpp
|
||||||
|
ui/boxes/country_select_box.h
|
||||||
ui/boxes/edit_invite_link.cpp
|
ui/boxes/edit_invite_link.cpp
|
||||||
ui/boxes/edit_invite_link.h
|
ui/boxes/edit_invite_link.h
|
||||||
ui/boxes/report_box.cpp
|
ui/boxes/report_box.cpp
|
||||||
ui/boxes/report_box.h
|
ui/boxes/report_box.h
|
||||||
|
ui/boxes/single_choice_box.cpp
|
||||||
|
ui/boxes/single_choice_box.h
|
||||||
ui/chat/attach/attach_album_thumbnail.cpp
|
ui/chat/attach/attach_album_thumbnail.cpp
|
||||||
ui/chat/attach/attach_album_thumbnail.h
|
ui/chat/attach/attach_album_thumbnail.h
|
||||||
ui/chat/attach/attach_album_preview.cpp
|
ui/chat/attach/attach_album_preview.cpp
|
||||||
|
|
Loading…
Add table
Reference in a new issue