diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp index 12f048f76..fb9584523 100644 --- a/Telegram/SourceFiles/apiwrap.cpp +++ b/Telegram/SourceFiles/apiwrap.cpp @@ -3267,13 +3267,13 @@ void ApiWrap::finishForwarding(const SendAction &action) { const auto topicRootId = action.replyTo.topicRootId; auto toForward = history->resolveForwardDraft(topicRootId); if (!toForward.items.empty()) { - const auto error = GetErrorTextForSending( + const auto error = GetErrorForSending( history->peer, { .topicRootId = topicRootId, .forward = &toForward.items, }); - if (!error.isEmpty()) { + if (error) { return; } diff --git a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.cpp b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.cpp index c8b4f2d48..29504ab63 100644 --- a/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.cpp +++ b/Telegram/SourceFiles/boxes/peers/edit_peer_invite_link.cpp @@ -26,7 +26,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_user.h" #include "data/stickers/data_custom_emoji.h" #include "history/history.h" -#include "history/history_item_helpers.h" // GetErrorTextForSending. +#include "history/history_item_helpers.h" // GetErrorForSending. #include "history/view/history_view_group_call_bar.h" // GenerateUserpics... #include "lang/lang_keys.h" #include "main/main_session.h" @@ -1492,27 +1492,14 @@ object_ptr ShareInviteLinkBox( return; } - const auto error = [&] { - for (const auto thread : result) { - const auto error = GetErrorTextForSending( - thread, - { .text = &comment }); - if (!error.isEmpty()) { - return std::make_pair(error, thread); - } - } - return std::make_pair(QString(), result.front()); - }(); - if (!error.first.isEmpty()) { - auto text = TextWithEntities(); - if (result.size() > 1) { - text.append( - Ui::Text::Bold(error.second->chatListName()) - ).append("\n\n"); - } - text.append(error.first); + const auto errorWithThread = GetErrorForSending( + result, + { .text = &comment }); + if (errorWithThread.error) { if (*box) { - (*box)->uiShow()->showBox(Ui::MakeInformBox(text)); + (*box)->uiShow()->showBox(MakeSendErrorBox( + errorWithThread, + result.size() > 1)); } return; } diff --git a/Telegram/SourceFiles/boxes/send_files_box.cpp b/Telegram/SourceFiles/boxes/send_files_box.cpp index 17cfeefb8..7eb1a676b 100644 --- a/Telegram/SourceFiles/boxes/send_files_box.cpp +++ b/Telegram/SourceFiles/boxes/send_files_box.cpp @@ -220,7 +220,7 @@ SendFilesCheck DefaultCheckForPeer( } SendFilesCheck DefaultCheckForPeer( - std::shared_ptr show, + std::shared_ptr show, not_null peer) { return [=]( const Ui::PreparedFile &file, @@ -228,7 +228,7 @@ SendFilesCheck DefaultCheckForPeer( bool silent) { const auto error = Data::FileRestrictionError(peer, file, compress); if (error && !silent) { - show->showToast(*error); + Data::ShowSendErrorToast(show, peer, error); } return !error.has_value(); }; diff --git a/Telegram/SourceFiles/boxes/send_files_box.h b/Telegram/SourceFiles/boxes/send_files_box.h index 6646ec107..9b4123d1c 100644 --- a/Telegram/SourceFiles/boxes/send_files_box.h +++ b/Telegram/SourceFiles/boxes/send_files_box.h @@ -80,7 +80,7 @@ using SendFilesCheck = Fn controller, not_null peer); [[nodiscard]] SendFilesCheck DefaultCheckForPeer( - std::shared_ptr show, + std::shared_ptr show, not_null peer); using SendFilesConfirmed = Fn 1) { - text.append( - Ui::Text::Bold(error.second->chatListName()) - ).append("\n\n"); - } - text.append(error.first); - show->showBox(Ui::MakeInformBox(text)); + const auto error = GetErrorForSending( + result, + { .forward = &items, .text = &comment }); + if (error.error) { + show->showBox(MakeSendErrorBox(error, result.size() > 1)); return; } @@ -1737,30 +1722,13 @@ void FastShareLink( return; } - const auto error = [&] { - for (const auto thread : result) { - const auto error = GetErrorTextForSending( - thread, - { .text = &comment }); - if (!error.isEmpty()) { - return std::make_pair(error, thread); - } - } - return std::make_pair(QString(), result.front()); - }(); - if (!error.first.isEmpty()) { - auto text = TextWithEntities(); - if (result.size() > 1) { - text.append( - Ui::Text::Bold(error.second->chatListName()) - ).append("\n\n"); - } - text.append(error.first); + const auto error = GetErrorForSending( + result, + { .text = &comment }); + if (error.error) { if (const auto weak = *box) { - weak->getDelegate()->show(Ui::MakeConfirmBox({ - .text = text, - .inform = true, - })); + weak->getDelegate()->show( + MakeSendErrorBox(error, result.size() > 1)); } return; } diff --git a/Telegram/SourceFiles/calls/group/calls_group_settings.cpp b/Telegram/SourceFiles/calls/group/calls_group_settings.cpp index 4c60a3f88..980842b99 100644 --- a/Telegram/SourceFiles/calls/group/calls_group_settings.cpp +++ b/Telegram/SourceFiles/calls/group/calls_group_settings.cpp @@ -23,7 +23,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "lang/lang_keys.h" #include "boxes/share_box.h" #include "history/view/history_view_schedule_box.h" -#include "history/history_item_helpers.h" // GetErrorTextForSending. +#include "history/history_item_helpers.h" // GetErrorForSending. #include "history/history.h" #include "data/data_histories.h" #include "data/data_session.h" @@ -139,30 +139,13 @@ object_ptr ShareInviteLinkBox( return; } - const auto error = [&] { - for (const auto thread : result) { - const auto error = GetErrorTextForSending( - thread, - { .text = &comment }); - if (!error.isEmpty()) { - return std::make_pair(error, thread); - } - } - return std::make_pair(QString(), result.front()); - }(); - if (!error.first.isEmpty()) { - auto text = TextWithEntities(); - if (result.size() > 1) { - text.append( - Ui::Text::Bold(error.second->chatListName()) - ).append("\n\n"); - } - text.append(error.first); + const auto error = GetErrorForSending( + result, + { .text = &comment }); + if (error.error) { if (const auto weak = *box) { - weak->getDelegate()->show(ConfirmBox({ - .text = text, - .inform = true, - })); + weak->getDelegate()->show( + MakeSendErrorBox(error, result.size() > 1)); } return; } diff --git a/Telegram/SourceFiles/chat_helpers/tabbed_selector.cpp b/Telegram/SourceFiles/chat_helpers/tabbed_selector.cpp index 0ac62dd2e..8148b31f8 100644 --- a/Telegram/SourceFiles/chat_helpers/tabbed_selector.cpp +++ b/Telegram/SourceFiles/chat_helpers/tabbed_selector.cpp @@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "chat_helpers/gifs_list_widget.h" #include "menu/menu_send.h" #include "ui/controls/tabbed_search.h" +#include "ui/text/text_utilities.h" #include "ui/widgets/buttons.h" #include "ui/widgets/labels.h" #include "ui/widgets/shadow.h" @@ -1042,23 +1043,39 @@ void TabbedSelector::checkRestrictedPeer() { ? Data::RestrictionError( _currentPeer, ChatRestriction::SendOther) - : std::nullopt) - : std::nullopt; - if (error) { - if (!_restrictedLabel) { - _restrictedLabel.create( - this, - *error, - st::stickersRestrictedLabel); - _restrictedLabel->show(); - updateRestrictedLabelGeometry(); - currentTab()->footer()->hide(); - _scroll->hide(); - _bottomShadow->hide(); - update(); - } + : Data::SendError()) + : Data::SendError(); + const auto changed = (_restrictedLabelKey != error.text); + if (!changed) { return; } + _restrictedLabelKey = error.text; + if (error) { + const auto show = _show; + const auto peer = _currentPeer; + _restrictedLabel.create( + this, + rpl::single(error.boostsToLift + ? Ui::Text::Link(error.text) + : TextWithEntities{ error.text }), + st::stickersRestrictedLabel); + const auto lifting = error.boostsToLift; + _restrictedLabel->setClickHandlerFilter([=](auto...) { + const auto window = show->resolveWindow( + ChatHelpers::WindowUsage::PremiumPromo); + window->resolveBoostState(peer->asChannel(), lifting); + return false; + }); + _restrictedLabel->show(); + updateRestrictedLabelGeometry(); + currentTab()->footer()->hide(); + _scroll->hide(); + _bottomShadow->hide(); + update(); + return; + } + } else { + _restrictedLabelKey = QString(); } if (_restrictedLabel) { _restrictedLabel.destroy(); diff --git a/Telegram/SourceFiles/chat_helpers/tabbed_selector.h b/Telegram/SourceFiles/chat_helpers/tabbed_selector.h index f17129903..f96ca6b6d 100644 --- a/Telegram/SourceFiles/chat_helpers/tabbed_selector.h +++ b/Telegram/SourceFiles/chat_helpers/tabbed_selector.h @@ -309,6 +309,7 @@ private: object_ptr _bottomShadow; object_ptr _scroll; object_ptr _restrictedLabel = { nullptr }; + QString _restrictedLabelKey; std::vector _tabs; SelectorTab _currentTabType = SelectorTab::Emoji; diff --git a/Telegram/SourceFiles/data/data_chat_participant_status.cpp b/Telegram/SourceFiles/data/data_chat_participant_status.cpp index 38fa1a27e..1b46621de 100644 --- a/Telegram/SourceFiles/data/data_chat_participant_status.cpp +++ b/Telegram/SourceFiles/data/data_chat_participant_status.cpp @@ -9,6 +9,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "base/unixtime.h" #include "boxes/peers/edit_peer_permissions_box.h" +#include "chat_helpers/compose/compose_show.h" #include "data/data_chat.h" #include "data/data_channel.h" #include "data/data_forum_topic.h" @@ -17,6 +18,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "lang/lang_keys.h" #include "main/main_session.h" #include "ui/chat/attach/attach_prepare.h" +#include "ui/text/text_utilities.h" +#include "ui/toast/toast.h" +#include "window/window_session_controller.h" namespace { @@ -167,7 +171,7 @@ bool CanSendAnyOf( Unexpected("Peer type in CanSendAnyOf."); } -std::optional RestrictionError( +SendError RestrictionError( not_null peer, ChatRestriction restriction) { using Flag = ChatRestriction; @@ -175,10 +179,13 @@ std::optional RestrictionError( if (const auto user = peer->asUser()) { if (user->meRequiresPremiumToWrite() && !user->session().premium()) { - return tr::lng_restricted_send_non_premium( - tr::now, - lt_user, - user->shortName()); + return SendError({ + .text = tr::lng_restricted_send_non_premium( + tr::now, + lt_user, + user->shortName()), + .premiumToLift = true, + }); } const auto result = (restriction == Flag::SendVoiceMessages) ? tr::lng_restricted_send_voice_messages( @@ -194,7 +201,7 @@ std::optional RestrictionError( ? u"can't send polls :("_q : (restriction == Flag::PinMessages) ? u"can't pin :("_q - : std::optional(); + : SendError(); Ensures(result.has_value()); return result; @@ -253,6 +260,15 @@ std::optional RestrictionError( Unexpected("Restriction in Data::RestrictionErrorKey."); } } + if (all + && channel->boostsUnrestrict() + && !channel->unrestrictedByBoosts()) { + return SendError({ + .text = tr::lng_restricted_boost_group(tr::now), + .boostsToLift = (channel->boostsUnrestrict() + - channel->boostsApplied()), + }); + } switch (restriction) { case Flag::SendPolls: return all @@ -302,10 +318,10 @@ std::optional RestrictionError( } Unexpected("Restriction in Data::RestrictionErrorKey."); } - return std::nullopt; + return SendError(); } -std::optional AnyFileRestrictionError(not_null peer) { +SendError AnyFileRestrictionError(not_null peer) { using Restriction = ChatRestriction; for (const auto right : FilesSendRestrictionsList()) { if (!RestrictionError(peer, right)) { @@ -315,7 +331,7 @@ std::optional AnyFileRestrictionError(not_null peer) { return RestrictionError(peer, Restriction::SendFiles); } -std::optional FileRestrictionError( +SendError FileRestrictionError( not_null peer, const Ui::PreparedList &list, std::optional compress) { @@ -339,7 +355,7 @@ std::optional FileRestrictionError( return {}; } -std::optional FileRestrictionError( +SendError FileRestrictionError( not_null peer, const Ui::PreparedFile &file, std::optional compress) { @@ -383,4 +399,32 @@ std::optional FileRestrictionError( return {}; } +void ShowSendErrorToast( + not_null navigation, + not_null peer, + Data::SendError error) { + return ShowSendErrorToast(navigation->uiShow(), peer, error); +} + +void ShowSendErrorToast( + std::shared_ptr show, + not_null peer, + Data::SendError error) { + Expects(peer->isChannel()); + + if (!error.boostsToLift) { + show->showToast(*error); + return; + } + const auto boost = [=] { + const auto window = show->resolveWindow( + ChatHelpers::WindowUsage::PremiumPromo); + window->resolveBoostState(peer->asChannel(), error.boostsToLift); + }; + show->showToast({ + .text = Ui::Text::Link(*error), + .filter = [=](const auto &...) { boost(); return false; }, + }); +} + } // namespace Data diff --git a/Telegram/SourceFiles/data/data_chat_participant_status.h b/Telegram/SourceFiles/data/data_chat_participant_status.h index 8dd7cd682..bfa24fb86 100644 --- a/Telegram/SourceFiles/data/data_chat_participant_status.h +++ b/Telegram/SourceFiles/data/data_chat_participant_status.h @@ -7,11 +7,19 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once +namespace ChatHelpers { +class Show; +} // namespace ChatHelpers + namespace Ui { struct PreparedList; struct PreparedFile; } // namespace Ui +namespace Window { +class SessionNavigation; +} // namespace Window + enum class ChatAdminRight { ChangeInfo = (1 << 0), PostMessages = (1 << 1), @@ -175,18 +183,65 @@ struct RestrictionsSetOptions { return CanSendAnyOf(peer, AllSendRestrictions(), forbidInForums); } -[[nodiscard]] std::optional RestrictionError( +struct SendError { + SendError(QString text = QString()) : text(std::move(text)) { + } + + struct Args { + QString text; + int boostsToLift = 0; + bool premiumToLift = false; + }; + SendError(Args &&args) + : text(std::move(args.text)) + , boostsToLift(args.boostsToLift) + , premiumToLift(args.premiumToLift) { + } + + QString text; + int boostsToLift = 0; + bool premiumToLift = false; + + [[nodiscard]] SendError value_or(SendError other) const { + return *this ? *this : other; + } + + explicit operator bool() const { + return !text.isEmpty(); + } + [[nodiscard]] bool has_value() const { + return !text.isEmpty(); + } + [[nodiscard]] const QString &operator*() const { + return text; + } +}; + +struct SendErrorWithThread { + SendError error; + Thread *thread = nullptr; +}; + +[[nodiscard]] SendError RestrictionError( not_null peer, ChatRestriction restriction); -[[nodiscard]] std::optional AnyFileRestrictionError( - not_null peer); -[[nodiscard]] std::optional FileRestrictionError( +[[nodiscard]] SendError AnyFileRestrictionError(not_null peer); +[[nodiscard]] SendError FileRestrictionError( not_null peer, const Ui::PreparedList &list, std::optional compress); -[[nodiscard]] std::optional FileRestrictionError( +[[nodiscard]] SendError FileRestrictionError( not_null peer, const Ui::PreparedFile &file, std::optional compress); +void ShowSendErrorToast( + not_null navigation, + not_null peer, + SendError error); +void ShowSendErrorToast( + std::shared_ptr show, + not_null peer, + SendError error); + } // namespace Data diff --git a/Telegram/SourceFiles/data/data_story.cpp b/Telegram/SourceFiles/data/data_story.cpp index 9adcad015..70a37ac21 100644 --- a/Telegram/SourceFiles/data/data_story.cpp +++ b/Telegram/SourceFiles/data/data_story.cpp @@ -423,7 +423,7 @@ bool Story::hasDirectLink() const { return !_peer->username().isEmpty(); } -std::optional Story::errorTextForForward( +Data::SendError Story::errorTextForForward( not_null to) const { const auto peer = to->peer(); const auto holdsPhoto = v::is>(_media.data); @@ -433,10 +433,10 @@ std::optional Story::errorTextForForward( const auto second = holdsPhoto ? ChatRestriction::SendVideos : ChatRestriction::SendPhotos; - if (const auto error = Data::RestrictionError(peer, first)) { - return *error; - } else if (const auto error = Data::RestrictionError(peer, second)) { - return *error; + if (const auto one = Data::RestrictionError(peer, first)) { + return one; + } else if (const auto two = Data::RestrictionError(peer, second)) { + return two; } else if (!Data::CanSend(to, first, false) || !Data::CanSend(to, second, false)) { return tr::lng_forward_cant(tr::now); diff --git a/Telegram/SourceFiles/data/data_story.h b/Telegram/SourceFiles/data/data_story.h index 490f979ce..a1de910fa 100644 --- a/Telegram/SourceFiles/data/data_story.h +++ b/Telegram/SourceFiles/data/data_story.h @@ -24,6 +24,7 @@ namespace Data { class Session; class Thread; class MediaPreload; +struct SendError; enum class StoryPrivacy : uchar { Public, @@ -191,7 +192,7 @@ public: [[nodiscard]] bool canReport() const; [[nodiscard]] bool hasDirectLink() const; - [[nodiscard]] std::optional errorTextForForward( + [[nodiscard]] Data::SendError errorTextForForward( not_null to) const; void setCaption(TextWithEntities &&caption); diff --git a/Telegram/SourceFiles/history/history_item.cpp b/Telegram/SourceFiles/history/history_item.cpp index 0c8c3ab3b..f9d0334c6 100644 --- a/Telegram/SourceFiles/history/history_item.cpp +++ b/Telegram/SourceFiles/history/history_item.cpp @@ -2479,17 +2479,17 @@ bool HistoryItem::requiresSendInlineRight() const { return Has(); } -std::optional HistoryItem::errorTextForForward( +Data::SendError HistoryItem::errorTextForForward( not_null to) const { const auto requiredRight = requiredSendRight(); const auto requiresInline = requiresSendInlineRight(); const auto peer = to->peer(); constexpr auto kInline = ChatRestriction::SendInline; if (const auto error = Data::RestrictionError(peer, requiredRight)) { - return *error; + return error; } else if (requiresInline && !Data::CanSend(to, kInline)) { - return Data::RestrictionError(peer, kInline).value_or( - tr::lng_forward_cant(tr::now)); + const auto forInline = Data::RestrictionError(peer, kInline); + return forInline ? forInline : tr::lng_forward_cant(tr::now); } else if (_media && _media->poll() && _media->poll()->publicVotes() diff --git a/Telegram/SourceFiles/history/history_item.h b/Telegram/SourceFiles/history/history_item.h index a5b633df0..6ef31b827 100644 --- a/Telegram/SourceFiles/history/history_item.h +++ b/Telegram/SourceFiles/history/history_item.h @@ -68,6 +68,7 @@ struct SponsoredFrom; class Story; class SavedSublist; struct PaidReactionSend; +struct SendError; } // namespace Data namespace Main { @@ -442,7 +443,7 @@ public: [[nodiscard]] bool suggestDeleteAllReport() const; [[nodiscard]] ChatRestriction requiredSendRight() const; [[nodiscard]] bool requiresSendInlineRight() const; - [[nodiscard]] std::optional errorTextForForward( + [[nodiscard]] Data::SendError errorTextForForward( not_null to) const; [[nodiscard]] const HistoryMessageTranslation *translation() const; [[nodiscard]] bool translationShowRequiresCheck(LanguageId to) const; diff --git a/Telegram/SourceFiles/history/history_item_helpers.cpp b/Telegram/SourceFiles/history/history_item_helpers.cpp index d0fdf1e6f..1c6584e85 100644 --- a/Telegram/SourceFiles/history/history_item_helpers.cpp +++ b/Telegram/SourceFiles/history/history_item_helpers.cpp @@ -38,6 +38,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "base/unixtime.h" #include "core/application.h" #include "core/click_handler_types.h" // ClickHandlerContext. +#include "ui/boxes/confirm_box.h" #include "ui/text/format_values.h" #include "ui/text/text_utilities.h" #include "ui/toast/toast.h" @@ -59,7 +60,7 @@ bool PeerCallKnown(not_null peer) { } // namespace -QString GetErrorTextForSending( +Data::SendError GetErrorForSending( not_null peer, SendingErrorRequest request) { const auto forum = request.topicRootId ? peer->forum() : nullptr; @@ -71,13 +72,13 @@ QString GetErrorTextForSending( : peer->owner().history(peer); if (request.story) { if (const auto error = request.story->errorTextForForward(thread)) { - return *error; + return error; } } if (request.forward) { for (const auto &item : *request.forward) { if (const auto error = item->errorTextForForward(thread)) { - return *error; + return error; } } } @@ -87,7 +88,7 @@ QString GetErrorTextForSending( peer, ChatRestriction::SendOther); if (error) { - return *error; + return error; } else if (!Data::CanSendTexts(thread)) { return tr::lng_forward_cant(tr::now); } @@ -134,14 +135,58 @@ QString GetErrorTextForSending( } } - return QString(); + return {}; } -QString GetErrorTextForSending( +Data::SendError GetErrorForSending( not_null thread, SendingErrorRequest request) { request.topicRootId = thread->topicRootId(); - return GetErrorTextForSending(thread->peer(), std::move(request)); + return GetErrorForSending(thread->peer(), std::move(request)); +} + +Data::SendErrorWithThread GetErrorForSending( + const std::vector> &threads, + SendingErrorRequest request) { + for (const auto thread : threads) { + const auto error = GetErrorForSending(thread, request); + if (error) { + return Data::SendErrorWithThread{ error, thread }; + } + } + return {}; +} +object_ptr MakeSendErrorBox( + const Data::SendErrorWithThread &error, + bool withTitle) { + Expects(error.error.has_value() && error.thread != nullptr); + + auto text = TextWithEntities(); + if (withTitle) { + text.append( + Ui::Text::Bold(error.thread->chatListName()) + ).append("\n\n"); + } + if (error.error.boostsToLift) { + text.append(Ui::Text::Link(error.error.text)); + } else { + text.append(error.error.text); + } + const auto peer = error.thread->peer(); + const auto lifting = error.error.boostsToLift; + const auto filter = [=](const auto &...) { + Expects(peer->isChannel()); + + const auto window = ChatHelpers::ResolveWindowDefault()( + &peer->session(), + ChatHelpers::WindowUsage::PremiumPromo); + window->resolveBoostState(peer->asChannel(), lifting); + return false; + }; + return Ui::MakeInformBox({ + .text = text, + .labelFilter = filter, + }); } void RequestDependentMessageItem( diff --git a/Telegram/SourceFiles/history/history_item_helpers.h b/Telegram/SourceFiles/history/history_item_helpers.h index ba5bd2cda..4160753bc 100644 --- a/Telegram/SourceFiles/history/history_item_helpers.h +++ b/Telegram/SourceFiles/history/history_item_helpers.h @@ -7,6 +7,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once +#include "base/object_ptr.h" + class History; namespace Api { @@ -17,12 +19,18 @@ struct SendAction; namespace Data { class Story; class Thread; +struct SendError; +struct SendErrorWithThread; } // namespace Data namespace Main { class Session; } // namespace Main +namespace Ui { +class BoxContent; +} // namespace Ui + struct PreparedServiceText { TextWithEntities text; std::vector links; @@ -108,13 +116,20 @@ struct SendingErrorRequest { const TextWithTags *text = nullptr; bool ignoreSlowmodeCountdown = false; }; -[[nodiscard]] QString GetErrorTextForSending( +[[nodiscard]] Data::SendError GetErrorForSending( not_null peer, SendingErrorRequest request); -[[nodiscard]] QString GetErrorTextForSending( +[[nodiscard]] Data::SendError GetErrorForSending( not_null thread, SendingErrorRequest request); +[[nodiscard]] Data::SendErrorWithThread GetErrorForSending( + const std::vector> &threads, + SendingErrorRequest request); +[[nodiscard]] object_ptr MakeSendErrorBox( + const Data::SendErrorWithThread &error, + bool withTitle); + [[nodiscard]] TextWithEntities DropDisallowedCustomEmoji( not_null to, TextWithEntities text); diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp index e1c35aab4..c574420eb 100644 --- a/Telegram/SourceFiles/history/history_widget.cpp +++ b/Telegram/SourceFiles/history/history_widget.cpp @@ -85,7 +85,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/stickers/data_custom_emoji.h" #include "history/history.h" #include "history/history_item.h" -#include "history/history_item_helpers.h" // GetErrorTextForSending. +#include "history/history_item_helpers.h" // GetErrorForSending. #include "history/history_drag_area.h" #include "history/history_inner_widget.h" #include "history/history_item_components.h" @@ -1028,7 +1028,7 @@ void HistoryWidget::refreshTabbedPanel() { void HistoryWidget::initVoiceRecordBar() { _voiceRecordBar->setStartRecordingFilter([=] { - const auto error = [&]() -> std::optional { + const auto error = [&]() -> Data::SendError { if (_peer) { if (const auto error = Data::RestrictionError( _peer, @@ -1036,10 +1036,10 @@ void HistoryWidget::initVoiceRecordBar() { return error; } } - return std::nullopt; + return {}; }(); if (error) { - controller()->showToast(*error); + Data::ShowSendErrorToast(controller(), _peer, error); return true; } else if (showSlowmodeError()) { return true; @@ -4672,7 +4672,7 @@ void HistoryWidget::chooseAttach( if (!_peer || !_canSendMessages) { return; } else if (const auto error = Data::AnyFileRestrictionError(_peer)) { - controller()->showToast(*error); + Data::ShowSendErrorToast(controller(), _peer, error); return; } else if (showSlowmodeError()) { return; @@ -5702,12 +5702,12 @@ bool HistoryWidget::showSendingFilesError( bool HistoryWidget::showSendingFilesError( const Ui::PreparedList &list, std::optional compress) const { - const auto text = [&] { + const auto error = [&]() -> Data::SendError { const auto error = _peer ? Data::FileRestrictionError(_peer, list, compress) - : std::nullopt; - if (error) { - return *error; + : Data::SendError(); + if (!_peer || error) { + return error; } else if (const auto left = _peer->slowmodeSecondsLeft()) { return tr::lng_slowmode_enabled( tr::now, @@ -5727,15 +5727,15 @@ bool HistoryWidget::showSendingFilesError( } return tr::lng_forward_send_files_cant(tr::now); }(); - if (text.isEmpty()) { + if (!error) { return false; - } else if (text == u"(toolarge)"_q) { + } else if (error.text == u"(toolarge)"_q) { const auto fileSize = list.files.back().size; controller()->show( Box(FileSizeLimitBox, &session(), fileSize, nullptr)); return true; } - controller()->showToast(text); + Data::ShowSendErrorToast(controller(), _peer, error); return true; } @@ -5775,7 +5775,7 @@ bool HistoryWidget::showSendMessageError( return false; } const auto topicRootId = resolveReplyToTopicRootId(); - const auto error = GetErrorTextForSending( + const auto error = GetErrorForSending( _peer, { .topicRootId = topicRootId, @@ -5783,10 +5783,10 @@ bool HistoryWidget::showSendMessageError( .text = &textWithTags, .ignoreSlowmodeCountdown = ignoreSlowmodeCountdown, }); - if (error.isEmpty()) { + if (!error) { return false; } - controller()->showToast(error); + Data::ShowSendErrorToast(controller(), _peer, error); return true; } @@ -6286,39 +6286,39 @@ int HistoryWidget::countAutomaticScrollTop() { return ScrollMax; } -QString HistoryWidget::computeSendRestriction() const { - if (const auto user = _peer ? _peer->asUser() : nullptr) { - if (user->meRequiresPremiumToWrite() - && !user->session().premium()) { - return u"premium_required"_q; - } - } +Data::SendError HistoryWidget::computeSendRestriction() const { const auto allWithoutPolls = Data::AllSendRestrictions() & ~ChatRestriction::SendPolls; - const auto error = (_peer && !Data::CanSendAnyOf(_peer, allWithoutPolls)) + return (_peer && !Data::CanSendAnyOf(_peer, allWithoutPolls)) ? Data::RestrictionError(_peer, ChatRestriction::SendOther) - : std::nullopt; - return error ? (u"restriction:"_q + *error) : QString(); + : Data::SendError(); } void HistoryWidget::updateSendRestriction() { const auto restriction = computeSendRestriction(); - if (_sendRestrictionKey == restriction) { + if (_sendRestrictionKey == restriction.text) { return; } - _sendRestrictionKey = restriction; - if (restriction.isEmpty()) { + _sendRestrictionKey = restriction.text; + if (!restriction) { _sendRestriction = nullptr; - } else if (restriction == u"premium_required"_q) { + } else if (restriction.premiumToLift) { _sendRestriction = PremiumRequiredSendRestriction( this, _peer->asUser(), controller()); - } else if (restriction.startsWith(u"restriction:"_q)) { - const auto error = restriction.mid(12); - _sendRestriction = TextErrorSendRestriction(this, error); + } else if (const auto lifting = restriction.boostsToLift) { + auto button = base::make_unique_q( + this, + restriction.text, + st::historyComposeButton); + const auto channel = _peer->asChannel(); + button->setClickedCallback([=] { + controller()->resolveBoostState(channel, lifting); + }); + _sendRestriction = std::move(button); } else { - Unexpected("Restriction type."); + _sendRestriction = TextErrorSendRestriction(this, restriction.text); } if (_sendRestriction) { _sendRestriction->show(); @@ -7130,9 +7130,8 @@ void HistoryWidget::sendInlineResult(InlineBots::ResultSelected result) { return; } - auto errorText = result.result->getErrorOnSend(_history); - if (!errorText.isEmpty()) { - controller()->showToast(errorText); + if (const auto error = result.result->getErrorOnSend(_history)) { + Data::ShowSendErrorToast(controller(), _peer, error); return; } @@ -7774,9 +7773,9 @@ bool HistoryWidget::sendExistingDocument( std::optional localId) { const auto error = _peer ? Data::RestrictionError(_peer, ChatRestriction::SendStickers) - : std::nullopt; + : Data::SendError(); if (error) { - controller()->showToast(*error); + Data::ShowSendErrorToast(controller(), _peer, error); return false; } else if (!_peer || !_canSendMessages @@ -7811,9 +7810,9 @@ bool HistoryWidget::sendExistingPhoto( Api::SendOptions options) { const auto error = _peer ? Data::RestrictionError(_peer, ChatRestriction::SendPhotos) - : std::nullopt; + : Data::SendError(); if (error) { - controller()->showToast(*error); + Data::ShowSendErrorToast(controller(), _peer, error); return false; } else if (!_peer || !_canSendMessages) { return false; diff --git a/Telegram/SourceFiles/history/history_widget.h b/Telegram/SourceFiles/history/history_widget.h index e1a8dc53d..ad9c1b74c 100644 --- a/Telegram/SourceFiles/history/history_widget.h +++ b/Telegram/SourceFiles/history/history_widget.h @@ -29,6 +29,7 @@ class Error; namespace Data { class PhotoMedia; +struct SendError; } // namespace Data namespace SendMenu { @@ -579,7 +580,7 @@ private: void addMessagesToBack(not_null peer, const QVector &messages); void updateSendRestriction(); - [[nodiscard]] QString computeSendRestriction() const; + [[nodiscard]] Data::SendError computeSendRestriction() const; void updateHistoryGeometry(bool initial = false, bool loadedDown = false, const ScrollChange &change = { ScrollChangeNone, 0 }); void updateListSize(); void startItemRevealAnimations(); diff --git a/Telegram/SourceFiles/history/view/controls/compose_controls_common.h b/Telegram/SourceFiles/history/view/controls/compose_controls_common.h index bebcc9c57..59a1e555e 100644 --- a/Telegram/SourceFiles/history/view/controls/compose_controls_common.h +++ b/Telegram/SourceFiles/history/view/controls/compose_controls_common.h @@ -48,6 +48,7 @@ struct WriteRestriction { QString text; QString button; Type type = Type::None; + int boostsToLift = false; [[nodiscard]] bool empty() const { return (type == Type::None); diff --git a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp index 4a20ca453..ab91da5c2 100644 --- a/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp +++ b/Telegram/SourceFiles/history/view/controls/history_view_compose_controls.cpp @@ -2222,7 +2222,7 @@ void SetupRestrictionView( not_null widget, not_null st, std::shared_ptr show, - const QString &name, + not_null peer, rpl::producer restriction, Fn paintBackground) { struct State { @@ -2234,7 +2234,9 @@ void SetupRestrictionView( }; const auto state = widget->lifetime().make_state(); state->updateGeometries = [=] { - if (!state->label) { + if (!state->label && state->button) { + state->button->setGeometry(widget->rect()); + } else if (!state->label) { return; } else if (state->button) { const auto available = widget->width() @@ -2307,14 +2309,24 @@ void SetupRestrictionView( ) | rpl::distinct_until_changed( ) | rpl::start_with_next([=](Controls::WriteRestriction value) { using Type = Controls::WriteRestriction::Type; - if (value.type == Type::Rights) { + if (const auto lifting = value.boostsToLift) { + state->button = std::make_unique( + widget, + tr::lng_restricted_boost_group(tr::now), + st::historyComposeButton); + state->button->setClickedCallback([=] { + const auto window = show->resolveWindow( + ChatHelpers::WindowUsage::PremiumPromo); + window->resolveBoostState(peer->asChannel(), lifting); + }); + } else if (value.type == Type::Rights) { state->icon = nullptr; state->unlock = nullptr; state->button = nullptr; state->label = makeLabel(value.text, st->restrictionLabel); } else if (value.type == Type::PremiumRequired) { state->icon = makeIcon(); - state->unlock = makeUnlock(value.button, name); + state->unlock = makeUnlock(value.button, peer->shortName()); state->button = std::make_unique(widget); state->button->setClickedCallback([=] { ::Settings::ShowPremiumPromoToast( @@ -2322,7 +2334,7 @@ void SetupRestrictionView( tr::lng_send_non_premium_message_toast( tr::now, lt_user, - TextWithEntities{ name }, + TextWithEntities{ peer->shortName() }, lt_link, Ui::Text::Link( Ui::Text::Bold( @@ -2373,7 +2385,7 @@ void ComposeControls::initWriteRestriction() { _writeRestricted.get(), &_st, _show, - _history->peer->shortName(), + _history->peer, _writeRestriction.value(), background); @@ -2405,7 +2417,7 @@ void ComposeControls::initVoiceRecordBar() { }, _wrap->lifetime()); _voiceRecordBar->setStartRecordingFilter([=] { - const auto error = [&]() -> std::optional { + const auto error = [&]() -> Data::SendError { const auto peer = _history ? _history->peer.get() : nullptr; if (peer) { if (const auto error = Data::RestrictionError( @@ -2414,10 +2426,10 @@ void ComposeControls::initVoiceRecordBar() { return error; } } - return std::nullopt; + return {}; }(); if (error) { - _show->showToast(*error); + Data::ShowSendErrorToast(_show, _history->peer, error); return true; } else if (_showSlowmodeError && _showSlowmodeError()) { return true; diff --git a/Telegram/SourceFiles/history/view/history_view_replies_section.cpp b/Telegram/SourceFiles/history/view/history_view_replies_section.cpp index 62b1e29d1..741f3cee8 100644 --- a/Telegram/SourceFiles/history/view/history_view_replies_section.cpp +++ b/Telegram/SourceFiles/history/view/history_view_replies_section.cpp @@ -23,7 +23,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/history.h" #include "history/history_drag_area.h" #include "history/history_item_components.h" -#include "history/history_item_helpers.h" // GetErrorTextForSending. +#include "history/history_item_helpers.h" // GetErrorForSending. #include "history/history_view_swipe.h" #include "ui/chat/pinned_bar.h" #include "ui/chat/chat_style.h" @@ -661,7 +661,7 @@ void RepliesWidget::setupComposeControls() { ? _topic : _history->peer->forumTopicFor(_rootId); return (!topic || topic->canToggleClosed() || !topic->closed()) - ? std::optional() + ? Data::SendError() : tr::lng_forum_topic_closed(tr::now); }); auto writeRestriction = rpl::combine( @@ -670,7 +670,7 @@ void RepliesWidget::setupComposeControls() { Data::PeerUpdate::Flag::Rights), Data::CanSendAnythingValue(_history->peer), std::move(topicWriteRestrictions) - ) | rpl::map([=](auto, auto, std::optional topicRestriction) { + ) | rpl::map([=](auto, auto, Data::SendError topicRestriction) { const auto allWithoutPolls = Data::AllSendRestrictions() & ~ChatRestriction::SendPolls; const auto canSendAnything = _topic @@ -687,10 +687,11 @@ void RepliesWidget::setupComposeControls() { : tr::lng_group_not_accessible(tr::now)) : topicRestriction ? std::move(topicRestriction) - : std::optional(); + : Data::SendError(); return text ? Controls::WriteRestriction{ .text = std::move(*text), .type = Controls::WriteRestrictionType::Rights, + .boostsToLift = text.boostsToLift, } : Controls::WriteRestriction(); }); @@ -936,7 +937,7 @@ void RepliesWidget::chooseAttach( std::optional overrideSendImagesAsPhotos) { _choosingAttach = false; if (const auto error = Data::AnyFileRestrictionError(_history->peer)) { - controller()->showToast(*error); + Data::ShowSendErrorToast(controller(), _history->peer, error); return; } else if (showSlowmodeError()) { return; @@ -1168,11 +1169,11 @@ bool RepliesWidget::showSendingFilesError( bool RepliesWidget::showSendingFilesError( const Ui::PreparedList &list, std::optional compress) const { - const auto text = [&] { + const auto error = [&]() -> Data::SendError { const auto peer = _history->peer; const auto error = Data::FileRestrictionError(peer, list, compress); if (error) { - return *error; + return error; } else if (const auto left = _history->peer->slowmodeSecondsLeft()) { return tr::lng_slowmode_enabled( tr::now, @@ -1192,16 +1193,16 @@ bool RepliesWidget::showSendingFilesError( } return tr::lng_forward_send_files_cant(tr::now); }(); - if (text.isEmpty()) { + if (!error) { return false; - } else if (text == u"(toolarge)"_q) { + } else if (error.text == u"(toolarge)"_q) { const auto fileSize = list.files.back().size; controller()->show( Box(FileSizeLimitBox, &session(), fileSize, nullptr)); return true; } - controller()->showToast(text); + Data::ShowSendErrorToast(controller(), _history->peer, error); return true; } @@ -1247,7 +1248,7 @@ void RepliesWidget::send(Api::SendOptions options) { message.textWithTags = _composeControls->getTextWithAppliedMarkdown(); message.webPage = _composeControls->webPageDraft(); - const auto error = GetErrorTextForSending( + const auto error = GetErrorForSending( _history->peer, { .topicRootId = _topic ? _topic->rootId() : MsgId(0), @@ -1255,8 +1256,8 @@ void RepliesWidget::send(Api::SendOptions options) { .text = &message.textWithTags, .ignoreSlowmodeCountdown = (options.scheduled != 0), }); - if (!error.isEmpty()) { - controller()->showToast(error); + if (error) { + Data::ShowSendErrorToast(controller(), _history->peer, error); return; } @@ -1407,7 +1408,7 @@ bool RepliesWidget::sendExistingDocument( _history->peer, ChatRestriction::SendStickers); if (error) { - controller()->showToast(*error); + Data::ShowSendErrorToast(controller(), _history->peer, error); return false; } else if (showSlowmodeError() || ShowSendPremiumError(controller(), document)) { @@ -1435,7 +1436,7 @@ bool RepliesWidget::sendExistingPhoto( _history->peer, ChatRestriction::SendPhotos); if (error) { - controller()->showToast(*error); + Data::ShowSendErrorToast(controller(), _history->peer, error); return false; } else if (showSlowmodeError()) { return false; @@ -1453,9 +1454,8 @@ bool RepliesWidget::sendExistingPhoto( void RepliesWidget::sendInlineResult( not_null result, not_null bot) { - const auto errorText = result->getErrorOnSend(_history); - if (!errorText.isEmpty()) { - controller()->showToast(errorText); + if (const auto error = result->getErrorOnSend(_history)) { + Data::ShowSendErrorToast(controller(), _history->peer, error); return; } sendInlineResult(result, bot, {}, std::nullopt); diff --git a/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp b/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp index 5090dd42b..950ae9f17 100644 --- a/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp +++ b/Telegram/SourceFiles/history/view/history_view_scheduled_section.cpp @@ -14,7 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/view/history_view_sticker_toast.h" #include "history/history.h" #include "history/history_drag_area.h" -#include "history/history_item_helpers.h" // GetErrorTextForSending. +#include "history/history_item_helpers.h" // GetErrorForSending. #include "menu/menu_send.h" // SendMenu::Type. #include "ui/widgets/buttons.h" #include "ui/widgets/tooltip.h" @@ -252,83 +252,85 @@ ScheduledWidget::~ScheduledWidget() = default; void ScheduledWidget::setupComposeControls() { auto writeRestriction = _forumTopic ? [&] { - auto topicWriteRestrictions = rpl::single( - ) | rpl::then(session().changes().topicUpdates( - Data::TopicUpdate::Flag::Closed - ) | rpl::filter([=](const Data::TopicUpdate &update) { - return (update.topic->history() == _history) - && (update.topic->rootId() == _forumTopic->rootId()); - }) | rpl::to_empty) | rpl::map([=] { - return (!_forumTopic - || _forumTopic->canToggleClosed() - || !_forumTopic->closed()) - ? std::optional() - : tr::lng_forum_topic_closed(tr::now); - }); - return rpl::combine( - session().changes().peerFlagsValue( - _history->peer, - Data::PeerUpdate::Flag::Rights), - Data::CanSendAnythingValue(_history->peer), - std::move(topicWriteRestrictions) - ) | rpl::map([=]( - auto, - auto, - std::optional topicRestriction) { - const auto allWithoutPolls = Data::AllSendRestrictions() - & ~ChatRestriction::SendPolls; - const auto canSendAnything = Data::CanSendAnyOf( - _forumTopic, - allWithoutPolls); - const auto restriction = Data::RestrictionError( - _history->peer, - ChatRestriction::SendOther); - auto text = !canSendAnything - ? (restriction - ? restriction + auto topicWriteRestrictions = rpl::single( + ) | rpl::then(session().changes().topicUpdates( + Data::TopicUpdate::Flag::Closed + ) | rpl::filter([=](const Data::TopicUpdate &update) { + return (update.topic->history() == _history) + && (update.topic->rootId() == _forumTopic->rootId()); + }) | rpl::to_empty) | rpl::map([=] { + return (!_forumTopic + || _forumTopic->canToggleClosed() + || !_forumTopic->closed()) + ? Data::SendError() + : tr::lng_forum_topic_closed(tr::now); + }); + return rpl::combine( + session().changes().peerFlagsValue( + _history->peer, + Data::PeerUpdate::Flag::Rights), + Data::CanSendAnythingValue(_history->peer), + std::move(topicWriteRestrictions) + ) | rpl::map([=]( + auto, + auto, + Data::SendError topicRestriction) { + const auto allWithoutPolls = Data::AllSendRestrictions() + & ~ChatRestriction::SendPolls; + const auto canSendAnything = Data::CanSendAnyOf( + _forumTopic, + allWithoutPolls); + const auto restriction = Data::RestrictionError( + _history->peer, + ChatRestriction::SendOther); + auto text = !canSendAnything + ? (restriction + ? restriction + : topicRestriction + ? std::move(topicRestriction) + : tr::lng_group_not_accessible(tr::now)) : topicRestriction ? std::move(topicRestriction) - : tr::lng_group_not_accessible(tr::now)) - : topicRestriction - ? std::move(topicRestriction) - : std::optional(); - return text ? Controls::WriteRestriction{ - .text = std::move(*text), - .type = Controls::WriteRestrictionType::Rights, - } : Controls::WriteRestriction(); - }) | rpl::type_erased(); - }() + : Data::SendError(); + return text ? Controls::WriteRestriction{ + .text = std::move(*text), + .type = Controls::WriteRestrictionType::Rights, + .boostsToLift = text.boostsToLift, + } : Controls::WriteRestriction(); + }) | rpl::type_erased(); + }() : [&] { - return rpl::combine( - session().changes().peerFlagsValue( - _history->peer, - Data::PeerUpdate::Flag::Rights), - Data::CanSendAnythingValue(_history->peer) - ) | rpl::map([=] { - const auto allWithoutPolls = Data::AllSendRestrictions() - & ~ChatRestriction::SendPolls; - const auto canSendAnything = Data::CanSendAnyOf( - _history->peer, - allWithoutPolls, - false); - const auto restriction = Data::RestrictionError( - _history->peer, - ChatRestriction::SendOther); - auto text = !canSendAnything - ? (restriction - ? restriction - : tr::lng_group_not_accessible(tr::now)) - : std::optional(); - return text ? Controls::WriteRestriction{ - .text = std::move(*text), - .type = Controls::WriteRestrictionType::Rights, - } : Controls::WriteRestriction(); - }) | rpl::type_erased(); - }(); + return rpl::combine( + session().changes().peerFlagsValue( + _history->peer, + Data::PeerUpdate::Flag::Rights), + Data::CanSendAnythingValue(_history->peer) + ) | rpl::map([=] { + const auto allWithoutPolls = Data::AllSendRestrictions() + & ~ChatRestriction::SendPolls; + const auto canSendAnything = Data::CanSendAnyOf( + _history->peer, + allWithoutPolls, + false); + const auto restriction = Data::RestrictionError( + _history->peer, + ChatRestriction::SendOther); + auto text = !canSendAnything + ? (restriction + ? restriction + : tr::lng_group_not_accessible(tr::now)) + : Data::SendError(); + return text ? Controls::WriteRestriction{ + .text = std::move(*text), + .type = Controls::WriteRestrictionType::Rights, + .boostsToLift = text.boostsToLift, + } : Controls::WriteRestriction(); + }) | rpl::type_erased(); + }(); _composeControls->setHistory({ .history = _history.get(), .writeRestriction = std::move(writeRestriction), - }); + }); _composeControls->height( ) | rpl::start_with_next([=] { @@ -463,7 +465,7 @@ void ScheduledWidget::setupComposeControls() { void ScheduledWidget::chooseAttach() { if (const auto error = Data::AnyFileRestrictionError(_history->peer)) { - controller()->showToast(*error); + Data::ShowSendErrorToast(controller(), _history->peer, error); return; } @@ -667,12 +669,12 @@ bool ScheduledWidget::showSendingFilesError( bool ScheduledWidget::showSendingFilesError( const Ui::PreparedList &list, std::optional compress) const { - const auto text = [&] { + const auto error = [&]() -> Data::SendError { using Error = Ui::PreparedList::Error; const auto peer = _history->peer; const auto error = Data::FileRestrictionError(peer, list, compress); if (error) { - return *error; + return error; } else switch (list.error) { case Error::None: return QString(); case Error::EmptyFile: @@ -685,16 +687,16 @@ bool ScheduledWidget::showSendingFilesError( } return tr::lng_forward_send_files_cant(tr::now); }(); - if (text.isEmpty()) { + if (!error) { return false; - } else if (text == u"(toolarge)"_q) { + } else if (error.text == u"(toolarge)"_q) { const auto fileSize = list.files.back().size; controller()->show( Box(FileSizeLimitBox, &session(), fileSize, nullptr)); return true; } - controller()->showToast(text); + Data::ShowSendErrorToast(controller(), _history->peer, error); return true; } @@ -717,7 +719,7 @@ void ScheduledWidget::send() { return; } - const auto error = GetErrorTextForSending( + const auto error = GetErrorForSending( _history->peer, { .topicRootId = _forumTopic @@ -729,8 +731,8 @@ void ScheduledWidget::send() { .text = &textWithTags, .ignoreSlowmodeCountdown = true, }); - if (!error.isEmpty()) { - controller()->showToast(error); + if (error) { + Data::ShowSendErrorToast(controller(), _history->peer, error); return; } const auto callback = [=](Api::SendOptions options) { send(options); }; @@ -865,7 +867,7 @@ bool ScheduledWidget::sendExistingDocument( _history->peer, ChatRestriction::SendStickers); if (error) { - controller()->showToast(*error); + Data::ShowSendErrorToast(controller(), _history->peer, error); return false; } else if (ShowSendPremiumError(controller(), document)) { return false; @@ -893,7 +895,7 @@ bool ScheduledWidget::sendExistingPhoto( _history->peer, ChatRestriction::SendPhotos); if (error) { - controller()->showToast(*error); + Data::ShowSendErrorToast(controller(), _history->peer, error); return false; } @@ -909,9 +911,8 @@ bool ScheduledWidget::sendExistingPhoto( void ScheduledWidget::sendInlineResult( not_null result, not_null bot) { - const auto errorText = result->getErrorOnSend(_history); - if (!errorText.isEmpty()) { - controller()->showToast(errorText); + if (const auto error = result->getErrorOnSend(_history)) { + Data::ShowSendErrorToast(controller(), _history->peer, error); return; } const auto callback = [=](Api::SendOptions options) { diff --git a/Telegram/SourceFiles/inline_bots/inline_bot_result.cpp b/Telegram/SourceFiles/inline_bots/inline_bot_result.cpp index 7c487f843..e5e1566d5 100644 --- a/Telegram/SourceFiles/inline_bots/inline_bot_result.cpp +++ b/Telegram/SourceFiles/inline_bots/inline_bot_result.cpp @@ -394,13 +394,9 @@ not_null Result::makeMessage( return sendData->makeMessage(this, history, std::move(fields)); } -QString Result::getErrorOnSend(not_null history) const { - const auto specific = sendData->getErrorOnSend(this, history); - return !specific.isEmpty() - ? specific - : Data::RestrictionError( - history->peer, - ChatRestriction::SendInline).value_or(QString()); +Data::SendError Result::getErrorOnSend(not_null history) const { + return sendData->getErrorOnSend(this, history).value_or( + Data::RestrictionError(history->peer, ChatRestriction::SendInline)); } std::optional Result::getLocationPoint() const { diff --git a/Telegram/SourceFiles/inline_bots/inline_bot_result.h b/Telegram/SourceFiles/inline_bots/inline_bot_result.h index e0b89e3d9..67b3a0fd1 100644 --- a/Telegram/SourceFiles/inline_bots/inline_bot_result.h +++ b/Telegram/SourceFiles/inline_bots/inline_bot_result.h @@ -20,6 +20,7 @@ struct HistoryItemCommonFields; namespace Data { class LocationPoint; +struct SendError; } // namespace Data namespace InlineBots { @@ -69,7 +70,8 @@ public: [[nodiscard]] not_null makeMessage( not_null history, HistoryItemCommonFields &&fields) const; - QString getErrorOnSend(not_null history) const; + [[nodiscard]] Data::SendError getErrorOnSend( + not_null history) const; // interface for Layout:: usage std::optional getLocationPoint() const; diff --git a/Telegram/SourceFiles/inline_bots/inline_bot_send_data.cpp b/Telegram/SourceFiles/inline_bots/inline_bot_send_data.cpp index bacd4e3f8..e4ac798fa 100644 --- a/Telegram/SourceFiles/inline_bots/inline_bot_send_data.cpp +++ b/Telegram/SourceFiles/inline_bots/inline_bot_send_data.cpp @@ -42,11 +42,11 @@ not_null SendDataCommon::makeMessage( std::move(distinct.media)); } -QString SendDataCommon::getErrorOnSend( +Data::SendError SendDataCommon::getErrorOnSend( const Result *owner, not_null history) const { const auto type = ChatRestriction::SendOther; - return Data::RestrictionError(history->peer, type).value_or(QString()); + return Data::RestrictionError(history->peer, type); } SendDataCommon::SentMessageFields SendText::getSentMessageFields() const { @@ -106,11 +106,11 @@ not_null SendPhoto::makeMessage( TextWithEntities{ _message, _entities }); } -QString SendPhoto::getErrorOnSend( +Data::SendError SendPhoto::getErrorOnSend( const Result *owner, not_null history) const { const auto type = ChatRestriction::SendPhotos; - return Data::RestrictionError(history->peer, type).value_or(QString()); + return Data::RestrictionError(history->peer, type); } not_null SendFile::makeMessage( @@ -123,11 +123,11 @@ not_null SendFile::makeMessage( TextWithEntities{ _message, _entities }); } -QString SendFile::getErrorOnSend( +Data::SendError SendFile::getErrorOnSend( const Result *owner, not_null history) const { const auto type = _document->requiredSendRight(); - return Data::RestrictionError(history->peer, type).value_or(QString()); + return Data::RestrictionError(history->peer, type); } not_null SendGame::makeMessage( @@ -137,11 +137,11 @@ not_null SendGame::makeMessage( return history->makeMessage(std::move(fields), _game); } -QString SendGame::getErrorOnSend( +Data::SendError SendGame::getErrorOnSend( const Result *owner, not_null history) const { const auto type = ChatRestriction::SendGames; - return Data::RestrictionError(history->peer, type).value_or(QString()); + return Data::RestrictionError(history->peer, type); } SendDataCommon::SentMessageFields SendInvoice::getSentMessageFields() const { diff --git a/Telegram/SourceFiles/inline_bots/inline_bot_send_data.h b/Telegram/SourceFiles/inline_bots/inline_bot_send_data.h index 9c53b6207..82a12e644 100644 --- a/Telegram/SourceFiles/inline_bots/inline_bot_send_data.h +++ b/Telegram/SourceFiles/inline_bots/inline_bot_send_data.h @@ -11,6 +11,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL struct HistoryItemCommonFields; +namespace Data { +struct SendError; +} // namespace Data + namespace Main { class Session; } // namespace Main @@ -44,7 +48,7 @@ public: const Result *owner, not_null history, HistoryItemCommonFields &&fields) const = 0; - virtual QString getErrorOnSend( + virtual Data::SendError getErrorOnSend( const Result *owner, not_null history) const = 0; @@ -80,7 +84,7 @@ public: not_null history, HistoryItemCommonFields &&fields) const override; - QString getErrorOnSend( + Data::SendError getErrorOnSend( const Result *owner, not_null history) const override; @@ -241,7 +245,7 @@ public: not_null history, HistoryItemCommonFields &&fields) const override; - QString getErrorOnSend( + Data::SendError getErrorOnSend( const Result *owner, not_null history) const override; @@ -275,7 +279,7 @@ public: not_null history, HistoryItemCommonFields &&fields) const override; - QString getErrorOnSend( + Data::SendError getErrorOnSend( const Result *owner, not_null history) const override; @@ -303,7 +307,7 @@ public: not_null history, HistoryItemCommonFields &&fields) const override; - QString getErrorOnSend( + Data::SendError getErrorOnSend( const Result *owner, not_null history) const override; diff --git a/Telegram/SourceFiles/inline_bots/inline_results_inner.cpp b/Telegram/SourceFiles/inline_bots/inline_results_inner.cpp index 002bf1f97..c2ab83e95 100644 --- a/Telegram/SourceFiles/inline_bots/inline_results_inner.cpp +++ b/Telegram/SourceFiles/inline_bots/inline_results_inner.cpp @@ -25,6 +25,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "mainwindow.h" #include "main/main_session.h" #include "window/window_session_controller.h" +#include "ui/text/text_utilities.h" #include "ui/widgets/popup_menu.h" #include "ui/widgets/buttons.h" #include "ui/widgets/labels.h" @@ -113,19 +114,35 @@ void Inner::checkRestrictedPeer() { const auto error = Data::RestrictionError( _inlineQueryPeer, ChatRestriction::SendInline); - if (error) { - if (!_restrictedLabel) { - _restrictedLabel.create(this, *error, st::stickersRestrictedLabel); - _restrictedLabel->show(); - _restrictedLabel->move(st::inlineResultsLeft - st::roundRadiusSmall, st::stickerPanPadding); - _restrictedLabel->resizeToNaturalWidth(width() - (st::inlineResultsLeft - st::roundRadiusSmall) * 2); - if (_switchPmButton) { - _switchPmButton->hide(); - } - repaintItems(); - } + const auto changed = (_restrictedLabelKey != error.text); + if (!changed) { return; } + _restrictedLabelKey = error.text; + if (error) { + const auto window = _controller; + const auto peer = _inlineQueryPeer; + _restrictedLabel.create( + this, + rpl::single(error.boostsToLift + ? Ui::Text::Link(error.text) + : TextWithEntities{ error.text }), + st::stickersRestrictedLabel); + const auto lifting = error.boostsToLift; + _restrictedLabel->setClickHandlerFilter([=](auto...) { + window->resolveBoostState(peer->asChannel(), lifting); + return false; + }); + _restrictedLabel->show(); + updateRestrictedLabelGeometry(); + if (_switchPmButton) { + _switchPmButton->hide(); + } + repaintItems(); + return; + } + } else { + _restrictedLabelKey = QString(); } if (_restrictedLabel) { _restrictedLabel.destroy(); @@ -136,6 +153,18 @@ void Inner::checkRestrictedPeer() { } } +void Inner::updateRestrictedLabelGeometry() { + if (!_restrictedLabel) { + return; + } + + auto labelWidth = width() - st::stickerPanPadding * 2; + _restrictedLabel->resizeToWidth(labelWidth); + _restrictedLabel->moveToLeft( + (width() - _restrictedLabel->width()) / 2, + st::stickerPanPadding); +} + bool Inner::isRestrictedView() { checkRestrictedPeer(); return (_restrictedLabel != nullptr); @@ -178,6 +207,10 @@ rpl::producer<> Inner::inlineRowsCleared() const { Inner::~Inner() = default; +void Inner::resizeEvent(QResizeEvent *e) { + updateRestrictedLabelGeometry(); +} + void Inner::paintEvent(QPaintEvent *e) { Painter p(this); QRect r = e ? e->rect() : rect(); diff --git a/Telegram/SourceFiles/inline_bots/inline_results_inner.h b/Telegram/SourceFiles/inline_bots/inline_results_inner.h index 4e6195e19..28b305513 100644 --- a/Telegram/SourceFiles/inline_bots/inline_results_inner.h +++ b/Telegram/SourceFiles/inline_bots/inline_results_inner.h @@ -108,6 +108,7 @@ protected: void mousePressEvent(QMouseEvent *e) override; void mouseReleaseEvent(QMouseEvent *e) override; void mouseMoveEvent(QMouseEvent *e) override; + void resizeEvent(QResizeEvent *e) override; void paintEvent(QPaintEvent *e) override; void leaveEventHook(QEvent *e) override; void leaveToChildEvent(QEvent *e, QWidget *child) override; @@ -136,6 +137,7 @@ private: void clearInlineRows(bool resultsDeleted); ItemBase *layoutPrepareInlineResult(Result *result); + void updateRestrictedLabelGeometry(); void deleteUnusedInlineLayouts(); int validateExistingInlineRows(const Results &results); @@ -162,6 +164,7 @@ private: QByteArray _switchPmUrl; object_ptr _restrictedLabel = { nullptr }; + QString _restrictedLabelKey; base::unique_qptr _menu; diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index 8938d5af4..06bea1d04 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -52,7 +52,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "apiwrap.h" #include "dialogs/dialogs_widget.h" #include "history/history_widget.h" -#include "history/history_item_helpers.h" // GetErrorTextForSending. +#include "history/history_item_helpers.h" // GetErrorForSending. #include "history/view/media/history_view_media.h" #include "history/view/history_view_service_message.h" #include "history/view/history_view_sublist_section.h" @@ -556,15 +556,15 @@ bool MainWidget::setForwardDraft( const auto history = thread->owningHistory(); const auto items = session().data().idsToItems(draft.ids); const auto topicRootId = thread->topicRootId(); - const auto error = GetErrorTextForSending( + const auto error = GetErrorForSending( history->peer, { .topicRootId = topicRootId, .forward = &items, .ignoreSlowmodeCountdown = true, }); - if (!error.isEmpty()) { - _controller->show(Ui::MakeInformBox(error)); + if (error) { + Data::ShowSendErrorToast(_controller, history->peer, error); return false; } @@ -611,12 +611,12 @@ bool MainWidget::sendPaths( not_null thread, const QStringList &paths) { if (!Data::CanSendAnyOf(thread, Data::FilesSendRestrictions())) { - _controller->show(Ui::MakeInformBox( - tr::lng_forward_send_files_cant())); + _controller->showToast( + tr::lng_forward_send_files_cant(tr::now)); return false; } else if (const auto error = Data::AnyFileRestrictionError( thread->peer())) { - _controller->show(Ui::MakeInformBox(*error)); + Data::ShowSendErrorToast(controller(), thread->peer(), error); return false; } else { _controller->showThread( @@ -659,12 +659,12 @@ bool MainWidget::filesOrForwardDrop( } return false; } else if (!Data::CanSendAnyOf(thread, Data::FilesSendRestrictions())) { - _controller->show(Ui::MakeInformBox( - tr::lng_forward_send_files_cant())); + _controller->showToast( + tr::lng_forward_send_files_cant(tr::now)); return false; } else if (const auto error = Data::AnyFileRestrictionError( thread->peer())) { - _controller->show(Ui::MakeInformBox(*error)); + Data::ShowSendErrorToast(_controller, thread->peer(), error); return false; } else { _controller->showThread( diff --git a/Telegram/SourceFiles/media/stories/media_stories_reply.cpp b/Telegram/SourceFiles/media/stories/media_stories_reply.cpp index c034d61dc..09e222737 100644 --- a/Telegram/SourceFiles/media/stories/media_stories_reply.cpp +++ b/Telegram/SourceFiles/media/stories/media_stories_reply.cpp @@ -20,6 +20,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "core/file_utilities.h" #include "core/mime_type.h" #include "data/stickers/data_custom_emoji.h" +#include "data/data_chat_participant_status.h" #include "data/data_document.h" #include "data/data_message_reaction_id.h" #include "data/data_peer_values.h" @@ -219,15 +220,15 @@ bool ReplyArea::send( return false; } - const auto error = GetErrorTextForSending( + const auto error = GetErrorForSending( _data.peer, { .topicRootId = MsgId(0), .text = &message.textWithTags, .ignoreSlowmodeCountdown = (options.scheduled != 0), }); - if (!error.isEmpty()) { - _controller->uiShow()->showToast(error); + if (error) { + Data::ShowSendErrorToast(_controller->uiShow(), _data.peer, error); return false; } @@ -262,7 +263,7 @@ bool ReplyArea::sendExistingDocument( _data.peer, ChatRestriction::SendStickers); if (error) { - show->showToast(*error); + Data::ShowSendErrorToast(show, _data.peer, error); return false; } else if (showSlowmodeError() || Window::ShowSendPremiumError(show, document)) { @@ -290,7 +291,7 @@ bool ReplyArea::sendExistingPhoto( _data.peer, ChatRestriction::SendPhotos); if (error) { - show->showToast(*error); + Data::ShowSendErrorToast(show, _data.peer, error); return false; } else if (showSlowmodeError()) { return false; @@ -308,9 +309,9 @@ bool ReplyArea::sendExistingPhoto( void ReplyArea::sendInlineResult( not_null result, not_null bot) { - const auto errorText = result->getErrorOnSend(history()); - if (!errorText.isEmpty()) { - _controller->uiShow()->showToast(errorText); + if (const auto error = result->getErrorOnSend(history())) { + const auto show = _controller->uiShow(); + Data::ShowSendErrorToast(show, history()->peer, error); return; } sendInlineResult(result, bot, {}, std::nullopt); @@ -363,11 +364,11 @@ bool ReplyArea::showSendingFilesError( bool ReplyArea::showSendingFilesError( const Ui::PreparedList &list, std::optional compress) const { - const auto text = [&] { + const auto error = [&]() -> Data::SendError { const auto peer = _data.peer; const auto error = Data::FileRestrictionError(peer, list, compress); if (error) { - return *error; + return error; } using Error = Ui::PreparedList::Error; switch (list.error) { @@ -382,9 +383,9 @@ bool ReplyArea::showSendingFilesError( } return tr::lng_forward_send_files_cant(tr::now); }(); - if (text.isEmpty()) { + if (!error) { return false; - } else if (text == u"(toolarge)"_q) { + } else if (error.text == u"(toolarge)"_q) { const auto fileSize = list.files.back().size; _controller->uiShow()->showBox(Box( FileSizeLimitBox, @@ -394,7 +395,7 @@ bool ReplyArea::showSendingFilesError( return true; } - _controller->uiShow()->showToast(text); + Data::ShowSendErrorToast(_controller->uiShow(), _data.peer, error); return true; } @@ -422,7 +423,7 @@ void ReplyArea::chooseAttach( } const auto peer = not_null(_data.peer); if (const auto error = Data::AnyFileRestrictionError(peer)) { - _controller->uiShow()->showToast(*error); + Data::ShowSendErrorToast(_controller->uiShow(), peer, error); return; } else if (showSlowmodeError()) { return; diff --git a/Telegram/SourceFiles/media/stories/media_stories_share.cpp b/Telegram/SourceFiles/media/stories/media_stories_share.cpp index 773239e0b..46e0520d1 100644 --- a/Telegram/SourceFiles/media/stories/media_stories_share.cpp +++ b/Telegram/SourceFiles/media/stories/media_stories_share.cpp @@ -22,7 +22,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_thread.h" #include "data/data_user.h" #include "history/history.h" -#include "history/history_item_helpers.h" // GetErrorTextForSending. +#include "history/history_item_helpers.h" // GetErrorForSending. #include "history/view/history_view_context_menu.h" // CopyStoryLink. #include "lang/lang_keys.h" #include "main/main_session.h" @@ -87,26 +87,11 @@ namespace Media::Stories { return; } const auto peer = story->peer(); - const auto error = [&] { - for (const auto thread : result) { - const auto error = GetErrorTextForSending( - thread, - { .story = story, .text = &comment }); - if (!error.isEmpty()) { - return std::make_pair(error, thread); - } - } - return std::make_pair(QString(), result.front()); - }(); - if (!error.first.isEmpty()) { - auto text = TextWithEntities(); - if (result.size() > 1) { - text.append( - Ui::Text::Bold(error.second->chatListName()) - ).append("\n\n"); - } - text.append(error.first); - show->showBox(Ui::MakeInformBox(text)); + const auto error = GetErrorForSending( + result, + { .story = story, .text = &comment }); + if (error.error) { + show->showBox(MakeSendErrorBox(error, result.size() > 1)); return; } diff --git a/Telegram/SourceFiles/platform/mac/touchbar/items/mac_scrubber_item.mm b/Telegram/SourceFiles/platform/mac/touchbar/items/mac_scrubber_item.mm index ac97db92d..6a55ff7d0 100644 --- a/Telegram/SourceFiles/platform/mac/touchbar/items/mac_scrubber_item.mm +++ b/Telegram/SourceFiles/platform/mac/touchbar/items/mac_scrubber_item.mm @@ -185,7 +185,9 @@ std::optional RestrictionToSend( not_null controller, ChatRestriction right) { if (const auto peer = ActiveChat(controller).peer()) { - return Data::RestrictionError(peer, right); + if (const auto error = Data::RestrictionError(peer, right)) { + return *error; + } } return std::nullopt; } diff --git a/Telegram/SourceFiles/settings/business/settings_shortcut_messages.cpp b/Telegram/SourceFiles/settings/business/settings_shortcut_messages.cpp index d1dfbbad5..995a98202 100644 --- a/Telegram/SourceFiles/settings/business/settings_shortcut_messages.cpp +++ b/Telegram/SourceFiles/settings/business/settings_shortcut_messages.cpp @@ -1538,10 +1538,8 @@ void ShortcutMessages::sendInlineResult( not_null bot) { if (showPremiumRequired()) { return; - } - const auto errorText = result->getErrorOnSend(_history); - if (!errorText.isEmpty()) { - _controller->showToast(errorText); + } else if (const auto error = result->getErrorOnSend(_history)) { + Data::ShowSendErrorToast(_controller, _history->peer, error); return; } sendInlineResult(result, bot, {}, std::nullopt); diff --git a/Telegram/SourceFiles/ui/boxes/boost_box.cpp b/Telegram/SourceFiles/ui/boxes/boost_box.cpp index 500748ac2..23e378146 100644 --- a/Telegram/SourceFiles/ui/boxes/boost_box.cpp +++ b/Telegram/SourceFiles/ui/boxes/boost_box.cpp @@ -325,8 +325,14 @@ void BoostBox( return (counters.mine > 1) ? u"x%1"_q.arg(counters.mine) : u""_q; }); + const auto wasMine = state->data.current().mine; + const auto wasLifting = data.lifting; auto text = state->data.value( ) | rpl::map([=](BoostCounters counters) { + const auto lifting = wasLifting + ? (wasLifting + - std::clamp(counters.mine - wasMine, 0, wasLifting - 1)) + : 0; const auto bold = Ui::Text::Bold(name); const auto now = counters.boosts; const auto full = !counters.nextLevelBoosts; @@ -337,7 +343,14 @@ void BoostBox( lt_count, rpl::single(float64(counters.level + (left ? 1 : 0))), Ui::Text::RichLangValue); - return (counters.mine || full) + return (lifting > 1) + ? tr::lng_boost_group_lift_restrictions_many( + lt_count, + rpl::single(float64(lifting)), + Ui::Text::RichLangValue) + : lifting + ? tr::lng_boost_group_lift_restrictions(Ui::Text::RichLangValue) + : (counters.mine || full) ? (left ? tr::lng_boost_channel_needs_unlock( lt_count, @@ -365,6 +378,14 @@ void BoostBox( rpl::single(bold), Ui::Text::RichLangValue); }) | rpl::flatten_latest(); + if (wasLifting) { + state->data.value( + ) | rpl::start_with_next([=](BoostCounters counters) { + if (counters.mine - wasMine >= wasLifting) { + box->closeBox(); + } + }, box->lifetime()); + } auto faded = object_ptr>( close->parentWidget(), diff --git a/Telegram/SourceFiles/ui/boxes/boost_box.h b/Telegram/SourceFiles/ui/boxes/boost_box.h index 4ecd58bf2..12c97c2c1 100644 --- a/Telegram/SourceFiles/ui/boxes/boost_box.h +++ b/Telegram/SourceFiles/ui/boxes/boost_box.h @@ -48,6 +48,7 @@ struct BoostBoxData { QString name; BoostCounters boost; BoostFeatures features; + int lifting = 0; bool allowMulti = false; bool group = false; }; diff --git a/Telegram/SourceFiles/window/window_peer_menu.cpp b/Telegram/SourceFiles/window/window_peer_menu.cpp index 0efcec3ca..5e7978c9c 100644 --- a/Telegram/SourceFiles/window/window_peer_menu.cpp +++ b/Telegram/SourceFiles/window/window_peer_menu.cpp @@ -61,7 +61,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "api/api_updates.h" #include "mtproto/mtproto_config.h" #include "history/history.h" -#include "history/history_item_helpers.h" // GetErrorTextForSending. +#include "history/history_item_helpers.h" // GetErrorForSending. #include "history/view/history_view_context_menu.h" #include "window/window_separate_id.h" #include "window/window_session_controller.h" @@ -2595,11 +2595,11 @@ QPointer ShowSendNowMessagesBox( : tr::lng_scheduled_send_now(tr::now); const auto list = session->data().idsToItems(items); - const auto error = GetErrorTextForSending( + const auto error = GetErrorForSending( history->peer, { .forward = &list }); - if (!error.isEmpty()) { - navigation->showToast(error); + if (error) { + Data::ShowSendErrorToast(navigation, history->peer, error); return { nullptr }; } auto done = [ diff --git a/Telegram/SourceFiles/window/window_session_controller.cpp b/Telegram/SourceFiles/window/window_session_controller.cpp index 9f4a72396..559b60f00 100644 --- a/Telegram/SourceFiles/window/window_session_controller.cpp +++ b/Telegram/SourceFiles/window/window_session_controller.cpp @@ -764,7 +764,10 @@ void SessionNavigation::showPeerByLinkResolved( } } -void SessionNavigation::resolveBoostState(not_null channel) { +void SessionNavigation::resolveBoostState( + not_null channel, + int boostsToLift) { + _boostsToLift = boostsToLift; if (_boostStateResolving == channel) { return; } @@ -772,18 +775,33 @@ void SessionNavigation::resolveBoostState(not_null channel) { _api.request(MTPpremium_GetBoostsStatus( channel->input )).done([=](const MTPpremium_BoostsStatus &result) { - _boostStateResolving = nullptr; + if (base::take(_boostStateResolving) != channel) { + return; + } + const auto boosted = std::make_shared(); channel->updateLevelHint(result.data().vlevel().v); const auto submit = [=](Fn done) { - applyBoost(channel, done); + applyBoost(channel, [=](Ui::BoostCounters counters) { + *boosted = true; + done(counters); + }); }; - uiShow()->show(Box(Ui::BoostBox, Ui::BoostBoxData{ + const auto lifting = base::take(_boostsToLift); + const auto box = uiShow()->show(Box(Ui::BoostBox, Ui::BoostBoxData{ .name = channel->name(), .boost = ParseBoostCounters(result), .features = LookupBoostFeatures(channel), + .lifting = lifting, .allowMulti = (BoostsForGift(_session) > 0), .group = channel->isMegagroup(), }, submit)); + if (lifting) { + box->boxClosing() | rpl::start_with_next([=] { + if (*boosted) { + channel->updateFullForced(); + } + }, box->lifetime()); + } }).fail([=](const MTP::Error &error) { _boostStateResolving = nullptr; showToast(u"Error: "_q + error.type()); diff --git a/Telegram/SourceFiles/window/window_session_controller.h b/Telegram/SourceFiles/window/window_session_controller.h index 9403467c0..0bde193f0 100644 --- a/Telegram/SourceFiles/window/window_session_controller.h +++ b/Telegram/SourceFiles/window/window_session_controller.h @@ -253,7 +253,9 @@ public: Dialogs::Key inChat, PeerData *searchFrom = nullptr); - void resolveBoostState(not_null channel); + void resolveBoostState( + not_null channel, + int boostsToLift = 0); void resolveCollectible( PeerId ownerId, @@ -319,6 +321,7 @@ private: mtpRequestId _showingRepliesRequestId = 0; ChannelData *_boostStateResolving = nullptr; + int _boostsToLift = 0; QString _collectibleEntity; mtpRequestId _collectibleRequestId = 0;