mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-15 21:57:10 +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/share_box.cpp
|
||||
boxes/share_box.h
|
||||
boxes/single_choice_box.cpp
|
||||
boxes/single_choice_box.h
|
||||
boxes/sticker_set_box.cpp
|
||||
boxes/sticker_set_box.h
|
||||
boxes/stickers_box.cpp
|
||||
|
@ -390,8 +388,6 @@ PRIVATE
|
|||
data/data_cloud_file.h
|
||||
data/data_cloud_themes.cpp
|
||||
data/data_cloud_themes.h
|
||||
data/data_countries.cpp
|
||||
data/data_countries.h
|
||||
data/data_document.cpp
|
||||
data/data_document.h
|
||||
data/data_document_media.cpp
|
||||
|
@ -803,8 +799,6 @@ PRIVATE
|
|||
passport/passport_panel.h
|
||||
passport/passport_panel_controller.cpp
|
||||
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.h
|
||||
passport/passport_panel_edit_document.cpp
|
||||
|
|
|
@ -11,7 +11,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "main/main_session.h"
|
||||
#include "boxes/add_contact_box.h"
|
||||
#include "boxes/confirm_box.h"
|
||||
#include "boxes/single_choice_box.h"
|
||||
#include "boxes/peer_list_controllers.h"
|
||||
#include "boxes/peers/edit_participants_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_linked_chat_box.h"
|
||||
#include "boxes/stickers_box.h"
|
||||
#include "ui/boxes/single_choice_box.h"
|
||||
#include "chat_helpers/emoji_suggestions_widget.h"
|
||||
#include "core/application.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_changes.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_media_devices.h"
|
||||
#include "settings/settings_common.h"
|
||||
|
|
|
@ -1820,7 +1820,7 @@ Utf8String FormatDateTime(
|
|||
).toUtf8();
|
||||
}
|
||||
|
||||
Utf8String FormatMoneyAmount(uint64 amount, const Utf8String ¤cy) {
|
||||
Utf8String FormatMoneyAmount(int64 amount, const Utf8String ¤cy) {
|
||||
return Ui::FillAmountAndCurrency(
|
||||
amount,
|
||||
QString::fromUtf8(currency)).toUtf8();
|
||||
|
|
|
@ -660,7 +660,7 @@ Utf8String FormatDateTime(
|
|||
QChar dateSeparator = QChar('.'),
|
||||
QChar timeSeparator = QChar(':'),
|
||||
QChar separator = QChar(' '));
|
||||
Utf8String FormatMoneyAmount(uint64 amount, const Utf8String ¤cy);
|
||||
Utf8String FormatMoneyAmount(int64 amount, const Utf8String ¤cy);
|
||||
Utf8String FormatFileSize(int64 size);
|
||||
Utf8String FormatDuration(int64 seconds);
|
||||
|
||||
|
|
|
@ -958,7 +958,9 @@ void HistoryService::createFromMtp(const MTPDmessageService &message) {
|
|||
UpdateComponents(HistoryServicePayment::Bit());
|
||||
const auto amount = data.vtotal_amount().v;
|
||||
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) {
|
||||
const auto &data = message.vaction().c_messageActionGroupCall();
|
||||
if (data.vduration()) {
|
||||
|
|
|
@ -61,8 +61,8 @@ PhoneWidget::PhoneWidget(
|
|||
setErrorCentered(true);
|
||||
setupQrLogin();
|
||||
|
||||
if (!_country->onChooseCountry(getData()->country)) {
|
||||
_country->onChooseCountry(qsl("US"));
|
||||
if (!_country->chooseCountry(getData()->country)) {
|
||||
_country->chooseCountry(qsl("US"));
|
||||
}
|
||||
_changed = false;
|
||||
}
|
||||
|
@ -251,7 +251,7 @@ QString PhoneWidget::fullNumber() const {
|
|||
}
|
||||
|
||||
void PhoneWidget::selectCountry(const QString &country) {
|
||||
_country->onChooseCountry(country);
|
||||
_country->chooseCountry(country);
|
||||
}
|
||||
|
||||
void PhoneWidget::setInnerFocus() {
|
||||
|
|
|
@ -9,10 +9,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
|
||||
#include "lang/lang_keys.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_scans.h"
|
||||
#include "passport/passport_panel.h"
|
||||
#include "passport/ui/passport_details_row.h"
|
||||
#include "base/openssl_help.h"
|
||||
#include "base/unixtime.h"
|
||||
#include "boxes/passcode_box.h"
|
||||
|
@ -212,7 +212,7 @@ EditDocumentScheme GetDocumentScheme(
|
|||
result.rows = {
|
||||
{
|
||||
ValueClass::Fields,
|
||||
PanelDetailsType::Text,
|
||||
Ui::PanelDetailsType::Text,
|
||||
qsl("first_name"),
|
||||
tr::lng_passport_first_name(tr::now),
|
||||
NameValidate,
|
||||
|
@ -221,7 +221,7 @@ EditDocumentScheme GetDocumentScheme(
|
|||
},
|
||||
{
|
||||
ValueClass::Fields,
|
||||
PanelDetailsType::Text,
|
||||
Ui::PanelDetailsType::Text,
|
||||
qsl("middle_name"),
|
||||
tr::lng_passport_middle_name(tr::now),
|
||||
NameOrEmptyValidate,
|
||||
|
@ -231,7 +231,7 @@ EditDocumentScheme GetDocumentScheme(
|
|||
},
|
||||
{
|
||||
ValueClass::Fields,
|
||||
PanelDetailsType::Text,
|
||||
Ui::PanelDetailsType::Text,
|
||||
qsl("last_name"),
|
||||
tr::lng_passport_last_name(tr::now),
|
||||
NameValidate,
|
||||
|
@ -241,7 +241,7 @@ EditDocumentScheme GetDocumentScheme(
|
|||
},
|
||||
{
|
||||
ValueClass::Fields,
|
||||
PanelDetailsType::Date,
|
||||
Ui::PanelDetailsType::Date,
|
||||
qsl("birth_date"),
|
||||
tr::lng_passport_birth_date(tr::now),
|
||||
DateValidate,
|
||||
|
@ -249,7 +249,7 @@ EditDocumentScheme GetDocumentScheme(
|
|||
},
|
||||
{
|
||||
ValueClass::Fields,
|
||||
PanelDetailsType::Gender,
|
||||
Ui::PanelDetailsType::Gender,
|
||||
qsl("gender"),
|
||||
tr::lng_passport_gender(tr::now),
|
||||
GenderValidate,
|
||||
|
@ -257,7 +257,7 @@ EditDocumentScheme GetDocumentScheme(
|
|||
},
|
||||
{
|
||||
ValueClass::Fields,
|
||||
PanelDetailsType::Country,
|
||||
Ui::PanelDetailsType::Country,
|
||||
qsl("country_code"),
|
||||
tr::lng_passport_country(tr::now),
|
||||
CountryValidate,
|
||||
|
@ -265,7 +265,7 @@ EditDocumentScheme GetDocumentScheme(
|
|||
},
|
||||
{
|
||||
ValueClass::Fields,
|
||||
PanelDetailsType::Country,
|
||||
Ui::PanelDetailsType::Country,
|
||||
qsl("residence_country_code"),
|
||||
tr::lng_passport_residence_country(tr::now),
|
||||
CountryValidate,
|
||||
|
@ -273,7 +273,7 @@ EditDocumentScheme GetDocumentScheme(
|
|||
},
|
||||
{
|
||||
ValueClass::Scans,
|
||||
PanelDetailsType::Text,
|
||||
Ui::PanelDetailsType::Text,
|
||||
qsl("document_no"),
|
||||
tr::lng_passport_document_number(tr::now),
|
||||
DocumentValidate,
|
||||
|
@ -282,7 +282,7 @@ EditDocumentScheme GetDocumentScheme(
|
|||
},
|
||||
{
|
||||
ValueClass::Scans,
|
||||
PanelDetailsType::Date,
|
||||
Ui::PanelDetailsType::Date,
|
||||
qsl("expiry_date"),
|
||||
tr::lng_passport_expiry_date(tr::now),
|
||||
DateOrEmptyValidate,
|
||||
|
@ -344,7 +344,7 @@ EditDocumentScheme GetDocumentScheme(
|
|||
auto additional = std::initializer_list<Row>{
|
||||
{
|
||||
ValueClass::Additional,
|
||||
PanelDetailsType::Text,
|
||||
Ui::PanelDetailsType::Text,
|
||||
qsl("first_name_native"),
|
||||
tr::lng_passport_first_name(tr::now),
|
||||
NativeNameValidate,
|
||||
|
@ -355,7 +355,7 @@ EditDocumentScheme GetDocumentScheme(
|
|||
},
|
||||
{
|
||||
ValueClass::Additional,
|
||||
PanelDetailsType::Text,
|
||||
Ui::PanelDetailsType::Text,
|
||||
qsl("middle_name_native"),
|
||||
tr::lng_passport_middle_name(tr::now),
|
||||
NativeNameOrEmptyValidate,
|
||||
|
@ -366,7 +366,7 @@ EditDocumentScheme GetDocumentScheme(
|
|||
},
|
||||
{
|
||||
ValueClass::Additional,
|
||||
PanelDetailsType::Text,
|
||||
Ui::PanelDetailsType::Text,
|
||||
qsl("last_name_native"),
|
||||
tr::lng_passport_last_name(tr::now),
|
||||
NativeNameValidate,
|
||||
|
@ -411,7 +411,7 @@ EditDocumentScheme GetDocumentScheme(
|
|||
result.rows = {
|
||||
{
|
||||
ValueClass::Fields,
|
||||
PanelDetailsType::Text,
|
||||
Ui::PanelDetailsType::Text,
|
||||
qsl("street_line1"),
|
||||
tr::lng_passport_street(tr::now),
|
||||
StreetValidate,
|
||||
|
@ -420,7 +420,7 @@ EditDocumentScheme GetDocumentScheme(
|
|||
},
|
||||
{
|
||||
ValueClass::Fields,
|
||||
PanelDetailsType::Text,
|
||||
Ui::PanelDetailsType::Text,
|
||||
qsl("street_line2"),
|
||||
tr::lng_passport_street(tr::now),
|
||||
DontValidate,
|
||||
|
@ -429,7 +429,7 @@ EditDocumentScheme GetDocumentScheme(
|
|||
},
|
||||
{
|
||||
ValueClass::Fields,
|
||||
PanelDetailsType::Text,
|
||||
Ui::PanelDetailsType::Text,
|
||||
qsl("city"),
|
||||
tr::lng_passport_city(tr::now),
|
||||
CityValidate,
|
||||
|
@ -438,7 +438,7 @@ EditDocumentScheme GetDocumentScheme(
|
|||
},
|
||||
{
|
||||
ValueClass::Fields,
|
||||
PanelDetailsType::Text,
|
||||
Ui::PanelDetailsType::Text,
|
||||
qsl("state"),
|
||||
tr::lng_passport_state(tr::now),
|
||||
DontValidate,
|
||||
|
@ -447,7 +447,7 @@ EditDocumentScheme GetDocumentScheme(
|
|||
},
|
||||
{
|
||||
ValueClass::Fields,
|
||||
PanelDetailsType::Country,
|
||||
Ui::PanelDetailsType::Country,
|
||||
qsl("country_code"),
|
||||
tr::lng_passport_country(tr::now),
|
||||
CountryValidate,
|
||||
|
@ -455,7 +455,7 @@ EditDocumentScheme GetDocumentScheme(
|
|||
},
|
||||
{
|
||||
ValueClass::Fields,
|
||||
PanelDetailsType::Postcode,
|
||||
Ui::PanelDetailsType::Postcode,
|
||||
qsl("post_code"),
|
||||
tr::lng_passport_postcode(tr::now),
|
||||
PostcodeValidate,
|
||||
|
|
|
@ -8,7 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "passport/passport_panel_edit_contact.h"
|
||||
|
||||
#include "passport/passport_panel_controller.h"
|
||||
#include "passport/passport_panel_details_row.h"
|
||||
#include "passport/ui/passport_details_row.h"
|
||||
#include "ui/widgets/input_fields.h"
|
||||
#include "ui/widgets/labels.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_controller.h"
|
||||
#include "passport/passport_panel_details_row.h"
|
||||
#include "passport/passport_panel_edit_scans.h"
|
||||
#include "passport/ui/passport_details_row.h"
|
||||
#include "ui/widgets/input_fields.h"
|
||||
#include "ui/widgets/scroll_area.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/fade_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 "boxes/abstract_box.h"
|
||||
#include "boxes/confirm_box.h"
|
||||
|
@ -363,7 +366,7 @@ not_null<Ui::RpWidget*> PanelEditDocument::setupContent(
|
|||
const ValueMap &fields) {
|
||||
accumulate_max(
|
||||
maxLabelWidth,
|
||||
PanelDetailsRow::LabelWidth(row.label));
|
||||
Ui::PanelDetailsRow::LabelWidth(row.label));
|
||||
});
|
||||
if (maxLabelWidth > 0) {
|
||||
if (error && !error->isEmpty()) {
|
||||
|
@ -513,12 +516,20 @@ void PanelEditDocument::createDetailsRow(
|
|||
};
|
||||
|
||||
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(
|
||||
i,
|
||||
container->add(PanelDetailsRow::Create(
|
||||
container->add(Ui::PanelDetailsRow::Create(
|
||||
container,
|
||||
showBox,
|
||||
isoByPhone,
|
||||
row.inputType,
|
||||
_controller,
|
||||
row.label,
|
||||
maxLabelWidth,
|
||||
current.text,
|
||||
|
@ -537,7 +548,7 @@ void PanelEditDocument::createDetailsRow(
|
|||
}, it->second->lifetime());
|
||||
}
|
||||
|
||||
not_null<PanelDetailsRow*> PanelEditDocument::findRow(
|
||||
not_null<Ui::PanelDetailsRow*> PanelEditDocument::findRow(
|
||||
const QString &key) const {
|
||||
for (auto i = 0, count = int(_scheme.rows.size()); i != count; ++i) {
|
||||
const auto &row = _scheme.rows[i];
|
||||
|
@ -636,7 +647,7 @@ bool PanelEditDocument::validate() {
|
|||
_scroll->scrollToY(_scroll->scrollTop() + scrolldelta);
|
||||
error = firsttop.y();
|
||||
}
|
||||
auto first = QPointer<PanelDetailsRow>();
|
||||
auto first = QPointer<Ui::PanelDetailsRow>();
|
||||
for (const auto &[i, field] : ranges::views::reverse(_details)) {
|
||||
const auto &row = _scheme.rows[i];
|
||||
if (row.valueClass == Scheme::ValueClass::Additional
|
||||
|
|
|
@ -24,15 +24,19 @@ template <typename Widget>
|
|||
class SlideWrap;
|
||||
} // namespace Ui
|
||||
|
||||
namespace Passport::Ui {
|
||||
using namespace ::Ui;
|
||||
enum class PanelDetailsType;
|
||||
class PanelDetailsRow;
|
||||
} // namespace Passport::Ui
|
||||
|
||||
namespace Passport {
|
||||
|
||||
class PanelController;
|
||||
struct ValueMap;
|
||||
struct ScanInfo;
|
||||
class EditScans;
|
||||
class PanelDetailsRow;
|
||||
enum class FileType;
|
||||
enum class PanelDetailsType;
|
||||
struct ScanListData;
|
||||
|
||||
struct EditDocumentScheme {
|
||||
|
@ -50,7 +54,7 @@ struct EditDocumentScheme {
|
|||
using Validator = Fn<std::optional<QString>(const QString &value)>;
|
||||
using Formatter = Fn<QString(const QString &value)>;
|
||||
ValueClass valueClass = ValueClass::Fields;
|
||||
PanelDetailsType inputType = PanelDetailsType();
|
||||
Ui::PanelDetailsType inputType = Ui::PanelDetailsType();
|
||||
QString key;
|
||||
QString label;
|
||||
Validator error;
|
||||
|
@ -140,7 +144,7 @@ private:
|
|||
const Scheme::Row &row,
|
||||
const ValueMap &fields,
|
||||
int maxLabelWidth);
|
||||
not_null<PanelDetailsRow*> findRow(const QString &key) const;
|
||||
not_null<Ui::PanelDetailsRow*> findRow(const QString &key) const;
|
||||
|
||||
not_null<PanelController*> _controller;
|
||||
Scheme _scheme;
|
||||
|
@ -151,7 +155,7 @@ private:
|
|||
|
||||
QPointer<EditScans> _editScans;
|
||||
QPointer<Ui::SlideWrap<Ui::FlatLabel>> _commonError;
|
||||
std::map<int, QPointer<PanelDetailsRow>> _details;
|
||||
std::map<int, QPointer<Ui::PanelDetailsRow>> _details;
|
||||
bool _fieldsChanged = 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_controller.h"
|
||||
#include "passport/passport_panel_details_row.h"
|
||||
#include "passport/ui/passport_details_row.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
#include "ui/widgets/labels.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_controller.h"
|
||||
#include "passport/ui/passport_form_row.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "boxes/abstract_box.h"
|
||||
#include "core/click_handler_types.h"
|
||||
|
@ -30,145 +31,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
|
||||
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(
|
||||
QWidget *parent,
|
||||
not_null<PanelController*> controller)
|
||||
|
|
|
@ -19,6 +19,11 @@ class FlatLabel;
|
|||
class UserpicButton;
|
||||
} // namespace Ui
|
||||
|
||||
namespace Passport::Ui {
|
||||
using namespace ::Ui;
|
||||
class FormRow;
|
||||
} // namespace Passport::Ui
|
||||
|
||||
namespace Passport {
|
||||
|
||||
class PanelController;
|
||||
|
@ -33,7 +38,7 @@ protected:
|
|||
void resizeEvent(QResizeEvent *e) override;
|
||||
|
||||
private:
|
||||
class Row;
|
||||
using Row = Ui::FormRow;
|
||||
|
||||
void setupControls();
|
||||
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:
|
||||
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 "base/platform/base_platform_info.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/checkbox.h"
|
||||
#include "ui/wrap/slide_wrap.h"
|
||||
#include "ui/countryinput.h"
|
||||
#include "main/main_session.h"
|
||||
#include "data/data_user.h"
|
||||
#include "ui/layers/box_content.h"
|
||||
#include "ui/boxes/country_select_box.h"
|
||||
#include "data/data_countries.h"
|
||||
#include "styles/style_layers.h"
|
||||
#include "styles/style_passport.h"
|
||||
|
||||
namespace Passport {
|
||||
#include <QtCore/QRegularExpression>
|
||||
|
||||
namespace Passport::Ui {
|
||||
namespace {
|
||||
|
||||
class PostcodeInput : public Ui::MaskedInputField {
|
||||
class PostcodeInput : public MaskedInputField {
|
||||
public:
|
||||
PostcodeInput(
|
||||
QWidget *parent,
|
||||
|
@ -103,7 +103,8 @@ class CountryRow : public PanelDetailsRow {
|
|||
public:
|
||||
CountryRow(
|
||||
QWidget *parent,
|
||||
not_null<PanelController*> controller,
|
||||
Fn<void(object_ptr<BoxContent>)> showBox,
|
||||
const QString &defaultCountry,
|
||||
const QString &label,
|
||||
int maxLabelWidth,
|
||||
const QString &value);
|
||||
|
@ -121,15 +122,16 @@ private:
|
|||
void toggleError(bool shown);
|
||||
void errorAnimationCallback();
|
||||
|
||||
not_null<PanelController*> _controller;
|
||||
object_ptr<Ui::LinkButton> _link;
|
||||
QString _defaultCountry;
|
||||
Fn<void(object_ptr<BoxContent>)> _showBox;
|
||||
object_ptr<LinkButton> _link;
|
||||
rpl::variable<QString> _value;
|
||||
bool _errorShown = false;
|
||||
Ui::Animations::Simple _errorAnimation;
|
||||
Animations::Simple _errorAnimation;
|
||||
|
||||
};
|
||||
|
||||
class DateInput final : public Ui::MaskedInputField {
|
||||
class DateInput final : public MaskedInputField {
|
||||
public:
|
||||
using MaskedInputField::MaskedInputField;
|
||||
|
||||
|
@ -191,21 +193,21 @@ private:
|
|||
int number(const object_ptr<DateInput> &field) const;
|
||||
|
||||
object_ptr<DateInput> _day;
|
||||
object_ptr<Ui::PaddingWrap<Ui::FlatLabel>> _separator1;
|
||||
object_ptr<PaddingWrap<FlatLabel>> _separator1;
|
||||
object_ptr<DateInput> _month;
|
||||
object_ptr<Ui::PaddingWrap<Ui::FlatLabel>> _separator2;
|
||||
object_ptr<PaddingWrap<FlatLabel>> _separator2;
|
||||
object_ptr<DateInput> _year;
|
||||
rpl::variable<QString> _value;
|
||||
|
||||
style::cursor _cursor = style::cur_default;
|
||||
Ui::Animations::Simple _a_borderShown;
|
||||
Animations::Simple _a_borderShown;
|
||||
int _borderAnimationStart = 0;
|
||||
Ui::Animations::Simple _a_borderOpacity;
|
||||
Animations::Simple _a_borderOpacity;
|
||||
bool _borderVisible = false;
|
||||
|
||||
Ui::Animations::Simple _a_error;
|
||||
Animations::Simple _a_error;
|
||||
bool _error = false;
|
||||
Ui::Animations::Simple _a_focused;
|
||||
Animations::Simple _a_focused;
|
||||
bool _focused = false;
|
||||
|
||||
};
|
||||
|
@ -238,18 +240,18 @@ private:
|
|||
void hideGenderError();
|
||||
void errorAnimationCallback();
|
||||
|
||||
std::unique_ptr<Ui::AbstractCheckView> createRadioView(
|
||||
Ui::RadioView* &weak) const;
|
||||
std::unique_ptr<AbstractCheckView> createRadioView(
|
||||
RadioView* &weak) const;
|
||||
|
||||
std::shared_ptr<Ui::RadioenumGroup<Gender>> _group;
|
||||
Ui::RadioView *_maleRadio = nullptr;
|
||||
Ui::RadioView *_femaleRadio = nullptr;
|
||||
object_ptr<Ui::Radioenum<Gender>> _male;
|
||||
object_ptr<Ui::Radioenum<Gender>> _female;
|
||||
std::shared_ptr<RadioenumGroup<Gender>> _group;
|
||||
RadioView *_maleRadio = nullptr;
|
||||
RadioView *_femaleRadio = nullptr;
|
||||
object_ptr<Radioenum<Gender>> _male;
|
||||
object_ptr<Radioenum<Gender>> _female;
|
||||
rpl::variable<QString> _value;
|
||||
|
||||
bool _errorShown = false;
|
||||
Ui::Animations::Simple _errorAnimation;
|
||||
Animations::Simple _errorAnimation;
|
||||
|
||||
};
|
||||
|
||||
|
@ -308,12 +310,14 @@ QString CountryString(const QString &code) {
|
|||
|
||||
CountryRow::CountryRow(
|
||||
QWidget *parent,
|
||||
not_null<PanelController*> controller,
|
||||
Fn<void(object_ptr<BoxContent>)> showBox,
|
||||
const QString &defaultCountry,
|
||||
const QString &label,
|
||||
int maxLabelWidth,
|
||||
const QString &value)
|
||||
: PanelDetailsRow(parent, label, maxLabelWidth)
|
||||
, _controller(controller)
|
||||
, _defaultCountry(defaultCountry)
|
||||
, _showBox(std::move(showBox))
|
||||
, _link(this, CountryString(value), st::boxLinkButton)
|
||||
, _value(value) {
|
||||
_value.changes(
|
||||
|
@ -380,20 +384,23 @@ void CountryRow::errorAnimationCallback() {
|
|||
void CountryRow::chooseCountry() {
|
||||
const auto top = _value.current();
|
||||
const auto name = Data::CountryNameByISO2(top);
|
||||
const auto isoByPhone = Data::CountryISO2ByPhone(
|
||||
_controller->bot()->session().user()->phone());
|
||||
const auto box = _controller->show(Box<CountrySelectBox>(!name.isEmpty()
|
||||
const auto country = !name.isEmpty()
|
||||
? top
|
||||
: !isoByPhone.isEmpty()
|
||||
? isoByPhone
|
||||
: Platform::SystemCountry(),
|
||||
CountrySelectBox::Type::Countries));
|
||||
connect(box, &CountrySelectBox::countryChosen, this, [=](QString iso) {
|
||||
: !_defaultCountry.isEmpty()
|
||||
? _defaultCountry
|
||||
: Platform::SystemCountry();
|
||||
auto box = Box<CountrySelectBox>(
|
||||
country,
|
||||
CountrySelectBox::Type::Countries);
|
||||
const auto raw = box.data();
|
||||
raw->countryChosen(
|
||||
) | rpl::start_with_next([=](QString iso) {
|
||||
_value = iso;
|
||||
_link->setText(CountryString(iso));
|
||||
hideCountryError();
|
||||
box->closeBox();
|
||||
});
|
||||
raw->closeBox();
|
||||
}, lifetime());
|
||||
_showBox(std::move(box));
|
||||
}
|
||||
|
||||
QDate ValidateDate(const QString &value) {
|
||||
|
@ -528,7 +535,7 @@ DateRow::DateRow(
|
|||
GetDay(value))
|
||||
, _separator1(
|
||||
this,
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
object_ptr<FlatLabel>(
|
||||
this,
|
||||
QString(" / "),
|
||||
st::passportDetailsSeparator),
|
||||
|
@ -540,7 +547,7 @@ DateRow::DateRow(
|
|||
GetMonth(value))
|
||||
, _separator2(
|
||||
this,
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
object_ptr<FlatLabel>(
|
||||
this,
|
||||
QString(" / "),
|
||||
st::passportDetailsSeparator),
|
||||
|
@ -552,7 +559,7 @@ DateRow::DateRow(
|
|||
GetYear(value))
|
||||
, _value(valueCurrent()) {
|
||||
const auto focused = [=](const object_ptr<DateInput> &field) {
|
||||
return [this, pointer = Ui::MakeWeak(field.data())]{
|
||||
return [this, pointer = MakeWeak(field.data())]{
|
||||
_borderAnimationStart = pointer->borderAnimationStart()
|
||||
+ pointer->x()
|
||||
- _day->x();
|
||||
|
@ -565,15 +572,15 @@ DateRow::DateRow(
|
|||
const auto changed = [=] {
|
||||
_value = valueCurrent();
|
||||
};
|
||||
connect(_day, &Ui::MaskedInputField::focused, focused(_day));
|
||||
connect(_month, &Ui::MaskedInputField::focused, focused(_month));
|
||||
connect(_year, &Ui::MaskedInputField::focused, focused(_year));
|
||||
connect(_day, &Ui::MaskedInputField::blurred, blurred);
|
||||
connect(_month, &Ui::MaskedInputField::blurred, blurred);
|
||||
connect(_year, &Ui::MaskedInputField::blurred, blurred);
|
||||
connect(_day, &Ui::MaskedInputField::changed, changed);
|
||||
connect(_month, &Ui::MaskedInputField::changed, changed);
|
||||
connect(_year, &Ui::MaskedInputField::changed, changed);
|
||||
connect(_day, &MaskedInputField::focused, focused(_day));
|
||||
connect(_month, &MaskedInputField::focused, focused(_month));
|
||||
connect(_year, &MaskedInputField::focused, focused(_year));
|
||||
connect(_day, &MaskedInputField::blurred, blurred);
|
||||
connect(_month, &MaskedInputField::blurred, blurred);
|
||||
connect(_year, &MaskedInputField::blurred, blurred);
|
||||
connect(_day, &MaskedInputField::changed, changed);
|
||||
connect(_month, &MaskedInputField::changed, changed);
|
||||
connect(_year, &MaskedInputField::changed, changed);
|
||||
_day->setMaxValue(31);
|
||||
_day->putNext() | rpl::start_with_next([=](QChar ch) {
|
||||
putNext(_month, ch);
|
||||
|
@ -845,8 +852,8 @@ GenderRow::GenderRow(
|
|||
const QString &value)
|
||||
: PanelDetailsRow(parent, label, maxLabelWidth)
|
||||
, _group(StringToGender(value).has_value()
|
||||
? std::make_shared<Ui::RadioenumGroup<Gender>>(*StringToGender(value))
|
||||
: std::make_shared<Ui::RadioenumGroup<Gender>>())
|
||||
? std::make_shared<RadioenumGroup<Gender>>(*StringToGender(value))
|
||||
: std::make_shared<RadioenumGroup<Gender>>())
|
||||
, _male(
|
||||
this,
|
||||
_group,
|
||||
|
@ -868,9 +875,9 @@ GenderRow::GenderRow(
|
|||
});
|
||||
}
|
||||
|
||||
std::unique_ptr<Ui::AbstractCheckView> GenderRow::createRadioView(
|
||||
Ui::RadioView* &weak) const {
|
||||
auto result = std::make_unique<Ui::RadioView>(st::defaultRadio, false);
|
||||
std::unique_ptr<AbstractCheckView> GenderRow::createRadioView(
|
||||
RadioView* &weak) const {
|
||||
auto result = std::make_unique<RadioView>(st::defaultRadio, false);
|
||||
weak = result.get();
|
||||
return result;
|
||||
}
|
||||
|
@ -959,8 +966,9 @@ PanelDetailsRow::PanelDetailsRow(
|
|||
|
||||
object_ptr<PanelDetailsRow> PanelDetailsRow::Create(
|
||||
QWidget *parent,
|
||||
Fn<void(object_ptr<BoxContent>)> showBox,
|
||||
const QString &defaultCountry,
|
||||
Type type,
|
||||
not_null<PanelController*> controller,
|
||||
const QString &label,
|
||||
int maxLabelWidth,
|
||||
const QString &value,
|
||||
|
@ -969,7 +977,7 @@ object_ptr<PanelDetailsRow> PanelDetailsRow::Create(
|
|||
auto result = [&]() -> object_ptr<PanelDetailsRow> {
|
||||
switch (type) {
|
||||
case Type::Text:
|
||||
return object_ptr<AbstractTextRow<Ui::InputField>>(
|
||||
return object_ptr<AbstractTextRow<InputField>>(
|
||||
parent,
|
||||
label,
|
||||
maxLabelWidth,
|
||||
|
@ -985,7 +993,8 @@ object_ptr<PanelDetailsRow> PanelDetailsRow::Create(
|
|||
case Type::Country:
|
||||
return object_ptr<CountryRow>(
|
||||
parent,
|
||||
controller,
|
||||
showBox,
|
||||
defaultCountry,
|
||||
label,
|
||||
maxLabelWidth,
|
||||
value);
|
||||
|
@ -1062,7 +1071,7 @@ void PanelDetailsRow::showError(std::optional<QString> error) {
|
|||
if (!_error) {
|
||||
_error.create(
|
||||
this,
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
object_ptr<FlatLabel>(
|
||||
this,
|
||||
*error,
|
||||
st::passportVerifyErrorLabel));
|
||||
|
@ -1122,4 +1131,4 @@ void PanelDetailsRow::paintEvent(QPaintEvent *e) {
|
|||
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/wrap/padding_wrap.h"
|
||||
#include "ui/widgets/labels.h"
|
||||
#include "boxes/abstract_box.h"
|
||||
|
||||
namespace Ui {
|
||||
class BoxContent;
|
||||
class InputField;
|
||||
class FlatLabel;
|
||||
template <typename Widget>
|
||||
class SlideWrap;
|
||||
} // namespace Ui
|
||||
|
||||
namespace Passport {
|
||||
namespace Passport::Ui {
|
||||
|
||||
class PanelController;
|
||||
using namespace ::Ui;
|
||||
|
||||
enum class PanelDetailsType {
|
||||
Text,
|
||||
|
@ -32,7 +32,7 @@ enum class PanelDetailsType {
|
|||
Gender,
|
||||
};
|
||||
|
||||
class PanelDetailsRow : public Ui::RpWidget {
|
||||
class PanelDetailsRow : public RpWidget {
|
||||
public:
|
||||
using Type = PanelDetailsType;
|
||||
|
||||
|
@ -43,8 +43,9 @@ public:
|
|||
|
||||
static object_ptr<PanelDetailsRow> Create(
|
||||
QWidget *parent,
|
||||
Fn<void(object_ptr<BoxContent>)> showBox,
|
||||
const QString &defaultCountry,
|
||||
Type type,
|
||||
not_null<PanelController*> controller,
|
||||
const QString &label,
|
||||
int maxLabelWidth,
|
||||
const QString &value,
|
||||
|
@ -74,11 +75,11 @@ private:
|
|||
|
||||
QString _label;
|
||||
int _maxLabelWidth = 0;
|
||||
object_ptr<Ui::SlideWrap<Ui::FlatLabel>> _error = { nullptr };
|
||||
object_ptr<SlideWrap<FlatLabel>> _error = { nullptr };
|
||||
bool _errorShown = 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) {
|
||||
v::match(update.data, [&](const FormReady &) {
|
||||
_panel->showForm(_form->invoice());
|
||||
}, [&](const FormError &error) {
|
||||
showForm();
|
||||
}, [&](const FormError &error) { // #TODO payments refactor errors
|
||||
handleFormError(error);
|
||||
}, [&](const ValidateError &error) {
|
||||
handleValidateError(error);
|
||||
}, [&](const SendError &error) {
|
||||
handleSendError(error);
|
||||
}, [&](const ValidateFinished &) {
|
||||
showForm();
|
||||
if (_submitState == SubmitState::Validation) {
|
||||
_submitState = SubmitState::Validated;
|
||||
panelSubmit();
|
||||
}
|
||||
}, [&](const VerificationNeeded &info) {
|
||||
if (_webviewWindow) {
|
||||
_webviewWindow->navigate(info.url);
|
||||
|
@ -130,8 +138,44 @@ void CheckoutProcess::handleFormError(const FormError &error) {
|
|||
} else if (type == u"INVOICE_ALREADY_PAID"_q) {
|
||||
|
||||
}
|
||||
App::wnd()->activate();
|
||||
Ui::Toast::Show("payments.getPaymentForm: " + type);
|
||||
if (_panel) {
|
||||
_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) {
|
||||
|
@ -150,8 +194,12 @@ void CheckoutProcess::handleSendError(const SendError &error) {
|
|||
} else if (type == u"BOT_PRECHECKOUT_FAILED"_q) {
|
||||
|
||||
}
|
||||
App::wnd()->activate();
|
||||
Ui::Toast::Show("payments.sendPaymentForm: " + type);
|
||||
if (_panel) {
|
||||
_panel->showToast("payments.sendPaymentForm: " + type);
|
||||
} else {
|
||||
App::wnd()->activate();
|
||||
Ui::Toast::Show("payments.sendPaymentForm: " + type);
|
||||
}
|
||||
}
|
||||
|
||||
void CheckoutProcess::panelRequestClose() {
|
||||
|
@ -176,6 +224,26 @@ void CheckoutProcess::panelCloseSure() {
|
|||
}
|
||||
|
||||
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>(
|
||||
_form->details().url,
|
||||
panelDelegate());
|
||||
|
@ -237,4 +305,62 @@ bool CheckoutProcess::panelWebviewNavigationAttempt(const QString &uri) {
|
|||
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
|
||||
|
|
|
@ -19,6 +19,7 @@ class Session;
|
|||
namespace Payments::Ui {
|
||||
class Panel;
|
||||
class WebviewWindow;
|
||||
enum class EditField;
|
||||
} // namespace Payments::Ui
|
||||
|
||||
namespace Payments {
|
||||
|
@ -27,6 +28,7 @@ class Form;
|
|||
struct FormUpdate;
|
||||
struct FormError;
|
||||
struct SendError;
|
||||
struct ValidateError;
|
||||
|
||||
class CheckoutProcess final
|
||||
: public base::has_weak_ptr
|
||||
|
@ -45,22 +47,44 @@ public:
|
|||
void requestActivate();
|
||||
|
||||
private:
|
||||
enum class SubmitState {
|
||||
None,
|
||||
Validation,
|
||||
Validated,
|
||||
Finishing,
|
||||
};
|
||||
[[nodiscard]] not_null<PanelDelegate*> panelDelegate();
|
||||
|
||||
void handleFormUpdate(const FormUpdate &update);
|
||||
void handleFormError(const FormError &error);
|
||||
void handleValidateError(const ValidateError &error);
|
||||
void handleSendError(const SendError &error);
|
||||
|
||||
void showForm();
|
||||
void showEditInformation(Ui::EditField field);
|
||||
void chooseShippingOption();
|
||||
|
||||
void panelRequestClose() override;
|
||||
void panelCloseSure() override;
|
||||
void panelSubmit() override;
|
||||
void panelWebviewMessage(const QJsonDocument &message) 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 std::unique_ptr<Form> _form;
|
||||
const std::unique_ptr<Ui::Panel> _panel;
|
||||
std::unique_ptr<Ui::WebviewWindow> _webviewWindow;
|
||||
SubmitState _submitState = SubmitState::None;
|
||||
|
||||
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
|
||||
|
||||
Form::Form(not_null<Main::Session*> session, FullMsgId itemId)
|
||||
: _session(session)
|
||||
, _api(&_session->mtp())
|
||||
, _msgId(itemId.msg) {
|
||||
requestForm();
|
||||
}
|
||||
|
||||
void Form::requestForm() {
|
||||
_session->api().request(MTPpayments_GetPaymentForm(
|
||||
_api.request(MTPpayments_GetPaymentForm(
|
||||
MTP_int(_msgId)
|
||||
)).done([=](const MTPpayments_PaymentForm &result) {
|
||||
result.match([&](const auto &data) {
|
||||
|
@ -69,18 +106,8 @@ void Form::processForm(const MTPDpayments_paymentForm &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{
|
||||
.prices = prices | ranges::to_vector,
|
||||
.prices = ParsePrices(data.vprices()),
|
||||
.currency = qs(data.vcurrency()),
|
||||
|
||||
.isNameRequested = data.is_name_requested(),
|
||||
|
@ -115,7 +142,7 @@ void Form::processDetails(const MTPDpayments_paymentForm &data) {
|
|||
|
||||
void Form::processSavedInformation(const MTPDpaymentRequestedInfo &data) {
|
||||
const auto address = data.vshipping_address();
|
||||
_savedInformation = Ui::SavedInformation{
|
||||
_savedInformation = Ui::RequestedInformation{
|
||||
.name = qs(data.vname().value_or_empty()),
|
||||
.phone = qs(data.vphone().value_or_empty()),
|
||||
.email = qs(data.vemail().value_or_empty()),
|
||||
|
@ -132,11 +159,17 @@ void Form::processSavedCredentials(
|
|||
}
|
||||
|
||||
void Form::send(const QByteArray &serializedCredentials) {
|
||||
_session->api().request(MTPpayments_SendPaymentForm(
|
||||
MTP_flags(0),
|
||||
using Flag = MTPpayments_SendPaymentForm::Flag;
|
||||
_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),
|
||||
MTPstring(), // requested_info_id
|
||||
MTPstring(), // shipping_option_id,
|
||||
MTP_string(_requestedInformationId),
|
||||
MTP_string(_shippingOptions.selectedId),
|
||||
MTP_inputPaymentCredentials(
|
||||
MTP_flags(0),
|
||||
MTP_dataJSON(MTP_bytes(serializedCredentials)))
|
||||
|
@ -151,4 +184,59 @@ void Form::send(const QByteArray &serializedCredentials) {
|
|||
}).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
|
||||
|
|
|
@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#pragma once
|
||||
|
||||
#include "payments/ui/payments_panel_data.h"
|
||||
#include "mtproto/sender.h"
|
||||
|
||||
namespace Main {
|
||||
class Session;
|
||||
|
@ -34,10 +35,16 @@ struct FormDetails {
|
|||
|
||||
struct FormReady {};
|
||||
|
||||
struct ValidateFinished {};
|
||||
|
||||
struct FormError {
|
||||
QString type;
|
||||
};
|
||||
|
||||
struct ValidateError {
|
||||
QString type;
|
||||
};
|
||||
|
||||
struct SendError {
|
||||
QString type;
|
||||
};
|
||||
|
@ -54,8 +61,10 @@ struct FormUpdate {
|
|||
std::variant<
|
||||
FormReady,
|
||||
FormError,
|
||||
ValidateError,
|
||||
SendError,
|
||||
VerificationNeeded,
|
||||
ValidateFinished,
|
||||
PaymentFinished> data;
|
||||
};
|
||||
|
||||
|
@ -69,17 +78,22 @@ public:
|
|||
[[nodiscard]] const FormDetails &details() const {
|
||||
return _details;
|
||||
}
|
||||
[[nodiscard]] const Ui::SavedInformation &savedInformation() const {
|
||||
[[nodiscard]] const Ui::RequestedInformation &savedInformation() const {
|
||||
return _savedInformation;
|
||||
}
|
||||
[[nodiscard]] const Ui::SavedCredentials &savedCredentials() const {
|
||||
return _savedCredentials;
|
||||
}
|
||||
[[nodiscard]] const Ui::ShippingOptions &shippingOptions() const {
|
||||
return _shippingOptions;
|
||||
}
|
||||
|
||||
[[nodiscard]] rpl::producer<FormUpdate> updates() const {
|
||||
return _updates.events();
|
||||
}
|
||||
|
||||
void validateInformation(const Ui::RequestedInformation &information);
|
||||
void setShippingOption(const QString &id);
|
||||
void send(const QByteArray &serializedCredentials);
|
||||
|
||||
private:
|
||||
|
@ -90,15 +104,23 @@ private:
|
|||
void processSavedInformation(const MTPDpaymentRequestedInfo &data);
|
||||
void processSavedCredentials(
|
||||
const MTPDpaymentSavedCredentialsCard &data);
|
||||
void processShippingOptions(const QVector<MTPShippingOption> &data);
|
||||
|
||||
const not_null<Main::Session*> _session;
|
||||
MTP::Sender _api;
|
||||
MsgId _msgId = 0;
|
||||
|
||||
Ui::Invoice _invoice;
|
||||
FormDetails _details;
|
||||
Ui::SavedInformation _savedInformation;
|
||||
Ui::RequestedInformation _savedInformation;
|
||||
Ui::SavedCredentials _savedCredentials;
|
||||
|
||||
Ui::RequestedInformation _validatedInformation;
|
||||
mtpRequestId _validateRequestId = 0;
|
||||
|
||||
Ui::ShippingOptions _shippingOptions;
|
||||
QString _requestedInformationId;
|
||||
|
||||
rpl::event_stream<FormUpdate> _updates;
|
||||
|
||||
};
|
||||
|
|
|
@ -8,3 +8,5 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
using "ui/basic.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_panel_delegate.h"
|
||||
#include "passport/ui/passport_form_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 "ui/text/format_values.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "styles/style_payments.h"
|
||||
#include "styles/style_passport.h"
|
||||
|
@ -19,24 +22,56 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
namespace Payments::Ui {
|
||||
|
||||
using namespace ::Ui;
|
||||
using namespace Passport::Ui;
|
||||
|
||||
class PanelDelegate;
|
||||
|
||||
FormSummary::FormSummary(
|
||||
QWidget *parent,
|
||||
const Invoice &invoice,
|
||||
const RequestedInformation ¤t,
|
||||
const ShippingOptions &options,
|
||||
not_null<PanelDelegate*> delegate)
|
||||
: _delegate(delegate)
|
||||
, _invoice(invoice)
|
||||
, _options(options)
|
||||
, _information(current)
|
||||
, _scroll(this, st::passportPanelScroll)
|
||||
, _topShadow(this)
|
||||
, _bottomShadow(this)
|
||||
, _submit(
|
||||
this,
|
||||
tr::lng_payments_pay_amount(lt_amount, rpl::single(QString("much"))),
|
||||
tr::lng_payments_pay_amount(
|
||||
lt_amount,
|
||||
rpl::single(computeTotalAmount())),
|
||||
st::passportPanelAuthorize) {
|
||||
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() {
|
||||
const auto inner = setupContent();
|
||||
|
||||
|
@ -64,6 +99,116 @@ not_null<Ui::RpWidget*> FormSummary::setupContent() {
|
|||
inner->resizeToWidth(width);
|
||||
}, 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;
|
||||
}
|
||||
|
||||
|
|
|
@ -28,6 +28,8 @@ public:
|
|||
FormSummary(
|
||||
QWidget *parent,
|
||||
const Invoice &invoice,
|
||||
const RequestedInformation ¤t,
|
||||
const ShippingOptions &options,
|
||||
not_null<PanelDelegate*> delegate);
|
||||
|
||||
private:
|
||||
|
@ -37,7 +39,13 @@ private:
|
|||
[[nodiscard]] not_null<Ui::RpWidget*> setupContent();
|
||||
void updateControlsGeometry();
|
||||
|
||||
[[nodiscard]] QString computeAmount(int64 amount) const;
|
||||
[[nodiscard]] QString computeTotalAmount() const;
|
||||
|
||||
const not_null<PanelDelegate*> _delegate;
|
||||
Invoice _invoice;
|
||||
ShippingOptions _options;
|
||||
RequestedInformation _information;
|
||||
object_ptr<ScrollArea> _scroll;
|
||||
object_ptr<FadeShadow> _topShadow;
|
||||
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_form_summary.h"
|
||||
#include "payments/ui/payments_edit_information.h"
|
||||
#include "payments/ui/payments_panel_delegate.h"
|
||||
#include "ui/widgets/separate_panel.h"
|
||||
#include "ui/boxes/single_choice_box.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "styles/style_payments.h"
|
||||
#include "styles/style_passport.h"
|
||||
|
@ -39,9 +41,63 @@ void Panel::requestActivate() {
|
|||
_widget->showAndActivate();
|
||||
}
|
||||
|
||||
void Panel::showForm(const Invoice &invoice) {
|
||||
void Panel::showForm(
|
||||
const Invoice &invoice,
|
||||
const RequestedInformation ¤t,
|
||||
const ShippingOptions &options) {
|
||||
_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
|
||||
|
|
|
@ -7,8 +7,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
*/
|
||||
#pragma once
|
||||
|
||||
#include "base/object_ptr.h"
|
||||
|
||||
namespace Ui {
|
||||
class SeparatePanel;
|
||||
class BoxContent;
|
||||
} // namespace Ui
|
||||
|
||||
namespace Payments::Ui {
|
||||
|
@ -17,6 +20,9 @@ using namespace ::Ui;
|
|||
|
||||
class PanelDelegate;
|
||||
struct Invoice;
|
||||
struct RequestedInformation;
|
||||
struct ShippingOptions;
|
||||
enum class EditField;
|
||||
|
||||
class Panel final {
|
||||
public:
|
||||
|
@ -25,7 +31,18 @@ public:
|
|||
|
||||
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:
|
||||
const not_null<PanelDelegate*> _delegate;
|
||||
|
|
|
@ -11,7 +11,7 @@ namespace Payments::Ui {
|
|||
|
||||
struct LabeledPrice {
|
||||
QString label;
|
||||
uint64 price = 0;
|
||||
int64 price = 0;
|
||||
};
|
||||
|
||||
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 {
|
||||
QString address1;
|
||||
QString address2;
|
||||
|
@ -52,9 +63,21 @@ struct Address {
|
|||
[[nodiscard]] explicit operator bool() const {
|
||||
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 phone;
|
||||
QString email;
|
||||
|
@ -69,6 +92,16 @@ struct SavedInformation {
|
|||
[[nodiscard]] explicit operator bool() const {
|
||||
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 {
|
||||
|
@ -83,4 +116,11 @@ struct SavedCredentials {
|
|||
}
|
||||
};
|
||||
|
||||
enum class EditField {
|
||||
ShippingInformation,
|
||||
Name,
|
||||
Email,
|
||||
Phone,
|
||||
};
|
||||
|
||||
} // namespace Payments::Ui
|
||||
|
|
|
@ -7,11 +7,21 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
*/
|
||||
#pragma once
|
||||
|
||||
#include "base/object_ptr.h"
|
||||
|
||||
class QJsonDocument;
|
||||
class QString;
|
||||
|
||||
namespace Ui {
|
||||
class BoxContent;
|
||||
} // namespace Ui
|
||||
|
||||
namespace Payments::Ui {
|
||||
|
||||
using namespace ::Ui;
|
||||
|
||||
struct RequestedInformation;
|
||||
|
||||
class PanelDelegate {
|
||||
public:
|
||||
virtual void panelRequestClose() = 0;
|
||||
|
@ -19,6 +29,16 @@ public:
|
|||
virtual void panelSubmit() = 0;
|
||||
virtual void panelWebviewMessage(const QJsonDocument &message) = 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
|
||||
|
|
|
@ -14,7 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "ui/widgets/checkbox.h"
|
||||
#include "ui/widgets/level_meter.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 "platform/platform_specific.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:
|
||||
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 "storage/localstorage.h"
|
||||
#include "mainwindow.h"
|
||||
#include "ui/widgets/checkbox.h"
|
||||
#include "ui/wrap/vertical_layout.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/multi_select.h"
|
||||
#include "ui/effects/ripple_animation.h"
|
||||
#include "ui/boxes/country_select_box.h"
|
||||
#include "data/data_countries.h"
|
||||
#include "base/qt_adapters.h"
|
||||
#include "styles/style_layers.h"
|
||||
|
@ -23,7 +24,8 @@ QString LastValidISO;
|
|||
|
||||
} // namespace
|
||||
|
||||
CountryInput::CountryInput(QWidget *parent, const style::InputField &st) : TWidget(parent)
|
||||
CountryInput::CountryInput(QWidget *parent, const style::InputField &st)
|
||||
: RpWidget(parent)
|
||||
, _st(st)
|
||||
, _text(tr::lng_country_code(tr::now)) {
|
||||
resize(_st.width, _st.heightMin);
|
||||
|
@ -91,8 +93,11 @@ void CountryInput::mouseMoveEvent(QMouseEvent *e) {
|
|||
void CountryInput::mousePressEvent(QMouseEvent *e) {
|
||||
mouseMoveEvent(e);
|
||||
if (_active) {
|
||||
auto box = Ui::show(Box<CountrySelectBox>());
|
||||
connect(box, SIGNAL(countryChosen(const QString&)), this, SLOT(onChooseCountry(const QString&)));
|
||||
auto box = Ui::show(Box<Ui::CountrySelectBox>());
|
||||
box->countryChosen(
|
||||
) | rpl::start_with_next([=](QString iso) {
|
||||
chooseCountry(iso);
|
||||
}, lifetime());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -125,7 +130,7 @@ void CountryInput::onChooseCode(const QString &code) {
|
|||
update();
|
||||
}
|
||||
|
||||
bool CountryInput::onChooseCountry(const QString &iso) {
|
||||
bool CountryInput::chooseCountry(const QString &iso) {
|
||||
Ui::hideLayer();
|
||||
|
||||
const auto &byISO2 = Data::CountriesByISO2();
|
||||
|
@ -146,349 +151,3 @@ bool CountryInput::onChooseCountry(const QString &iso) {
|
|||
void CountryInput::setText(const QString &newText) {
|
||||
_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
|
||||
|
||||
#include "boxes/abstract_box.h"
|
||||
#include "ui/rp_widget.h"
|
||||
#include "styles/style_widgets.h"
|
||||
|
||||
namespace Data {
|
||||
|
@ -19,19 +19,19 @@ class MultiSelect;
|
|||
class RippleAnimation;
|
||||
} // namespace Ui
|
||||
|
||||
class CountryInput : public TWidget {
|
||||
class CountryInput : public Ui::RpWidget {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
CountryInput(QWidget *parent, const style::InputField &st);
|
||||
|
||||
QString iso() const {
|
||||
[[nodiscard]] QString iso() const {
|
||||
return _chosenIso;
|
||||
}
|
||||
bool chooseCountry(const QString &country);
|
||||
|
||||
public Q_SLOTS:
|
||||
void onChooseCode(const QString &code);
|
||||
bool onChooseCountry(const QString &country);
|
||||
|
||||
Q_SIGNALS:
|
||||
void codeChanged(const QString &code);
|
||||
|
@ -53,94 +53,3 @@ private:
|
|||
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));
|
||||
}
|
||||
|
||||
QString FillAmountAndCurrency(uint64 amount, const QString ¤cy) {
|
||||
QString FillAmountAndCurrency(int64 amount, const QString ¤cy) {
|
||||
static const auto ShortCurrencyNames = QMap<QString, QString>{
|
||||
{ u"USD"_q, QString::fromUtf8("\x24") },
|
||||
{ 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 FillAmountAndCurrency(
|
||||
uint64 amount,
|
||||
int64 amount,
|
||||
const QString ¤cy);
|
||||
|
||||
[[nodiscard]] QString ComposeNameString(
|
||||
|
|
|
@ -12,6 +12,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "ui/effects/animations.h"
|
||||
#include "base/object_ptr.h"
|
||||
|
||||
#include <set>
|
||||
|
||||
namespace Ui {
|
||||
|
||||
class InputField;
|
||||
|
|
|
@ -52,6 +52,9 @@ PRIVATE
|
|||
core/mime_type.cpp
|
||||
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.h
|
||||
media/clip/media_clip_ffmpeg.cpp
|
||||
|
@ -61,6 +64,13 @@ PRIVATE
|
|||
media/clip/media_clip_reader.cpp
|
||||
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.h
|
||||
payments/ui/payments_panel.cpp
|
||||
|
@ -80,10 +90,14 @@ PRIVATE
|
|||
ui/boxes/calendar_box.h
|
||||
ui/boxes/choose_date_time.cpp
|
||||
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.h
|
||||
ui/boxes/report_box.cpp
|
||||
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.h
|
||||
ui/chat/attach/attach_album_preview.cpp
|
||||
|
|
Loading…
Add table
Reference in a new issue