From 824237deb3b05b4460ccbfc60ba64e04504330f7 Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 29 Nov 2024 14:18:30 +0400 Subject: [PATCH] Nice starref link box. --- Telegram/SourceFiles/data/data_user.cpp | 38 +- Telegram/SourceFiles/data/data_user.h | 5 + .../bot/starref/info_bot_starref_common.cpp | 399 +++++++++++++++++- .../bot/starref/info_bot_starref_common.h | 54 ++- .../starref/info_bot_starref_join_widget.cpp | 254 ----------- .../starref/info_bot_starref_setup_widget.cpp | 17 +- .../inline_bots/bot_attach_web_view.cpp | 81 +--- Telegram/SourceFiles/ui/effects/premium.style | 23 +- 8 files changed, 521 insertions(+), 350 deletions(-) diff --git a/Telegram/SourceFiles/data/data_user.cpp b/Telegram/SourceFiles/data/data_user.cpp index e73f5fecb..71d5d27cb 100644 --- a/Telegram/SourceFiles/data/data_user.cpp +++ b/Telegram/SourceFiles/data/data_user.cpp @@ -205,6 +205,16 @@ void UserData::setBusinessDetails(Data::BusinessDetails details) { session().changes().peerUpdated(this, UpdateFlag::BusinessDetails); } +void UserData::setStarRefProgram(StarRefProgram program) { + const auto info = botInfo.get(); + if (info && info->starRefProgram != program) { + info->starRefProgram = program; + session().changes().peerUpdated( + this, + Data::PeerUpdate::Flag::StarRefProgram); + } +} + ChannelId UserData::personalChannelId() const { return _personalChannelId; } @@ -600,20 +610,8 @@ void ApplyUserUpdate(not_null user, const MTPDuserFull &update) { } if (const auto info = user->botInfo.get()) { info->canManageEmojiStatus = update.is_bot_can_manage_emoji_status(); - auto starRefProgram = StarRefProgram(); - if (const auto program = update.vstarref_program()) { - const auto &data = program->data(); - starRefProgram.commission = data.vcommission_permille().v; - starRefProgram.durationMonths - = data.vduration_months().value_or_empty(); - starRefProgram.endDate = data.vend_date().value_or_empty(); - } - if (info->starRefProgram != starRefProgram) { - info->starRefProgram = starRefProgram; - user->session().changes().peerUpdated( - user, - Data::PeerUpdate::Flag::StarRefProgram); - } + user->setStarRefProgram( + Data::ParseStarRefProgram(update.vstarref_program())); } if (const auto pinned = update.vpinned_msg_id()) { SetTopPinnedMessageId(user, pinned->v); @@ -733,4 +731,16 @@ void ApplyUserUpdate(not_null user, const MTPDuserFull &update) { user->fullUpdated(); } +StarRefProgram ParseStarRefProgram(const MTPStarRefProgram *program) { + if (!program) { + return {}; + } + auto result = StarRefProgram(); + const auto &data = program->data(); + result.commission = data.vcommission_permille().v; + result.durationMonths = data.vduration_months().value_or_empty(); + result.endDate = data.vend_date().value_or_empty(); + return result; +} + } // namespace Data diff --git a/Telegram/SourceFiles/data/data_user.h b/Telegram/SourceFiles/data/data_user.h index f8d7ba48a..26256dedc 100644 --- a/Telegram/SourceFiles/data/data_user.h +++ b/Telegram/SourceFiles/data/data_user.h @@ -218,6 +218,8 @@ public: [[nodiscard]] const Data::BusinessDetails &businessDetails() const; void setBusinessDetails(Data::BusinessDetails details); + void setStarRefProgram(StarRefProgram program); + [[nodiscard]] ChannelId personalChannelId() const; [[nodiscard]] MsgId personalChannelMessageId() const; void setPersonalChannel(ChannelId channelId, MsgId messageId); @@ -265,4 +267,7 @@ namespace Data { void ApplyUserUpdate(not_null user, const MTPDuserFull &update); +[[nodiscard]] StarRefProgram ParseStarRefProgram( + const MTPStarRefProgram *program); + } // namespace Data diff --git a/Telegram/SourceFiles/info/bot/starref/info_bot_starref_common.cpp b/Telegram/SourceFiles/info/bot/starref/info_bot_starref_common.cpp index 2254b48b5..e8ccc9b11 100644 --- a/Telegram/SourceFiles/info/bot/starref/info_bot_starref_common.cpp +++ b/Telegram/SourceFiles/info/bot/starref/info_bot_starref_common.cpp @@ -7,17 +7,70 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #include "info/bot/starref/info_bot_starref_common.h" +#include "apiwrap.h" +#include "boxes/peers/replace_boost_box.h" // CreateUserpicsTransfer. +#include "data/data_session.h" +#include "lang/lang_keys.h" +#include "main/main_session.h" #include "settings/settings_common.h" +#include "ui/controls/userpic_button.h" +#include "ui/layers/generic_box.h" #include "ui/widgets/buttons.h" #include "ui/widgets/labels.h" #include "ui/wrap/vertical_layout.h" #include "ui/text/text_utilities.h" -//#include "styles/style_info.h" +#include "ui/painter.h" +#include "ui/vertical_list.h" +#include "styles/style_chat.h" #include "styles/style_layers.h" +#include "styles/style_premium.h" #include "styles/style_settings.h" -//#include "styles/style_premium.h" + +#include namespace Info::BotStarRef { +namespace { + +void ConnectStarRef( + not_null bot, + not_null peer, + Fn done, + Fn fail) { + bot->session().api().request(MTPpayments_ConnectStarRefBot( + peer->input, + bot->inputUser + )).done([=](const MTPpayments_ConnectedStarRefBots &result) { + const auto parsed = Parse(&bot->session(), result); + if (parsed.empty()) { + fail(u"EMPTY"_q); + } else { + done(parsed.front()); + } + }).fail([=](const MTP::Error &error) { + fail(error.type()); + }).send(); +} + +} // namespace + +QString FormatStarRefCommission(ushort commission) { + return QString::number(commission / 10.) + '%'; +} + +rpl::producer FormatProgramDuration( + StarRefProgram program) { + return !program.durationMonths + ? tr::lng_star_ref_one_about_for_forever(Ui::Text::RichLangValue) + : (program.durationMonths < 12) + ? tr::lng_star_ref_one_about_for_months( + lt_count, + rpl::single(program.durationMonths * 1.), + Ui::Text::RichLangValue) + : tr::lng_star_ref_one_about_for_years( + lt_count, + rpl::single((program.durationMonths / 12) * 1.), + Ui::Text::RichLangValue); +} not_null AddViewListButton( not_null parent, @@ -108,8 +161,346 @@ not_null AddViewListButton( return button; } -QString FormatStarRefCommission(ushort commission) { - return QString::number(commission / 10.) + '%'; +not_null AddFullWidthButton( + not_null box, + rpl::producer text, + Fn callback, + const style::RoundButton *stOverride) { + const auto &boxSt = box->getDelegate()->style(); + const auto result = box->addButton( + std::move(text), + std::move(callback), + stOverride ? *stOverride : boxSt.button); + rpl::combine( + box->widthValue(), + result->widthValue() + ) | rpl::start_with_next([=](int width, int buttonWidth) { + const auto correct = width + - boxSt.buttonPadding.left() + - boxSt.buttonPadding.right(); + if (correct > 0 && buttonWidth != correct) { + result->resizeToWidth(correct); + result->moveToLeft( + boxSt.buttonPadding.left(), + boxSt.buttonPadding.top(), + width); + } + }, result->lifetime()); + return result; +} + +void AddFullWidthButtonFooter( + not_null box, + not_null button, + rpl::producer text) { + const auto footer = Ui::CreateChild( + button->parentWidget(), + std::move(text), + st::starrefJoinFooter); + button->geometryValue() | rpl::start_with_next([=](QRect geometry) { + footer->resizeToWidth(geometry.width()); + const auto &st = box->getDelegate()->style(); + const auto top = geometry.y() + geometry.height(); + const auto available = st.buttonPadding.bottom(); + footer->moveToLeft( + geometry.left(), + top + (available - footer->height()) / 2); + }, footer->lifetime()); +} + +object_ptr StarRefLinkBox( + ConnectedBot row, + not_null peer) { + return Box([=](not_null box) { + const auto show = box->uiShow(); + + const auto bot = row.bot; + const auto program = row.state.program; + + box->setStyle(st::starrefFooterBox); + box->setNoContentMargin(true); + box->addTopButton(st::boxTitleClose, [=] { + box->closeBox(); + }); + + box->addRow( + CreateUserpicsTransfer( + box, + rpl::single(std::vector{ not_null(bot) }), + peer, + UserpicsTransferType::StarRefJoin), + st::boxRowPadding + st::starrefJoinUserpicsPadding); + box->addRow( + object_ptr>( + box, + object_ptr( + box, + tr::lng_star_ref_link_title(), + st::boxTitle)), + st::boxRowPadding + st::starrefJoinTitlePadding); + box->addRow( + object_ptr( + box, + (peer->isSelf() + ? tr::lng_star_ref_link_about_user + : peer->isUser() + ? tr::lng_star_ref_link_about_user + : tr::lng_star_ref_link_about_channel)( + lt_amount, + rpl::single(Ui::Text::Bold( + FormatStarRefCommission(program.commission))), + lt_app, + rpl::single(Ui::Text::Bold(bot->name())), + lt_duration, + FormatProgramDuration(program), + Ui::Text::WithEntities), + st::starrefCenteredText), + st::boxRowPadding); + + Ui::AddSkip(box->verticalLayout(), st::defaultVerticalListSkip * 4); + + box->addRow( + object_ptr( + box, + tr::lng_star_ref_link_recipient(), + st::starrefCenteredText)); + Ui::AddSkip(box->verticalLayout()); + box->addRow(object_ptr::fromRaw( + MakePeerBubbleButton(box, peer).release() + ))->setAttribute(Qt::WA_TransparentForMouseEvents); + + Ui::AddSkip(box->verticalLayout()); + row.state.link; + + const auto copy = [=] { + QApplication::clipboard()->setText(row.state.link); + box->uiShow()->showToast(tr::lng_username_copied(tr::now)); + box->closeBox(); + }; + const auto button = AddFullWidthButton( + box, + tr::lng_star_ref_link_copy(), + copy, + &st::starrefCopyButton); + const auto name = TextWithEntities{ bot->name() }; + AddFullWidthButtonFooter( + box, + button, + (row.state.users > 0 + ? tr::lng_star_ref_link_copy_users( + lt_count, + rpl::single(row.state.users * 1.), + lt_app, + rpl::single(name), + Ui::Text::WithEntities) + : tr::lng_star_ref_link_copy_none( + lt_app, + rpl::single(name), + Ui::Text::WithEntities))); + }); +} + +[[nodiscard]] object_ptr JoinStarRefBox( + ConnectedBot row, + not_null peer) { + Expects(row.bot->isUser()); + + return Box([=](not_null box) { + const auto show = box->uiShow(); + + const auto bot = row.bot; + const auto program = row.state.program; + + box->setStyle(st::starrefFooterBox); + box->setNoContentMargin(true); + box->addTopButton(st::boxTitleClose, [=] { + box->closeBox(); + }); + + box->addRow( + CreateUserpicsTransfer( + box, + rpl::single(std::vector{ not_null(bot) }), + peer, + UserpicsTransferType::StarRefJoin), + st::boxRowPadding + st::starrefJoinUserpicsPadding); + box->addRow( + object_ptr>( + box, + object_ptr( + box, + tr::lng_star_ref_title(), + st::boxTitle)), + st::boxRowPadding + st::starrefJoinTitlePadding); + box->addRow( + object_ptr( + box, + tr::lng_star_ref_one_about( + lt_app, + rpl::single(Ui::Text::Bold(bot->name())), + lt_amount, + rpl::single(Ui::Text::Bold( + FormatStarRefCommission(program.commission))), + lt_duration, + FormatProgramDuration(program), + Ui::Text::WithEntities), + st::starrefCenteredText), + st::boxRowPadding); + + Ui::AddSkip(box->verticalLayout(), st::defaultVerticalListSkip * 4); + + box->addRow( + object_ptr( + box, + tr::lng_star_ref_link_recipient(), + st::starrefCenteredText)); + Ui::AddSkip(box->verticalLayout()); + box->addRow(object_ptr::fromRaw( + MakePeerBubbleButton(box, peer).release() + ))->setAttribute(Qt::WA_TransparentForMouseEvents); + + struct State { + QPointer weak; + bool sent = false; + }; + const auto state = std::make_shared(); + state->weak = box; + + const auto send = [=] { + if (state->sent) { + return; + } + state->sent = true; + ConnectStarRef(bot->asUser(), peer, [=](ConnectedBot info) { + show->show(StarRefLinkBox(info, peer)); + if (const auto strong = state->weak.data()) { + strong->closeBox(); + } + }, [=](const QString &error) { + state->sent = false; + show->showToast(u"Failed: "_q + error); + }); + }; + const auto button = AddFullWidthButton( + box, + tr::lng_star_ref_one_join(), + send); + AddFullWidthButtonFooter( + box, + button, + tr::lng_star_ref_one_join_text( + lt_terms, + tr::lng_star_ref_button_link( + ) | Ui::Text::ToLink(tr::lng_star_ref_tos_url(tr::now)), + Ui::Text::WithEntities)); + }); +} + +std::unique_ptr MakePeerBubbleButton( + not_null parent, + not_null peer, + Ui::RpWidget *right) { + auto result = std::make_unique(parent); + + const auto size = st::chatGiveawayPeerSize; + const auto padding = st::chatGiveawayPeerPadding; + + const auto raw = result.get(); + + const auto width = raw->lifetime().make_state(); + const auto name = raw->lifetime().make_state( + raw, + rpl::single(peer->name()), + st::botEmojiStatusName); + const auto userpic = raw->lifetime().make_state( + raw, + peer, + st::botEmojiStatusUserpic); + name->setAttribute(Qt::WA_TransparentForMouseEvents); + userpic->setAttribute(Qt::WA_TransparentForMouseEvents); + + if (right) { + right->setParent(result.get()); + right->show(); + right->setAttribute(Qt::WA_TransparentForMouseEvents); + } + + auto rightWidth = right ? right->widthValue() : rpl::single(0); + + raw->resize(size, size); + rpl::combine( + raw->sizeValue(), + std::move(rightWidth) + ) | rpl::start_with_next([=](QSize outer, int rwidth) { + const auto full = outer.width(); + const auto decorations = size + + padding.left() + + padding.right() + + rwidth; + const auto inner = full - decorations; + const auto use = std::min(inner, name->textMaxWidth()); + *width = use + decorations; + const auto left = (full - *width) / 2; + if (inner > 0) { + userpic->moveToLeft(left, 0, outer.width()); + if (right) { + right->moveToLeft( + left + *width - padding.right() - right->width(), + padding.top(), + outer.width()); + } + name->resizeToWidth(use); + name->moveToLeft( + left + size + padding.left(), + padding.top(), + outer.width()); + } + }, raw->lifetime()); + raw->paintRequest() | rpl::start_with_next([=] { + auto p = QPainter(raw); + const auto left = (raw->width() - *width) / 2; + const auto skip = size / 2; + p.setClipRect(left + skip, 0, *width - skip, size); + auto hq = PainterHighQualityEnabler(p); + p.setPen(Qt::NoPen); + p.setBrush(st::windowBgOver); + p.drawRoundedRect(left, 0, *width, size, skip, skip); + }, raw->lifetime()); + + return result; +} +ConnectedBots Parse( + not_null session, + const MTPpayments_ConnectedStarRefBots &bots) { + const auto &data = bots.data(); + session->data().processUsers(data.vusers()); + const auto &list = data.vconnected_bots().v; + auto result = ConnectedBots(); + for (const auto &bot : list) { + const auto &data = bot.data(); + const auto botId = UserId(data.vbot_id()); + const auto link = qs(data.vurl()); + const auto date = data.vdate().v; + const auto commission = data.vcommission_permille().v; + const auto durationMonths + = data.vduration_months().value_or_empty(); + const auto users = int(data.vparticipants().v); + const auto revoked = data.is_revoked(); + result.push_back({ + .bot = session->data().user(botId), + .state = { + .program = { + .commission = ushort(commission), + .durationMonths = uchar(durationMonths), + }, + .link = link, + .date = date, + .users = users, + .revoked = revoked, + }, + }); + } + return result; } } // namespace Info::BotStarRef \ No newline at end of file diff --git a/Telegram/SourceFiles/info/bot/starref/info_bot_starref_common.h b/Telegram/SourceFiles/info/bot/starref/info_bot_starref_common.h index 10e31e872..6e6b33a5e 100644 --- a/Telegram/SourceFiles/info/bot/starref/info_bot_starref_common.h +++ b/Telegram/SourceFiles/info/bot/starref/info_bot_starref_common.h @@ -7,18 +7,70 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once +#include "base/object_ptr.h" +#include "data/data_user.h" + namespace Ui { class AbstractButton; +class RoundButton; class VerticalLayout; +class BoxContent; +class RpWidget; } // namespace Ui +namespace style { +struct RoundButton; +} // namespace style + namespace Info::BotStarRef { +struct ConnectedBotState { + StarRefProgram program; + QString link; + TimeId date = 0; + int users = 0; + bool revoked = false; +}; +struct ConnectedBot { + not_null bot; + ConnectedBotState state; +}; +using ConnectedBots = std::vector; + +[[nodiscard]] QString FormatStarRefCommission(ushort commission); +[[nodiscard]] rpl::producer FormatProgramDuration( + StarRefProgram program); + [[nodiscard]] not_null AddViewListButton( not_null parent, rpl::producer title, rpl::producer subtitle); -[[nodiscard]] QString FormatStarRefCommission(ushort commission); +[[nodiscard]] not_null AddFullWidthButton( + not_null box, + rpl::producer text, + Fn callback = nullptr, + const style::RoundButton *stOverride = nullptr); + +void AddFullWidthButtonFooter( + not_null box, + not_null button, + rpl::producer text); + +[[nodiscard]] object_ptr StarRefLinkBox( + ConnectedBot row, + not_null peer); +[[nodiscard]] object_ptr JoinStarRefBox( + ConnectedBot row, + not_null peer); + +std::unique_ptr MakePeerBubbleButton( + not_null parent, + not_null peer, + Ui::RpWidget *right = nullptr); + +[[nodiscard]] ConnectedBots Parse( + not_null session, + const MTPpayments_ConnectedStarRefBots &bots); } // namespace Info::BotStarRef diff --git a/Telegram/SourceFiles/info/bot/starref/info_bot_starref_join_widget.cpp b/Telegram/SourceFiles/info/bot/starref/info_bot_starref_join_widget.cpp index 10899173e..984e94f09 100644 --- a/Telegram/SourceFiles/info/bot/starref/info_bot_starref_join_widget.cpp +++ b/Telegram/SourceFiles/info/bot/starref/info_bot_starref_join_widget.cpp @@ -10,7 +10,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "apiwrap.h" #include "base/timer_rpl.h" #include "base/unixtime.h" -#include "boxes/peers/replace_boost_box.h" // CreateUserpicsTransfer. #include "boxes/peer_list_box.h" #include "core/click_handler_types.h" #include "data/data_session.h" @@ -56,19 +55,6 @@ enum class JoinType { Suggested, }; -struct ConnectedBotState { - StarRefProgram program; - QString link; - TimeId date = 0; - int users = 0; - bool revoked = false; -}; -struct ConnectedBot { - not_null bot; - ConnectedBotState state; -}; -using ConnectedBots = std::vector; - class ListController final : public PeerListController { public: ListController( @@ -108,246 +94,6 @@ private: }; -[[nodiscard]] ConnectedBots Parse( - not_null session, - const MTPpayments_ConnectedStarRefBots &bots) { - const auto &data = bots.data(); - session->data().processUsers(data.vusers()); - const auto &list = data.vconnected_bots().v; - auto result = ConnectedBots(); - for (const auto &bot : list) { - const auto &data = bot.data(); - const auto botId = UserId(data.vbot_id()); - const auto link = qs(data.vurl()); - const auto date = data.vdate().v; - const auto commission = data.vcommission_permille().v; - const auto durationMonths - = data.vduration_months().value_or_empty(); - const auto users = int(data.vparticipants().v); - const auto revoked = data.is_revoked(); - result.push_back({ - .bot = session->data().user(botId), - .state = { - .program = { - .commission = ushort(commission), - .durationMonths = uchar(durationMonths), - }, - .link = link, - .date = date, - .users = users, - .revoked = revoked, - }, - }); - } - return result; -} - -[[nodiscard]] rpl::producer FormatProgramDuration( - StarRefProgram program) { - return !program.durationMonths - ? tr::lng_star_ref_one_about_for_forever(Ui::Text::RichLangValue) - : (program.durationMonths < 12) - ? tr::lng_star_ref_one_about_for_months( - lt_count, - rpl::single(program.durationMonths * 1.), - Ui::Text::RichLangValue) - : tr::lng_star_ref_one_about_for_years( - lt_count, - rpl::single((program.durationMonths / 12) * 1.), - Ui::Text::RichLangValue); -} - -[[nodiscard]] not_null AddFullWidthButton( - not_null box, - rpl::producer text, - Fn callback = nullptr) { - const auto &boxSt = box->getDelegate()->style(); - const auto result = box->addButton(std::move(text), std::move(callback)); - rpl::combine( - box->widthValue(), - result->widthValue() - ) | rpl::start_with_next([=](int width, int buttonWidth) { - const auto correct = width - - boxSt.buttonPadding.left() - - boxSt.buttonPadding.right(); - if (correct > 0 && buttonWidth != correct) { - result->resizeToWidth(correct); - result->moveToLeft( - boxSt.buttonPadding.left(), - boxSt.buttonPadding.top(), - width); - } - }, result->lifetime()); - return result; -} - -[[nodiscard]] object_ptr StarRefLinkBox( - ConnectedBot row, - not_null peer) { - return Box([=](not_null box) { - box->setTitle(tr::lng_star_ref_link_title()); - - const auto bot = row.bot; - const auto program = row.state.program; - auto duration = !program.durationMonths - ? tr::lng_star_ref_one_about_for_forever(Ui::Text::RichLangValue) - : (program.durationMonths < 12) - ? tr::lng_star_ref_one_about_for_months( - lt_count, - rpl::single(program.durationMonths * 1.), - Ui::Text::RichLangValue) - : tr::lng_star_ref_one_about_for_years( - lt_count, - rpl::single((program.durationMonths / 12) * 1.), - Ui::Text::RichLangValue); - auto text = tr::lng_star_ref_link_about_channel( - lt_amount, - rpl::single(Ui::Text::Bold(FormatStarRefCommission(program.commission))), - lt_app, - rpl::single(Ui::Text::Bold(bot->name())), - lt_duration, - std::move(duration), - Ui::Text::WithEntities); - box->addRow( - object_ptr(box, std::move(text), st::boxLabel)); - Ui::AddSkip(box->verticalLayout()); - box->addRow( - object_ptr( - box, - rpl::single(row.state.link) | Ui::Text::ToLink(), - st::boxLabel)); - Ui::AddSkip(box->verticalLayout()); - if (row.state.users > 0) { - box->addRow( - object_ptr( - box, - tr::lng_star_ref_link_copy_users( - lt_count, - rpl::single(row.state.users * 1.), - lt_app, - rpl::single(bot->name())), - st::boxLabel)); - } else { - box->addRow( - object_ptr( - box, - tr::lng_star_ref_link_copy_none( - lt_app, - rpl::single(bot->name())), - st::boxLabel)); - } - - box->addButton(tr::lng_star_ref_link_copy(), [=] { - QApplication::clipboard()->setText(row.state.link); - box->uiShow()->showToast(tr::lng_username_copied(tr::now)); - }); - box->addButton(tr::lng_cancel(), [=] { - box->closeBox(); - }); - }); -} - -[[nodiscard]] object_ptr JoinStarRefBox( - ConnectedBot row, - not_null peer) { - Expects(row.bot->isUser()); - - return Box([=](not_null box) { - const auto show = box->uiShow(); - - const auto bot = row.bot; - const auto program = row.state.program; - - box->setStyle(st::starrefFooterBox); - box->setNoContentMargin(true); - box->addTopButton(st::boxTitleClose, [=] { - box->closeBox(); - }); - - box->addRow( - CreateUserpicsTransfer( - box, - rpl::single(std::vector{ not_null(bot) }), - peer, - UserpicsTransferType::StarRefJoin), - st::boxRowPadding + st::starrefJoinUserpicsPadding); - box->addRow( - object_ptr>( - box, - object_ptr( - box, - tr::lng_star_ref_title(), - st::boxTitle)), - st::boxRowPadding + st::starrefJoinTitlePadding); - box->addRow( - object_ptr( - box, - tr::lng_star_ref_one_about( - lt_app, - rpl::single(Ui::Text::Bold(bot->name())), - lt_amount, - rpl::single(Ui::Text::Bold( - FormatStarRefCommission(program.commission))), - lt_duration, - FormatProgramDuration(program), - Ui::Text::WithEntities), - st::boxLabel), - st::boxRowPadding); - struct State { - QPointer weak; - bool sent = false; - }; - const auto state = std::make_shared(); - state->weak = box; - - const auto send = [=] { - if (state->sent) { - return; - } - state->sent = true; - bot->session().api().request(MTPpayments_ConnectStarRefBot( - peer->input, - bot->asUser()->inputUser - )).done([=](const MTPpayments_ConnectedStarRefBots &result) { - const auto parsed = Parse(&bot->session(), result); - if (parsed.empty()) { - state->sent = false; - show->showToast(u"Failed."_q); - } else { - show->show(StarRefLinkBox(parsed.front(), peer)); - if (const auto strong = state->weak.data()) { - strong->closeBox(); - } - } - }).fail([=](const MTP::Error &error) { - state->sent = false; - show->showToast(u"Failed: "_q + error.type()); - }).send(); - }; - const auto button = AddFullWidthButton( - box, - tr::lng_star_ref_one_join(), - send); - const auto footer = Ui::CreateChild( - button->parentWidget(), - tr::lng_star_ref_one_join_text( - lt_terms, - tr::lng_star_ref_button_link( - ) | Ui::Text::ToLink(tr::lng_star_ref_tos_url(tr::now)), - Ui::Text::WithEntities), - st::starrefJoinFooter); - button->geometryValue() | rpl::start_with_next([=](QRect geometry) { - footer->resizeToWidth(geometry.width()); - const auto &st = box->getDelegate()->style(); - const auto top = geometry.y() + geometry.height(); - const auto available = st.buttonPadding.bottom(); - footer->moveToLeft( - geometry.left(), - top + (available - footer->height()) / 2); - }, footer->lifetime()); - }); -} - ListController::ListController( not_null controller, not_null peer, diff --git a/Telegram/SourceFiles/info/bot/starref/info_bot_starref_setup_widget.cpp b/Telegram/SourceFiles/info/bot/starref/info_bot_starref_setup_widget.cpp index cdf269d79..87e402ffd 100644 --- a/Telegram/SourceFiles/info/bot/starref/info_bot_starref_setup_widget.cpp +++ b/Telegram/SourceFiles/info/bot/starref/info_bot_starref_setup_widget.cpp @@ -579,16 +579,15 @@ void InnerWidget::setupEnd() { user->inputUser, MTP_int(0), MTP_int(0) - )).done([=] { - user->botInfo->starRefProgram.commission = 0; - user->botInfo->starRefProgram.durationMonths = 0; - user->updateFullForced(); - if (weak) { - _controller->showToast("Removed!"); - _controller->showBackFromStack(); + )).done([=](const MTPStarRefProgram &result) { + user->setStarRefProgram(Data::ParseStarRefProgram(&result)); + if (const auto controller = weak ? _controller.get() : nullptr) { + const auto window = controller->parentController(); + controller->showBackFromStack(); + window->showToast("Removed!"); } - }).fail(crl::guard(weak, [=] { - _controller->showToast("Remove failed!"); + }).fail(crl::guard(weak, [=](const MTP::Error &error) { + _controller->showToast(u"Failed: "_q + error.type()); })).send(); }); Ui::AddSkip(_container); diff --git a/Telegram/SourceFiles/inline_bots/bot_attach_web_view.cpp b/Telegram/SourceFiles/inline_bots/bot_attach_web_view.cpp index a23d3b53f..cf50857b1 100644 --- a/Telegram/SourceFiles/inline_bots/bot_attach_web_view.cpp +++ b/Telegram/SourceFiles/inline_bots/bot_attach_web_view.cpp @@ -41,6 +41,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/stickers/data_stickers.h" #include "history/history.h" #include "history/history_item.h" +#include "info/bot/starref/info_bot_starref_common.h" // MakePeerBubbleButton #include "info/profile/info_profile_values.h" #include "inline_bots/inline_bot_result.h" #include "inline_bots/inline_bot_confirm_prepared.h" @@ -444,74 +445,32 @@ std::unique_ptr MakeEmojiSetStatusPreview( not_null parent, not_null peer, not_null document) { - auto result = std::make_unique(parent); - - const auto size = st::chatGiveawayPeerSize; - const auto padding = st::chatGiveawayPeerPadding; - - const auto raw = result.get(); - - const auto width = raw->lifetime().make_state(); - const auto name = raw->lifetime().make_state( - raw, - rpl::single(peer->name()), - st::botEmojiStatusName); const auto makeContext = [=](Fn update) { return Core::MarkedTextContext{ .session = &peer->session(), .customEmojiRepaint = update, }; }; - const auto emoji = raw->lifetime().make_state( - raw, - rpl::single( - Ui::Text::SingleCustomEmoji( - Data::SerializeCustomEmojiId(document->id), - document->sticker() ? document->sticker()->alt : QString())), - st::botEmojiStatusEmoji, - st::defaultPopupMenu, - makeContext); - const auto userpic = raw->lifetime().make_state( - raw, + const auto emoji = Ui::CreateChild>( + parent, + object_ptr( + parent, + rpl::single( + Ui::Text::SingleCustomEmoji( + Data::SerializeCustomEmojiId(document->id), + (document->sticker() + ? document->sticker()->alt + : QString()))), + st::botEmojiStatusEmoji, + st::defaultPopupMenu, + makeContext), + style::margins(st::normalFont->spacew, 0, 0, 0)); + + auto result = Info::BotStarRef::MakePeerBubbleButton( + parent, peer, - st::botEmojiStatusUserpic); - - raw->resize(size, size); - raw->sizeValue() | rpl::start_with_next([=](QSize outer) { - const auto full = outer.width(); - const auto decorations = size - + padding.left() - + padding.right() - + emoji->width() - + st::normalFont->spacew; - const auto inner = full - decorations; - const auto use = std::min(inner, name->textMaxWidth()); - *width = use + decorations; - const auto left = (full - *width) / 2; - if (inner > 0) { - userpic->moveToLeft(left, 0, outer.width()); - emoji->moveToLeft( - left + *width - padding.right() - emoji->width(), - padding.top(), - outer.width()); - name->resizeToWidth(use); - name->moveToLeft( - left + size + padding.left(), - padding.top(), - outer.width()); - } - }, raw->lifetime()); - raw->paintRequest() | rpl::start_with_next([=] { - auto p = QPainter(raw); - const auto left = (raw->width() - *width) / 2; - const auto skip = size / 2; - p.setClipRect(left + skip, 0, *width - skip, size); - auto hq = PainterHighQualityEnabler(p); - p.setPen(Qt::NoPen); - p.setBrush(st::windowBgOver); - p.drawRoundedRect(left, 0, *width, size, skip, skip); - }, raw->lifetime()); - + emoji); + result->setAttribute(Qt::WA_TransparentForMouseEvents); return result; } diff --git a/Telegram/SourceFiles/ui/effects/premium.style b/Telegram/SourceFiles/ui/effects/premium.style index 5946ac357..4141a8d1a 100644 --- a/Telegram/SourceFiles/ui/effects/premium.style +++ b/Telegram/SourceFiles/ui/effects/premium.style @@ -81,24 +81,33 @@ starrefCover: PremiumCover(userPremiumCover) { titlePadding: margins(0px, 12px, 0px, 11px); } starrefCoverHeight: 180px; +starrefFooterButton: RoundButton(defaultActiveButton) { + height: 42px; + textTop: 12px; + style: semiboldTextStyle; +} starrefFooterBox: Box(defaultBox) { buttonPadding: margins(22px, 11px, 22px, 54px); buttonHeight: 42px; - button: RoundButton(defaultActiveButton) { - height: 42px; - textTop: 12px; - style: semiboldTextStyle; - } + button: starrefFooterButton; shadowIgnoreTopSkip: true; } +starrefCopyButton: RoundButton(starrefFooterButton) { + icon: icon {{ "info/edit/links_copy", activeButtonFg }}; + iconOver: icon {{ "info/edit/links_copy", activeButtonFgOver }}; + iconPosition: point(-1px, 5px); +} + starrefJoinIcon: icon{{ "payments/small_star", premiumButtonFg }}; starrefJoinUserpicsPadding: margins(0px, 32px, 0px, 10px); starrefJoinTitlePadding: margins(0px, 0px, 0px, 12px); -starrefJoinFooter: FlatLabel(defaultFlatLabel) { - textFg: windowSubTextFg; +starrefCenteredText: FlatLabel(defaultFlatLabel) { align: align(top); minWidth: 240px; } +starrefJoinFooter: FlatLabel(starrefCenteredText) { + textFg: windowSubTextFg; +} defaultPremiumBoxLabel: FlatLabel(defaultFlatLabel) { minWidth: 220px;