mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-18 07:07:08 +02:00
Initial starref programs list implementation.
This commit is contained in:
parent
1e15764bb9
commit
62d2346471
21 changed files with 1073 additions and 154 deletions
Telegram
CMakeLists.txt
SourceFiles
boxes/peers
core
info
bot/starref
info_bot_starref_join_widget.cppinfo_bot_starref_join_widget.hinfo_bot_starref_setup_widget.cppinfo_bot_starref_setup_widget.h
channel_statistics/earn
info_content_widget.cppinfo_content_widget.hinfo_controller.cppinfo_controller.hinfo_wrap_widget.cppsettings
ui/effects
window
|
@ -918,8 +918,10 @@ PRIVATE
|
|||
info/bot/earn/info_bot_earn_list.h
|
||||
info/bot/earn/info_bot_earn_widget.cpp
|
||||
info/bot/earn/info_bot_earn_widget.h
|
||||
info/bot/starref/info_bot_starref_widget.cpp
|
||||
info/bot/starref/info_bot_starref_widget.h
|
||||
info/bot/starref/info_bot_starref_join_widget.cpp
|
||||
info/bot/starref/info_bot_starref_join_widget.h
|
||||
info/bot/starref/info_bot_starref_setup_widget.cpp
|
||||
info/bot/starref/info_bot_starref_setup_widget.h
|
||||
info/channel_statistics/boosts/create_giveaway_box.cpp
|
||||
info/channel_statistics/boosts/create_giveaway_box.h
|
||||
info/channel_statistics/boosts/giveaway/giveaway_list_controllers.cpp
|
||||
|
|
|
@ -46,7 +46,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "data/data_user.h"
|
||||
#include "history/admin_log/history_admin_log_section.h"
|
||||
#include "info/bot/earn/info_bot_earn_widget.h"
|
||||
#include "info/bot/starref/info_bot_starref_widget.h"
|
||||
#include "info/bot/starref/info_bot_starref_setup_widget.h"
|
||||
#include "info/channel_statistics/boosts/info_boosts_widget.h"
|
||||
#include "info/channel_statistics/earn/earn_format.h"
|
||||
#include "info/channel_statistics/earn/earn_icons.h"
|
||||
|
@ -1726,7 +1726,7 @@ void Controller::fillBotAffiliateProgram() {
|
|||
? user->botInfo->starRefProgram.commission
|
||||
: 0;
|
||||
return commission
|
||||
? u"%1%"_q.arg(commission)
|
||||
? u"%1%"_q.arg(commission / 10.)
|
||||
: tr::lng_manage_peer_bot_star_ref_off(tr::now);
|
||||
});
|
||||
AddButtonWithCount(
|
||||
|
@ -1734,7 +1734,7 @@ void Controller::fillBotAffiliateProgram() {
|
|||
tr::lng_manage_peer_bot_star_ref(),
|
||||
std::move(label),
|
||||
[controller = _navigation->parentController(), user] {
|
||||
controller->showSection(Info::BotStarRef::Make(user));
|
||||
controller->showSection(Info::BotStarRef::Setup::Make(user));
|
||||
},
|
||||
{ &st::menuIconSharing });
|
||||
}
|
||||
|
|
|
@ -555,8 +555,13 @@ bool ResolveUsernameOrPhone(
|
|||
auto resolveType = params.contains(u"profile"_q)
|
||||
? ResolveType::Profile
|
||||
: ResolveType::Default;
|
||||
auto starref = params.value(u"ref"_q);
|
||||
auto startToken = params.value(u"start"_q);
|
||||
if (!startToken.isEmpty()) {
|
||||
const auto kTgRefPrefix = u"_tgref_"_q;
|
||||
if (startToken.startsWith(kTgRefPrefix)) {
|
||||
startToken = startToken.mid(kTgRefPrefix.size());
|
||||
resolveType = ResolveType::StarRef;
|
||||
} else if (!startToken.isEmpty()) {
|
||||
resolveType = ResolveType::BotStart;
|
||||
} else if (params.contains(u"startgroup"_q)) {
|
||||
resolveType = ResolveType::AddToGroup;
|
||||
|
@ -565,6 +570,8 @@ bool ResolveUsernameOrPhone(
|
|||
resolveType = ResolveType::AddToChannel;
|
||||
} else if (params.contains(u"boost"_q)) {
|
||||
resolveType = ResolveType::Boost;
|
||||
} else if (!starref.isEmpty()) {
|
||||
resolveType = ResolveType::StarRef;
|
||||
}
|
||||
auto post = ShowAtUnreadMsgId;
|
||||
auto adminRights = ChatAdminRights();
|
||||
|
|
|
@ -0,0 +1,767 @@
|
|||
/*
|
||||
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 "info/bot/starref/info_bot_starref_join_widget.h"
|
||||
|
||||
#include "apiwrap.h"
|
||||
#include "base/timer_rpl.h"
|
||||
#include "base/unixtime.h"
|
||||
#include "boxes/peer_list_box.h"
|
||||
#include "core/click_handler_types.h"
|
||||
#include "data/data_session.h"
|
||||
#include "data/data_user.h"
|
||||
#include "info/profile/info_profile_icon.h"
|
||||
#include "info/info_controller.h"
|
||||
#include "info/info_memento.h"
|
||||
#include "lang/lang_keys.h"
|
||||
#include "main/main_session.h"
|
||||
#include "settings/settings_common.h"
|
||||
#include "ui/boxes/confirm_box.h"
|
||||
#include "ui/effects/premium_top_bar.h"
|
||||
#include "ui/layers/generic_box.h"
|
||||
#include "ui/text/text_utilities.h"
|
||||
#include "ui/widgets/buttons.h"
|
||||
#include "ui/widgets/continuous_sliders.h"
|
||||
#include "ui/widgets/labels.h"
|
||||
#include "ui/widgets/popup_menu.h"
|
||||
#include "ui/wrap/fade_wrap.h"
|
||||
#include "ui/wrap/slide_wrap.h"
|
||||
#include "ui/wrap/vertical_layout.h"
|
||||
#include "ui/ui_utility.h"
|
||||
#include "ui/vertical_list.h"
|
||||
#include "styles/style_info.h"
|
||||
#include "styles/style_layers.h"
|
||||
#include "styles/style_menu_icons.h"
|
||||
#include "styles/style_premium.h"
|
||||
#include "styles/style_settings.h"
|
||||
|
||||
#include <QApplication>
|
||||
|
||||
namespace Info::BotStarRef::Join {
|
||||
namespace {
|
||||
|
||||
constexpr auto kPerPage = 50;
|
||||
|
||||
enum class JoinType {
|
||||
Joined,
|
||||
Suggested,
|
||||
};
|
||||
|
||||
class ListController final : public PeerListController {
|
||||
public:
|
||||
ListController(
|
||||
not_null<Controller*> controller,
|
||||
not_null<PeerData*> peer,
|
||||
JoinType type);
|
||||
~ListController();
|
||||
|
||||
Main::Session &session() const override;
|
||||
void prepare() override;
|
||||
void rowClicked(not_null<PeerListRow*> row) override;
|
||||
base::unique_qptr<Ui::PopupMenu> rowContextMenu(
|
||||
QWidget *parent,
|
||||
not_null<PeerListRow*> row) override;
|
||||
void loadMoreRows() override;
|
||||
|
||||
[[nodiscard]] rpl::producer<int> rowCountValue() const;
|
||||
|
||||
//std::unique_ptr<PeerListRow> createRestoredRow(
|
||||
// not_null<PeerData*> peer) override {
|
||||
// return createRow(peer);
|
||||
//}
|
||||
|
||||
//std::unique_ptr<PeerListState> saveState() const override;
|
||||
//void restoreState(std::unique_ptr<PeerListState> state) override;
|
||||
|
||||
void setContentWidget(not_null<Ui::RpWidget*> widget);
|
||||
[[nodiscard]] rpl::producer<int> unlockHeightValue() const;
|
||||
|
||||
private:
|
||||
struct RowState {
|
||||
StarRefProgram program;
|
||||
QString link;
|
||||
int users = 0;
|
||||
};
|
||||
|
||||
[[nodiscard]] std::unique_ptr<PeerListRow> createRow(
|
||||
not_null<PeerData*> peer,
|
||||
RowState state);
|
||||
void showLink(not_null<PeerData*> peer, RowState state);
|
||||
|
||||
struct SavedState : SavedStateBase {
|
||||
};
|
||||
const not_null<Controller*> _controller;
|
||||
const not_null<PeerData*> _peer;
|
||||
const JoinType _type = {};
|
||||
|
||||
base::flat_map<not_null<PeerData*>, RowState> _states;
|
||||
|
||||
mtpRequestId _requestId = 0;
|
||||
TimeId _offsetDate = 0;
|
||||
QString _offsetThing;
|
||||
bool _allLoaded = false;
|
||||
|
||||
rpl::variable<int> _rowCount = 0;
|
||||
|
||||
};
|
||||
|
||||
ListController::ListController(
|
||||
not_null<Controller*> controller,
|
||||
not_null<PeerData*> peer,
|
||||
JoinType type)
|
||||
: PeerListController()
|
||||
, _controller(controller)
|
||||
, _peer(peer)
|
||||
, _type(type) {
|
||||
}
|
||||
|
||||
ListController::~ListController() {
|
||||
if (_requestId) {
|
||||
session().api().request(_requestId).cancel();
|
||||
}
|
||||
}
|
||||
|
||||
Main::Session &ListController::session() const {
|
||||
return _peer->session();
|
||||
}
|
||||
|
||||
std::unique_ptr<PeerListRow> ListController::createRow(
|
||||
not_null<PeerData*> peer,
|
||||
RowState state) {
|
||||
_states.emplace(peer, state);
|
||||
auto result = std::make_unique<PeerListRow>(peer);
|
||||
const auto program = state.program;
|
||||
const auto duration = !program.durationMonths
|
||||
? tr::lng_star_ref_duration_forever(tr::now)
|
||||
: (program.durationMonths < 12)
|
||||
? tr::lng_months(tr::now, lt_count, program.durationMonths)
|
||||
: tr::lng_years(tr::now, lt_count, program.durationMonths / 12);
|
||||
result->setCustomStatus(u"+%1%, %2"_q.arg(
|
||||
QString::number(program.commission / 10.),
|
||||
duration));
|
||||
return result;
|
||||
}
|
||||
|
||||
void ListController::prepare() {
|
||||
delegate()->peerListSetTitle((_type == JoinType::Joined)
|
||||
? tr::lng_star_ref_list_my()
|
||||
: tr::lng_star_ref_list_subtitle());
|
||||
|
||||
loadMoreRows();
|
||||
}
|
||||
|
||||
void ListController::loadMoreRows() {
|
||||
if (_requestId || _allLoaded) {
|
||||
return;
|
||||
} else if (_type == JoinType::Joined) {
|
||||
using Flag = MTPpayments_GetConnectedStarRefBots::Flag;
|
||||
_requestId = session().api().request(MTPpayments_GetConnectedStarRefBots(
|
||||
MTP_flags(Flag()
|
||||
| (_offsetDate ? Flag::f_offset_date : Flag())
|
||||
| (_offsetThing.isEmpty() ? Flag() : Flag::f_offset_link)),
|
||||
_peer->input,
|
||||
MTP_int(_offsetDate),
|
||||
MTP_string(_offsetThing),
|
||||
MTP_int(kPerPage)
|
||||
)).done([=](const MTPpayments_ConnectedStarRefBots &result) {
|
||||
const auto &data = result.data();
|
||||
session().data().processUsers(data.vusers());
|
||||
const auto &list = data.vconnected_bots().v;
|
||||
if (list.empty()) {
|
||||
_allLoaded = true;
|
||||
} else {
|
||||
for (const auto &bot : list) {
|
||||
const auto &data = bot.data();
|
||||
const auto botId = UserId(data.vbot_id());
|
||||
_offsetThing = qs(data.vurl());
|
||||
_offsetDate = data.vdate().v;
|
||||
const auto commission = data.vcommission_permille().v;
|
||||
const auto durationMonths
|
||||
= data.vduration_months().value_or_empty();
|
||||
const auto user = session().data().user(botId);
|
||||
if (data.is_revoked()) {
|
||||
continue;
|
||||
}
|
||||
delegate()->peerListAppendRow(createRow(user, {
|
||||
.program = {
|
||||
.commission = ushort(commission),
|
||||
.durationMonths = uchar(durationMonths),
|
||||
},
|
||||
.link = _offsetThing,
|
||||
.users = int(data.vparticipants().v),
|
||||
}));
|
||||
}
|
||||
delegate()->peerListRefreshRows();
|
||||
_rowCount = delegate()->peerListFullRowsCount();
|
||||
}
|
||||
_requestId = 0;
|
||||
}).fail([=](const MTP::Error &error) {
|
||||
_requestId = 0;
|
||||
}).send();
|
||||
} else {
|
||||
using Flag = MTPpayments_GetSuggestedStarRefBots::Flag;
|
||||
_requestId = session().api().request(MTPpayments_GetSuggestedStarRefBots(
|
||||
MTP_flags(Flag::f_order_by_revenue),
|
||||
_peer->input,
|
||||
MTP_string(_offsetThing),
|
||||
MTP_int(kPerPage)
|
||||
)).done([=](const MTPpayments_SuggestedStarRefBots &result) {
|
||||
const auto &data = result.data();
|
||||
if (data.vnext_offset()) {
|
||||
_offsetThing = qs(*data.vnext_offset());
|
||||
} else {
|
||||
_allLoaded = true;
|
||||
}
|
||||
session().data().processUsers(data.vusers());
|
||||
for (const auto &bot : data.vsuggested_bots().v) {
|
||||
const auto &data = bot.data();
|
||||
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);
|
||||
delegate()->peerListAppendRow(createRow(user, {
|
||||
.program = {
|
||||
.commission = ushort(commission),
|
||||
.durationMonths = uchar(durationMonths),
|
||||
},
|
||||
}));
|
||||
}
|
||||
delegate()->peerListRefreshRows();
|
||||
_rowCount = delegate()->peerListFullRowsCount();
|
||||
_requestId = 0;
|
||||
}).fail([=](const MTP::Error &error) {
|
||||
_allLoaded = true;
|
||||
_requestId = 0;
|
||||
}).send();
|
||||
}
|
||||
}
|
||||
|
||||
rpl::producer<int> ListController::rowCountValue() const {
|
||||
return _rowCount.value();
|
||||
}
|
||||
//
|
||||
//std::unique_ptr<PeerListState> ListController::saveState() const {
|
||||
// auto result = PeerListController::saveState();
|
||||
// auto my = std::make_unique<SavedState>();
|
||||
// result->controllerState = std::move(my);
|
||||
// return result;
|
||||
//}
|
||||
//
|
||||
//void ListController::restoreState(
|
||||
// std::unique_ptr<PeerListState> state) {
|
||||
// auto typeErasedState = state
|
||||
// ? state->controllerState.get()
|
||||
// : nullptr;
|
||||
// if (dynamic_cast<SavedState*>(typeErasedState)) {
|
||||
// PeerListController::restoreState(std::move(state));
|
||||
// }
|
||||
//}
|
||||
|
||||
void ListController::showLink(not_null<PeerData*> peer, RowState state) {
|
||||
const auto window = _controller->parentController();
|
||||
window->show(Box([=](not_null<Ui::GenericBox*> box) {
|
||||
box->setTitle(tr::lng_star_ref_link_title());
|
||||
|
||||
const auto program = 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(QString::number(program.commission / 10.) + '%')),
|
||||
lt_app,
|
||||
rpl::single(Ui::Text::Bold(peer->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(state.link) | Ui::Text::ToLink(),
|
||||
st::boxLabel));
|
||||
Ui::AddSkip(box->verticalLayout());
|
||||
if (state.users > 0) {
|
||||
box->addRow(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
box,
|
||||
tr::lng_star_ref_link_copy_users(
|
||||
lt_count,
|
||||
rpl::single(state.users * 1.),
|
||||
lt_app,
|
||||
rpl::single(peer->name())),
|
||||
st::boxLabel));
|
||||
} else {
|
||||
box->addRow(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
box,
|
||||
tr::lng_star_ref_link_copy_none(
|
||||
lt_app,
|
||||
rpl::single(peer->name())),
|
||||
st::boxLabel));
|
||||
}
|
||||
|
||||
box->addButton(tr::lng_star_ref_link_copy(), [=] {
|
||||
QApplication::clipboard()->setText(state.link);
|
||||
window->showToast(u"Link copied to clipboard."_q);
|
||||
});
|
||||
box->addButton(tr::lng_cancel(), [=] {
|
||||
box->closeBox();
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
||||
void ListController::rowClicked(not_null<PeerListRow*> row) {
|
||||
const auto peer = row->peer();
|
||||
const auto i = _states.find(peer);
|
||||
Assert(i != end(_states));
|
||||
const auto state = i->second;
|
||||
const auto program = state.program;
|
||||
const auto window = _controller->parentController();
|
||||
if (_type == JoinType::Joined || !state.link.isEmpty()) {
|
||||
showLink(row->peer(), state);
|
||||
} else {
|
||||
const auto join = [=](Fn<void()> close) {
|
||||
session().api().request(MTPpayments_ConnectStarRefBot(
|
||||
_peer->input,
|
||||
peer->asUser()->inputUser
|
||||
)).done([=](const MTPpayments_ConnectedStarRefBots &result) {
|
||||
window->showToast(u"Connected!"_q);
|
||||
close();
|
||||
}).fail([=](const MTP::Error &error) {
|
||||
window->showToast(u"Failed: "_q + error.type());
|
||||
}).send();
|
||||
};
|
||||
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_one_about(
|
||||
lt_app,
|
||||
rpl::single(Ui::Text::Bold(peer->name())),
|
||||
lt_amount,
|
||||
rpl::single(Ui::Text::Bold(QString::number(program.commission / 10.) + '%')),
|
||||
lt_duration,
|
||||
std::move(duration),
|
||||
Ui::Text::WithEntities);
|
||||
auto added = tr::lng_star_ref_one_join_text(
|
||||
lt_terms,
|
||||
tr::lng_star_ref_button_link() | Ui::Text::ToLink(),
|
||||
Ui::Text::WithEntities);
|
||||
auto joined = rpl::combine(
|
||||
std::move(text),
|
||||
std::move(added)
|
||||
) | rpl::map([=](TextWithEntities a, TextWithEntities b) {
|
||||
return a.append("\n\n").append(b);
|
||||
});
|
||||
window->show(Ui::MakeConfirmBox({
|
||||
.text = std::move(joined),
|
||||
.confirmed = join,
|
||||
.confirmText = tr::lng_star_ref_one_join(),
|
||||
.title = tr::lng_star_ref_title(),
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
base::unique_qptr<Ui::PopupMenu> ListController::rowContextMenu(
|
||||
QWidget *parent,
|
||||
not_null<PeerListRow*> row) {
|
||||
const auto peer = row->peer();
|
||||
const auto i = _states.find(peer);
|
||||
Assert(i != end(_states));
|
||||
const auto state = i->second;
|
||||
if (state.link.isEmpty()) {
|
||||
return nullptr;
|
||||
}
|
||||
auto result = base::make_unique_q<Ui::PopupMenu>(
|
||||
parent,
|
||||
st::popupMenuWithIcons);
|
||||
result->addAction(tr::lng_star_ref_list_my_open(tr::now), [=] {
|
||||
_controller->parentController()->showPeerHistory(peer);
|
||||
}, &st::menuIconBotCommands);
|
||||
result->addAction(tr::lng_star_ref_list_my_copy(tr::now), [=] {
|
||||
QApplication::clipboard()->setText(state.link);
|
||||
_controller->parentController()->showToast(u"Link copied to clipboard."_q);
|
||||
}, &st::menuIconLinks);
|
||||
result->addAction(tr::lng_star_ref_list_my_leave(tr::now), [=] {
|
||||
session().api().request(MTPpayments_EditConnectedStarRefBot(
|
||||
MTP_flags(MTPpayments_EditConnectedStarRefBot::Flag::f_revoked),
|
||||
_peer->input,
|
||||
MTP_string(state.link)
|
||||
)).done([=] {
|
||||
_controller->parentController()->showToast(u"Revoked!"_q);
|
||||
}).fail([=](const MTP::Error &error) {
|
||||
_controller->parentController()->showToast(u"Failed: "_q + error.type());
|
||||
}).send();
|
||||
}, &st::menuIconLeaveAttention);
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
class InnerWidget final : public Ui::RpWidget {
|
||||
public:
|
||||
InnerWidget(QWidget *parent, not_null<Controller*> controller);
|
||||
|
||||
[[nodiscard]] not_null<PeerData*> peer() const;
|
||||
|
||||
void showFinished();
|
||||
void setInnerFocus();
|
||||
|
||||
void saveState(not_null<Memento*> memento);
|
||||
void restoreState(not_null<Memento*> memento);
|
||||
|
||||
private:
|
||||
void prepare();
|
||||
void setupInfo();
|
||||
void setupMy();
|
||||
void setupSuggested();
|
||||
|
||||
[[nodiscard]] object_ptr<Ui::RpWidget> infoRow(
|
||||
rpl::producer<QString> title,
|
||||
rpl::producer<QString> text,
|
||||
not_null<const style::icon*> icon);
|
||||
|
||||
const not_null<Controller*> _controller;
|
||||
const not_null<Ui::VerticalLayout*> _container;
|
||||
|
||||
};
|
||||
|
||||
InnerWidget::InnerWidget(QWidget *parent, not_null<Controller*> controller)
|
||||
: RpWidget(parent)
|
||||
, _controller(controller)
|
||||
, _container(Ui::CreateChild<Ui::VerticalLayout>(this)) {
|
||||
prepare();
|
||||
}
|
||||
|
||||
void InnerWidget::prepare() {
|
||||
Ui::ResizeFitChild(this, _container);
|
||||
|
||||
setupInfo();
|
||||
Ui::AddSkip(_container);
|
||||
Ui::AddDivider(_container);
|
||||
setupMy();
|
||||
setupSuggested();
|
||||
}
|
||||
|
||||
void InnerWidget::setupInfo() {
|
||||
AddSkip(_container, st::defaultVerticalListSkip * 2);
|
||||
|
||||
_container->add(infoRow(
|
||||
tr::lng_star_ref_reliable_title(),
|
||||
tr::lng_star_ref_reliable_about(),
|
||||
&st::menuIconAntispam));
|
||||
|
||||
_container->add(infoRow(
|
||||
tr::lng_star_ref_transparent_title(),
|
||||
tr::lng_star_ref_transparent_about(),
|
||||
&st::menuIconShowInChat));
|
||||
|
||||
_container->add(infoRow(
|
||||
tr::lng_star_ref_simple_title(),
|
||||
tr::lng_star_ref_simple_about(),
|
||||
&st::menuIconBoosts));
|
||||
}
|
||||
|
||||
void InnerWidget::setupMy() {
|
||||
const auto wrap = _container->add(
|
||||
object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
|
||||
_container,
|
||||
object_ptr<Ui::VerticalLayout>(_container)));
|
||||
const auto inner = wrap->entity();
|
||||
|
||||
Ui::AddSkip(inner);
|
||||
Ui::AddSubsectionTitle(inner, tr::lng_star_ref_list_my());
|
||||
|
||||
const auto delegate = lifetime().make_state<
|
||||
PeerListContentDelegateSimple
|
||||
>();
|
||||
const auto controller = lifetime().make_state<ListController>(
|
||||
_controller,
|
||||
peer(),
|
||||
JoinType::Joined);
|
||||
const auto content = inner->add(
|
||||
object_ptr<PeerListContent>(
|
||||
_container,
|
||||
controller));
|
||||
delegate->setContent(content);
|
||||
controller->setDelegate(delegate);
|
||||
|
||||
Ui::AddDivider(inner);
|
||||
|
||||
wrap->toggleOn(controller->rowCountValue(
|
||||
) | rpl::map(rpl::mappers::_1 > 0));
|
||||
}
|
||||
|
||||
void InnerWidget::setupSuggested() {
|
||||
Ui::AddSkip(_container);
|
||||
Ui::AddSubsectionTitle(_container, tr::lng_star_ref_list_subtitle());
|
||||
|
||||
const auto delegate = lifetime().make_state<
|
||||
PeerListContentDelegateSimple
|
||||
>();
|
||||
auto controller = lifetime().make_state<ListController>(
|
||||
_controller,
|
||||
peer(),
|
||||
JoinType::Suggested);
|
||||
const auto content = _container->add(
|
||||
object_ptr<PeerListContent>(
|
||||
_container,
|
||||
controller));
|
||||
delegate->setContent(content);
|
||||
controller->setDelegate(delegate);
|
||||
}
|
||||
|
||||
object_ptr<Ui::RpWidget> InnerWidget::infoRow(
|
||||
rpl::producer<QString> title,
|
||||
rpl::producer<QString> text,
|
||||
not_null<const style::icon*> icon) {
|
||||
auto result = object_ptr<Ui::VerticalLayout>(_container);
|
||||
const auto raw = result.data();
|
||||
|
||||
raw->add(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
raw,
|
||||
std::move(title) | Ui::Text::ToBold(),
|
||||
st::defaultFlatLabel),
|
||||
st::settingsPremiumRowTitlePadding);
|
||||
raw->add(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
raw,
|
||||
std::move(text),
|
||||
st::boxDividerLabel),
|
||||
st::settingsPremiumRowAboutPadding);
|
||||
object_ptr<Info::Profile::FloatingIcon>(
|
||||
raw,
|
||||
*icon,
|
||||
st::starrefInfoIconPosition);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
not_null<PeerData*> InnerWidget::peer() const {
|
||||
return _controller->key().starrefPeer();
|
||||
}
|
||||
|
||||
void InnerWidget::showFinished() {
|
||||
|
||||
}
|
||||
|
||||
void InnerWidget::setInnerFocus() {
|
||||
setFocus();
|
||||
}
|
||||
|
||||
void InnerWidget::saveState(not_null<Memento*> memento) {
|
||||
|
||||
}
|
||||
|
||||
void InnerWidget::restoreState(not_null<Memento*> memento) {
|
||||
|
||||
}
|
||||
|
||||
Memento::Memento(not_null<Controller*> controller)
|
||||
: ContentMemento(Tag(controller->starrefPeer(), controller->starrefType())) {
|
||||
}
|
||||
|
||||
Memento::Memento(not_null<PeerData*> peer)
|
||||
: ContentMemento(Tag(peer, Type::Join)) {
|
||||
}
|
||||
|
||||
Memento::~Memento() = default;
|
||||
|
||||
Section Memento::section() const {
|
||||
return Section(Section::Type::BotStarRef);
|
||||
}
|
||||
|
||||
object_ptr<ContentWidget> Memento::createWidget(
|
||||
QWidget *parent,
|
||||
not_null<Controller*> controller,
|
||||
const QRect &geometry) {
|
||||
auto result = object_ptr<Widget>(parent, controller);
|
||||
result->setInternalState(geometry, this);
|
||||
return result;
|
||||
}
|
||||
|
||||
Widget::Widget(
|
||||
QWidget *parent,
|
||||
not_null<Controller*> controller)
|
||||
: ContentWidget(parent, controller)
|
||||
, _inner(setInnerWidget(object_ptr<InnerWidget>(this, controller))) {
|
||||
_top = setupTop();
|
||||
}
|
||||
|
||||
not_null<PeerData*> Widget::peer() const {
|
||||
return _inner->peer();
|
||||
}
|
||||
|
||||
bool Widget::showInternal(not_null<ContentMemento*> memento) {
|
||||
return (memento->starrefPeer() == peer());
|
||||
}
|
||||
|
||||
rpl::producer<QString> Widget::title() {
|
||||
return tr::lng_star_ref_list_title();
|
||||
}
|
||||
|
||||
void Widget::setInternalState(
|
||||
const QRect &geometry,
|
||||
not_null<Memento*> memento) {
|
||||
setGeometry(geometry);
|
||||
Ui::SendPendingMoveResizeEvents(this);
|
||||
restoreState(memento);
|
||||
}
|
||||
|
||||
rpl::producer<bool> Widget::desiredShadowVisibility() const {
|
||||
return rpl::single<bool>(true);
|
||||
}
|
||||
|
||||
void Widget::showFinished() {
|
||||
_inner->showFinished();
|
||||
}
|
||||
|
||||
void Widget::setInnerFocus() {
|
||||
_inner->setInnerFocus();
|
||||
}
|
||||
|
||||
void Widget::enableBackButton() {
|
||||
_backEnabled = true;
|
||||
}
|
||||
|
||||
std::shared_ptr<ContentMemento> Widget::doCreateMemento() {
|
||||
auto result = std::make_shared<Memento>(controller());
|
||||
saveState(result.get());
|
||||
return result;
|
||||
}
|
||||
|
||||
void Widget::saveState(not_null<Memento*> memento) {
|
||||
memento->setScrollTop(scrollTopSave());
|
||||
_inner->saveState(memento);
|
||||
}
|
||||
|
||||
void Widget::restoreState(not_null<Memento*> memento) {
|
||||
_inner->restoreState(memento);
|
||||
scrollTopRestore(memento->scrollTop());
|
||||
}
|
||||
|
||||
std::unique_ptr<Ui::Premium::TopBarAbstract> Widget::setupTop() {
|
||||
auto title = tr::lng_star_ref_list_title();
|
||||
auto about = tr::lng_star_ref_list_about_channel()
|
||||
| Ui::Text::ToWithEntities();
|
||||
|
||||
const auto controller = this->controller();
|
||||
const auto weak = base::make_weak(controller->parentController());
|
||||
const auto clickContextOther = [=] {
|
||||
return QVariant::fromValue(ClickHandlerContext{
|
||||
.sessionWindow = weak,
|
||||
.botStartAutoSubmit = true,
|
||||
});
|
||||
};
|
||||
auto result = std::make_unique<Ui::Premium::TopBar>(
|
||||
this,
|
||||
st::starrefCover,
|
||||
Ui::Premium::TopBarDescriptor{
|
||||
.clickContextOther = clickContextOther,
|
||||
.logo = u"affiliate"_q,
|
||||
.title = std::move(title),
|
||||
.about = std::move(about),
|
||||
.light = true,
|
||||
});
|
||||
const auto raw = result.get();
|
||||
|
||||
controller->wrapValue(
|
||||
) | rpl::start_with_next([=](Info::Wrap wrap) {
|
||||
raw->setRoundEdges(wrap == Info::Wrap::Layer);
|
||||
}, raw->lifetime());
|
||||
|
||||
const auto baseHeight = st::starrefCoverHeight;
|
||||
raw->resize(width(), baseHeight);
|
||||
|
||||
raw->additionalHeight(
|
||||
) | rpl::start_with_next([=](int additionalHeight) {
|
||||
raw->setMaximumHeight(baseHeight + additionalHeight);
|
||||
raw->setMinimumHeight(baseHeight + additionalHeight);
|
||||
setPaintPadding({ 0, raw->height(), 0, 0 });
|
||||
}, raw->lifetime());
|
||||
|
||||
controller->wrapValue(
|
||||
) | rpl::start_with_next([=](Info::Wrap wrap) {
|
||||
const auto isLayer = (wrap == Info::Wrap::Layer);
|
||||
_back = base::make_unique_q<Ui::FadeWrap<Ui::IconButton>>(
|
||||
raw,
|
||||
object_ptr<Ui::IconButton>(
|
||||
raw,
|
||||
(isLayer
|
||||
? st::infoLayerTopBar.back
|
||||
: st::infoTopBar.back)),
|
||||
st::infoTopBarScale);
|
||||
_back->setDuration(0);
|
||||
_back->toggleOn(isLayer
|
||||
? _backEnabled.value() | rpl::type_erased()
|
||||
: rpl::single(true));
|
||||
_back->entity()->addClickHandler([=] {
|
||||
controller->showBackFromStack();
|
||||
});
|
||||
_back->toggledValue(
|
||||
) | rpl::start_with_next([=](bool toggled) {
|
||||
const auto &st = isLayer ? st::infoLayerTopBar : st::infoTopBar;
|
||||
raw->setTextPosition(
|
||||
toggled ? st.back.width : st.titlePosition.x(),
|
||||
st.titlePosition.y());
|
||||
}, _back->lifetime());
|
||||
|
||||
if (!isLayer) {
|
||||
_close = nullptr;
|
||||
} else {
|
||||
_close = base::make_unique_q<Ui::IconButton>(
|
||||
raw,
|
||||
st::infoTopBarClose);
|
||||
_close->addClickHandler([=] {
|
||||
controller->parentController()->hideLayer();
|
||||
controller->parentController()->hideSpecialLayer();
|
||||
});
|
||||
raw->widthValue(
|
||||
) | rpl::start_with_next([=] {
|
||||
_close->moveToRight(0, 0);
|
||||
}, _close->lifetime());
|
||||
}
|
||||
}, raw->lifetime());
|
||||
|
||||
raw->move(0, 0);
|
||||
widthValue() | rpl::start_with_next([=](int width) {
|
||||
raw->resizeToWidth(width);
|
||||
setScrollTopSkip(raw->height());
|
||||
}, raw->lifetime());
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::shared_ptr<Info::Memento> Make(not_null<PeerData*> peer) {
|
||||
return std::make_shared<Info::Memento>(
|
||||
std::vector<std::shared_ptr<ContentMemento>>(
|
||||
1,
|
||||
std::make_shared<Memento>(peer)));
|
||||
}
|
||||
|
||||
} // namespace Info::BotStarRef::Join
|
||||
|
|
@ -17,11 +17,12 @@ namespace Ui {
|
|||
template <typename Widget>
|
||||
class FadeWrap;
|
||||
class IconButton;
|
||||
class AbstractButton;
|
||||
class VerticalLayout;
|
||||
} // namespace Ui
|
||||
|
||||
namespace Info::BotStarRef {
|
||||
namespace Info::BotStarRef::Join {
|
||||
|
||||
struct State;
|
||||
class InnerWidget;
|
||||
|
||||
class Memento final : public ContentMemento {
|
||||
|
@ -61,22 +62,18 @@ private:
|
|||
void restoreState(not_null<Memento*> memento);
|
||||
|
||||
[[nodiscard]] std::unique_ptr<Ui::Premium::TopBarAbstract> setupTop();
|
||||
[[nodiscard]] std::unique_ptr<Ui::RpWidget> setupBottom();
|
||||
|
||||
std::shared_ptr<ContentMemento> doCreateMemento() override;
|
||||
|
||||
const not_null<InnerWidget*> _inner;
|
||||
const not_null<State*> _state;
|
||||
|
||||
std::unique_ptr<Ui::Premium::TopBarAbstract> _top;
|
||||
base::unique_qptr<Ui::FadeWrap<Ui::IconButton>> _back;
|
||||
base::unique_qptr<Ui::IconButton> _close;
|
||||
rpl::variable<bool> _backEnabled;
|
||||
|
||||
std::unique_ptr<Ui::RpWidget> _bottom;
|
||||
|
||||
};
|
||||
|
||||
[[nodiscard]] std::shared_ptr<Info::Memento> Make(not_null<PeerData*> peer);
|
||||
|
||||
} // namespace Info::BotStarRef
|
||||
} // namespace Info::BotStarRef::Join
|
|
@ -5,7 +5,7 @@ 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 "info/bot/starref/info_bot_starref_widget.h"
|
||||
#include "info/bot/starref/info_bot_starref_setup_widget.h"
|
||||
|
||||
#include "apiwrap.h"
|
||||
#include "base/timer_rpl.h"
|
||||
|
@ -33,11 +33,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "styles/style_premium.h"
|
||||
#include "styles/style_settings.h"
|
||||
|
||||
namespace Info::BotStarRef {
|
||||
namespace Info::BotStarRef::Setup {
|
||||
namespace {
|
||||
|
||||
constexpr auto kDurationForeverValue = 999;
|
||||
constexpr auto kCommissionDefault = 20;
|
||||
constexpr auto kCommissionDefault = 200;
|
||||
constexpr auto kDurationDefault = 12;
|
||||
|
||||
} // namespace
|
||||
|
@ -126,9 +126,6 @@ void InnerWidget::prepare() {
|
|||
setupDuration();
|
||||
Ui::AddSkip(_container);
|
||||
setupViewExisting();
|
||||
Ui::AddSkip(_container);
|
||||
Ui::AddDivider(_container);
|
||||
Ui::AddSkip(_container);
|
||||
setupEnd();
|
||||
}
|
||||
|
||||
|
@ -155,9 +152,17 @@ void InnerWidget::setupCommission() {
|
|||
Ui::AddSkip(_container);
|
||||
Ui::AddSubsectionTitle(_container, tr::lng_star_ref_commission_title());
|
||||
|
||||
const auto commission = ValueForCommission(_state);
|
||||
|
||||
auto values = std::vector<int>();
|
||||
if (commission > 0 && commission < 10) {
|
||||
values.push_back(commission);
|
||||
}
|
||||
for (auto i = 1; i != 91; ++i) {
|
||||
values.push_back(i);
|
||||
values.push_back(i * 10);
|
||||
if (i * 10 < commission && (i == 90 || (i + 1) * 10 > commission)) {
|
||||
values.push_back(commission);
|
||||
}
|
||||
}
|
||||
const auto valuesCount = int(values.size());
|
||||
|
||||
|
@ -166,7 +171,7 @@ void InnerWidget::setupCommission() {
|
|||
st::settingsScale,
|
||||
st::settingsScaleLabel,
|
||||
st::normalFont->spacew * 2,
|
||||
st::settingsScaleLabel.style.font->width("90%"),
|
||||
st::settingsScaleLabel.style.font->width("89.9%"),
|
||||
true);
|
||||
_container->add(
|
||||
std::move(sliderWithLabel.widget),
|
||||
|
@ -175,10 +180,9 @@ void InnerWidget::setupCommission() {
|
|||
const auto label = sliderWithLabel.label;
|
||||
|
||||
const auto updateLabel = [=](int value) {
|
||||
const auto labelText = QString::number(value) + '%';
|
||||
const auto labelText = QString::number(value / 10.) + '%';
|
||||
label->setText(labelText);
|
||||
};
|
||||
const auto commission = ValueForCommission(_state);
|
||||
const auto setCommission = [=](int value) {
|
||||
_state.program.commission = value;
|
||||
updateLabel(value);
|
||||
|
@ -245,91 +249,17 @@ void InnerWidget::setupDuration() {
|
|||
}
|
||||
|
||||
void InnerWidget::setupViewExisting() {
|
||||
const auto &stLabel = st::defaultFlatLabel;
|
||||
const auto iconSize = st::settingsPremiumIconDouble.size();
|
||||
const auto &titlePadding = st::settingsPremiumRowTitlePadding;
|
||||
const auto &descriptionPadding = st::settingsPremiumRowAboutPadding;
|
||||
|
||||
const auto content = _container;
|
||||
const auto labelAscent = stLabel.style.font->ascent;
|
||||
const auto button = Ui::CreateChild<Ui::SettingsButton>(
|
||||
content.get(),
|
||||
rpl::single(QString()));
|
||||
|
||||
const auto label = content->add(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
content,
|
||||
tr::lng_star_ref_existing_title() | Ui::Text::ToBold(),
|
||||
stLabel),
|
||||
titlePadding);
|
||||
label->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||
const auto description = content->add(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
content,
|
||||
tr::lng_star_ref_existing_about(),
|
||||
st::boxDividerLabel),
|
||||
descriptionPadding);
|
||||
description->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||
|
||||
const auto dummy = Ui::CreateChild<Ui::AbstractButton>(content.get());
|
||||
dummy->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||
|
||||
content->sizeValue(
|
||||
) | rpl::start_with_next([=](const QSize &s) {
|
||||
dummy->resize(s.width(), iconSize.height());
|
||||
}, dummy->lifetime());
|
||||
|
||||
label->geometryValue(
|
||||
) | rpl::start_with_next([=](const QRect &r) {
|
||||
dummy->moveToLeft(0, r.y() + (r.height() - labelAscent));
|
||||
}, dummy->lifetime());
|
||||
|
||||
::Settings::AddButtonIcon(dummy, st::settingsButton, {
|
||||
.icon = &st::settingsPremiumIconStar,
|
||||
.backgroundBrush = st::premiumIconBg3,
|
||||
});
|
||||
|
||||
rpl::combine(
|
||||
content->widthValue(),
|
||||
label->heightValue(),
|
||||
description->heightValue()
|
||||
) | rpl::start_with_next([=,
|
||||
topPadding = titlePadding,
|
||||
bottomPadding = descriptionPadding](
|
||||
int width,
|
||||
int topHeight,
|
||||
int bottomHeight) {
|
||||
button->resize(
|
||||
width,
|
||||
topPadding.top()
|
||||
+ topHeight
|
||||
+ topPadding.bottom()
|
||||
+ bottomPadding.top()
|
||||
+ bottomHeight
|
||||
+ bottomPadding.bottom());
|
||||
}, button->lifetime());
|
||||
label->topValue(
|
||||
) | rpl::start_with_next([=, padding = titlePadding.top()](int top) {
|
||||
button->moveToLeft(0, top - padding);
|
||||
}, button->lifetime());
|
||||
const auto arrow = Ui::CreateChild<Ui::IconButton>(
|
||||
button,
|
||||
st::backButton);
|
||||
arrow->setIconOverride(
|
||||
&st::settingsPremiumArrow,
|
||||
&st::settingsPremiumArrowOver);
|
||||
arrow->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||
button->sizeValue(
|
||||
) | rpl::start_with_next([=](const QSize &s) {
|
||||
const auto &point = st::settingsPremiumArrowShift;
|
||||
arrow->moveToRight(
|
||||
-point.x(),
|
||||
point.y() + (s.height() - arrow->height()) / 2);
|
||||
}, arrow->lifetime());
|
||||
|
||||
const auto button = AddViewListButton(
|
||||
_container,
|
||||
tr::lng_star_ref_existing_title(),
|
||||
tr::lng_star_ref_existing_about());
|
||||
button->setClickedCallback([=] {
|
||||
_controller->showToast(u"List or smth.."_q);
|
||||
});
|
||||
|
||||
Ui::AddSkip(_container);
|
||||
Ui::AddDivider(_container);
|
||||
Ui::AddSkip(_container);
|
||||
}
|
||||
|
||||
void InnerWidget::setupEnd() {
|
||||
|
@ -361,6 +291,8 @@ void InnerWidget::setupEnd() {
|
|||
_controller->showToast("Remove failed!");
|
||||
})).send();
|
||||
});
|
||||
Ui::AddSkip(_container);
|
||||
Ui::AddDivider(_container);
|
||||
}
|
||||
|
||||
object_ptr<Ui::RpWidget> InnerWidget::infoRow(
|
||||
|
@ -411,11 +343,11 @@ void InnerWidget::restoreState(not_null<Memento*> memento) {
|
|||
}
|
||||
|
||||
Memento::Memento(not_null<Controller*> controller)
|
||||
: ContentMemento(Tag(controller->starrefPeer())) {
|
||||
: ContentMemento(Tag(controller->starrefPeer(), controller->starrefType())) {
|
||||
}
|
||||
|
||||
Memento::Memento(not_null<PeerData*> peer)
|
||||
: ContentMemento(Tag(peer)) {
|
||||
: ContentMemento(Tag(peer, Type::Setup)) {
|
||||
}
|
||||
|
||||
Memento::~Memento() = default;
|
||||
|
@ -509,7 +441,7 @@ std::unique_ptr<Ui::Premium::TopBarAbstract> Widget::setupTop() {
|
|||
};
|
||||
auto result = std::make_unique<Ui::Premium::TopBar>(
|
||||
this,
|
||||
st::userPremiumCover,
|
||||
st::starrefCover,
|
||||
Ui::Premium::TopBarDescriptor{
|
||||
.clickContextOther = clickContextOther,
|
||||
.logo = u"affiliate"_q,
|
||||
|
@ -524,16 +456,15 @@ std::unique_ptr<Ui::Premium::TopBarAbstract> Widget::setupTop() {
|
|||
raw->setRoundEdges(wrap == Info::Wrap::Layer);
|
||||
}, raw->lifetime());
|
||||
|
||||
const auto calculateMaximumHeight = [=] {
|
||||
return st::settingsPremiumTopHeight;
|
||||
};
|
||||
const auto baseHeight = st::starrefCoverHeight;
|
||||
raw->resize(width(), baseHeight);
|
||||
|
||||
raw->setMaximumHeight(st::settingsPremiumTopHeight);
|
||||
raw->setMinimumHeight(st::settingsPremiumTopHeight);
|
||||
|
||||
raw->resize(width(), raw->maximumHeight());
|
||||
|
||||
setPaintPadding({ 0, st::settingsPremiumTopHeight, 0, 0 });
|
||||
raw->additionalHeight(
|
||||
) | rpl::start_with_next([=](int additionalHeight) {
|
||||
raw->setMaximumHeight(baseHeight + additionalHeight);
|
||||
raw->setMinimumHeight(baseHeight + additionalHeight);
|
||||
setPaintPadding({ 0, raw->height(), 0, 0 });
|
||||
}, raw->lifetime());
|
||||
|
||||
controller->wrapValue(
|
||||
) | rpl::start_with_next([=](Info::Wrap wrap) {
|
||||
|
@ -566,7 +497,7 @@ std::unique_ptr<Ui::Premium::TopBarAbstract> Widget::setupTop() {
|
|||
} else {
|
||||
_close = base::make_unique_q<Ui::IconButton>(
|
||||
raw,
|
||||
st::settingsPremiumTopBarClose);
|
||||
st::infoTopBarClose);
|
||||
_close->addClickHandler([=] {
|
||||
controller->parentController()->hideLayer();
|
||||
controller->parentController()->hideSpecialLayer();
|
||||
|
@ -677,5 +608,92 @@ std::shared_ptr<Info::Memento> Make(not_null<PeerData*> peer) {
|
|||
std::make_shared<Memento>(peer)));
|
||||
}
|
||||
|
||||
} // namespace Info::BotStarRef
|
||||
not_null<Ui::AbstractButton*> AddViewListButton(
|
||||
not_null<Ui::VerticalLayout*> parent,
|
||||
rpl::producer<QString> title,
|
||||
rpl::producer<QString> subtitle) {
|
||||
const auto &stLabel = st::defaultFlatLabel;
|
||||
const auto iconSize = st::settingsPremiumIconDouble.size();
|
||||
const auto &titlePadding = st::settingsPremiumRowTitlePadding;
|
||||
const auto &descriptionPadding = st::settingsPremiumRowAboutPadding;
|
||||
|
||||
const auto button = Ui::CreateChild<Ui::SettingsButton>(
|
||||
parent,
|
||||
rpl::single(QString()));
|
||||
|
||||
const auto label = parent->add(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
parent,
|
||||
std::move(title) | Ui::Text::ToBold(),
|
||||
stLabel),
|
||||
titlePadding);
|
||||
label->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||
const auto description = parent->add(
|
||||
object_ptr<Ui::FlatLabel>(
|
||||
parent,
|
||||
std::move(subtitle),
|
||||
st::boxDividerLabel),
|
||||
descriptionPadding);
|
||||
description->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||
|
||||
const auto dummy = Ui::CreateChild<Ui::AbstractButton>(parent);
|
||||
dummy->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||
|
||||
parent->sizeValue(
|
||||
) | rpl::start_with_next([=](const QSize &s) {
|
||||
dummy->resize(s.width(), iconSize.height());
|
||||
}, dummy->lifetime());
|
||||
|
||||
button->geometryValue(
|
||||
) | rpl::start_with_next([=](const QRect &r) {
|
||||
dummy->moveToLeft(0, r.y() + (r.height() - iconSize.height()) / 2);
|
||||
}, dummy->lifetime());
|
||||
|
||||
::Settings::AddButtonIcon(dummy, st::settingsButton, {
|
||||
.icon = &st::settingsPremiumIconStar,
|
||||
.backgroundBrush = st::premiumIconBg3,
|
||||
});
|
||||
|
||||
rpl::combine(
|
||||
parent->widthValue(),
|
||||
label->heightValue(),
|
||||
description->heightValue()
|
||||
) | rpl::start_with_next([=,
|
||||
topPadding = titlePadding,
|
||||
bottomPadding = descriptionPadding](
|
||||
int width,
|
||||
int topHeight,
|
||||
int bottomHeight) {
|
||||
button->resize(
|
||||
width,
|
||||
topPadding.top()
|
||||
+ topHeight
|
||||
+ topPadding.bottom()
|
||||
+ bottomPadding.top()
|
||||
+ bottomHeight
|
||||
+ bottomPadding.bottom());
|
||||
}, button->lifetime());
|
||||
label->topValue(
|
||||
) | rpl::start_with_next([=, padding = titlePadding.top()](int top) {
|
||||
button->moveToLeft(0, top - padding);
|
||||
}, button->lifetime());
|
||||
const auto arrow = Ui::CreateChild<Ui::IconButton>(
|
||||
button,
|
||||
st::backButton);
|
||||
arrow->setIconOverride(
|
||||
&st::settingsPremiumArrow,
|
||||
&st::settingsPremiumArrowOver);
|
||||
arrow->setAttribute(Qt::WA_TransparentForMouseEvents);
|
||||
button->sizeValue(
|
||||
) | rpl::start_with_next([=](const QSize &s) {
|
||||
const auto &point = st::settingsPremiumArrowShift;
|
||||
arrow->moveToRight(
|
||||
-point.x(),
|
||||
point.y() + (s.height() - arrow->height()) / 2);
|
||||
}, arrow->lifetime());
|
||||
|
||||
return button;
|
||||
}
|
||||
|
||||
} // namespace Info::BotStarRef::Setup
|
||||
|
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
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 "info/info_content_widget.h"
|
||||
|
||||
namespace Ui::Premium {
|
||||
class TopBarAbstract;
|
||||
} // namespace Ui::Premium
|
||||
|
||||
namespace Ui {
|
||||
template <typename Widget>
|
||||
class FadeWrap;
|
||||
class IconButton;
|
||||
class AbstractButton;
|
||||
class VerticalLayout;
|
||||
} // namespace Ui
|
||||
|
||||
namespace Info::BotStarRef::Setup {
|
||||
|
||||
struct State;
|
||||
class InnerWidget;
|
||||
|
||||
class Memento final : public ContentMemento {
|
||||
public:
|
||||
Memento(not_null<Controller*> controller);
|
||||
Memento(not_null<PeerData*> peer);
|
||||
~Memento();
|
||||
|
||||
object_ptr<ContentWidget> createWidget(
|
||||
QWidget *parent,
|
||||
not_null<Controller*> controller,
|
||||
const QRect &geometry) override;
|
||||
|
||||
Section section() const override;
|
||||
|
||||
};
|
||||
|
||||
class Widget final : public ContentWidget {
|
||||
public:
|
||||
Widget(QWidget *parent, not_null<Controller*> controller);
|
||||
|
||||
bool showInternal(not_null<ContentMemento*> memento) override;
|
||||
rpl::producer<QString> title() override;
|
||||
rpl::producer<bool> desiredShadowVisibility() const override;
|
||||
void showFinished() override;
|
||||
void setInnerFocus() override;
|
||||
void enableBackButton() override;
|
||||
|
||||
[[nodiscard]] not_null<PeerData*> peer() const;
|
||||
|
||||
void setInternalState(
|
||||
const QRect &geometry,
|
||||
not_null<Memento*> memento);
|
||||
|
||||
private:
|
||||
void saveState(not_null<Memento*> memento);
|
||||
void restoreState(not_null<Memento*> memento);
|
||||
|
||||
[[nodiscard]] std::unique_ptr<Ui::Premium::TopBarAbstract> setupTop();
|
||||
[[nodiscard]] std::unique_ptr<Ui::RpWidget> setupBottom();
|
||||
|
||||
std::shared_ptr<ContentMemento> doCreateMemento() override;
|
||||
|
||||
const not_null<InnerWidget*> _inner;
|
||||
const not_null<State*> _state;
|
||||
|
||||
std::unique_ptr<Ui::Premium::TopBarAbstract> _top;
|
||||
base::unique_qptr<Ui::FadeWrap<Ui::IconButton>> _back;
|
||||
base::unique_qptr<Ui::IconButton> _close;
|
||||
rpl::variable<bool> _backEnabled;
|
||||
|
||||
std::unique_ptr<Ui::RpWidget> _bottom;
|
||||
|
||||
};
|
||||
|
||||
[[nodiscard]] std::shared_ptr<Info::Memento> Make(not_null<PeerData*> peer);
|
||||
|
||||
[[nodiscard]] not_null<Ui::AbstractButton*> AddViewListButton(
|
||||
not_null<Ui::VerticalLayout*> parent,
|
||||
rpl::producer<QString> title,
|
||||
rpl::producer<QString> subtitle);
|
||||
|
||||
} // namespace Info::BotStarRef::Setup
|
|
@ -25,10 +25,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
|
|||
#include "data/data_user.h"
|
||||
#include "data/stickers/data_custom_emoji.h"
|
||||
#include "history/view/controls/history_view_webpage_processor.h"
|
||||
#include "info/bot/starref/info_bot_starref_join_widget.h"
|
||||
#include "info/bot/starref/info_bot_starref_setup_widget.h"
|
||||
#include "info/channel_statistics/earn/earn_format.h"
|
||||
#include "info/channel_statistics/earn/earn_icons.h"
|
||||
#include "info/channel_statistics/earn/info_channel_earn_widget.h"
|
||||
#include "info/info_controller.h"
|
||||
#include "info/info_memento.h"
|
||||
#include "info/profile/info_profile_values.h" // Info::Profile::NameValue.
|
||||
#include "info/statistics/info_statistics_inner_widget.h" // FillLoading.
|
||||
#include "info/statistics/info_statistics_list_controllers.h"
|
||||
|
@ -960,6 +963,17 @@ void InnerWidget::fill() {
|
|||
) | rpl::map(creditsToUsdMap));
|
||||
}
|
||||
|
||||
const auto button = Info::BotStarRef::Setup::AddViewListButton(
|
||||
container,
|
||||
tr::lng_credits_summary_earn_title(),
|
||||
tr::lng_credits_summary_earn_about());
|
||||
button->setClickedCallback([=] {
|
||||
_controller->showSection(Info::BotStarRef::Join::Make(_peer));
|
||||
});
|
||||
Ui::AddSkip(container);
|
||||
Ui::AddDivider(container);
|
||||
Ui::AddSkip(container);
|
||||
|
||||
const auto sectionIndex = container->lifetime().make_state<int>(0);
|
||||
const auto rebuildLists = [=](
|
||||
const Memento::SavedState &data,
|
||||
|
|
|
@ -379,7 +379,7 @@ Key ContentMemento::key() const {
|
|||
} else if (statisticsTag().peer) {
|
||||
return statisticsTag();
|
||||
} else if (const auto starref = starrefPeer()) {
|
||||
return BotStarRef::Tag(starref);
|
||||
return BotStarRef::Tag(starref, starrefType());
|
||||
} else if (const auto who = reactionsWhoReadIds()) {
|
||||
return Key(who, _reactionsSelected, _pollReactionsContextId);
|
||||
} else {
|
||||
|
@ -423,7 +423,8 @@ ContentMemento::ContentMemento(Statistics::Tag statistics)
|
|||
}
|
||||
|
||||
ContentMemento::ContentMemento(BotStarRef::Tag starref)
|
||||
: _starrefPeer(starref.peer) {
|
||||
: _starrefPeer(starref.peer)
|
||||
, _starrefType(starref.type) {
|
||||
}
|
||||
|
||||
ContentMemento::ContentMemento(
|
||||
|
|
|
@ -53,6 +53,7 @@ struct Tag;
|
|||
} // namespace Info::Statistics
|
||||
|
||||
namespace Info::BotStarRef {
|
||||
enum class Type : uchar;
|
||||
struct Tag;
|
||||
} // namespace Info::BotStarRef
|
||||
|
||||
|
@ -234,6 +235,9 @@ public:
|
|||
PeerData *starrefPeer() const {
|
||||
return _starrefPeer;
|
||||
}
|
||||
BotStarRef::Type starrefType() const {
|
||||
return _starrefType;
|
||||
}
|
||||
PollData *poll() const {
|
||||
return _poll;
|
||||
}
|
||||
|
@ -289,6 +293,7 @@ private:
|
|||
Stories::Tab _storiesTab = {};
|
||||
Statistics::Tag _statisticsTag;
|
||||
PeerData * const _starrefPeer = nullptr;
|
||||
BotStarRef::Type _starrefType = {};
|
||||
PollData * const _poll = nullptr;
|
||||
std::shared_ptr<Api::WhoReadList> _reactionsWhoReadIds;
|
||||
Data::ReactionId _reactionsSelected;
|
||||
|
|
|
@ -116,6 +116,13 @@ PeerData *Key::starrefPeer() const {
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
BotStarRef::Type Key::starrefType() const {
|
||||
if (const auto tag = std::get_if<BotStarRef::Tag>(&_value)) {
|
||||
return tag->type;
|
||||
}
|
||||
return BotStarRef::Type();
|
||||
}
|
||||
|
||||
PollData *Key::poll() const {
|
||||
if (const auto data = std::get_if<PollKey>(&_value)) {
|
||||
return data->poll;
|
||||
|
@ -330,7 +337,8 @@ bool Controller::validateMementoPeer(
|
|||
&& memento->settingsSelf() == settingsSelf()
|
||||
&& memento->storiesPeer() == storiesPeer()
|
||||
&& memento->statisticsTag().peer == statisticsTag().peer
|
||||
&& memento->starrefPeer() == starrefPeer();
|
||||
&& memento->starrefPeer() == starrefPeer()
|
||||
&& memento->starrefType() == starrefType();
|
||||
}
|
||||
|
||||
void Controller::setSection(not_null<ContentMemento*> memento) {
|
||||
|
|
|
@ -63,11 +63,16 @@ struct Tag {
|
|||
|
||||
namespace Info::BotStarRef {
|
||||
|
||||
enum class Type : uchar {
|
||||
Setup,
|
||||
Join,
|
||||
};
|
||||
struct Tag {
|
||||
explicit Tag(not_null<PeerData*> peer) : peer(peer) {
|
||||
Tag(not_null<PeerData*> peer, Type type) : peer(peer), type(type) {
|
||||
}
|
||||
|
||||
not_null<PeerData*> peer;
|
||||
Type type = {};
|
||||
};
|
||||
|
||||
} // namespace Info::BotStarRef
|
||||
|
@ -97,6 +102,7 @@ public:
|
|||
Stories::Tab storiesTab() const;
|
||||
Statistics::Tag statisticsTag() const;
|
||||
PeerData *starrefPeer() const;
|
||||
BotStarRef::Type starrefType() const;
|
||||
PollData *poll() const;
|
||||
FullMsgId pollContextId() const;
|
||||
std::shared_ptr<Api::WhoReadList> reactionsWhoReadIds() const;
|
||||
|
@ -220,6 +226,9 @@ public:
|
|||
[[nodiscard]] PeerData *starrefPeer() const {
|
||||
return key().starrefPeer();
|
||||
}
|
||||
[[nodiscard]] BotStarRef::Type starrefType() const {
|
||||
return key().starrefType();
|
||||
}
|
||||
[[nodiscard]] PollData *poll() const;
|
||||
[[nodiscard]] FullMsgId pollContextId() const {
|
||||
return key().pollContextId();
|
||||
|
|
|
@ -292,6 +292,7 @@ Dialogs::RowDescriptor WrapWidget::activeChat() const {
|
|||
|| key().isDownloads()
|
||||
|| key().reactionsContextId()
|
||||
|| key().poll()
|
||||
|| key().starrefPeer()
|
||||
|| key().statisticsTag().peer) {
|
||||
return Dialogs::RowDescriptor();
|
||||
}
|
||||
|
|
|
@ -484,8 +484,8 @@ settingsPremiumArrow: icon{{ "settings/premium/arrow", menuIconFg }};
|
|||
settingsPremiumArrowOver: icon{{ "settings/premium/arrow", menuIconFgOver }};
|
||||
|
||||
settingsPremiumOptionsPadding: margins(0px, 9px, 0px, 2px);
|
||||
settingsPremiumTopHeight: 220px;
|
||||
settingsPremiumUserHeight: 223px;
|
||||
settingsPremiumTopHeight: 202px;
|
||||
settingsPremiumUserHeight: 205px;
|
||||
settingsPremiumUserTitlePadding: margins(0px, 16px, 0px, 6px);
|
||||
settingsPremiumUserTitle: FlatLabel(boxTitle) {
|
||||
style: TextStyle(defaultTextStyle) {
|
||||
|
|
|
@ -612,23 +612,10 @@ QPointer<Ui::RpWidget> Business::createPinnedToTop(
|
|||
content->setRoundEdges(wrap == Info::Wrap::Layer);
|
||||
}, content->lifetime());
|
||||
|
||||
const auto calculateMaximumHeight = [=] {
|
||||
return st::settingsPremiumTopHeight;
|
||||
};
|
||||
|
||||
content->setMaximumHeight(calculateMaximumHeight());
|
||||
content->setMinimumHeight(st::settingsPremiumTopHeight);// st::infoLayerTopBarHeight);
|
||||
content->setMaximumHeight(st::settingsPremiumTopHeight);
|
||||
content->setMinimumHeight(st::settingsPremiumTopHeight);
|
||||
|
||||
content->resize(content->width(), content->maximumHeight());
|
||||
//content->additionalHeight(
|
||||
//) | rpl::start_with_next([=](int additionalHeight) {
|
||||
// const auto wasMax = (content->height() == content->maximumHeight());
|
||||
// content->setMaximumHeight(calculateMaximumHeight()
|
||||
// + additionalHeight);
|
||||
// if (wasMax) {
|
||||
// content->resize(content->width(), content->maximumHeight());
|
||||
// }
|
||||
//}, content->lifetime());
|
||||
|
||||
_wrap.value(
|
||||
) | rpl::start_with_next([=](Info::Wrap wrap) {
|
||||
|
|
|
@ -37,7 +37,7 @@ creditsLowBalancePremiumCover: PremiumCover(creditsPremiumCover) {
|
|||
starSize: size(64px, 62px);
|
||||
starTopSkip: 30px;
|
||||
}
|
||||
creditsLowBalancePremiumCoverHeight: 180px;
|
||||
creditsLowBalancePremiumCoverHeight: 162px;
|
||||
creditsTopupButton: SettingsButton(settingsButton) {
|
||||
style: semiboldTextStyle;
|
||||
}
|
||||
|
|
|
@ -75,6 +75,12 @@ userPremiumCoverAbout: FlatLabel(boxDividerLabel) {
|
|||
userPremiumCover: PremiumCover(defaultPremiumCover) {
|
||||
about: userPremiumCoverAbout;
|
||||
}
|
||||
starrefCover: PremiumCover(userPremiumCover) {
|
||||
bg: windowBgOver;
|
||||
starTopSkip: 24px;
|
||||
titlePadding: margins(0px, 12px, 0px, 11px);
|
||||
}
|
||||
starrefCoverHeight: 180px;
|
||||
|
||||
defaultPremiumBoxLabel: FlatLabel(defaultFlatLabel) {
|
||||
minWidth: 220px;
|
||||
|
|
|
@ -140,8 +140,7 @@ TopBar::TopBar(
|
|||
QGradientStops{{ 0, st::premiumButtonFg->c }});
|
||||
} else if (_logo == u"affiliate"_q) {
|
||||
_dollar = ScaleTo(QImage(u":/gui/art/affiliate_logo.png"_q));
|
||||
_ministars.setColorOverride(
|
||||
QGradientStops{{ 0, st::premiumButtonFg->c }});
|
||||
_ministars.setColorOverride(descriptor.gradientStops);
|
||||
} else if (!_light && !TopBarAbstract::isDark()) {
|
||||
_star.load(Svg());
|
||||
_ministars.setColorOverride(
|
||||
|
@ -184,7 +183,7 @@ void TopBar::setTextPosition(int x, int y) {
|
|||
rpl::producer<int> TopBar::additionalHeight() const {
|
||||
return _about->heightValue(
|
||||
) | rpl::map([l = st().about.style.lineHeight](int height) {
|
||||
return std::max(height - l * 2, 0);
|
||||
return std::max(height - l, 0);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -223,8 +222,6 @@ void TopBar::resizeEvent(QResizeEvent *e) {
|
|||
void TopBar::paintEvent(QPaintEvent *e) {
|
||||
auto p = QPainter(this);
|
||||
|
||||
p.fillRect(e->rect(), Qt::transparent);
|
||||
|
||||
const auto r = rect();
|
||||
|
||||
if (!_light && !TopBarAbstract::isDark()) {
|
||||
|
|
|
@ -381,6 +381,9 @@ void SessionNavigation::showPeerByLink(const PeerByLinkInfo &info) {
|
|||
showPeerByLinkResolved(peer, info);
|
||||
});
|
||||
} else if (const auto name = std::get_if<QString>(&info.usernameOrId)) {
|
||||
const auto starref = (info.resolveType == ResolveType::StarRef)
|
||||
? info.startToken
|
||||
: QString();
|
||||
resolveUsername(*name, [=](not_null<PeerData*> peer) {
|
||||
if (info.startAutoSubmit) {
|
||||
peer->session().api().blockedPeers().unblock(
|
||||
|
@ -392,7 +395,7 @@ void SessionNavigation::showPeerByLink(const PeerByLinkInfo &info) {
|
|||
} else {
|
||||
showPeerByLinkResolved(peer, info);
|
||||
}
|
||||
});
|
||||
}, starref);
|
||||
} else if (const auto id = std::get_if<ChannelId>(&info.usernameOrId)) {
|
||||
resolveChannelById(*id, [=](not_null<ChannelData*> channel) {
|
||||
showPeerByLinkResolved(channel, info);
|
||||
|
@ -454,16 +457,20 @@ void SessionNavigation::resolveChatLink(
|
|||
|
||||
void SessionNavigation::resolveUsername(
|
||||
const QString &username,
|
||||
Fn<void(not_null<PeerData*>)> done) {
|
||||
if (const auto peer = _session->data().peerByUsername(username)) {
|
||||
done(peer);
|
||||
return;
|
||||
Fn<void(not_null<PeerData*>)> done,
|
||||
const QString &starref) {
|
||||
if (starref.isEmpty()) {
|
||||
if (const auto peer = _session->data().peerByUsername(username)) {
|
||||
done(peer);
|
||||
return;
|
||||
}
|
||||
}
|
||||
_api.request(base::take(_resolveRequestId)).cancel();
|
||||
using Flag = MTPcontacts_ResolveUsername::Flag;
|
||||
_resolveRequestId = _api.request(MTPcontacts_ResolveUsername(
|
||||
MTP_flags(0),
|
||||
MTP_flags(starref.isEmpty() ? Flag() : Flag::f_referer),
|
||||
MTP_string(username),
|
||||
MTP_string()
|
||||
MTP_string(starref)
|
||||
)).done([=](const MTPcontacts_ResolvedPeer &result) {
|
||||
resolveDone(result, done);
|
||||
}).fail([=](const MTP::Error &error) {
|
||||
|
@ -678,6 +685,8 @@ void SessionNavigation::showPeerByLinkResolved(
|
|||
}
|
||||
} else if (resolveType == ResolveType::Boost && peer->isChannel()) {
|
||||
resolveBoostState(peer->asChannel());
|
||||
} else if (resolveType == ResolveType::StarRef) {
|
||||
showPeerHistory(peer, params);
|
||||
} else {
|
||||
// Show specific posts only in channels / supergroups.
|
||||
const auto msgId = peer->isChannel()
|
||||
|
|
|
@ -280,7 +280,8 @@ private:
|
|||
Fn<void(not_null<PeerData*> peer, TextWithEntities draft)> done);
|
||||
void resolveUsername(
|
||||
const QString &username,
|
||||
Fn<void(not_null<PeerData*>)> done);
|
||||
Fn<void(not_null<PeerData*>)> done,
|
||||
const QString &starref = QString());
|
||||
void resolveChannelById(
|
||||
ChannelId channelId,
|
||||
Fn<void(not_null<ChannelData*>)> done);
|
||||
|
|
|
@ -24,6 +24,7 @@ enum class ResolveType {
|
|||
Mention,
|
||||
Boost,
|
||||
Profile,
|
||||
StarRef,
|
||||
};
|
||||
|
||||
struct CommentId {
|
||||
|
|
Loading…
Add table
Reference in a new issue