diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 3ca197ddd6..b57e4409ee 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -204,14 +204,14 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_fave_sticker_limit_title#one" = "The Limit of {count} Stickers Reached"; "lng_fave_sticker_limit_title#other" = "The Limit of {count} Stickers Reached"; -"lng_fave_sticker_limit_more#one" = "An older sticker was replaced with this one. You can {link} to {count} sticker."; -"lng_fave_sticker_limit_more#other" = "An older sticker was replaced with this one. You can {link} to {count} stickers."; +"lng_fave_sticker_limit_more#one" = "An older sticker was replaced with this one.\nYou can {link} to {count} sticker."; +"lng_fave_sticker_limit_more#other" = "An older sticker was replaced with this one.\nYou can {link} to {count} stickers."; "lng_fave_sticker_limit_link" = "increase the limit"; "lng_saved_gif_limit_title#one" = "The Limit of {count} GIF Reached"; "lng_saved_gif_limit_title#other" = "The Limit of {count} GIFs Reached"; -"lng_saved_gif_limit_more#one" = "An older GIF was replaced with this one. You can {link} to {count} GIF."; -"lng_saved_gif_limit_more#other" = "An older GIF was replaced with this one. You can {link} to {count} GIFs."; +"lng_saved_gif_limit_more#one" = "An older GIF was replaced with this one.\nYou can {link} to {count} GIF."; +"lng_saved_gif_limit_more#other" = "An older GIF was replaced with this one.\nYou can {link} to {count} GIFs."; "lng_saved_gif_limit_link" = "increase the limit"; "lng_limits_increase" = "Increase Limit"; diff --git a/Telegram/SourceFiles/api/api_chat_filters.cpp b/Telegram/SourceFiles/api/api_chat_filters.cpp index 68b0dbebe0..5cb157b5c2 100644 --- a/Telegram/SourceFiles/api/api_chat_filters.cpp +++ b/Telegram/SourceFiles/api/api_chat_filters.cpp @@ -21,9 +21,7 @@ void SaveNewFilterPinned( nullptr, filterId); auto &filters = session->data().chatsFilters(); - const auto &filter = filters.applyUpdatedPinned( - filterId, - order); + const auto &filter = filters.applyUpdatedPinned(filterId, order); session->api().request(MTPmessages_UpdateDialogFilter( MTP_flags(MTPmessages_UpdateDialogFilter::Flag::f_filter), MTP_int(filterId), diff --git a/Telegram/SourceFiles/api/api_toggling_media.cpp b/Telegram/SourceFiles/api/api_toggling_media.cpp index dbfcfde349..b67e3002de 100644 --- a/Telegram/SourceFiles/api/api_toggling_media.cpp +++ b/Telegram/SourceFiles/api/api_toggling_media.cpp @@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_file_origin.h" #include "data/data_session.h" #include "data/stickers/data_stickers.h" +#include "window/window_session_controller.h" #include "main/main_session.h" namespace Api { @@ -47,28 +48,35 @@ void ToggleExistingMedia( } // namespace void ToggleFavedSticker( + not_null controller, not_null document, Data::FileOrigin origin) { ToggleFavedSticker( + controller, document, std::move(origin), !document->owner().stickers().isFaved(document)); } void ToggleFavedSticker( + not_null controller, not_null document, Data::FileOrigin origin, bool faved) { if (faved && !document->sticker()) { return; } + const auto weak = base::make_weak(controller.get()); + auto done = [=] { + document->owner().stickers().setFaved(weak.get(), document, faved); + }; ToggleExistingMedia( document, std::move(origin), [=, d = document] { return MTPmessages_FaveSticker(d->mtpInput(), MTP_bool(!faved)); }, - [=] { document->owner().stickers().setFaved(document, faved); }); + std::move(done)); } void ToggleRecentSticker( @@ -96,15 +104,17 @@ void ToggleRecentSticker( } void ToggleSavedGif( + Window::SessionController *controller, not_null document, Data::FileOrigin origin, bool saved) { if (saved && !document->isGifv()) { return; } + const auto weak = base::make_weak(controller); auto done = [=] { if (saved) { - document->owner().stickers().addSavedGif(document); + document->owner().stickers().addSavedGif(weak.get(), document); } }; ToggleExistingMedia( diff --git a/Telegram/SourceFiles/api/api_toggling_media.h b/Telegram/SourceFiles/api/api_toggling_media.h index fc1c84e175..a028824d5b 100644 --- a/Telegram/SourceFiles/api/api_toggling_media.h +++ b/Telegram/SourceFiles/api/api_toggling_media.h @@ -7,13 +7,19 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once +namespace Window { +class SessionController; +} // namespace Window + namespace Api { void ToggleFavedSticker( + not_null controller, not_null document, Data::FileOrigin origin); void ToggleFavedSticker( + not_null controller, not_null document, Data::FileOrigin origin, bool faved); @@ -24,6 +30,7 @@ void ToggleRecentSticker( bool saved); void ToggleSavedGif( + Window::SessionController *controller, not_null document, Data::FileOrigin origin, bool saved); diff --git a/Telegram/SourceFiles/boxes/choose_filter_box.cpp b/Telegram/SourceFiles/boxes/choose_filter_box.cpp index d5efcde408..868b95ecba 100644 --- a/Telegram/SourceFiles/boxes/choose_filter_box.cpp +++ b/Telegram/SourceFiles/boxes/choose_filter_box.cpp @@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "boxes/choose_filter_box.h" #include "apiwrap.h" +#include "boxes/premium_limits_box.h" #include "core/application.h" // primaryWindow #include "data/data_chat_filters.h" #include "data/data_session.h" @@ -116,6 +117,15 @@ bool ChooseFilterValidator::canRemove(FilterId filterId) const { return false; } +bool ChooseFilterValidator::limitReached(FilterId filterId) const { + const auto list = _history->owner().chatsFilters().list(); + const auto i = ranges::find(list, filterId, &Data::ChatFilter::id); + const auto limit = _history->owner().pinnedChatsLimit(nullptr, filterId); + return (i != end(list)) + && !ranges::contains(i->always(), _history) + && (i->always().size() >= limit); +} + void ChooseFilterValidator::add(FilterId filterId) const { ChangeFilterById(filterId, _history, true); } @@ -125,8 +135,10 @@ void ChooseFilterValidator::remove(FilterId filterId) const { } void FillChooseFilterMenu( + not_null controller, not_null menu, not_null history) { + const auto weak = base::make_weak(controller.get()); const auto validator = ChooseFilterValidator(history); for (const auto &filter : history->owner().chatsFilters().list()) { const auto id = filter.id(); @@ -136,10 +148,11 @@ void FillChooseFilterMenu( if (validator.canRemove(id)) { validator.remove(id); } - } else { - if (validator.canAdd()) { - validator.add(id); - } + } else if (validator.limitReached(id)) { + controller->show( + Box(FilterChatsLimitBox, &controller->session())); + } else if (validator.canAdd()) { + validator.add(id); } }, contains ? &st::mediaPlayerMenuCheck : nullptr); action->setEnabled(contains diff --git a/Telegram/SourceFiles/boxes/choose_filter_box.h b/Telegram/SourceFiles/boxes/choose_filter_box.h index 03dac402ca..eeb49a765a 100644 --- a/Telegram/SourceFiles/boxes/choose_filter_box.h +++ b/Telegram/SourceFiles/boxes/choose_filter_box.h @@ -11,6 +11,10 @@ namespace Ui { class PopupMenu; } // namespace Ui +namespace Window { +class SessionController; +} // namespace Window + class History; class ChooseFilterValidator final { @@ -19,6 +23,7 @@ public: [[nodiscard]] bool canAdd() const; [[nodiscard]] bool canRemove(FilterId filterId) const; + [[nodiscard]] bool limitReached(FilterId filterId) const; void add(FilterId filterId) const; void remove(FilterId filterId) const; @@ -29,5 +34,6 @@ private: }; void FillChooseFilterMenu( + not_null controller, not_null menu, not_null history); diff --git a/Telegram/SourceFiles/boxes/premium_limits_box.cpp b/Telegram/SourceFiles/boxes/premium_limits_box.cpp index 7d3ad254eb..5c07564bac 100644 --- a/Telegram/SourceFiles/boxes/premium_limits_box.cpp +++ b/Telegram/SourceFiles/boxes/premium_limits_box.cpp @@ -23,6 +23,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "apiwrap.h" #include "styles/style_boxes.h" #include "styles/style_layers.h" +#include "styles/style_info.h" namespace { @@ -243,8 +244,8 @@ std::unique_ptr InactiveController::createRow( [[nodiscard]] float64 Limit( not_null session, const QString &key, - double fallback) { - return session->account().appConfig().get(key, fallback); + int fallback) { + return 1. * AppConfigLimit(session, key, fallback); } void SimpleLimitBox( @@ -277,6 +278,19 @@ void SimpleLimitBox( st::changePhoneDescription)), st::changePhoneDescriptionPadding); + box->setNoContentMargin(true); + + const auto close = Ui::CreateChild( + box.get(), + st::infoLayerTopBarClose); + close->addClickHandler([=] { box->closeBox(); }); + box->widthValue() | rpl::start_with_next([=](int width) { + close->moveToRight(0, 0, width); + }, close->lifetime()); + box->setFocusCallback([=] { + close->raise(); + }); + if (premium) { box->addButton(tr::lng_box_ok(), [=] { box->closeBox(); @@ -531,9 +545,21 @@ void FilterPinsLimitBox( SimplePinsLimitBox( box, session, - "dialog_filters_pinned_limit_default", + "dialog_filters_chats_limit_default", 100, - "dialog_filters_pinned_limit_premium", + "dialog_filters_chats_limit_premium", + 200); +} + +void FolderPinsLimitBox( + not_null box, + not_null session) { + SimplePinsLimitBox( + box, + session, + "dialog_filters_chats_limit_default", + 100, + "dialog_filters_chats_limit_premium", 200); } @@ -548,3 +574,24 @@ void PinsLimitBox( "dialogs_pinned_limit_premium", 10); } + +int AppConfigLimit( + not_null session, + const QString &key, + int fallback) { + return int(base::SafeRound( + session->account().appConfig().get(key, 1. * fallback))); +} + +int CurrentPremiumLimit( + not_null session, + const QString &keyDefault, + int limitDefault, + const QString &keyPremium, + int limitPremium) { + const auto premium = session->user()->isPremium(); + return AppConfigLimit( + session, + premium ? keyPremium : keyDefault, + premium ? limitPremium : limitDefault); +} diff --git a/Telegram/SourceFiles/boxes/premium_limits_box.h b/Telegram/SourceFiles/boxes/premium_limits_box.h index fe93d3a351..92b3b04b9a 100644 --- a/Telegram/SourceFiles/boxes/premium_limits_box.h +++ b/Telegram/SourceFiles/boxes/premium_limits_box.h @@ -28,6 +28,20 @@ void FiltersLimitBox( void FilterPinsLimitBox( not_null box, not_null session); +void FolderPinsLimitBox( + not_null box, + not_null session); void PinsLimitBox( not_null box, not_null session); + +[[nodiscard]] int AppConfigLimit( + not_null session, + const QString &key, + int fallback); +[[nodiscard]] int CurrentPremiumLimit( + not_null session, + const QString &keyDefault, + int limitDefault, + const QString &keyPremium, + int limitPremium); diff --git a/Telegram/SourceFiles/boxes/sticker_set_box.cpp b/Telegram/SourceFiles/boxes/sticker_set_box.cpp index d125f346dd..aa65215d82 100644 --- a/Telegram/SourceFiles/boxes/sticker_set_box.cpp +++ b/Telegram/SourceFiles/boxes/sticker_set_box.cpp @@ -699,8 +699,10 @@ void StickerSetBox::Inner::contextMenuEvent(QContextMenuEvent *e) { SendMenu::DefaultSilentCallback(sendSelected), SendMenu::DefaultScheduleCallback(this, type, sendSelected)); + const auto controller = _controller; const auto toggleFavedSticker = [=] { Api::ToggleFavedSticker( + controller, document, Data::FileOriginStickerSet(Data::Stickers::FavedSetId, 0)); }; diff --git a/Telegram/SourceFiles/chat_helpers/gifs_list_widget.cpp b/Telegram/SourceFiles/chat_helpers/gifs_list_widget.cpp index a7e99f2f20..f0d301417a 100644 --- a/Telegram/SourceFiles/chat_helpers/gifs_list_widget.cpp +++ b/Telegram/SourceFiles/chat_helpers/gifs_list_widget.cpp @@ -52,6 +52,7 @@ constexpr auto kMinAfterScrollDelay = crl::time(33); void AddGifAction( Fn &&, const style::icon*)> callback, + Window::SessionController *controller, not_null document) { if (!document->isGifv()) { return; @@ -64,6 +65,7 @@ void AddGifAction( : tr::lng_context_save_gif)(tr::now); callback(text, [=] { Api::ToggleSavedGif( + controller, document, Data::FileOriginSavedGifs(), !saved); @@ -396,7 +398,7 @@ void GifsListWidget::fillContextMenu( const style::icon *icon) { menu->addAction(text, std::move(done), icon); }; - AddGifAction(std::move(callback), document); + AddGifAction(std::move(callback), controller(), document); } }; } diff --git a/Telegram/SourceFiles/chat_helpers/gifs_list_widget.h b/Telegram/SourceFiles/chat_helpers/gifs_list_widget.h index 8bc6e3d230..14bb998e07 100644 --- a/Telegram/SourceFiles/chat_helpers/gifs_list_widget.h +++ b/Telegram/SourceFiles/chat_helpers/gifs_list_widget.h @@ -42,6 +42,7 @@ namespace ChatHelpers { void AddGifAction( Fn &&, const style::icon*)> callback, + Window::SessionController *controller, not_null document); class GifsListWidget diff --git a/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp b/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp index 09c992013b..f260abc40c 100644 --- a/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp +++ b/Telegram/SourceFiles/chat_helpers/stickers_list_widget.cpp @@ -2509,8 +2509,10 @@ void StickersListWidget::fillContextMenu( SendMenu::DefaultSilentCallback(send), SendMenu::DefaultScheduleCallback(this, type, send)); + const auto window = controller(); const auto toggleFavedSticker = [=] { Api::ToggleFavedSticker( + window, document, Data::FileOriginStickerSet(Data::Stickers::FavedSetId, 0)); }; @@ -2683,8 +2685,9 @@ void StickersListWidget::removeFavedSticker(int section, int index) { clearSelection(); const auto &sticker = _mySets[section].stickers[index]; const auto document = sticker.document; - session().data().stickers().setFaved(document, false); + session().data().stickers().setFaved(controller(), document, false); Api::ToggleFavedSticker( + controller(), document, Data::FileOriginStickerSet(Data::Stickers::FavedSetId, 0), false); diff --git a/Telegram/SourceFiles/data/data_chat_filters.cpp b/Telegram/SourceFiles/data/data_chat_filters.cpp index 68812aaa83..3f67e4fda7 100644 --- a/Telegram/SourceFiles/data/data_chat_filters.cpp +++ b/Telegram/SourceFiles/data/data_chat_filters.cpp @@ -16,9 +16,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_folder.h" #include "data/data_histories.h" #include "dialogs/dialogs_main_list.h" +#include "history/history.h" #include "history/history_unread_things.h" #include "ui/ui_utility.h" #include "main/main_session.h" +#include "main/main_account.h" +#include "main/main_app_config.h" #include "apiwrap.h" namespace Data { @@ -225,10 +228,15 @@ ChatFilters::~ChatFilters() = default; not_null ChatFilters::chatsList(FilterId filterId) { auto &pointer = _chatsLists[filterId]; if (!pointer) { + auto limit = rpl::single(rpl::empty_value()) | rpl::then( + _owner->session().account().appConfig().refreshed() + ) | rpl::map([=] { + return _owner->pinnedChatsLimit(nullptr, filterId); + }); pointer = std::make_unique( &_owner->session(), filterId, - rpl::single(ChatFilter::kPinnedLimit)); + _owner->maxPinnedChatsLimitValue(nullptr, filterId)); } return pointer.get(); } @@ -450,6 +458,7 @@ const ChatFilter &ChatFilters::applyUpdatedPinned( const auto i = ranges::find(_list, id, &ChatFilter::id); Assert(i != end(_list)); + const auto limit = _owner->pinnedChatsLimit(nullptr, id); auto always = i->always(); auto pinned = std::vector>(); pinned.reserve(dialogs.size()); @@ -457,7 +466,7 @@ const ChatFilter &ChatFilters::applyUpdatedPinned( if (const auto history = row.history()) { if (always.contains(history)) { pinned.push_back(history); - } else if (always.size() < ChatFilter::kPinnedLimit) { + } else if (always.size() < limit) { always.insert(history); pinned.push_back(history); } diff --git a/Telegram/SourceFiles/data/data_chat_filters.h b/Telegram/SourceFiles/data/data_chat_filters.h index 43cb0d5817..40bf34dda4 100644 --- a/Telegram/SourceFiles/data/data_chat_filters.h +++ b/Telegram/SourceFiles/data/data_chat_filters.h @@ -35,8 +35,6 @@ public: friend constexpr inline bool is_flag_type(Flag) { return true; }; using Flags = base::flags; - static constexpr int kPinnedLimit = 100; - ChatFilter() = default; ChatFilter( FilterId id, diff --git a/Telegram/SourceFiles/data/data_folder.cpp b/Telegram/SourceFiles/data/data_folder.cpp index 2714b446df..103569e19e 100644 --- a/Telegram/SourceFiles/data/data_folder.cpp +++ b/Telegram/SourceFiles/data/data_folder.cpp @@ -38,7 +38,7 @@ Folder::Folder(not_null owner, FolderId id) , _chatsList( &owner->session(), FilterId(), - owner->session().serverConfig().pinnedDialogsInFolderMax.value()) + owner->maxPinnedChatsLimitValue(this, FilterId())) , _name(tr::lng_archived_name(tr::now)) , _chatListNameSortKey(owner->nameSortKey(_name)) { indexNameParts(); diff --git a/Telegram/SourceFiles/data/data_session.cpp b/Telegram/SourceFiles/data/data_session.cpp index 74d1e226d5..281d1972ba 100644 --- a/Telegram/SourceFiles/data/data_session.cpp +++ b/Telegram/SourceFiles/data/data_session.cpp @@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "main/main_session.h" #include "main/main_session_settings.h" #include "main/main_account.h" +#include "main/main_app_config.h" #include "apiwrap.h" #include "mainwidget.h" #include "api/api_text_entities.h" @@ -33,6 +34,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "media/player/media_player_instance.h" // instance()->play() #include "media/audio/media_audio.h" #include "boxes/abstract_box.h" +#include "boxes/premium_limits_box.h" #include "passport/passport_form_controller.h" #include "lang/lang_keys.h" // tr::lng_deleted(tr::now) in user name #include "data/stickers/data_stickers.h" @@ -225,7 +227,7 @@ Session::Session(not_null session) , _chatsList( session, FilterId(), - session->serverConfig().pinnedDialogsCountMax.value()) + maxPinnedChatsLimitValue(nullptr, FilterId())) , _contactsList(Dialogs::SortMode::Name) , _contactsNoChatsList(Dialogs::SortMode::Name) , _ttlCheckTimer([=] { checkTTLs(); }) @@ -1832,20 +1834,54 @@ int Session::pinnedCanPin( FilterId filterId, not_null history) const { if (!filterId) { - const auto limit = pinnedChatsLimit(folder); + const auto limit = pinnedChatsLimit(folder, filterId); return pinnedChatsOrder(folder, FilterId()).size() < limit; } const auto &list = chatsFilters().list(); const auto i = ranges::find(list, filterId, &Data::ChatFilter::id); return (i == end(list)) || (i->always().contains(history)) - || (i->always().size() < Data::ChatFilter::kPinnedLimit); + || (i->always().size() < pinnedChatsLimit(folder, filterId)); } -int Session::pinnedChatsLimit(Data::Folder *folder) const { - return folder - ? session().serverConfig().pinnedDialogsInFolderMax.current() - : session().serverConfig().pinnedDialogsCountMax.current(); +int Session::pinnedChatsLimit( + Data::Folder *folder, + FilterId filterId) const { + return CurrentPremiumLimit( + _session, + (filterId + ? "dialog_filters_chats_limit_default" + : folder + ? "dialog_filters_chats_limit_default" + : "dialogs_pinned_limit_default"), + (filterId || folder) ? 100 : 5, + (filterId + ? "dialog_filters_chats_limit_premium" + : folder + ? "dialog_filters_chats_limit_premium" + : "dialogs_pinned_limit_premium"), + (filterId || folder) ? 200 : 10); +} + +rpl::producer Session::maxPinnedChatsLimitValue( + Data::Folder *folder, + FilterId filterId) const { + // Premium limit from appconfig. + // We always use premium limit in the MainList limit producer, + // because it slices the list to that limit. We don't want to slice + // premium-ly added chats from the pinned list because of sync issues. + return rpl::single(rpl::empty_value()) | rpl::then( + _session->account().appConfig().refreshed() + ) | rpl::map([=] { + return AppConfigLimit( + _session, + (filterId + ? "dialog_filters_chats_limit_premium" + : folder + ? "dialog_filters_chats_limit_premium" + : "dialogs_pinned_limit_premium"), + (filterId || folder) ? 200 : 10); + }); } const std::vector &Session::pinnedChatsOrder( diff --git a/Telegram/SourceFiles/data/data_session.h b/Telegram/SourceFiles/data/data_session.h index a21fe4c642..e61fc75a6b 100644 --- a/Telegram/SourceFiles/data/data_session.h +++ b/Telegram/SourceFiles/data/data_session.h @@ -329,7 +329,12 @@ public: Data::Folder *folder, FilterId filterId, not_null history) const; - int pinnedChatsLimit(Data::Folder *folder) const; + int pinnedChatsLimit( + Data::Folder *folder, + FilterId filterId) const; + rpl::producer maxPinnedChatsLimitValue( + Data::Folder *folder, + FilterId filterId) const; const std::vector &pinnedChatsOrder( Data::Folder *folder, FilterId filterId) const; diff --git a/Telegram/SourceFiles/data/stickers/data_stickers.cpp b/Telegram/SourceFiles/data/stickers/data_stickers.cpp index e7c76ec051..5b532f4ada 100644 --- a/Telegram/SourceFiles/data/stickers/data_stickers.cpp +++ b/Telegram/SourceFiles/data/stickers/data_stickers.cpp @@ -12,7 +12,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_session.h" #include "data/data_user.h" #include "ui/boxes/confirm_box.h" +#include "ui/text/text_utilities.h" #include "lang/lang_keys.h" +#include "boxes/premium_limits_box.h" #include "history/history.h" #include "history/history_item.h" #include "history/history_item_components.h" @@ -23,7 +25,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "main/main_session.h" #include "mtproto/mtproto_config.h" #include "ui/toast/toast.h" +#include "ui/toasts/common_toasts.h" #include "ui/image/image_location_factory.h" +#include "window/window_controller.h" +#include "window/window_session_controller.h" +#include "mainwindow.h" #include "base/unixtime.h" #include "boxes/abstract_box.h" // Ui::show(). #include "styles/style_chat_helpers.h" @@ -31,8 +37,81 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace Data { namespace { +constexpr auto kPremiumToastDuration = 5 * crl::time(1000); + using SetFlag = StickersSetFlag; +[[nodiscard]] TextWithEntities SavedGifsToast( + not_null session) { + const auto defaultLimit = AppConfigLimit( + session, + "saved_gifs_limit_default", + 200); + const auto premiumLimit = AppConfigLimit( + session, + "saved_gifs_limit_premium", + 400); + return Ui::Text::Bold( + tr::lng_saved_gif_limit_title(tr::now, lt_count, defaultLimit) + ).append('\n').append( + tr::lng_saved_gif_limit_more( + tr::now, + lt_count, + premiumLimit, + lt_link, + Ui::Text::Link(tr::lng_saved_gif_limit_link(tr::now)), + Ui::Text::WithEntities)); +} + +[[nodiscard]] TextWithEntities FaveStickersToast( + not_null session) { + const auto defaultLimit = AppConfigLimit( + session, + "stickers_faved_limit_default", + 5); + const auto premiumLimit = AppConfigLimit( + session, + "stickers_faved_limit_premium", + 200); + return Ui::Text::Bold( + tr::lng_fave_sticker_limit_title(tr::now, lt_count, defaultLimit) + ).append('\n').append( + tr::lng_fave_sticker_limit_more( + tr::now, + lt_count, + premiumLimit, + lt_link, + Ui::Text::Link(tr::lng_fave_sticker_limit_link(tr::now)), + Ui::Text::WithEntities)); +} + +void MaybeShowPremiumToast( + Window::SessionController *controller, + TextWithEntities text) { + if (!controller) { + return; + } + const auto session = &controller->session(); + if (session->user()->isPremium()) { + return; + } + const auto widget = QPointer( + controller->window().widget()->bodyWidget()); + const auto filter = [=](const auto ...) { + Ui::ShowMultilineToast({ + .parentOverride = widget, + .text = { u"Premium!"_q }, + }); + return false; + }; + Ui::ShowMultilineToast({ + .parentOverride = widget, + .text = std::move(text), + .duration = kPremiumToastDuration, + .filter = filter, + }); +} + void RemoveFromSet( StickersSets &sets, not_null document, @@ -225,7 +304,9 @@ void Stickers::incrementSticker(not_null document) { notifyRecentUpdated(); } -void Stickers::addSavedGif(not_null document) { +void Stickers::addSavedGif( + Window::SessionController *controller, + not_null document) { const auto index = _savedGifs.indexOf(document); if (!index) { return; @@ -234,14 +315,22 @@ void Stickers::addSavedGif(not_null document) { _savedGifs.remove(index); } _savedGifs.push_front(document); - if (_savedGifs.size() > session().serverConfig().savedGifsLimit) { + const auto session = &document->session(); + const auto limit = CurrentPremiumLimit( + session, + "saved_gifs_limit_default", + 200, + "saved_gifs_limit_premium", + 400); + if (_savedGifs.size() > limit) { _savedGifs.pop_back(); + MaybeShowPremiumToast(controller, SavedGifsToast(session)); } - session().local().writeSavedGifs(); + session->local().writeSavedGifs(); notifySavedGifsUpdated(); setLastSavedGifsUpdate(0); - session().api().updateStickers(); + session->api().updateStickers(); } void Stickers::checkSavedGif(not_null item) { @@ -253,7 +342,7 @@ void Stickers::checkSavedGif(not_null item) { if (const auto media = item->media()) { if (const auto document = media->document()) { if (document->isGifv()) { - addSavedGif(document); + addSavedGif(nullptr, document); } } } @@ -421,8 +510,17 @@ bool Stickers::isFaved(not_null document) { return false; } -void Stickers::checkFavedLimit(StickersSet &set) { - if (set.stickers.size() <= session().serverConfig().stickersFavedLimit) { +void Stickers::checkFavedLimit( + StickersSet &set, + Window::SessionController *controller) { + const auto session = &_owner->session(); + const auto limit = CurrentPremiumLimit( + session, + "stickers_faved_limit_default", + 5, + "stickers_faved_limit_premium", + 200); + if (set.stickers.size() <= limit) { return; } auto removing = set.stickers.back(); @@ -438,17 +536,19 @@ void Stickers::checkFavedLimit(StickersSet &set) { } ++i; } + MaybeShowPremiumToast(controller, FaveStickersToast(session)); } void Stickers::pushFavedToFront( StickersSet &set, + Window::SessionController *controller, not_null document, const std::vector> &emojiList) { set.stickers.push_front(document); for (auto emoji : emojiList) { set.emoji[emoji].push_front(document); } - checkFavedLimit(set); + checkFavedLimit(set, controller); } void Stickers::moveFavedToFront(StickersSet &set, int index) { @@ -471,6 +571,7 @@ void Stickers::moveFavedToFront(StickersSet &set, int index) { } void Stickers::setIsFaved( + Window::SessionController *controller, not_null document, std::optional>> emojiList) { auto &sets = setsRef(); @@ -495,11 +596,11 @@ void Stickers::setIsFaved( if (index > 0) { moveFavedToFront(*set, index); } else if (emojiList) { - pushFavedToFront(*set, document, *emojiList); + pushFavedToFront(*set, controller, document, *emojiList); } else if (auto list = getEmojiListFromSet(document)) { - pushFavedToFront(*set, document, *list); + pushFavedToFront(*set, controller, document, *list); } else { - requestSetToPushFaved(document); + requestSetToPushFaved(controller, document); return; } session().local().writeFavedStickers(); @@ -507,7 +608,11 @@ void Stickers::setIsFaved( notifyStickerSetInstalled(FavedSetId); } -void Stickers::requestSetToPushFaved(not_null document) { +void Stickers::requestSetToPushFaved( + Window::SessionController *controller, + not_null document) { + controller = nullptr; + const auto weak = base::make_weak(controller); auto addAnyway = [=](std::vector> list) { if (list.empty()) { if (auto sticker = document->sticker()) { @@ -516,7 +621,7 @@ void Stickers::requestSetToPushFaved(not_null document) { } } } - setIsFaved(document, std::move(list)); + setIsFaved(weak.get(), document, std::move(list)); }; session().api().request(MTPmessages_GetStickerSet( Data::InputStickerSet(document->sticker()->set), @@ -558,9 +663,12 @@ void Stickers::setIsNotFaved(not_null document) { notifyUpdated(); } -void Stickers::setFaved(not_null document, bool faved) { +void Stickers::setFaved( + Window::SessionController *controller, + not_null document, + bool faved) { if (faved) { - setIsFaved(document); + setIsFaved(controller, document); } else { setIsNotFaved(document); } diff --git a/Telegram/SourceFiles/data/stickers/data_stickers.h b/Telegram/SourceFiles/data/stickers/data_stickers.h index 2347e895d2..f484818eb8 100644 --- a/Telegram/SourceFiles/data/stickers/data_stickers.h +++ b/Telegram/SourceFiles/data/stickers/data_stickers.h @@ -18,6 +18,10 @@ namespace Main { class Session; } // namespace Main +namespace Window { +class SessionController; +} // namespace Window + namespace Data { class Session; @@ -167,7 +171,9 @@ public: } void removeFromRecentSet(not_null document); - void addSavedGif(not_null document); + void addSavedGif( + Window::SessionController *controller, + not_null document); void checkSavedGif(not_null item); void applyArchivedResult( @@ -175,7 +181,10 @@ public: void installLocally(uint64 setId); void undoInstallLocally(uint64 setId); bool isFaved(not_null document); - void setFaved(not_null document, bool faved); + void setFaved( + Window::SessionController *controller, + not_null document, + bool faved); void setsReceived(const QVector &data, uint64 hash); void masksReceived(const QVector &data, uint64 hash); @@ -212,18 +221,24 @@ private: return (lastUpdate == 0) || (now >= lastUpdate + kUpdateTimeout); } - void checkFavedLimit(StickersSet &set); + void checkFavedLimit( + StickersSet &set, + Window::SessionController *controller = nullptr); void setIsFaved( + Window::SessionController *controller, not_null document, std::optional>> emojiList = std::nullopt); void setIsNotFaved(not_null document); void pushFavedToFront( StickersSet &set, + Window::SessionController *controller, not_null document, const std::vector> &emojiList); void moveFavedToFront(StickersSet &set, int index); - void requestSetToPushFaved(not_null document); + void requestSetToPushFaved( + Window::SessionController *controller, + not_null document); void setPackAndEmoji( StickersSet &set, StickersPack &&pack, diff --git a/Telegram/SourceFiles/history/history_inner_widget.cpp b/Telegram/SourceFiles/history/history_inner_widget.cpp index cef92dcffb..d5677f8a5a 100644 --- a/Telegram/SourceFiles/history/history_inner_widget.cpp +++ b/Telegram/SourceFiles/history/history_inner_widget.cpp @@ -2251,7 +2251,7 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) { }, &st::menuIconStickers); const auto isFaved = session->data().stickers().isFaved(document); _menu->addAction(isFaved ? tr::lng_faved_stickers_remove(tr::now) : tr::lng_faved_stickers_add(tr::now), [=] { - Api::ToggleFavedSticker(document, itemId); + Api::ToggleFavedSticker(controller, document, itemId); }, isFaved ? &st::menuIconUnfave : &st::menuIconFave); } if (!hasCopyRestriction(item)) { @@ -2492,7 +2492,11 @@ void HistoryInner::saveContextGif(FullMsgId itemId) { if (!hasCopyRestriction(item)) { if (const auto media = item->media()) { if (const auto document = media->document()) { - Api::ToggleSavedGif(document, item->fullId(), true); + Api::ToggleSavedGif( + _controller, + document, + item->fullId(), + true); } } } diff --git a/Telegram/SourceFiles/history/view/history_view_context_menu.cpp b/Telegram/SourceFiles/history/view/history_view_context_menu.cpp index 6c260d7deb..824f29769d 100644 --- a/Telegram/SourceFiles/history/view/history_view_context_menu.cpp +++ b/Telegram/SourceFiles/history/view/history_view_context_menu.cpp @@ -129,9 +129,10 @@ void ShowStickerPackInfo( } void ToggleFavedSticker( + not_null controller, not_null document, FullMsgId contextId) { - Api::ToggleFavedSticker(document, contextId); + Api::ToggleFavedSticker(controller, document, contextId); } void AddPhotoActions( @@ -174,7 +175,11 @@ void SaveGif( if (const auto item = controller->session().data().message(itemId)) { if (const auto media = item->media()) { if (const auto document = media->document()) { - Api::ToggleSavedGif(document, item->fullId(), true); + Api::ToggleSavedGif( + controller, + document, + item->fullId(), + true); } } } @@ -243,6 +248,7 @@ void AddDocumentActions( }, &st::menuIconCancel); return; } + const auto controller = list->controller(); const auto contextId = item ? item->fullId() : FullMsgId(); const auto session = &document->session(); if (item && document->isGifv()) { @@ -273,7 +279,7 @@ void AddDocumentActions( (isFaved ? tr::lng_faved_stickers_remove(tr::now) : tr::lng_faved_stickers_add(tr::now)), - [=] { ToggleFavedSticker(document, contextId); }, + [=] { ToggleFavedSticker(controller, document, contextId); }, isFaved ? &st::menuIconUnfave : &st::menuIconFave); } if (!document->filepath(true).isEmpty()) { diff --git a/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp b/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp index 122e1899e3..9467f4f1f5 100644 --- a/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp +++ b/Telegram/SourceFiles/inline_bots/inline_bot_layout_internal.cpp @@ -130,6 +130,7 @@ void Gif::setPosition(int32 position) { void DeleteSavedGifClickHandler::onClickImpl() const { ChatHelpers::AddGifAction( [](QString, Fn &&done, const style::icon*) { done(); }, + nullptr, _data); } diff --git a/Telegram/SourceFiles/inline_bots/inline_results_inner.cpp b/Telegram/SourceFiles/inline_bots/inline_results_inner.cpp index 4bf7287d95..d6d4816a03 100644 --- a/Telegram/SourceFiles/inline_bots/inline_results_inner.cpp +++ b/Telegram/SourceFiles/inline_bots/inline_results_inner.cpp @@ -353,7 +353,10 @@ void Inner::contextMenuEvent(QContextMenuEvent *e) { const style::icon *icon) { _menu->addAction(text, std::move(done), icon); }; - ChatHelpers::AddGifAction(std::move(callback), previewDocument); + ChatHelpers::AddGifAction( + std::move(callback), + _controller, + previewDocument); } if (!_menu->empty()) { diff --git a/Telegram/SourceFiles/settings/settings_folders.cpp b/Telegram/SourceFiles/settings/settings_folders.cpp index d4f09ddd4b..0a8bfcab92 100644 --- a/Telegram/SourceFiles/settings/settings_folders.cpp +++ b/Telegram/SourceFiles/settings/settings_folders.cpp @@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "settings/settings_folders.h" #include "apiwrap.h" +#include "boxes/premium_limits_box.h" #include "boxes/filters/edit_filter_box.h" #include "core/application.h" #include "data/data_chat_filters.h" @@ -39,8 +40,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL namespace Settings { namespace { -constexpr auto kFiltersLimit = 10; - using Flag = Data::ChatFilter::Flag; using Flags = Data::ChatFilter::Flags; @@ -327,6 +326,14 @@ void FilterRowButton::paintEvent(QPaintEvent *e) { auto &lifetime = container->lifetime(); const auto session = &controller->session(); + const auto limit = [=] { + return CurrentPremiumLimit( + session, + "dialog_filters_limit_default", + 10, + "dialog_filters_limit_premium", + 20); + }; AddSkip(container, st::settingsSectionSkip); AddSubsectionTitle(container, tr::lng_filters_subtitle()); @@ -339,10 +346,10 @@ void FilterRowButton::paintEvent(QPaintEvent *e) { }; const auto showLimitReached = [=] { const auto removed = ranges::count_if(*rows, &FilterRow::removed); - if (rows->size() < kFiltersLimit + removed) { + if (rows->size() < limit() + removed) { return false; } - controller->window().showToast(tr::lng_filters_limit(tr::now)); + controller->show(Box(FiltersLimitBox, session)); return true; }; const auto wrap = container->add(object_ptr( @@ -487,7 +494,7 @@ void FilterRowButton::paintEvent(QPaintEvent *e) { auto showSuggestions = rpl::combine( suggested->value(), rowsCount->value() - ) | rpl::map(rpl::mappers::_1 > 0 && rpl::mappers::_2 < kFiltersLimit); + ) | rpl::map(rpl::mappers::_1 > 0 && rpl::mappers::_2 < limit()); nonEmptyAbout->toggleOn(std::move(showSuggestions)); const auto prepareGoodIdsForNewFilters = [=] { diff --git a/Telegram/SourceFiles/window/window_peer_menu.cpp b/Telegram/SourceFiles/window/window_peer_menu.cpp index 3b60dccc36..62c7416c16 100644 --- a/Telegram/SourceFiles/window/window_peer_menu.cpp +++ b/Telegram/SourceFiles/window/window_peer_menu.cpp @@ -249,13 +249,11 @@ bool PinnedLimitReached( owner->setChatPinned(history, FilterId(), true); history->session().api().savePinnedOrder(folder); } else if (filterId) { - controller->show( - Box(FilterPinsLimitBox, &history->session()), - Ui::LayerOption::CloseOther); + controller->show(Box(FilterPinsLimitBox, &history->session())); + } else if (folder) { + controller->show(Box(FolderPinsLimitBox, &history->session())); } else { - controller->show( - Box(PinsLimitBox, &history->session()), - Ui::LayerOption::CloseOther); + controller->show(Box(PinsLimitBox, &history->session())); } return true; } @@ -419,6 +417,7 @@ void Filler::addInfo() { } void Filler::addToggleFolder() { + const auto controller = _controller; const auto history = _request.key.history(); if (!history || history->owner().chatsFilters().list().empty()) { return; @@ -428,7 +427,7 @@ void Filler::addToggleFolder() { .handler = nullptr, .icon = &st::menuIconAddToFolder, .fillSubmenu = [=](not_null menu) { - FillChooseFilterMenu(menu, history); + FillChooseFilterMenu(controller, menu, history); }, }); }