Initial starref programs list implementation.

This commit is contained in:
John Preston 2024-11-28 11:40:07 +04:00
parent 1e15764bb9
commit 62d2346471
21 changed files with 1073 additions and 154 deletions

View file

@ -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

View file

@ -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 });
}

View file

@ -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();

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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,

View file

@ -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(

View file

@ -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;

View file

@ -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) {

View file

@ -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();

View file

@ -292,6 +292,7 @@ Dialogs::RowDescriptor WrapWidget::activeChat() const {
|| key().isDownloads()
|| key().reactionsContextId()
|| key().poll()
|| key().starrefPeer()
|| key().statisticsTag().peer) {
return Dialogs::RowDescriptor();
}

View file

@ -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) {

View file

@ -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) {

View file

@ -37,7 +37,7 @@ creditsLowBalancePremiumCover: PremiumCover(creditsPremiumCover) {
starSize: size(64px, 62px);
starTopSkip: 30px;
}
creditsLowBalancePremiumCoverHeight: 180px;
creditsLowBalancePremiumCoverHeight: 162px;
creditsTopupButton: SettingsButton(settingsButton) {
style: semiboldTextStyle;
}

View file

@ -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;

View file

@ -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()) {

View file

@ -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()

View file

@ -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);

View file

@ -24,6 +24,7 @@ enum class ResolveType {
Mention,
Boost,
Profile,
StarRef,
};
struct CommentId {