diff --git a/Telegram/SourceFiles/api/api_premium.cpp b/Telegram/SourceFiles/api/api_premium.cpp index 088c3cf87..b59547e99 100644 --- a/Telegram/SourceFiles/api/api_premium.cpp +++ b/Telegram/SourceFiles/api/api_premium.cpp @@ -15,6 +15,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_peer.h" #include "data/data_peer_values.h" #include "data/data_session.h" +#include "data/data_user.h" +#include "history/view/history_view_element.h" +#include "history/history.h" +#include "history/history_item.h" #include "main/main_account.h" #include "main/main_app_config.h" #include "main/main_session.h" @@ -334,6 +338,72 @@ const Data::SubscriptionOptions &Premium::subscriptionOptions() const { return _subscriptionOptions; } +rpl::producer<> Premium::somePremiumRequiredResolved() const { + return _somePremiumRequiredResolved.events(); +} + +void Premium::resolvePremiumRequired(not_null user) { + _resolvePremiumRequiredUsers.emplace(user); + if (!_premiumRequiredRequestScheduled + && _resolvePremiumRequestedUsers.empty()) { + _premiumRequiredRequestScheduled = true; + crl::on_main(_session, [=] { + requestPremiumRequiredSlice(); + }); + } +} + +void Premium::requestPremiumRequiredSlice() { + _premiumRequiredRequestScheduled = false; + if (!_resolvePremiumRequestedUsers.empty() + || _resolvePremiumRequiredUsers.empty()) { + return; + } + constexpr auto kPerRequest = 100; + auto users = MTP_vector_from_range(_resolvePremiumRequiredUsers + | ranges::views::transform(&UserData::inputUser)); + if (users.v.size() > kPerRequest) { + auto shortened = users.v; + shortened.resize(kPerRequest); + users = MTP_vector(std::move(shortened)); + const auto from = begin(_resolvePremiumRequiredUsers); + _resolvePremiumRequestedUsers = { from, from + kPerRequest }; + _resolvePremiumRequiredUsers.erase(from, from + kPerRequest); + } else { + _resolvePremiumRequestedUsers + = base::take(_resolvePremiumRequiredUsers); + } + const auto finish = [=](const QVector &list) { + constexpr auto me = UserDataFlag::MeRequiresPremiumToWrite; + constexpr auto known = UserDataFlag::RequirePremiumToWriteKnown; + constexpr auto mask = me | known; + + auto index = 0; + for (const auto &user : base::take(_resolvePremiumRequestedUsers)) { + const auto require = (index < list.size()) + && mtpIsTrue(list[index++]); + user->setFlags((user->flags() & ~mask) + | known + | (require ? me : UserDataFlag())); + } + if (!_premiumRequiredRequestScheduled + && !_resolvePremiumRequiredUsers.empty()) { + _premiumRequiredRequestScheduled = true; + crl::on_main(_session, [=] { + requestPremiumRequiredSlice(); + }); + } + _somePremiumRequiredResolved.fire({}); + }; + _session->api().request( + MTPusers_GetIsPremiumRequiredToContact(std::move(users)) + ).done([=](const MTPVector &result) { + finish(result.v); + }).fail([=] { + finish({}); + }).send(); +} + PremiumGiftCodeOptions::PremiumGiftCodeOptions(not_null peer) : _peer(peer) , _api(&peer->session().api().instance()) { @@ -494,4 +564,33 @@ bool PremiumGiftCodeOptions::giveawayGiftsPurchaseAvailable() const { false); } +RequirePremiumState ResolveRequiresPremiumToWrite( + not_null history) { + const auto user = history->peer->asUser(); + if (!user + || !user->someRequirePremiumToWrite() + || user->session().premium()) { + return RequirePremiumState::No; + } else if (user->requirePremiumToWriteKnown()) { + return user->meRequiresPremiumToWrite() + ? RequirePremiumState::Yes + : RequirePremiumState::No; + } + // We allow this potentially-heavy loop because in case we've opened + // the chat and have a lot of messages `requires_premium` will be known. + for (const auto &block : history->blocks) { + for (const auto &view : block->messages) { + const auto item = view->data(); + if (!item->out() && !item->isService()) { + using Flag = UserDataFlag; + constexpr auto known = Flag::RequirePremiumToWriteKnown; + constexpr auto me = Flag::MeRequiresPremiumToWrite; + user->setFlags((user->flags() | known) & ~me); + return RequirePremiumState::No; + } + } + } + return RequirePremiumState::Unknown; +} + } // namespace Api diff --git a/Telegram/SourceFiles/api/api_premium.h b/Telegram/SourceFiles/api/api_premium.h index 1199b4819..19f946691 100644 --- a/Telegram/SourceFiles/api/api_premium.h +++ b/Telegram/SourceFiles/api/api_premium.h @@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_subscription_option.h" #include "mtproto/sender.h" +class History; class ApiWrap; namespace Main { @@ -103,10 +104,14 @@ public: [[nodiscard]] auto subscriptionOptions() const -> const Data::SubscriptionOptions &; + [[nodiscard]] rpl::producer<> somePremiumRequiredResolved() const; + void resolvePremiumRequired(not_null user); + private: void reloadPromo(); void reloadStickers(); void reloadCloudSet(); + void requestPremiumRequiredSlice(); const not_null _session; MTP::Sender _api; @@ -143,6 +148,11 @@ private: Data::SubscriptionOptions _subscriptionOptions; + rpl::event_stream<> _somePremiumRequiredResolved; + base::flat_set> _resolvePremiumRequiredUsers; + base::flat_set> _resolvePremiumRequestedUsers; + bool _premiumRequiredRequestScheduled = false; + }; class PremiumGiftCodeOptions final { @@ -196,4 +206,12 @@ private: }; +enum class RequirePremiumState { + Unknown, + Yes, + No, +}; +[[nodiscard]] RequirePremiumState ResolveRequiresPremiumToWrite( + not_null history); + } // namespace Api diff --git a/Telegram/SourceFiles/boxes/peer_list_box.cpp b/Telegram/SourceFiles/boxes/peer_list_box.cpp index 9f12a539e..3278088ec 100644 --- a/Telegram/SourceFiles/boxes/peer_list_box.cpp +++ b/Telegram/SourceFiles/boxes/peer_list_box.cpp @@ -544,6 +544,12 @@ bool PeerListRow::checked() const { return _checkbox && _checkbox->checked(); } +void PeerListRow::preloadUserpic() { + if (_peer) { + _peer->loadUserpic(); + } +} + void PeerListRow::setCustomStatus(const QString &status, bool active) { setStatusText(status); _statusType = active ? StatusType::CustomActive : StatusType::Custom; @@ -1914,10 +1920,7 @@ void PeerListContent::loadProfilePhotos() { if (to > rowsCount) to = rowsCount; for (auto index = from; index != to; ++index) { - const auto row = getRow(RowIndex(index)); - if (!row->special()) { - row->peer()->loadUserpic(); - } + getRow(RowIndex(index))->preloadUserpic(); } } } diff --git a/Telegram/SourceFiles/boxes/peer_list_box.h b/Telegram/SourceFiles/boxes/peer_list_box.h index fb357da1c..25e96f60b 100644 --- a/Telegram/SourceFiles/boxes/peer_list_box.h +++ b/Telegram/SourceFiles/boxes/peer_list_box.h @@ -101,6 +101,8 @@ public: [[nodiscard]] virtual auto generateNameWords() const -> const base::flat_set &; + virtual void preloadUserpic(); + void setCustomStatus(const QString &status, bool active = false); void clearCustomStatus(); diff --git a/Telegram/SourceFiles/boxes/peer_list_controllers.cpp b/Telegram/SourceFiles/boxes/peer_list_controllers.cpp index 6b4404d17..7d8c2d291 100644 --- a/Telegram/SourceFiles/boxes/peer_list_controllers.cpp +++ b/Telegram/SourceFiles/boxes/peer_list_controllers.cpp @@ -8,10 +8,13 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "boxes/peer_list_controllers.h" #include "api/api_chat_participants.h" +#include "api/api_premium.h" #include "base/random.h" #include "boxes/filters/edit_filter_chats_list.h" +#include "settings/settings_premium.h" #include "ui/boxes/confirm_box.h" #include "ui/effects/round_checkbox.h" +#include "ui/text/text_utilities.h" #include "ui/widgets/menu/menu_add_action_callback_factory.h" #include "ui/widgets/checkbox.h" #include "ui/widgets/popup_menu.h" @@ -19,6 +22,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/painter.h" #include "ui/ui_utility.h" #include "main/main_session.h" +#include "data/data_peer_values.h" #include "data/data_session.h" #include "data/data_stories.h" #include "data/data_channel.h" @@ -44,10 +48,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "styles/style_boxes.h" #include "styles/style_profile.h" #include "styles/style_dialogs.h" - -#include "data/data_stories.h" -#include "dialogs/ui/dialogs_stories_content.h" -#include "dialogs/ui/dialogs_stories_list.h" +#include "styles/style_chat_helpers.h" namespace { @@ -257,9 +258,63 @@ bool PeerListGlobalSearchController::isLoading() { return _timer.isActive() || _requestId; } -ChatsListBoxController::Row::Row(not_null history) +void ChatsListBoxController::RowDelegate::rowPreloadUserpic( + not_null row) { + row->PeerListRow::preloadUserpic(); +} + +ChatsListBoxController::Row::Row( + not_null history, + RowDelegate *delegate) : PeerListRow(history->peer) -, _history(history) { +, _history(history) +, _delegate(delegate) { +} + +void PaintLock( + Painter &p, + not_null st, + int x, + int y, + int outerWidth, + int size) { + auto hq = PainterHighQualityEnabler(p); + const auto &check = st->checkbox.check; + auto pen = check.border->p; + pen.setWidthF(check.width); + p.setPen(pen); + p.setBrush(st::premiumButtonBg2); + const auto &icon = st::stickersPremiumLock; + const auto width = icon.width(); + const auto height = icon.height(); + const auto rect = QRect( + QPoint(x + size - width, y + size - height), + icon.size()); + p.drawEllipse(rect); + icon.paintInCenter(p, rect); +} + +auto ChatsListBoxController::Row::generatePaintUserpicCallback( + bool forceRound) +-> PaintRoundImageCallback { + auto result = PeerListRow::generatePaintUserpicCallback(forceRound); + if (_locked) { + AssertIsDebug(); + const auto st = /*_delegate ? _delegate->rowSt() : */&st::defaultPeerListItem; + return [=](Painter &p, int x, int y, int outerWidth, int size) { + result(p, x, y, outerWidth, size); + PaintLock(p, st, x, y, outerWidth, size); + }; + } + return result; +} + +void ChatsListBoxController::Row::preloadUserpic() { + if (_delegate) { + _delegate->rowPreloadUserpic(this); + } else { + PeerListRow::preloadUserpic(); + } } ChatsListBoxController::ChatsListBoxController( @@ -629,14 +684,40 @@ std::unique_ptr ContactsBoxController::createRow( return std::make_unique(user); } +ChooseRecipientPremiumRequiredError WritePremiumRequiredError( + not_null user) { + return { + .text = tr::lng_send_non_premium_message_toast( + tr::now, + lt_user, + TextWithEntities{ user->shortName() }, + lt_link, + Ui::Text::Link( + Ui::Text::Bold( + tr::lng_send_non_premium_message_toast_link( + tr::now))), + Ui::Text::RichLangValue), + }; +} + ChooseRecipientBoxController::ChooseRecipientBoxController( not_null session, FnMut)> callback, Fn)> filter) -: ChatsListBoxController(session) -, _session(session) -, _callback(std::move(callback)) -, _filter(std::move(filter)) { +: ChooseRecipientBoxController({ + .session = session, + .callback = std::move(callback), + .filter = std::move(filter), +}) { +} + +ChooseRecipientBoxController::ChooseRecipientBoxController( + ChooseRecipientArgs &&args) +: ChatsListBoxController(args.session) +, _session(args.session) +, _callback(std::move(args.callback)) +, _filter(std::move(args.filter)) +, _premiumRequiredError(std::move(args.premiumRequiredError)) { } Main::Session &ChooseRecipientBoxController::session() const { @@ -645,9 +726,50 @@ Main::Session &ChooseRecipientBoxController::session() const { void ChooseRecipientBoxController::prepareViewHook() { delegate()->peerListSetTitle(tr::lng_forward_choose()); + + if (_premiumRequiredError) { + rpl::merge( + Data::AmPremiumValue(_session) | rpl::to_empty, + _session->api().premium().somePremiumRequiredResolved() + ) | rpl::start_with_next([=] { + refreshLockedRows(); + }, _lifetime); + } +} + +void ChooseRecipientBoxController::refreshLockedRows() { + auto count = delegate()->peerListFullRowsCount(); + for (auto i = 0; i != count; ++i) { + const auto raw = delegate()->peerListRowAt(i); + const auto row = static_cast(raw.get()); + if (const auto user = row->peer()->asUser()) { + const auto history = row->history(); + const auto locked = (Api::ResolveRequiresPremiumToWrite(history) + == Api::RequirePremiumState::Yes); + if (row->locked() != locked) { + row->setLocked(locked); + delegate()->peerListUpdateRow(row); + } + } + } +} + +void ChooseRecipientBoxController::rowPreloadUserpic(not_null row) { + row->PeerListRow::preloadUserpic(); + + if (!_premiumRequiredError) { + return; + } else if (Api::ResolveRequiresPremiumToWrite(row->history()) + == Api::RequirePremiumState::Unknown) { + const auto user = row->peer()->asUser(); + session().api().premium().resolvePremiumRequired(user); + } } void ChooseRecipientBoxController::rowClicked(not_null row) { + if (showLockedError(row)) { + return; + } auto guard = base::make_weak(this); const auto peer = row->peer(); if (const auto forum = peer->forum()) { @@ -698,6 +820,19 @@ void ChooseRecipientBoxController::rowClicked(not_null row) { } } +bool ChooseRecipientBoxController::showLockedError( + not_null row) const { + if (!static_cast(row.get())->locked()) { + return false; + } + ::Settings::ShowPremiumPromoToast( + delegate()->peerListUiShow(), + ChatHelpers::ResolveWindowDefault(), + _premiumRequiredError(row->peer()->asUser()).text, + u"require_premium"_q); + return true; +} + QString ChooseRecipientBoxController::savedMessagesChatStatus() const { return tr::lng_saved_forward_here(tr::now); } @@ -708,8 +843,23 @@ auto ChooseRecipientBoxController::createRow( const auto skip = _filter ? !_filter(history) : ((peer->isBroadcast() && !Data::CanSendAnything(peer)) - || (peer->isUser() && !Data::CanSendAnything(peer))); - return skip ? nullptr : std::make_unique(history); + || peer->isRepliesChat() + || (peer->isUser() && (_premiumRequiredError + ? peer->asUser()->isInaccessible() + : !Data::CanSendAnything(peer)))); + if (skip) { + return nullptr; + } + auto result = std::make_unique( + history, + static_cast(this)); + if (_premiumRequiredError) { + const auto require = Api::ResolveRequiresPremiumToWrite(history); + if (require == Api::RequirePremiumState::Yes) { + result->setLocked(true); + } + } + return result; } ChooseTopicSearchController::ChooseTopicSearchController( diff --git a/Telegram/SourceFiles/boxes/peer_list_controllers.h b/Telegram/SourceFiles/boxes/peer_list_controllers.h index d720a3423..9ce3dd0bf 100644 --- a/Telegram/SourceFiles/boxes/peer_list_controllers.h +++ b/Telegram/SourceFiles/boxes/peer_list_controllers.h @@ -91,16 +91,34 @@ private: class ChatsListBoxController : public PeerListController { public: + class Row; + class RowDelegate { + public: + virtual void rowPreloadUserpic(not_null row); + }; + class Row : public PeerListRow { public: - Row(not_null history); + Row(not_null history, RowDelegate *delegate = nullptr); - not_null history() const { + [[nodiscard]] not_null history() const { return _history; } + [[nodiscard]] bool locked() const { + return _locked; + } + void setLocked(bool locked) { + _locked = locked; + } + PaintRoundImageCallback generatePaintUserpicCallback( + bool forceRound) override; + + void preloadUserpic() override; private: - not_null _history; + const not_null _history; + RowDelegate *_delegate = nullptr; + bool _locked = false; }; @@ -207,14 +225,32 @@ private: }; +struct ChooseRecipientPremiumRequiredError { + TextWithEntities text; +}; + +[[nodiscard]] ChooseRecipientPremiumRequiredError WritePremiumRequiredError( + not_null user); + +struct ChooseRecipientArgs { + not_null session; + FnMut)> callback; + Fn)> filter; + + using PremiumRequiredError = ChooseRecipientPremiumRequiredError; + Fn)> premiumRequiredError; +}; + class ChooseRecipientBoxController : public ChatsListBoxController - , public base::has_weak_ptr { + , public base::has_weak_ptr + , private ChatsListBoxController::RowDelegate { public: ChooseRecipientBoxController( not_null session, FnMut)> callback, Fn)> filter = nullptr); + explicit ChooseRecipientBoxController(ChooseRecipientArgs &&args); Main::Session &session() const override; void rowClicked(not_null row) override; @@ -225,10 +261,19 @@ protected: void prepareViewHook() override; std::unique_ptr createRow(not_null history) override; + [[nodiscard]] bool showLockedError(not_null row) const; + private: + void refreshLockedRows(); + void rowPreloadUserpic(not_null row) override; + const not_null _session; FnMut)> _callback; Fn)> _filter; + Fn)> _premiumRequiredError; + + rpl::lifetime _lifetime; }; diff --git a/Telegram/SourceFiles/chat_helpers/compose/compose_show.cpp b/Telegram/SourceFiles/chat_helpers/compose/compose_show.cpp index 9a27c6f82..01bc33b8c 100644 --- a/Telegram/SourceFiles/chat_helpers/compose/compose_show.cpp +++ b/Telegram/SourceFiles/chat_helpers/compose/compose_show.cpp @@ -18,29 +18,35 @@ rpl::producer Show::adjustShadowLeft() const { return rpl::single(false); } -Window::SessionController *Show::resolveWindow(WindowUsage usage) const { - const auto session = &this->session(); - const auto check = [&](Window::Controller *window) { - if (const auto controller = window->sessionController()) { - if (&controller->session() == session) { - return controller; +ResolveWindow ResolveWindowDefault() { + return [](not_null session, WindowUsage usage) + -> Window::SessionController* { + const auto check = [&](Window::Controller *window) { + if (const auto controller = window->sessionController()) { + if (&controller->session() == session) { + return controller; + } } + return (Window::SessionController*)nullptr; + }; + auto &app = Core::App(); + if (const auto a = check(app.activeWindow())) { + return a; + } else if (const auto b = check(app.activePrimaryWindow())) { + return b; + } else if (const auto c = check(app.windowFor(&session->account()))) { + return c; + } else if (const auto d = check( + app.ensureSeparateWindowForAccount( + &session->account()))) { + return d; } - return (Window::SessionController*)nullptr; + return nullptr; }; - auto &app = Core::App(); - if (const auto a = check(app.activeWindow())) { - return a; - } else if (const auto b = check(app.activePrimaryWindow())) { - return b; - } else if (const auto c = check(app.windowFor(&session->account()))) { - return c; - } else if (const auto d = check( - app.ensureSeparateWindowForAccount( - &session->account()))) { - return d; - } - return nullptr; +} + +Window::SessionController *Show::resolveWindow(WindowUsage usage) const { + return ResolveWindowDefault()(&session(), usage); } } // namespace ChatHelpers diff --git a/Telegram/SourceFiles/chat_helpers/compose/compose_show.h b/Telegram/SourceFiles/chat_helpers/compose/compose_show.h index 4a418c042..7dcbab513 100644 --- a/Telegram/SourceFiles/chat_helpers/compose/compose_show.h +++ b/Telegram/SourceFiles/chat_helpers/compose/compose_show.h @@ -44,6 +44,11 @@ enum class WindowUsage { PremiumPromo, }; +using ResolveWindow = Fn, + WindowUsage)>; +[[nodiscard]] ResolveWindow ResolveWindowDefault(); + class Show : public Main::SessionShow { public: virtual void activate() = 0; diff --git a/Telegram/SourceFiles/history/view/controls/history_view_draft_options.cpp b/Telegram/SourceFiles/history/view/controls/history_view_draft_options.cpp index 4eef16970..5ba81cd1e 100644 --- a/Telegram/SourceFiles/history/view/controls/history_view_draft_options.cpp +++ b/Telegram/SourceFiles/history/view/controls/history_view_draft_options.cpp @@ -892,14 +892,13 @@ void ShowReplyToChatBox( using Chosen = not_null; Controller(not_null session) - : ChooseRecipientBoxController( - session, - [=](Chosen thread) mutable { _singleChosen.fire_copy(thread); }, - nullptr) { - } - - void rowClicked(not_null row) override final { - ChooseRecipientBoxController::rowClicked(row); + : ChooseRecipientBoxController({ + .session = session, + .callback = [=](Chosen thread) { + _singleChosen.fire_copy(thread); + }, + .premiumRequiredError = WritePremiumRequiredError, + }) { } [[nodiscard]] rpl::producer singleChosen() const { @@ -912,6 +911,7 @@ void ShowReplyToChatBox( private: void prepareViewHook() override { + ChooseRecipientBoxController::prepareViewHook(); delegate()->peerListSetTitle(tr::lng_reply_in_another_title()); } diff --git a/Telegram/SourceFiles/settings/settings_premium.cpp b/Telegram/SourceFiles/settings/settings_premium.cpp index a20a15be3..646829d59 100644 --- a/Telegram/SourceFiles/settings/settings_premium.cpp +++ b/Telegram/SourceFiles/settings/settings_premium.cpp @@ -1316,6 +1316,22 @@ void ShowPremiumPromoToast( std::shared_ptr show, TextWithEntities textWithLink, const QString &ref) { + ShowPremiumPromoToast(show, [=]( + not_null session, + ChatHelpers::WindowUsage usage) { + Expects(&show->session() == session); + + return show->resolveWindow(usage); + }, std::move(textWithLink), ref); +} + +void ShowPremiumPromoToast( + std::shared_ptr show, + Fn, + ChatHelpers::WindowUsage)> resolveWindow, + TextWithEntities textWithLink, + const QString &ref) { using WeakToast = base::weak_ptr; const auto toast = std::make_shared(); (*toast) = show->showToast({ @@ -1331,7 +1347,8 @@ void ShowPremiumPromoToast( if (const auto strong = toast->get()) { strong->hideAnimated(); (*toast) = nullptr; - if (const auto controller = show->resolveWindow( + if (const auto controller = resolveWindow( + &show->session(), ChatHelpers::WindowUsage::PremiumPromo)) { Settings::ShowPremium(controller, ref); } diff --git a/Telegram/SourceFiles/settings/settings_premium.h b/Telegram/SourceFiles/settings/settings_premium.h index 52a9e2b9e..a8603aca6 100644 --- a/Telegram/SourceFiles/settings/settings_premium.h +++ b/Telegram/SourceFiles/settings/settings_premium.h @@ -17,6 +17,7 @@ struct RoundButton; namespace ChatHelpers { class Show; +enum class WindowUsage; } // namespace ChatHelpers namespace Ui { @@ -28,6 +29,7 @@ class VerticalLayout; namespace Main { class Session; +class SessionShow; } // namespace Main namespace Window { @@ -61,6 +63,13 @@ void ShowPremiumPromoToast( std::shared_ptr show, TextWithEntities textWithLink, const QString &ref); +void ShowPremiumPromoToast( + std::shared_ptr<::Main::SessionShow> show, + Fn, + ChatHelpers::WindowUsage)> resolveWindow, + TextWithEntities textWithLink, + const QString &ref); struct SubscribeButtonArgs final { Window::SessionController *controller = nullptr; diff --git a/Telegram/SourceFiles/window/window_peer_menu.cpp b/Telegram/SourceFiles/window/window_peer_menu.cpp index e5764b193..3b9925bcf 100644 --- a/Telegram/SourceFiles/window/window_peer_menu.cpp +++ b/Telegram/SourceFiles/window/window_peer_menu.cpp @@ -1504,8 +1504,11 @@ void PeerMenuShareContactBox( *weak = navigation->parentController()->show( Box( std::make_unique( - &navigation->session(), - std::move(callback)), + ChooseRecipientArgs{ + .session = &navigation->session(), + .callback = std::move(callback), + .premiumRequiredError = WritePremiumRequiredError, + }), [](not_null box) { box->addButton(tr::lng_cancel(), [=] { box->closeBox(); @@ -1742,10 +1745,12 @@ QPointer ShowChooseRecipientBox( } }; *weak = navigation->parentController()->show(Box( - std::make_unique( - &navigation->session(), - std::move(callback), - std::move(filter)), + std::make_unique(ChooseRecipientArgs{ + .session = &navigation->session(), + .callback = std::move(callback), + .filter = std::move(filter), + .premiumRequiredError = WritePremiumRequiredError, + }), std::move(initBox))); return weak->data(); } @@ -1799,15 +1804,18 @@ QPointer ShowForwardMessagesBox( using Chosen = not_null; Controller(not_null session) - : ChooseRecipientBoxController( - session, - [=](Chosen thread) mutable { _singleChosen.fire_copy(thread); }, - nullptr) { + : ChooseRecipientBoxController({ + .session = session, + .callback = [=](Chosen thread) { + _singleChosen.fire_copy(thread); + }, + .premiumRequiredError = WritePremiumRequiredError, + }) { } void rowClicked(not_null row) override final { const auto count = delegate()->peerListSelectedRowsCount(); - if (count && row->peer()->isForum()) { + if (showLockedError(row) || (count && row->peer()->isForum())) { return; } else if (!count || row->peer()->isForum()) { ChooseRecipientBoxController::rowClicked(row); @@ -2126,10 +2134,12 @@ QPointer ShowShareGameBox( }); }; *weak = navigation->parentController()->show(Box( - std::make_unique( - &navigation->session(), - std::move(chosen), - std::move(filter)), + std::make_unique(ChooseRecipientArgs{ + .session = &navigation->session(), + .callback = std::move(chosen), + .filter = std::move(filter), + .premiumRequiredError = WritePremiumRequiredError, + }), std::move(initBox))); return weak->data(); }