"Add {bot}" button in existing starrefs list.

This commit is contained in:
John Preston 2024-12-02 17:11:08 +04:00
parent 1e14667006
commit 82cec83d87
7 changed files with 128 additions and 30 deletions

View file

@ -1641,6 +1641,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_star_ref_duration_about" = "Set the duration for which affiliates will earn commissions from referred users."; "lng_star_ref_duration_about" = "Set the duration for which affiliates will earn commissions from referred users.";
"lng_star_ref_existing_title" = "View existing programs"; "lng_star_ref_existing_title" = "View existing programs";
"lng_star_ref_existing_about" = "Explore what other mini apps offer."; "lng_star_ref_existing_about" = "Explore what other mini apps offer.";
"lng_star_ref_add_bot" = "Add {bot}";
"lng_star_ref_end" = "End Affiliate Program"; "lng_star_ref_end" = "End Affiliate Program";
"lng_star_ref_start" = "Start Affiliate Program"; "lng_star_ref_start" = "Start Affiliate Program";
"lng_star_ref_start_disabled" = "Available in {time}"; "lng_star_ref_start_disabled" = "Available in {time}";
@ -1690,6 +1691,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
"lng_star_ref_one_about_for_months#other" = "for **{count} months**"; "lng_star_ref_one_about_for_months#other" = "for **{count} months**";
"lng_star_ref_one_about_for_years#one" = "for **{count} year**"; "lng_star_ref_one_about_for_years#one" = "for **{count} year**";
"lng_star_ref_one_about_for_years#other" = "for **{count} years**"; "lng_star_ref_one_about_for_years#other" = "for **{count} years**";
"lng_star_ref_one_daily_revenue" = "Daily revenue per user: {amount}";
"lng_star_ref_one_join" = "Join Program"; "lng_star_ref_one_join" = "Join Program";
"lng_star_ref_one_join_text" = "By joining this program, you agree to the {terms} of Affiliate Programs."; "lng_star_ref_one_join_text" = "By joining this program, you agree to the {terms} of Affiliate Programs.";
"lng_star_ref_joined_title" = "Program joined"; "lng_star_ref_joined_title" = "Program joined";

View file

@ -739,6 +739,9 @@ StarRefProgram ParseStarRefProgram(const MTPStarRefProgram *program) {
const auto &data = program->data(); const auto &data = program->data();
result.commission = data.vcommission_permille().v; result.commission = data.vcommission_permille().v;
result.durationMonths = data.vduration_months().value_or_empty(); result.durationMonths = data.vduration_months().value_or_empty();
result.revenuePerUser = data.vdaily_revenue_per_user()
? Data::FromTL(*data.vdaily_revenue_per_user())
: StarsAmount();
result.endDate = data.vend_date().value_or_empty(); result.endDate = data.vend_date().value_or_empty();
return result; return result;
} }

View file

@ -7,6 +7,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/ */
#pragma once #pragma once
#include "core/stars_amount.h"
#include "data/data_birthday.h" #include "data/data_birthday.h"
#include "data/data_peer.h" #include "data/data_peer.h"
#include "data/data_chat_participant_status.h" #include "data/data_chat_participant_status.h"
@ -20,6 +21,7 @@ struct BusinessDetails;
} // namespace Data } // namespace Data
struct StarRefProgram { struct StarRefProgram {
StarsAmount revenuePerUser;
TimeId endDate = 0; TimeId endDate = 0;
ushort commission = 0; ushort commission = 0;
uint8 durationMonths = 0; uint8 durationMonths = 0;

View file

@ -9,7 +9,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "apiwrap.h" #include "apiwrap.h"
#include "boxes/peers/replace_boost_box.h" // CreateUserpicsTransfer. #include "boxes/peers/replace_boost_box.h" // CreateUserpicsTransfer.
#include "boxes/send_credits_box.h" // Ui::CreditsEmoji.
#include "chat_helpers/stickers_lottie.h" #include "chat_helpers/stickers_lottie.h"
#include "core/ui_integration.h"
#include "data/data_document.h" #include "data/data_document.h"
#include "data/data_session.h" #include "data/data_session.h"
#include "history/view/media/history_view_sticker.h" #include "history/view/media/history_view_sticker.h"
@ -499,8 +501,32 @@ object_ptr<Ui::BoxContent> StarRefLinkBox(
st::starrefCenteredText), st::starrefCenteredText),
st::boxRowPadding); st::boxRowPadding);
Ui::AddSkip(box->verticalLayout(), st::defaultVerticalListSkip * 4); Ui::AddSkip(box->verticalLayout(), st::defaultVerticalListSkip * 3);
if (const auto average = program.revenuePerUser) {
const auto layout = box->verticalLayout();
const auto session = &peer->session();
const auto makeContext = [session](Fn<void()> update) {
return Core::MarkedTextContext{
.session = session,
.customEmojiRepaint = std::move(update),
};
};
auto text = Ui::Text::Colorized(Ui::CreditsEmoji(session));
text.append(Lang::FormatStarsAmountDecimal(average));
layout->add(
object_ptr<Ui::FlatLabel>(
box,
tr::lng_star_ref_one_daily_revenue(
lt_amount,
rpl::single(Ui::Text::Wrapped(text, EntityType::Bold)),
Ui::Text::WithEntities),
st::starrefRevenueText,
st::defaultPopupMenu,
makeContext),
st::boxRowPadding);
Ui::AddSkip(layout, st::defaultVerticalListSkip);
}
#if 0
box->addRow( box->addRow(
object_ptr<Ui::FlatLabel>( object_ptr<Ui::FlatLabel>(
box, box,
@ -510,6 +536,7 @@ object_ptr<Ui::BoxContent> StarRefLinkBox(
box->addRow(object_ptr<Ui::AbstractButton>::fromRaw( box->addRow(object_ptr<Ui::AbstractButton>::fromRaw(
MakePeerBubbleButton(box, peer).release() MakePeerBubbleButton(box, peer).release()
))->setAttribute(Qt::WA_TransparentForMouseEvents); ))->setAttribute(Qt::WA_TransparentForMouseEvents);
#endif
struct State { struct State {
QPointer<Ui::GenericBox> weak; QPointer<Ui::GenericBox> weak;

View file

@ -57,6 +57,7 @@ constexpr auto kPerPage = 50;
enum class JoinType { enum class JoinType {
Joined, Joined,
Suggested, Suggested,
Existing,
}; };
class ListController final class ListController final
@ -79,12 +80,14 @@ public:
[[nodiscard]] rpl::producer<int> rowCountValue() const; [[nodiscard]] rpl::producer<int> rowCountValue() const;
[[nodiscard]] rpl::producer<ConnectedBot> connected() const; [[nodiscard]] rpl::producer<ConnectedBot> connected() const;
[[nodiscard]] rpl::producer<> addForBotRequests() const;
void process(ConnectedBot row); void process(ConnectedBot row);
private: private:
[[nodiscard]] std::unique_ptr<PeerListRow> createRow(ConnectedBot bot); [[nodiscard]] std::unique_ptr<PeerListRow> createRow(ConnectedBot bot);
void open(not_null<UserData*> bot, ConnectedBotState state); void open(not_null<UserData*> bot, ConnectedBotState state);
void setupAddForBot();
const not_null<Window::SessionController*> _controller; const not_null<Window::SessionController*> _controller;
const not_null<PeerData*> _peer; const not_null<PeerData*> _peer;
@ -95,6 +98,7 @@ private:
UserData *_openOnResolve = nullptr; UserData *_openOnResolve = nullptr;
rpl::event_stream<ConnectedBot> _connected; rpl::event_stream<ConnectedBot> _connected;
rpl::event_stream<> _addForBot;
mtpRequestId _requestId = 0; mtpRequestId _requestId = 0;
TimeId _offsetDate = 0; TimeId _offsetDate = 0;
@ -171,15 +175,16 @@ void ListController::loadMoreRows() {
return; return;
} else if (_type == JoinType::Joined) { } else if (_type == JoinType::Joined) {
using Flag = MTPpayments_GetConnectedStarRefBots::Flag; using Flag = MTPpayments_GetConnectedStarRefBots::Flag;
_requestId = session().api().request(MTPpayments_GetConnectedStarRefBots( _requestId = session().api().request(
MTP_flags(Flag() MTPpayments_GetConnectedStarRefBots(
| (_offsetDate ? Flag::f_offset_date : Flag()) MTP_flags(Flag()
| (_offsetThing.isEmpty() ? Flag() : Flag::f_offset_link)), | (_offsetDate ? Flag::f_offset_date : Flag())
_peer->input, | (_offsetThing.isEmpty() ? Flag() : Flag::f_offset_link)),
MTP_int(_offsetDate), _peer->input,
MTP_string(_offsetThing), MTP_int(_offsetDate),
MTP_int(kPerPage) MTP_string(_offsetThing),
)).done([=](const MTPpayments_ConnectedStarRefBots &result) { MTP_int(kPerPage))
).done([=](const MTPpayments_ConnectedStarRefBots &result) {
const auto parsed = Parse(&session(), result); const auto parsed = Parse(&session(), result);
if (parsed.empty()) { if (parsed.empty()) {
_allLoaded = true; _allLoaded = true;
@ -195,6 +200,9 @@ void ListController::loadMoreRows() {
_requestId = 0; _requestId = 0;
}).send(); }).send();
} else { } else {
if (_type == JoinType::Existing) {
setDescriptionText(tr::lng_contacts_loading(tr::now));
}
using Flag = MTPpayments_GetSuggestedStarRefBots::Flag; using Flag = MTPpayments_GetSuggestedStarRefBots::Flag;
_requestId = session().api().request( _requestId = session().api().request(
MTPpayments_GetSuggestedStarRefBots( MTPpayments_GetSuggestedStarRefBots(
@ -203,6 +211,9 @@ void ListController::loadMoreRows() {
MTP_string(_offsetThing), MTP_string(_offsetThing),
MTP_int(kPerPage)) MTP_int(kPerPage))
).done([=](const MTPpayments_SuggestedStarRefBots &result) { ).done([=](const MTPpayments_SuggestedStarRefBots &result) {
setDescriptionText(QString());
setupAddForBot();
const auto &data = result.data(); const auto &data = result.data();
if (data.vnext_offset()) { if (data.vnext_offset()) {
_offsetThing = qs(*data.vnext_offset()); _offsetThing = qs(*data.vnext_offset());
@ -210,20 +221,13 @@ void ListController::loadMoreRows() {
_allLoaded = true; _allLoaded = true;
} }
session().data().processUsers(data.vusers()); session().data().processUsers(data.vusers());
for (const auto &bot : data.vsuggested_bots().v) { for (const auto &program : data.vsuggested_bots().v) {
const auto &data = bot.data(); const auto botId = UserId(program.data().vbot_id());
const auto botId = UserId(data.vbot_id());
const auto commission = data.vcommission_permille().v;
const auto durationMonths
= data.vduration_months().value_or_empty();
const auto user = session().data().user(botId); const auto user = session().data().user(botId);
delegate()->peerListAppendRow(createRow({ delegate()->peerListAppendRow(createRow({
.bot = user, .bot = user,
.state = { .state = {
.program = { .program = Data::ParseStarRefProgram(&program),
.commission = ushort(commission),
.durationMonths = uchar(durationMonths),
},
.unresolved = true, .unresolved = true,
}, },
})); }));
@ -238,6 +242,46 @@ void ListController::loadMoreRows() {
} }
} }
void ListController::setupAddForBot() {
const auto user = _peer->asUser();
if (_type != JoinType::Existing
|| !user
|| !user->isBot()
|| user->botInfo->starRefProgram.commission > 0) {
return;
}
auto button = object_ptr<Ui::PaddingWrap<Ui::SettingsButton>>(
nullptr,
object_ptr<Ui::SettingsButton>(
nullptr,
tr::lng_star_ref_add_bot(lt_bot, rpl::single(user->name())),
st::inviteViaLinkButton),
style::margins(0, st::membersMarginTop, 0, 0));
const auto icon = Ui::CreateChild<Info::Profile::FloatingIcon>(
button->entity(),
st::starrefAddForBotIcon,
QPoint());
button->entity()->heightValue(
) | rpl::start_with_next([=](int height) {
icon->moveToLeft(
st::starrefAddForBotIconPosition.x(),
(height - st::starrefAddForBotIcon.height()) / 2);
}, icon->lifetime());
button->entity()->setClickedCallback([=] {
_addForBot.fire({});
});
button->entity()->events(
) | rpl::filter([=](not_null<QEvent*> e) {
return (e->type() == QEvent::Enter);
}) | rpl::start_with_next([=] {
delegate()->peerListMouseLeftGeometry();
}, button->lifetime());
delegate()->peerListSetAboveWidget(std::move(button));
delegate()->peerListRefreshRows();
}
rpl::producer<int> ListController::rowCountValue() const { rpl::producer<int> ListController::rowCountValue() const {
return _rowCount.value(); return _rowCount.value();
} }
@ -246,6 +290,10 @@ rpl::producer<ConnectedBot> ListController::connected() const {
return _connected.events(); return _connected.events();
} }
rpl::producer<> ListController::addForBotRequests() const {
return _addForBot.events();
}
void ListController::process(ConnectedBot row) { void ListController::process(ConnectedBot row) {
if (!delegate()->peerListFindRow(PeerListRowId(row.bot->id.value))) { if (!delegate()->peerListFindRow(PeerListRowId(row.bot->id.value))) {
delegate()->peerListPrependRow(createRow(row)); delegate()->peerListPrependRow(createRow(row));
@ -720,19 +768,27 @@ std::shared_ptr<Info::Memento> Make(not_null<PeerData*> peer) {
} }
object_ptr<Ui::BoxContent> ProgramsListBox( object_ptr<Ui::BoxContent> ProgramsListBox(
not_null<Window::SessionController*> controller, not_null<Window::SessionController*> window,
not_null<PeerData*> peer) { not_null<PeerData*> peer) {
const auto weak = std::make_shared<QPointer<PeerListBox>>();
const auto initBox = [=](not_null<PeerListBox*> box) { const auto initBox = [=](not_null<PeerListBox*> box) {
*weak = box;
box->addButton(tr::lng_close(), [=] { box->addButton(tr::lng_close(), [=] {
box->closeBox(); box->closeBox();
}); });
}; };
return Box<PeerListBox>(
std::make_unique<ListController>( auto controller = std::make_unique<ListController>(
controller, window,
peer, peer,
JoinType::Suggested), JoinType::Existing);
initBox); controller->addForBotRequests() | rpl::start_with_next([=] {
if (const auto strong = weak->data()) {
strong->closeBox();
}
}, controller->lifetime());
return Box<PeerListBox>(std::move(controller), initBox);
} }
} // namespace Info::BotStarRef::Join } // namespace Info::BotStarRef::Join

View file

@ -83,7 +83,7 @@ private:
[[nodiscard]] std::shared_ptr<Info::Memento> Make(not_null<PeerData*> peer); [[nodiscard]] std::shared_ptr<Info::Memento> Make(not_null<PeerData*> peer);
[[nodiscard]] object_ptr<Ui::BoxContent> ProgramsListBox( [[nodiscard]] object_ptr<Ui::BoxContent> ProgramsListBox(
not_null<Window::SessionController*> controller, not_null<Window::SessionController*> window,
not_null<PeerData*> peer); not_null<PeerData*> peer);
} // namespace Info::BotStarRef::Join } // namespace Info::BotStarRef::Join

View file

@ -441,6 +441,11 @@ starrefCenteredText: FlatLabel(defaultFlatLabel) {
starrefJoinFooter: FlatLabel(starrefCenteredText) { starrefJoinFooter: FlatLabel(starrefCenteredText) {
textFg: windowSubTextFg; textFg: windowSubTextFg;
} }
starrefRevenueText: FlatLabel(starrefCenteredText) {
palette: TextPalette(defaultTextPalette) {
linkFg: creditsBg1;
}
}
starrefInfoIconPosition: point(16px, 8px); starrefInfoIconPosition: point(16px, 8px);
starrefBottomButton: RoundButton(defaultActiveButton) { starrefBottomButton: RoundButton(defaultActiveButton) {
@ -469,4 +474,7 @@ starrefLinkCountAdd: 6px;
starrefLinkCountIcon: icon{{ "chat/mini_subscribers", historyPeerUserpicFg }}; starrefLinkCountIcon: icon{{ "chat/mini_subscribers", historyPeerUserpicFg }};
starrefLinkCountIconPosition: point(0px, 1px); starrefLinkCountIconPosition: point(0px, 1px);
starrefLinkCountFont: font(10px bold); starrefLinkCountFont: font(10px bold);
starrefLinkCountPadding: margins(2px, 0px, 3px, 1px); starrefLinkCountPadding: margins(2px, 0px, 3px, 1px);
starrefAddForBotIcon: icon {{ "menu/bot_add", lightButtonFg }};
starrefAddForBotIconPosition: point(23px, 2px);