diff --git a/Telegram/CMakeLists.txt b/Telegram/CMakeLists.txt index 5b86db84b..97debd0ad 100644 --- a/Telegram/CMakeLists.txt +++ b/Telegram/CMakeLists.txt @@ -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 diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp index de763fb3d..757dc01e4 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_info_box.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 }); } diff --git a/Telegram/SourceFiles/core/local_url_handlers.cpp b/Telegram/SourceFiles/core/local_url_handlers.cpp index 48705366b..aa3afb9a1 100644 --- a/Telegram/SourceFiles/core/local_url_handlers.cpp +++ b/Telegram/SourceFiles/core/local_url_handlers.cpp @@ -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(); diff --git a/Telegram/SourceFiles/info/bot/starref/info_bot_starref_join_widget.cpp b/Telegram/SourceFiles/info/bot/starref/info_bot_starref_join_widget.cpp new file mode 100644 index 000000000..9e82b101a --- /dev/null +++ b/Telegram/SourceFiles/info/bot/starref/info_bot_starref_join_widget.cpp @@ -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 + +namespace Info::BotStarRef::Join { +namespace { + +constexpr auto kPerPage = 50; + +enum class JoinType { + Joined, + Suggested, +}; + +class ListController final : public PeerListController { +public: + ListController( + not_null controller, + not_null peer, + JoinType type); + ~ListController(); + + Main::Session &session() const override; + void prepare() override; + void rowClicked(not_null row) override; + base::unique_qptr rowContextMenu( + QWidget *parent, + not_null row) override; + void loadMoreRows() override; + + [[nodiscard]] rpl::producer rowCountValue() const; + + //std::unique_ptr createRestoredRow( + // not_null peer) override { + // return createRow(peer); + //} + + //std::unique_ptr saveState() const override; + //void restoreState(std::unique_ptr state) override; + + void setContentWidget(not_null widget); + [[nodiscard]] rpl::producer unlockHeightValue() const; + +private: + struct RowState { + StarRefProgram program; + QString link; + int users = 0; + }; + + [[nodiscard]] std::unique_ptr createRow( + not_null peer, + RowState state); + void showLink(not_null peer, RowState state); + + struct SavedState : SavedStateBase { + }; + const not_null _controller; + const not_null _peer; + const JoinType _type = {}; + + base::flat_map, RowState> _states; + + mtpRequestId _requestId = 0; + TimeId _offsetDate = 0; + QString _offsetThing; + bool _allLoaded = false; + + rpl::variable _rowCount = 0; + +}; + +ListController::ListController( + not_null controller, + not_null 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 ListController::createRow( + not_null peer, + RowState state) { + _states.emplace(peer, state); + auto result = std::make_unique(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 ListController::rowCountValue() const { + return _rowCount.value(); +} +// +//std::unique_ptr ListController::saveState() const { +// auto result = PeerListController::saveState(); +// auto my = std::make_unique(); +// result->controllerState = std::move(my); +// return result; +//} +// +//void ListController::restoreState( +// std::unique_ptr state) { +// auto typeErasedState = state +// ? state->controllerState.get() +// : nullptr; +// if (dynamic_cast(typeErasedState)) { +// PeerListController::restoreState(std::move(state)); +// } +//} + +void ListController::showLink(not_null peer, RowState state) { + const auto window = _controller->parentController(); + window->show(Box([=](not_null 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(box, std::move(text), st::boxLabel)); + Ui::AddSkip(box->verticalLayout()); + box->addRow( + object_ptr( + box, + rpl::single(state.link) | Ui::Text::ToLink(), + st::boxLabel)); + Ui::AddSkip(box->verticalLayout()); + if (state.users > 0) { + box->addRow( + object_ptr( + 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( + 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 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 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 ListController::rowContextMenu( + QWidget *parent, + not_null 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( + 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); + + [[nodiscard]] not_null peer() const; + + void showFinished(); + void setInnerFocus(); + + void saveState(not_null memento); + void restoreState(not_null memento); + +private: + void prepare(); + void setupInfo(); + void setupMy(); + void setupSuggested(); + + [[nodiscard]] object_ptr infoRow( + rpl::producer title, + rpl::producer text, + not_null icon); + + const not_null _controller; + const not_null _container; + +}; + +InnerWidget::InnerWidget(QWidget *parent, not_null controller) +: RpWidget(parent) +, _controller(controller) +, _container(Ui::CreateChild(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>( + _container, + object_ptr(_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( + _controller, + peer(), + JoinType::Joined); + const auto content = inner->add( + object_ptr( + _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( + _controller, + peer(), + JoinType::Suggested); + const auto content = _container->add( + object_ptr( + _container, + controller)); + delegate->setContent(content); + controller->setDelegate(delegate); +} + +object_ptr InnerWidget::infoRow( + rpl::producer title, + rpl::producer text, + not_null icon) { + auto result = object_ptr(_container); + const auto raw = result.data(); + + raw->add( + object_ptr( + raw, + std::move(title) | Ui::Text::ToBold(), + st::defaultFlatLabel), + st::settingsPremiumRowTitlePadding); + raw->add( + object_ptr( + raw, + std::move(text), + st::boxDividerLabel), + st::settingsPremiumRowAboutPadding); + object_ptr( + raw, + *icon, + st::starrefInfoIconPosition); + + return result; +} + +not_null InnerWidget::peer() const { + return _controller->key().starrefPeer(); +} + +void InnerWidget::showFinished() { + +} + +void InnerWidget::setInnerFocus() { + setFocus(); +} + +void InnerWidget::saveState(not_null memento) { + +} + +void InnerWidget::restoreState(not_null memento) { + +} + +Memento::Memento(not_null controller) +: ContentMemento(Tag(controller->starrefPeer(), controller->starrefType())) { +} + +Memento::Memento(not_null peer) +: ContentMemento(Tag(peer, Type::Join)) { +} + +Memento::~Memento() = default; + +Section Memento::section() const { + return Section(Section::Type::BotStarRef); +} + +object_ptr Memento::createWidget( + QWidget *parent, + not_null controller, + const QRect &geometry) { + auto result = object_ptr(parent, controller); + result->setInternalState(geometry, this); + return result; +} + +Widget::Widget( + QWidget *parent, + not_null controller) +: ContentWidget(parent, controller) +, _inner(setInnerWidget(object_ptr(this, controller))) { + _top = setupTop(); +} + +not_null Widget::peer() const { + return _inner->peer(); +} + +bool Widget::showInternal(not_null memento) { + return (memento->starrefPeer() == peer()); +} + +rpl::producer Widget::title() { + return tr::lng_star_ref_list_title(); +} + +void Widget::setInternalState( + const QRect &geometry, + not_null memento) { + setGeometry(geometry); + Ui::SendPendingMoveResizeEvents(this); + restoreState(memento); +} + +rpl::producer Widget::desiredShadowVisibility() const { + return rpl::single(true); +} + +void Widget::showFinished() { + _inner->showFinished(); +} + +void Widget::setInnerFocus() { + _inner->setInnerFocus(); +} + +void Widget::enableBackButton() { + _backEnabled = true; +} + +std::shared_ptr Widget::doCreateMemento() { + auto result = std::make_shared(controller()); + saveState(result.get()); + return result; +} + +void Widget::saveState(not_null memento) { + memento->setScrollTop(scrollTopSave()); + _inner->saveState(memento); +} + +void Widget::restoreState(not_null memento) { + _inner->restoreState(memento); + scrollTopRestore(memento->scrollTop()); +} + +std::unique_ptr 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( + 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>( + raw, + object_ptr( + 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( + 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 Make(not_null peer) { + return std::make_shared( + std::vector>( + 1, + std::make_shared(peer))); +} + +} // namespace Info::BotStarRef::Join + diff --git a/Telegram/SourceFiles/info/bot/starref/info_bot_starref_widget.h b/Telegram/SourceFiles/info/bot/starref/info_bot_starref_join_widget.h similarity index 89% rename from Telegram/SourceFiles/info/bot/starref/info_bot_starref_widget.h rename to Telegram/SourceFiles/info/bot/starref/info_bot_starref_join_widget.h index bbcd4ae7c..129caed9f 100644 --- a/Telegram/SourceFiles/info/bot/starref/info_bot_starref_widget.h +++ b/Telegram/SourceFiles/info/bot/starref/info_bot_starref_join_widget.h @@ -17,11 +17,12 @@ namespace Ui { template 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); [[nodiscard]] std::unique_ptr setupTop(); - [[nodiscard]] std::unique_ptr setupBottom(); std::shared_ptr doCreateMemento() override; const not_null _inner; - const not_null _state; std::unique_ptr _top; base::unique_qptr> _back; base::unique_qptr _close; rpl::variable _backEnabled; - std::unique_ptr _bottom; - }; [[nodiscard]] std::shared_ptr Make(not_null peer); -} // namespace Info::BotStarRef +} // namespace Info::BotStarRef::Join diff --git a/Telegram/SourceFiles/info/bot/starref/info_bot_starref_widget.cpp b/Telegram/SourceFiles/info/bot/starref/info_bot_starref_setup_widget.cpp similarity index 90% rename from Telegram/SourceFiles/info/bot/starref/info_bot_starref_widget.cpp rename to Telegram/SourceFiles/info/bot/starref/info_bot_starref_setup_widget.cpp index 00e0e7d5c..4ba776b1d 100644 --- a/Telegram/SourceFiles/info/bot/starref/info_bot_starref_widget.cpp +++ b/Telegram/SourceFiles/info/bot/starref/info_bot_starref_setup_widget.cpp @@ -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(); + 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( - content.get(), - rpl::single(QString())); - - const auto label = content->add( - object_ptr( - content, - tr::lng_star_ref_existing_title() | Ui::Text::ToBold(), - stLabel), - titlePadding); - label->setAttribute(Qt::WA_TransparentForMouseEvents); - const auto description = content->add( - object_ptr( - content, - tr::lng_star_ref_existing_about(), - st::boxDividerLabel), - descriptionPadding); - description->setAttribute(Qt::WA_TransparentForMouseEvents); - - const auto dummy = Ui::CreateChild(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( - 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 InnerWidget::infoRow( @@ -411,11 +343,11 @@ void InnerWidget::restoreState(not_null memento) { } Memento::Memento(not_null controller) -: ContentMemento(Tag(controller->starrefPeer())) { +: ContentMemento(Tag(controller->starrefPeer(), controller->starrefType())) { } Memento::Memento(not_null peer) -: ContentMemento(Tag(peer)) { +: ContentMemento(Tag(peer, Type::Setup)) { } Memento::~Memento() = default; @@ -509,7 +441,7 @@ std::unique_ptr Widget::setupTop() { }; auto result = std::make_unique( this, - st::userPremiumCover, + st::starrefCover, Ui::Premium::TopBarDescriptor{ .clickContextOther = clickContextOther, .logo = u"affiliate"_q, @@ -524,16 +456,15 @@ std::unique_ptr 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 Widget::setupTop() { } else { _close = base::make_unique_q( raw, - st::settingsPremiumTopBarClose); + st::infoTopBarClose); _close->addClickHandler([=] { controller->parentController()->hideLayer(); controller->parentController()->hideSpecialLayer(); @@ -677,5 +608,92 @@ std::shared_ptr Make(not_null peer) { std::make_shared(peer))); } -} // namespace Info::BotStarRef +not_null AddViewListButton( + not_null parent, + rpl::producer title, + rpl::producer 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( + parent, + rpl::single(QString())); + + const auto label = parent->add( + object_ptr( + parent, + std::move(title) | Ui::Text::ToBold(), + stLabel), + titlePadding); + label->setAttribute(Qt::WA_TransparentForMouseEvents); + const auto description = parent->add( + object_ptr( + parent, + std::move(subtitle), + st::boxDividerLabel), + descriptionPadding); + description->setAttribute(Qt::WA_TransparentForMouseEvents); + + const auto dummy = Ui::CreateChild(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( + 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 diff --git a/Telegram/SourceFiles/info/bot/starref/info_bot_starref_setup_widget.h b/Telegram/SourceFiles/info/bot/starref/info_bot_starref_setup_widget.h new file mode 100644 index 000000000..4a862a0f4 --- /dev/null +++ b/Telegram/SourceFiles/info/bot/starref/info_bot_starref_setup_widget.h @@ -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 +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); + Memento(not_null peer); + ~Memento(); + + object_ptr createWidget( + QWidget *parent, + not_null controller, + const QRect &geometry) override; + + Section section() const override; + +}; + +class Widget final : public ContentWidget { +public: + Widget(QWidget *parent, not_null controller); + + bool showInternal(not_null memento) override; + rpl::producer title() override; + rpl::producer desiredShadowVisibility() const override; + void showFinished() override; + void setInnerFocus() override; + void enableBackButton() override; + + [[nodiscard]] not_null peer() const; + + void setInternalState( + const QRect &geometry, + not_null memento); + +private: + void saveState(not_null memento); + void restoreState(not_null memento); + + [[nodiscard]] std::unique_ptr setupTop(); + [[nodiscard]] std::unique_ptr setupBottom(); + + std::shared_ptr doCreateMemento() override; + + const not_null _inner; + const not_null _state; + + std::unique_ptr _top; + base::unique_qptr> _back; + base::unique_qptr _close; + rpl::variable _backEnabled; + + std::unique_ptr _bottom; + +}; + +[[nodiscard]] std::shared_ptr Make(not_null peer); + +[[nodiscard]] not_null AddViewListButton( + not_null parent, + rpl::producer title, + rpl::producer subtitle); + +} // namespace Info::BotStarRef::Setup diff --git a/Telegram/SourceFiles/info/channel_statistics/earn/info_channel_earn_list.cpp b/Telegram/SourceFiles/info/channel_statistics/earn/info_channel_earn_list.cpp index ea6b64c32..3d721bb11 100644 --- a/Telegram/SourceFiles/info/channel_statistics/earn/info_channel_earn_list.cpp +++ b/Telegram/SourceFiles/info/channel_statistics/earn/info_channel_earn_list.cpp @@ -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(0); const auto rebuildLists = [=]( const Memento::SavedState &data, diff --git a/Telegram/SourceFiles/info/info_content_widget.cpp b/Telegram/SourceFiles/info/info_content_widget.cpp index b33417bab..444b50358 100644 --- a/Telegram/SourceFiles/info/info_content_widget.cpp +++ b/Telegram/SourceFiles/info/info_content_widget.cpp @@ -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( diff --git a/Telegram/SourceFiles/info/info_content_widget.h b/Telegram/SourceFiles/info/info_content_widget.h index 12ac85074..57c95b09f 100644 --- a/Telegram/SourceFiles/info/info_content_widget.h +++ b/Telegram/SourceFiles/info/info_content_widget.h @@ -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 _reactionsWhoReadIds; Data::ReactionId _reactionsSelected; diff --git a/Telegram/SourceFiles/info/info_controller.cpp b/Telegram/SourceFiles/info/info_controller.cpp index 38ef2852f..44a80343c 100644 --- a/Telegram/SourceFiles/info/info_controller.cpp +++ b/Telegram/SourceFiles/info/info_controller.cpp @@ -116,6 +116,13 @@ PeerData *Key::starrefPeer() const { return nullptr; } +BotStarRef::Type Key::starrefType() const { + if (const auto tag = std::get_if(&_value)) { + return tag->type; + } + return BotStarRef::Type(); +} + PollData *Key::poll() const { if (const auto data = std::get_if(&_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 memento) { diff --git a/Telegram/SourceFiles/info/info_controller.h b/Telegram/SourceFiles/info/info_controller.h index 39f2d2bb1..19cc7c936 100644 --- a/Telegram/SourceFiles/info/info_controller.h +++ b/Telegram/SourceFiles/info/info_controller.h @@ -63,11 +63,16 @@ struct Tag { namespace Info::BotStarRef { +enum class Type : uchar { + Setup, + Join, +}; struct Tag { - explicit Tag(not_null peer) : peer(peer) { + Tag(not_null peer, Type type) : peer(peer), type(type) { } not_null 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 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(); diff --git a/Telegram/SourceFiles/info/info_wrap_widget.cpp b/Telegram/SourceFiles/info/info_wrap_widget.cpp index 4bd0dc12b..24b133d38 100644 --- a/Telegram/SourceFiles/info/info_wrap_widget.cpp +++ b/Telegram/SourceFiles/info/info_wrap_widget.cpp @@ -292,6 +292,7 @@ Dialogs::RowDescriptor WrapWidget::activeChat() const { || key().isDownloads() || key().reactionsContextId() || key().poll() + || key().starrefPeer() || key().statisticsTag().peer) { return Dialogs::RowDescriptor(); } diff --git a/Telegram/SourceFiles/settings/settings.style b/Telegram/SourceFiles/settings/settings.style index cff0d7f38..050fffc73 100644 --- a/Telegram/SourceFiles/settings/settings.style +++ b/Telegram/SourceFiles/settings/settings.style @@ -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) { diff --git a/Telegram/SourceFiles/settings/settings_business.cpp b/Telegram/SourceFiles/settings/settings_business.cpp index 66b707c5f..c60d20999 100644 --- a/Telegram/SourceFiles/settings/settings_business.cpp +++ b/Telegram/SourceFiles/settings/settings_business.cpp @@ -612,23 +612,10 @@ QPointer 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) { diff --git a/Telegram/SourceFiles/ui/effects/credits.style b/Telegram/SourceFiles/ui/effects/credits.style index 84008e849..fe03ea0a6 100644 --- a/Telegram/SourceFiles/ui/effects/credits.style +++ b/Telegram/SourceFiles/ui/effects/credits.style @@ -37,7 +37,7 @@ creditsLowBalancePremiumCover: PremiumCover(creditsPremiumCover) { starSize: size(64px, 62px); starTopSkip: 30px; } -creditsLowBalancePremiumCoverHeight: 180px; +creditsLowBalancePremiumCoverHeight: 162px; creditsTopupButton: SettingsButton(settingsButton) { style: semiboldTextStyle; } diff --git a/Telegram/SourceFiles/ui/effects/premium.style b/Telegram/SourceFiles/ui/effects/premium.style index 7cbe72f68..a4f57331f 100644 --- a/Telegram/SourceFiles/ui/effects/premium.style +++ b/Telegram/SourceFiles/ui/effects/premium.style @@ -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; diff --git a/Telegram/SourceFiles/ui/effects/premium_top_bar.cpp b/Telegram/SourceFiles/ui/effects/premium_top_bar.cpp index e0bee03ee..4d538ce37 100644 --- a/Telegram/SourceFiles/ui/effects/premium_top_bar.cpp +++ b/Telegram/SourceFiles/ui/effects/premium_top_bar.cpp @@ -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 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()) { diff --git a/Telegram/SourceFiles/window/window_session_controller.cpp b/Telegram/SourceFiles/window/window_session_controller.cpp index e2f6c51c3..74a0bae7d 100644 --- a/Telegram/SourceFiles/window/window_session_controller.cpp +++ b/Telegram/SourceFiles/window/window_session_controller.cpp @@ -381,6 +381,9 @@ void SessionNavigation::showPeerByLink(const PeerByLinkInfo &info) { showPeerByLinkResolved(peer, info); }); } else if (const auto name = std::get_if(&info.usernameOrId)) { + const auto starref = (info.resolveType == ResolveType::StarRef) + ? info.startToken + : QString(); resolveUsername(*name, [=](not_null 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(&info.usernameOrId)) { resolveChannelById(*id, [=](not_null channel) { showPeerByLinkResolved(channel, info); @@ -454,16 +457,20 @@ void SessionNavigation::resolveChatLink( void SessionNavigation::resolveUsername( const QString &username, - Fn)> done) { - if (const auto peer = _session->data().peerByUsername(username)) { - done(peer); - return; + Fn)> 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() diff --git a/Telegram/SourceFiles/window/window_session_controller.h b/Telegram/SourceFiles/window/window_session_controller.h index f6880bdbe..041c55113 100644 --- a/Telegram/SourceFiles/window/window_session_controller.h +++ b/Telegram/SourceFiles/window/window_session_controller.h @@ -280,7 +280,8 @@ private: Fn peer, TextWithEntities draft)> done); void resolveUsername( const QString &username, - Fn)> done); + Fn)> done, + const QString &starref = QString()); void resolveChannelById( ChannelId channelId, Fn)> done); diff --git a/Telegram/SourceFiles/window/window_session_controller_link_info.h b/Telegram/SourceFiles/window/window_session_controller_link_info.h index 145da2d41..8a8995273 100644 --- a/Telegram/SourceFiles/window/window_session_controller_link_info.h +++ b/Telegram/SourceFiles/window/window_session_controller_link_info.h @@ -24,6 +24,7 @@ enum class ResolveType { Mention, Boost, Profile, + StarRef, }; struct CommentId {