mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-16 06:07:06 +02:00
Nice starref link box.
This commit is contained in:
parent
ca8c70cc95
commit
824237deb3
8 changed files with 521 additions and 350 deletions
|
@ -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<UserData*> 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<UserData*> 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
|
||||
|
|
|
@ -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<UserData*> user, const MTPDuserFull &update);
|
||||
|
||||
[[nodiscard]] StarRefProgram ParseStarRefProgram(
|
||||
const MTPStarRefProgram *program);
|
||||
|
||||
} // namespace Data
|
||||
|
|
|
@ -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 <QtWidgets/QApplication>
|
||||
|
||||
namespace Info::BotStarRef {
|
||||
namespace {
|
||||
|
||||
void ConnectStarRef(
|
||||
not_null<UserData*> bot,
|
||||
not_null<PeerData*> peer,
|
||||
Fn<void(ConnectedBot)> done,
|
||||
Fn<void(const QString &)> 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<TextWithEntities> 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<Ui::AbstractButton*> AddViewListButton(
|
||||
not_null<Ui::VerticalLayout*> parent,
|
||||
|
@ -108,8 +161,346 @@ not_null<Ui::AbstractButton*> AddViewListButton(
|
|||
return button;
|
||||
}
|
||||
|
||||
QString FormatStarRefCommission(ushort commission) {
|
||||
return QString::number(commission / 10.) + '%';
|
||||
not_null<Ui::RoundButton*> AddFullWidthButton(
|
||||
not_null<Ui::BoxContent*> box,
|
||||
rpl::producer<QString> text,
|
||||
Fn<void()> 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<Ui::BoxContent*> box,
|
||||
not_null<Ui::RpWidget*> button,
|
||||
rpl::producer<TextWithEntities> text) {
|
||||
const auto footer = Ui::CreateChild<Ui::FlatLabel>(
|
||||
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<Ui::BoxContent> StarRefLinkBox(
|
||||
ConnectedBot row,
|
||||
not_null<PeerData*> peer) {
|
||||
return Box([=](not_null<Ui::GenericBox*> 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<PeerData*>(bot) }),
|
||||
peer,
|
||||
UserpicsTransferType::StarRefJoin),
|
||||
st::boxRowPadding + st::starrefJoinUserpicsPadding);
|
||||
box->addRow(
|
||||
object_ptr<Ui::CenterWrap<Ui::FlatLabel>>(
|
||||
box,
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
box,
|
||||
tr::lng_star_ref_link_title(),
|
||||
st::boxTitle)),
|
||||
st::boxRowPadding + st::starrefJoinTitlePadding);
|
||||
box->addRow(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
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<Ui::FlatLabel>(
|
||||
box,
|
||||
tr::lng_star_ref_link_recipient(),
|
||||
st::starrefCenteredText));
|
||||
Ui::AddSkip(box->verticalLayout());
|
||||
box->addRow(object_ptr<Ui::AbstractButton>::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<Ui::BoxContent> JoinStarRefBox(
|
||||
ConnectedBot row,
|
||||
not_null<PeerData*> peer) {
|
||||
Expects(row.bot->isUser());
|
||||
|
||||
return Box([=](not_null<Ui::GenericBox*> 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<PeerData*>(bot) }),
|
||||
peer,
|
||||
UserpicsTransferType::StarRefJoin),
|
||||
st::boxRowPadding + st::starrefJoinUserpicsPadding);
|
||||
box->addRow(
|
||||
object_ptr<Ui::CenterWrap<Ui::FlatLabel>>(
|
||||
box,
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
box,
|
||||
tr::lng_star_ref_title(),
|
||||
st::boxTitle)),
|
||||
st::boxRowPadding + st::starrefJoinTitlePadding);
|
||||
box->addRow(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
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<Ui::FlatLabel>(
|
||||
box,
|
||||
tr::lng_star_ref_link_recipient(),
|
||||
st::starrefCenteredText));
|
||||
Ui::AddSkip(box->verticalLayout());
|
||||
box->addRow(object_ptr<Ui::AbstractButton>::fromRaw(
|
||||
MakePeerBubbleButton(box, peer).release()
|
||||
))->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||
|
||||
struct State {
|
||||
QPointer<Ui::GenericBox> weak;
|
||||
bool sent = false;
|
||||
};
|
||||
const auto state = std::make_shared<State>();
|
||||
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<Ui::AbstractButton> MakePeerBubbleButton(
|
||||
not_null<QWidget*> parent,
|
||||
not_null<PeerData*> peer,
|
||||
Ui::RpWidget *right) {
|
||||
auto result = std::make_unique<Ui::AbstractButton>(parent);
|
||||
|
||||
const auto size = st::chatGiveawayPeerSize;
|
||||
const auto padding = st::chatGiveawayPeerPadding;
|
||||
|
||||
const auto raw = result.get();
|
||||
|
||||
const auto width = raw->lifetime().make_state<int>();
|
||||
const auto name = raw->lifetime().make_state<Ui::FlatLabel>(
|
||||
raw,
|
||||
rpl::single(peer->name()),
|
||||
st::botEmojiStatusName);
|
||||
const auto userpic = raw->lifetime().make_state<Ui::UserpicButton>(
|
||||
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<Main::Session*> 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
|
|
@ -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<UserData*> bot;
|
||||
ConnectedBotState state;
|
||||
};
|
||||
using ConnectedBots = std::vector<ConnectedBot>;
|
||||
|
||||
[[nodiscard]] QString FormatStarRefCommission(ushort commission);
|
||||
[[nodiscard]] rpl::producer<TextWithEntities> FormatProgramDuration(
|
||||
StarRefProgram program);
|
||||
|
||||
[[nodiscard]] not_null<Ui::AbstractButton*> AddViewListButton(
|
||||
not_null<Ui::VerticalLayout*> parent,
|
||||
rpl::producer<QString> title,
|
||||
rpl::producer<QString> subtitle);
|
||||
|
||||
[[nodiscard]] QString FormatStarRefCommission(ushort commission);
|
||||
[[nodiscard]] not_null<Ui::RoundButton*> AddFullWidthButton(
|
||||
not_null<Ui::BoxContent*> box,
|
||||
rpl::producer<QString> text,
|
||||
Fn<void()> callback = nullptr,
|
||||
const style::RoundButton *stOverride = nullptr);
|
||||
|
||||
void AddFullWidthButtonFooter(
|
||||
not_null<Ui::BoxContent*> box,
|
||||
not_null<Ui::RpWidget*> button,
|
||||
rpl::producer<TextWithEntities> text);
|
||||
|
||||
[[nodiscard]] object_ptr<Ui::BoxContent> StarRefLinkBox(
|
||||
ConnectedBot row,
|
||||
not_null<PeerData*> peer);
|
||||
[[nodiscard]] object_ptr<Ui::BoxContent> JoinStarRefBox(
|
||||
ConnectedBot row,
|
||||
not_null<PeerData*> peer);
|
||||
|
||||
std::unique_ptr<Ui::AbstractButton> MakePeerBubbleButton(
|
||||
not_null<QWidget*> parent,
|
||||
not_null<PeerData*> peer,
|
||||
Ui::RpWidget *right = nullptr);
|
||||
|
||||
[[nodiscard]] ConnectedBots Parse(
|
||||
not_null<Main::Session*> session,
|
||||
const MTPpayments_ConnectedStarRefBots &bots);
|
||||
|
||||
} // namespace Info::BotStarRef
|
||||
|
|
|
@ -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<UserData*> bot;
|
||||
ConnectedBotState state;
|
||||
};
|
||||
using ConnectedBots = std::vector<ConnectedBot>;
|
||||
|
||||
class ListController final : public PeerListController {
|
||||
public:
|
||||
ListController(
|
||||
|
@ -108,246 +94,6 @@ private:
|
|||
|
||||
};
|
||||
|
||||
[[nodiscard]] ConnectedBots Parse(
|
||||
not_null<Main::Session*> 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<TextWithEntities> 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<Ui::RoundButton*> AddFullWidthButton(
|
||||
not_null<Ui::BoxContent*> box,
|
||||
rpl::producer<QString> text,
|
||||
Fn<void()> 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<Ui::BoxContent> StarRefLinkBox(
|
||||
ConnectedBot row,
|
||||
not_null<PeerData*> peer) {
|
||||
return Box([=](not_null<Ui::GenericBox*> 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<Ui::FlatLabel>(box, std::move(text), st::boxLabel));
|
||||
Ui::AddSkip(box->verticalLayout());
|
||||
box->addRow(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
box,
|
||||
rpl::single(row.state.link) | Ui::Text::ToLink(),
|
||||
st::boxLabel));
|
||||
Ui::AddSkip(box->verticalLayout());
|
||||
if (row.state.users > 0) {
|
||||
box->addRow(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
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<Ui::FlatLabel>(
|
||||
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<Ui::BoxContent> JoinStarRefBox(
|
||||
ConnectedBot row,
|
||||
not_null<PeerData*> peer) {
|
||||
Expects(row.bot->isUser());
|
||||
|
||||
return Box([=](not_null<Ui::GenericBox*> 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<PeerData*>(bot) }),
|
||||
peer,
|
||||
UserpicsTransferType::StarRefJoin),
|
||||
st::boxRowPadding + st::starrefJoinUserpicsPadding);
|
||||
box->addRow(
|
||||
object_ptr<Ui::CenterWrap<Ui::FlatLabel>>(
|
||||
box,
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
box,
|
||||
tr::lng_star_ref_title(),
|
||||
st::boxTitle)),
|
||||
st::boxRowPadding + st::starrefJoinTitlePadding);
|
||||
box->addRow(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
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<Ui::GenericBox> weak;
|
||||
bool sent = false;
|
||||
};
|
||||
const auto state = std::make_shared<State>();
|
||||
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<Ui::FlatLabel>(
|
||||
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*> controller,
|
||||
not_null<PeerData*> peer,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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<Ui::RpWidget> MakeEmojiSetStatusPreview(
|
|||
not_null<QWidget*> parent,
|
||||
not_null<PeerData*> peer,
|
||||
not_null<DocumentData*> document) {
|
||||
auto result = std::make_unique<Ui::RpWidget>(parent);
|
||||
|
||||
const auto size = st::chatGiveawayPeerSize;
|
||||
const auto padding = st::chatGiveawayPeerPadding;
|
||||
|
||||
const auto raw = result.get();
|
||||
|
||||
const auto width = raw->lifetime().make_state<int>();
|
||||
const auto name = raw->lifetime().make_state<Ui::FlatLabel>(
|
||||
raw,
|
||||
rpl::single(peer->name()),
|
||||
st::botEmojiStatusName);
|
||||
const auto makeContext = [=](Fn<void()> update) {
|
||||
return Core::MarkedTextContext{
|
||||
.session = &peer->session(),
|
||||
.customEmojiRepaint = update,
|
||||
};
|
||||
};
|
||||
const auto emoji = raw->lifetime().make_state<Ui::FlatLabel>(
|
||||
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<Ui::UserpicButton>(
|
||||
raw,
|
||||
const auto emoji = Ui::CreateChild<Ui::PaddingWrap<Ui::FlatLabel>>(
|
||||
parent,
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Add table
Reference in a new issue