Show limit toasts and boxes.

This commit is contained in:
John Preston 2022-05-09 17:09:46 +04:00
parent d11f1c22be
commit dd05fb4d14
25 changed files with 363 additions and 69 deletions

View file

@ -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#one" = "The Limit of {count} Stickers Reached";
"lng_fave_sticker_limit_title#other" = "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#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. You can {link} to {count} stickers."; "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_fave_sticker_limit_link" = "increase the limit";
"lng_saved_gif_limit_title#one" = "The Limit of {count} GIF Reached"; "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_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#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. You can {link} to {count} GIFs."; "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_saved_gif_limit_link" = "increase the limit";
"lng_limits_increase" = "Increase Limit"; "lng_limits_increase" = "Increase Limit";

View file

@ -21,9 +21,7 @@ void SaveNewFilterPinned(
nullptr, nullptr,
filterId); filterId);
auto &filters = session->data().chatsFilters(); auto &filters = session->data().chatsFilters();
const auto &filter = filters.applyUpdatedPinned( const auto &filter = filters.applyUpdatedPinned(filterId, order);
filterId,
order);
session->api().request(MTPmessages_UpdateDialogFilter( session->api().request(MTPmessages_UpdateDialogFilter(
MTP_flags(MTPmessages_UpdateDialogFilter::Flag::f_filter), MTP_flags(MTPmessages_UpdateDialogFilter::Flag::f_filter),
MTP_int(filterId), MTP_int(filterId),

View file

@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_file_origin.h" #include "data/data_file_origin.h"
#include "data/data_session.h" #include "data/data_session.h"
#include "data/stickers/data_stickers.h" #include "data/stickers/data_stickers.h"
#include "window/window_session_controller.h"
#include "main/main_session.h" #include "main/main_session.h"
namespace Api { namespace Api {
@ -47,28 +48,35 @@ void ToggleExistingMedia(
} // namespace } // namespace
void ToggleFavedSticker( void ToggleFavedSticker(
not_null<Window::SessionController*> controller,
not_null<DocumentData*> document, not_null<DocumentData*> document,
Data::FileOrigin origin) { Data::FileOrigin origin) {
ToggleFavedSticker( ToggleFavedSticker(
controller,
document, document,
std::move(origin), std::move(origin),
!document->owner().stickers().isFaved(document)); !document->owner().stickers().isFaved(document));
} }
void ToggleFavedSticker( void ToggleFavedSticker(
not_null<Window::SessionController*> controller,
not_null<DocumentData*> document, not_null<DocumentData*> document,
Data::FileOrigin origin, Data::FileOrigin origin,
bool faved) { bool faved) {
if (faved && !document->sticker()) { if (faved && !document->sticker()) {
return; return;
} }
const auto weak = base::make_weak(controller.get());
auto done = [=] {
document->owner().stickers().setFaved(weak.get(), document, faved);
};
ToggleExistingMedia( ToggleExistingMedia(
document, document,
std::move(origin), std::move(origin),
[=, d = document] { [=, d = document] {
return MTPmessages_FaveSticker(d->mtpInput(), MTP_bool(!faved)); return MTPmessages_FaveSticker(d->mtpInput(), MTP_bool(!faved));
}, },
[=] { document->owner().stickers().setFaved(document, faved); }); std::move(done));
} }
void ToggleRecentSticker( void ToggleRecentSticker(
@ -96,15 +104,17 @@ void ToggleRecentSticker(
} }
void ToggleSavedGif( void ToggleSavedGif(
Window::SessionController *controller,
not_null<DocumentData*> document, not_null<DocumentData*> document,
Data::FileOrigin origin, Data::FileOrigin origin,
bool saved) { bool saved) {
if (saved && !document->isGifv()) { if (saved && !document->isGifv()) {
return; return;
} }
const auto weak = base::make_weak(controller);
auto done = [=] { auto done = [=] {
if (saved) { if (saved) {
document->owner().stickers().addSavedGif(document); document->owner().stickers().addSavedGif(weak.get(), document);
} }
}; };
ToggleExistingMedia( ToggleExistingMedia(

View file

@ -7,13 +7,19 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/ */
#pragma once #pragma once
namespace Window {
class SessionController;
} // namespace Window
namespace Api { namespace Api {
void ToggleFavedSticker( void ToggleFavedSticker(
not_null<Window::SessionController*> controller,
not_null<DocumentData*> document, not_null<DocumentData*> document,
Data::FileOrigin origin); Data::FileOrigin origin);
void ToggleFavedSticker( void ToggleFavedSticker(
not_null<Window::SessionController*> controller,
not_null<DocumentData*> document, not_null<DocumentData*> document,
Data::FileOrigin origin, Data::FileOrigin origin,
bool faved); bool faved);
@ -24,6 +30,7 @@ void ToggleRecentSticker(
bool saved); bool saved);
void ToggleSavedGif( void ToggleSavedGif(
Window::SessionController *controller,
not_null<DocumentData*> document, not_null<DocumentData*> document,
Data::FileOrigin origin, Data::FileOrigin origin,
bool saved); bool saved);

View file

@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "boxes/choose_filter_box.h" #include "boxes/choose_filter_box.h"
#include "apiwrap.h" #include "apiwrap.h"
#include "boxes/premium_limits_box.h"
#include "core/application.h" // primaryWindow #include "core/application.h" // primaryWindow
#include "data/data_chat_filters.h" #include "data/data_chat_filters.h"
#include "data/data_session.h" #include "data/data_session.h"
@ -116,6 +117,15 @@ bool ChooseFilterValidator::canRemove(FilterId filterId) const {
return false; 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 { void ChooseFilterValidator::add(FilterId filterId) const {
ChangeFilterById(filterId, _history, true); ChangeFilterById(filterId, _history, true);
} }
@ -125,8 +135,10 @@ void ChooseFilterValidator::remove(FilterId filterId) const {
} }
void FillChooseFilterMenu( void FillChooseFilterMenu(
not_null<Window::SessionController*> controller,
not_null<Ui::PopupMenu*> menu, not_null<Ui::PopupMenu*> menu,
not_null<History*> history) { not_null<History*> history) {
const auto weak = base::make_weak(controller.get());
const auto validator = ChooseFilterValidator(history); const auto validator = ChooseFilterValidator(history);
for (const auto &filter : history->owner().chatsFilters().list()) { for (const auto &filter : history->owner().chatsFilters().list()) {
const auto id = filter.id(); const auto id = filter.id();
@ -136,10 +148,11 @@ void FillChooseFilterMenu(
if (validator.canRemove(id)) { if (validator.canRemove(id)) {
validator.remove(id); validator.remove(id);
} }
} else { } else if (validator.limitReached(id)) {
if (validator.canAdd()) { controller->show(
validator.add(id); Box(FilterChatsLimitBox, &controller->session()));
} } else if (validator.canAdd()) {
validator.add(id);
} }
}, contains ? &st::mediaPlayerMenuCheck : nullptr); }, contains ? &st::mediaPlayerMenuCheck : nullptr);
action->setEnabled(contains action->setEnabled(contains

View file

@ -11,6 +11,10 @@ namespace Ui {
class PopupMenu; class PopupMenu;
} // namespace Ui } // namespace Ui
namespace Window {
class SessionController;
} // namespace Window
class History; class History;
class ChooseFilterValidator final { class ChooseFilterValidator final {
@ -19,6 +23,7 @@ public:
[[nodiscard]] bool canAdd() const; [[nodiscard]] bool canAdd() const;
[[nodiscard]] bool canRemove(FilterId filterId) const; [[nodiscard]] bool canRemove(FilterId filterId) const;
[[nodiscard]] bool limitReached(FilterId filterId) const;
void add(FilterId filterId) const; void add(FilterId filterId) const;
void remove(FilterId filterId) const; void remove(FilterId filterId) const;
@ -29,5 +34,6 @@ private:
}; };
void FillChooseFilterMenu( void FillChooseFilterMenu(
not_null<Window::SessionController*> controller,
not_null<Ui::PopupMenu*> menu, not_null<Ui::PopupMenu*> menu,
not_null<History*> history); not_null<History*> history);

View file

@ -23,6 +23,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "apiwrap.h" #include "apiwrap.h"
#include "styles/style_boxes.h" #include "styles/style_boxes.h"
#include "styles/style_layers.h" #include "styles/style_layers.h"
#include "styles/style_info.h"
namespace { namespace {
@ -243,8 +244,8 @@ std::unique_ptr<PeerListRow> InactiveController::createRow(
[[nodiscard]] float64 Limit( [[nodiscard]] float64 Limit(
not_null<Main::Session*> session, not_null<Main::Session*> session,
const QString &key, const QString &key,
double fallback) { int fallback) {
return session->account().appConfig().get<double>(key, fallback); return 1. * AppConfigLimit(session, key, fallback);
} }
void SimpleLimitBox( void SimpleLimitBox(
@ -277,6 +278,19 @@ void SimpleLimitBox(
st::changePhoneDescription)), st::changePhoneDescription)),
st::changePhoneDescriptionPadding); st::changePhoneDescriptionPadding);
box->setNoContentMargin(true);
const auto close = Ui::CreateChild<Ui::IconButton>(
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) { if (premium) {
box->addButton(tr::lng_box_ok(), [=] { box->addButton(tr::lng_box_ok(), [=] {
box->closeBox(); box->closeBox();
@ -531,9 +545,21 @@ void FilterPinsLimitBox(
SimplePinsLimitBox( SimplePinsLimitBox(
box, box,
session, session,
"dialog_filters_pinned_limit_default", "dialog_filters_chats_limit_default",
100, 100,
"dialog_filters_pinned_limit_premium", "dialog_filters_chats_limit_premium",
200);
}
void FolderPinsLimitBox(
not_null<Ui::GenericBox*> box,
not_null<Main::Session*> session) {
SimplePinsLimitBox(
box,
session,
"dialog_filters_chats_limit_default",
100,
"dialog_filters_chats_limit_premium",
200); 200);
} }
@ -548,3 +574,24 @@ void PinsLimitBox(
"dialogs_pinned_limit_premium", "dialogs_pinned_limit_premium",
10); 10);
} }
int AppConfigLimit(
not_null<Main::Session*> session,
const QString &key,
int fallback) {
return int(base::SafeRound(
session->account().appConfig().get<double>(key, 1. * fallback)));
}
int CurrentPremiumLimit(
not_null<Main::Session*> 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);
}

View file

@ -28,6 +28,20 @@ void FiltersLimitBox(
void FilterPinsLimitBox( void FilterPinsLimitBox(
not_null<Ui::GenericBox*> box, not_null<Ui::GenericBox*> box,
not_null<Main::Session*> session); not_null<Main::Session*> session);
void FolderPinsLimitBox(
not_null<Ui::GenericBox*> box,
not_null<Main::Session*> session);
void PinsLimitBox( void PinsLimitBox(
not_null<Ui::GenericBox*> box, not_null<Ui::GenericBox*> box,
not_null<Main::Session*> session); not_null<Main::Session*> session);
[[nodiscard]] int AppConfigLimit(
not_null<Main::Session*> session,
const QString &key,
int fallback);
[[nodiscard]] int CurrentPremiumLimit(
not_null<Main::Session*> session,
const QString &keyDefault,
int limitDefault,
const QString &keyPremium,
int limitPremium);

View file

@ -699,8 +699,10 @@ void StickerSetBox::Inner::contextMenuEvent(QContextMenuEvent *e) {
SendMenu::DefaultSilentCallback(sendSelected), SendMenu::DefaultSilentCallback(sendSelected),
SendMenu::DefaultScheduleCallback(this, type, sendSelected)); SendMenu::DefaultScheduleCallback(this, type, sendSelected));
const auto controller = _controller;
const auto toggleFavedSticker = [=] { const auto toggleFavedSticker = [=] {
Api::ToggleFavedSticker( Api::ToggleFavedSticker(
controller,
document, document,
Data::FileOriginStickerSet(Data::Stickers::FavedSetId, 0)); Data::FileOriginStickerSet(Data::Stickers::FavedSetId, 0));
}; };

View file

@ -52,6 +52,7 @@ constexpr auto kMinAfterScrollDelay = crl::time(33);
void AddGifAction( void AddGifAction(
Fn<void(QString, Fn<void()> &&, const style::icon*)> callback, Fn<void(QString, Fn<void()> &&, const style::icon*)> callback,
Window::SessionController *controller,
not_null<DocumentData*> document) { not_null<DocumentData*> document) {
if (!document->isGifv()) { if (!document->isGifv()) {
return; return;
@ -64,6 +65,7 @@ void AddGifAction(
: tr::lng_context_save_gif)(tr::now); : tr::lng_context_save_gif)(tr::now);
callback(text, [=] { callback(text, [=] {
Api::ToggleSavedGif( Api::ToggleSavedGif(
controller,
document, document,
Data::FileOriginSavedGifs(), Data::FileOriginSavedGifs(),
!saved); !saved);
@ -396,7 +398,7 @@ void GifsListWidget::fillContextMenu(
const style::icon *icon) { const style::icon *icon) {
menu->addAction(text, std::move(done), icon); menu->addAction(text, std::move(done), icon);
}; };
AddGifAction(std::move(callback), document); AddGifAction(std::move(callback), controller(), document);
} }
}; };
} }

View file

@ -42,6 +42,7 @@ namespace ChatHelpers {
void AddGifAction( void AddGifAction(
Fn<void(QString, Fn<void()> &&, const style::icon*)> callback, Fn<void(QString, Fn<void()> &&, const style::icon*)> callback,
Window::SessionController *controller,
not_null<DocumentData*> document); not_null<DocumentData*> document);
class GifsListWidget class GifsListWidget

View file

@ -2509,8 +2509,10 @@ void StickersListWidget::fillContextMenu(
SendMenu::DefaultSilentCallback(send), SendMenu::DefaultSilentCallback(send),
SendMenu::DefaultScheduleCallback(this, type, send)); SendMenu::DefaultScheduleCallback(this, type, send));
const auto window = controller();
const auto toggleFavedSticker = [=] { const auto toggleFavedSticker = [=] {
Api::ToggleFavedSticker( Api::ToggleFavedSticker(
window,
document, document,
Data::FileOriginStickerSet(Data::Stickers::FavedSetId, 0)); Data::FileOriginStickerSet(Data::Stickers::FavedSetId, 0));
}; };
@ -2683,8 +2685,9 @@ void StickersListWidget::removeFavedSticker(int section, int index) {
clearSelection(); clearSelection();
const auto &sticker = _mySets[section].stickers[index]; const auto &sticker = _mySets[section].stickers[index];
const auto document = sticker.document; const auto document = sticker.document;
session().data().stickers().setFaved(document, false); session().data().stickers().setFaved(controller(), document, false);
Api::ToggleFavedSticker( Api::ToggleFavedSticker(
controller(),
document, document,
Data::FileOriginStickerSet(Data::Stickers::FavedSetId, 0), Data::FileOriginStickerSet(Data::Stickers::FavedSetId, 0),
false); false);

View file

@ -16,9 +16,12 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_folder.h" #include "data/data_folder.h"
#include "data/data_histories.h" #include "data/data_histories.h"
#include "dialogs/dialogs_main_list.h" #include "dialogs/dialogs_main_list.h"
#include "history/history.h"
#include "history/history_unread_things.h" #include "history/history_unread_things.h"
#include "ui/ui_utility.h" #include "ui/ui_utility.h"
#include "main/main_session.h" #include "main/main_session.h"
#include "main/main_account.h"
#include "main/main_app_config.h"
#include "apiwrap.h" #include "apiwrap.h"
namespace Data { namespace Data {
@ -225,10 +228,15 @@ ChatFilters::~ChatFilters() = default;
not_null<Dialogs::MainList*> ChatFilters::chatsList(FilterId filterId) { not_null<Dialogs::MainList*> ChatFilters::chatsList(FilterId filterId) {
auto &pointer = _chatsLists[filterId]; auto &pointer = _chatsLists[filterId];
if (!pointer) { 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<Dialogs::MainList>( pointer = std::make_unique<Dialogs::MainList>(
&_owner->session(), &_owner->session(),
filterId, filterId,
rpl::single(ChatFilter::kPinnedLimit)); _owner->maxPinnedChatsLimitValue(nullptr, filterId));
} }
return pointer.get(); return pointer.get();
} }
@ -450,6 +458,7 @@ const ChatFilter &ChatFilters::applyUpdatedPinned(
const auto i = ranges::find(_list, id, &ChatFilter::id); const auto i = ranges::find(_list, id, &ChatFilter::id);
Assert(i != end(_list)); Assert(i != end(_list));
const auto limit = _owner->pinnedChatsLimit(nullptr, id);
auto always = i->always(); auto always = i->always();
auto pinned = std::vector<not_null<History*>>(); auto pinned = std::vector<not_null<History*>>();
pinned.reserve(dialogs.size()); pinned.reserve(dialogs.size());
@ -457,7 +466,7 @@ const ChatFilter &ChatFilters::applyUpdatedPinned(
if (const auto history = row.history()) { if (const auto history = row.history()) {
if (always.contains(history)) { if (always.contains(history)) {
pinned.push_back(history); pinned.push_back(history);
} else if (always.size() < ChatFilter::kPinnedLimit) { } else if (always.size() < limit) {
always.insert(history); always.insert(history);
pinned.push_back(history); pinned.push_back(history);
} }

View file

@ -35,8 +35,6 @@ public:
friend constexpr inline bool is_flag_type(Flag) { return true; }; friend constexpr inline bool is_flag_type(Flag) { return true; };
using Flags = base::flags<Flag>; using Flags = base::flags<Flag>;
static constexpr int kPinnedLimit = 100;
ChatFilter() = default; ChatFilter() = default;
ChatFilter( ChatFilter(
FilterId id, FilterId id,

View file

@ -38,7 +38,7 @@ Folder::Folder(not_null<Data::Session*> owner, FolderId id)
, _chatsList( , _chatsList(
&owner->session(), &owner->session(),
FilterId(), FilterId(),
owner->session().serverConfig().pinnedDialogsInFolderMax.value()) owner->maxPinnedChatsLimitValue(this, FilterId()))
, _name(tr::lng_archived_name(tr::now)) , _name(tr::lng_archived_name(tr::now))
, _chatListNameSortKey(owner->nameSortKey(_name)) { , _chatListNameSortKey(owner->nameSortKey(_name)) {
indexNameParts(); indexNameParts();

View file

@ -10,6 +10,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "main/main_session.h" #include "main/main_session.h"
#include "main/main_session_settings.h" #include "main/main_session_settings.h"
#include "main/main_account.h" #include "main/main_account.h"
#include "main/main_app_config.h"
#include "apiwrap.h" #include "apiwrap.h"
#include "mainwidget.h" #include "mainwidget.h"
#include "api/api_text_entities.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/player/media_player_instance.h" // instance()->play()
#include "media/audio/media_audio.h" #include "media/audio/media_audio.h"
#include "boxes/abstract_box.h" #include "boxes/abstract_box.h"
#include "boxes/premium_limits_box.h"
#include "passport/passport_form_controller.h" #include "passport/passport_form_controller.h"
#include "lang/lang_keys.h" // tr::lng_deleted(tr::now) in user name #include "lang/lang_keys.h" // tr::lng_deleted(tr::now) in user name
#include "data/stickers/data_stickers.h" #include "data/stickers/data_stickers.h"
@ -225,7 +227,7 @@ Session::Session(not_null<Main::Session*> session)
, _chatsList( , _chatsList(
session, session,
FilterId(), FilterId(),
session->serverConfig().pinnedDialogsCountMax.value()) maxPinnedChatsLimitValue(nullptr, FilterId()))
, _contactsList(Dialogs::SortMode::Name) , _contactsList(Dialogs::SortMode::Name)
, _contactsNoChatsList(Dialogs::SortMode::Name) , _contactsNoChatsList(Dialogs::SortMode::Name)
, _ttlCheckTimer([=] { checkTTLs(); }) , _ttlCheckTimer([=] { checkTTLs(); })
@ -1832,20 +1834,54 @@ int Session::pinnedCanPin(
FilterId filterId, FilterId filterId,
not_null<History*> history) const { not_null<History*> history) const {
if (!filterId) { if (!filterId) {
const auto limit = pinnedChatsLimit(folder); const auto limit = pinnedChatsLimit(folder, filterId);
return pinnedChatsOrder(folder, FilterId()).size() < limit; return pinnedChatsOrder(folder, FilterId()).size() < limit;
} }
const auto &list = chatsFilters().list(); const auto &list = chatsFilters().list();
const auto i = ranges::find(list, filterId, &Data::ChatFilter::id); const auto i = ranges::find(list, filterId, &Data::ChatFilter::id);
return (i == end(list)) return (i == end(list))
|| (i->always().contains(history)) || (i->always().contains(history))
|| (i->always().size() < Data::ChatFilter::kPinnedLimit); || (i->always().size() < pinnedChatsLimit(folder, filterId));
} }
int Session::pinnedChatsLimit(Data::Folder *folder) const { int Session::pinnedChatsLimit(
return folder Data::Folder *folder,
? session().serverConfig().pinnedDialogsInFolderMax.current() FilterId filterId) const {
: session().serverConfig().pinnedDialogsCountMax.current(); 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<int> 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<Dialogs::Key> &Session::pinnedChatsOrder( const std::vector<Dialogs::Key> &Session::pinnedChatsOrder(

View file

@ -329,7 +329,12 @@ public:
Data::Folder *folder, Data::Folder *folder,
FilterId filterId, FilterId filterId,
not_null<History*> history) const; not_null<History*> history) const;
int pinnedChatsLimit(Data::Folder *folder) const; int pinnedChatsLimit(
Data::Folder *folder,
FilterId filterId) const;
rpl::producer<int> maxPinnedChatsLimitValue(
Data::Folder *folder,
FilterId filterId) const;
const std::vector<Dialogs::Key> &pinnedChatsOrder( const std::vector<Dialogs::Key> &pinnedChatsOrder(
Data::Folder *folder, Data::Folder *folder,
FilterId filterId) const; FilterId filterId) const;

View file

@ -12,7 +12,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_session.h" #include "data/data_session.h"
#include "data/data_user.h" #include "data/data_user.h"
#include "ui/boxes/confirm_box.h" #include "ui/boxes/confirm_box.h"
#include "ui/text/text_utilities.h"
#include "lang/lang_keys.h" #include "lang/lang_keys.h"
#include "boxes/premium_limits_box.h"
#include "history/history.h" #include "history/history.h"
#include "history/history_item.h" #include "history/history_item.h"
#include "history/history_item_components.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 "main/main_session.h"
#include "mtproto/mtproto_config.h" #include "mtproto/mtproto_config.h"
#include "ui/toast/toast.h" #include "ui/toast/toast.h"
#include "ui/toasts/common_toasts.h"
#include "ui/image/image_location_factory.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 "base/unixtime.h"
#include "boxes/abstract_box.h" // Ui::show(). #include "boxes/abstract_box.h" // Ui::show().
#include "styles/style_chat_helpers.h" #include "styles/style_chat_helpers.h"
@ -31,8 +37,81 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace Data { namespace Data {
namespace { namespace {
constexpr auto kPremiumToastDuration = 5 * crl::time(1000);
using SetFlag = StickersSetFlag; using SetFlag = StickersSetFlag;
[[nodiscard]] TextWithEntities SavedGifsToast(
not_null<Main::Session*> 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<Main::Session*> 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<Ui::RpWidget>(
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( void RemoveFromSet(
StickersSets &sets, StickersSets &sets,
not_null<DocumentData*> document, not_null<DocumentData*> document,
@ -225,7 +304,9 @@ void Stickers::incrementSticker(not_null<DocumentData*> document) {
notifyRecentUpdated(); notifyRecentUpdated();
} }
void Stickers::addSavedGif(not_null<DocumentData*> document) { void Stickers::addSavedGif(
Window::SessionController *controller,
not_null<DocumentData*> document) {
const auto index = _savedGifs.indexOf(document); const auto index = _savedGifs.indexOf(document);
if (!index) { if (!index) {
return; return;
@ -234,14 +315,22 @@ void Stickers::addSavedGif(not_null<DocumentData*> document) {
_savedGifs.remove(index); _savedGifs.remove(index);
} }
_savedGifs.push_front(document); _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(); _savedGifs.pop_back();
MaybeShowPremiumToast(controller, SavedGifsToast(session));
} }
session().local().writeSavedGifs(); session->local().writeSavedGifs();
notifySavedGifsUpdated(); notifySavedGifsUpdated();
setLastSavedGifsUpdate(0); setLastSavedGifsUpdate(0);
session().api().updateStickers(); session->api().updateStickers();
} }
void Stickers::checkSavedGif(not_null<HistoryItem*> item) { void Stickers::checkSavedGif(not_null<HistoryItem*> item) {
@ -253,7 +342,7 @@ void Stickers::checkSavedGif(not_null<HistoryItem*> item) {
if (const auto media = item->media()) { if (const auto media = item->media()) {
if (const auto document = media->document()) { if (const auto document = media->document()) {
if (document->isGifv()) { if (document->isGifv()) {
addSavedGif(document); addSavedGif(nullptr, document);
} }
} }
} }
@ -421,8 +510,17 @@ bool Stickers::isFaved(not_null<const DocumentData*> document) {
return false; return false;
} }
void Stickers::checkFavedLimit(StickersSet &set) { void Stickers::checkFavedLimit(
if (set.stickers.size() <= session().serverConfig().stickersFavedLimit) { 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; return;
} }
auto removing = set.stickers.back(); auto removing = set.stickers.back();
@ -438,17 +536,19 @@ void Stickers::checkFavedLimit(StickersSet &set) {
} }
++i; ++i;
} }
MaybeShowPremiumToast(controller, FaveStickersToast(session));
} }
void Stickers::pushFavedToFront( void Stickers::pushFavedToFront(
StickersSet &set, StickersSet &set,
Window::SessionController *controller,
not_null<DocumentData*> document, not_null<DocumentData*> document,
const std::vector<not_null<EmojiPtr>> &emojiList) { const std::vector<not_null<EmojiPtr>> &emojiList) {
set.stickers.push_front(document); set.stickers.push_front(document);
for (auto emoji : emojiList) { for (auto emoji : emojiList) {
set.emoji[emoji].push_front(document); set.emoji[emoji].push_front(document);
} }
checkFavedLimit(set); checkFavedLimit(set, controller);
} }
void Stickers::moveFavedToFront(StickersSet &set, int index) { void Stickers::moveFavedToFront(StickersSet &set, int index) {
@ -471,6 +571,7 @@ void Stickers::moveFavedToFront(StickersSet &set, int index) {
} }
void Stickers::setIsFaved( void Stickers::setIsFaved(
Window::SessionController *controller,
not_null<DocumentData*> document, not_null<DocumentData*> document,
std::optional<std::vector<not_null<EmojiPtr>>> emojiList) { std::optional<std::vector<not_null<EmojiPtr>>> emojiList) {
auto &sets = setsRef(); auto &sets = setsRef();
@ -495,11 +596,11 @@ void Stickers::setIsFaved(
if (index > 0) { if (index > 0) {
moveFavedToFront(*set, index); moveFavedToFront(*set, index);
} else if (emojiList) { } else if (emojiList) {
pushFavedToFront(*set, document, *emojiList); pushFavedToFront(*set, controller, document, *emojiList);
} else if (auto list = getEmojiListFromSet(document)) { } else if (auto list = getEmojiListFromSet(document)) {
pushFavedToFront(*set, document, *list); pushFavedToFront(*set, controller, document, *list);
} else { } else {
requestSetToPushFaved(document); requestSetToPushFaved(controller, document);
return; return;
} }
session().local().writeFavedStickers(); session().local().writeFavedStickers();
@ -507,7 +608,11 @@ void Stickers::setIsFaved(
notifyStickerSetInstalled(FavedSetId); notifyStickerSetInstalled(FavedSetId);
} }
void Stickers::requestSetToPushFaved(not_null<DocumentData*> document) { void Stickers::requestSetToPushFaved(
Window::SessionController *controller,
not_null<DocumentData*> document) {
controller = nullptr;
const auto weak = base::make_weak(controller);
auto addAnyway = [=](std::vector<not_null<EmojiPtr>> list) { auto addAnyway = [=](std::vector<not_null<EmojiPtr>> list) {
if (list.empty()) { if (list.empty()) {
if (auto sticker = document->sticker()) { if (auto sticker = document->sticker()) {
@ -516,7 +621,7 @@ void Stickers::requestSetToPushFaved(not_null<DocumentData*> document) {
} }
} }
} }
setIsFaved(document, std::move(list)); setIsFaved(weak.get(), document, std::move(list));
}; };
session().api().request(MTPmessages_GetStickerSet( session().api().request(MTPmessages_GetStickerSet(
Data::InputStickerSet(document->sticker()->set), Data::InputStickerSet(document->sticker()->set),
@ -558,9 +663,12 @@ void Stickers::setIsNotFaved(not_null<DocumentData*> document) {
notifyUpdated(); notifyUpdated();
} }
void Stickers::setFaved(not_null<DocumentData*> document, bool faved) { void Stickers::setFaved(
Window::SessionController *controller,
not_null<DocumentData*> document,
bool faved) {
if (faved) { if (faved) {
setIsFaved(document); setIsFaved(controller, document);
} else { } else {
setIsNotFaved(document); setIsNotFaved(document);
} }

View file

@ -18,6 +18,10 @@ namespace Main {
class Session; class Session;
} // namespace Main } // namespace Main
namespace Window {
class SessionController;
} // namespace Window
namespace Data { namespace Data {
class Session; class Session;
@ -167,7 +171,9 @@ public:
} }
void removeFromRecentSet(not_null<DocumentData*> document); void removeFromRecentSet(not_null<DocumentData*> document);
void addSavedGif(not_null<DocumentData*> document); void addSavedGif(
Window::SessionController *controller,
not_null<DocumentData*> document);
void checkSavedGif(not_null<HistoryItem*> item); void checkSavedGif(not_null<HistoryItem*> item);
void applyArchivedResult( void applyArchivedResult(
@ -175,7 +181,10 @@ public:
void installLocally(uint64 setId); void installLocally(uint64 setId);
void undoInstallLocally(uint64 setId); void undoInstallLocally(uint64 setId);
bool isFaved(not_null<const DocumentData*> document); bool isFaved(not_null<const DocumentData*> document);
void setFaved(not_null<DocumentData*> document, bool faved); void setFaved(
Window::SessionController *controller,
not_null<DocumentData*> document,
bool faved);
void setsReceived(const QVector<MTPStickerSet> &data, uint64 hash); void setsReceived(const QVector<MTPStickerSet> &data, uint64 hash);
void masksReceived(const QVector<MTPStickerSet> &data, uint64 hash); void masksReceived(const QVector<MTPStickerSet> &data, uint64 hash);
@ -212,18 +221,24 @@ private:
return (lastUpdate == 0) return (lastUpdate == 0)
|| (now >= lastUpdate + kUpdateTimeout); || (now >= lastUpdate + kUpdateTimeout);
} }
void checkFavedLimit(StickersSet &set); void checkFavedLimit(
StickersSet &set,
Window::SessionController *controller = nullptr);
void setIsFaved( void setIsFaved(
Window::SessionController *controller,
not_null<DocumentData*> document, not_null<DocumentData*> document,
std::optional<std::vector<not_null<EmojiPtr>>> emojiList std::optional<std::vector<not_null<EmojiPtr>>> emojiList
= std::nullopt); = std::nullopt);
void setIsNotFaved(not_null<DocumentData*> document); void setIsNotFaved(not_null<DocumentData*> document);
void pushFavedToFront( void pushFavedToFront(
StickersSet &set, StickersSet &set,
Window::SessionController *controller,
not_null<DocumentData*> document, not_null<DocumentData*> document,
const std::vector<not_null<EmojiPtr>> &emojiList); const std::vector<not_null<EmojiPtr>> &emojiList);
void moveFavedToFront(StickersSet &set, int index); void moveFavedToFront(StickersSet &set, int index);
void requestSetToPushFaved(not_null<DocumentData*> document); void requestSetToPushFaved(
Window::SessionController *controller,
not_null<DocumentData*> document);
void setPackAndEmoji( void setPackAndEmoji(
StickersSet &set, StickersSet &set,
StickersPack &&pack, StickersPack &&pack,

View file

@ -2251,7 +2251,7 @@ void HistoryInner::showContextMenu(QContextMenuEvent *e, bool showFromTouch) {
}, &st::menuIconStickers); }, &st::menuIconStickers);
const auto isFaved = session->data().stickers().isFaved(document); 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), [=] { _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); }, isFaved ? &st::menuIconUnfave : &st::menuIconFave);
} }
if (!hasCopyRestriction(item)) { if (!hasCopyRestriction(item)) {
@ -2492,7 +2492,11 @@ void HistoryInner::saveContextGif(FullMsgId itemId) {
if (!hasCopyRestriction(item)) { if (!hasCopyRestriction(item)) {
if (const auto media = item->media()) { if (const auto media = item->media()) {
if (const auto document = media->document()) { if (const auto document = media->document()) {
Api::ToggleSavedGif(document, item->fullId(), true); Api::ToggleSavedGif(
_controller,
document,
item->fullId(),
true);
} }
} }
} }

View file

@ -129,9 +129,10 @@ void ShowStickerPackInfo(
} }
void ToggleFavedSticker( void ToggleFavedSticker(
not_null<Window::SessionController*> controller,
not_null<DocumentData*> document, not_null<DocumentData*> document,
FullMsgId contextId) { FullMsgId contextId) {
Api::ToggleFavedSticker(document, contextId); Api::ToggleFavedSticker(controller, document, contextId);
} }
void AddPhotoActions( void AddPhotoActions(
@ -174,7 +175,11 @@ void SaveGif(
if (const auto item = controller->session().data().message(itemId)) { if (const auto item = controller->session().data().message(itemId)) {
if (const auto media = item->media()) { if (const auto media = item->media()) {
if (const auto document = media->document()) { 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); }, &st::menuIconCancel);
return; return;
} }
const auto controller = list->controller();
const auto contextId = item ? item->fullId() : FullMsgId(); const auto contextId = item ? item->fullId() : FullMsgId();
const auto session = &document->session(); const auto session = &document->session();
if (item && document->isGifv()) { if (item && document->isGifv()) {
@ -273,7 +279,7 @@ void AddDocumentActions(
(isFaved (isFaved
? tr::lng_faved_stickers_remove(tr::now) ? tr::lng_faved_stickers_remove(tr::now)
: tr::lng_faved_stickers_add(tr::now)), : tr::lng_faved_stickers_add(tr::now)),
[=] { ToggleFavedSticker(document, contextId); }, [=] { ToggleFavedSticker(controller, document, contextId); },
isFaved ? &st::menuIconUnfave : &st::menuIconFave); isFaved ? &st::menuIconUnfave : &st::menuIconFave);
} }
if (!document->filepath(true).isEmpty()) { if (!document->filepath(true).isEmpty()) {

View file

@ -130,6 +130,7 @@ void Gif::setPosition(int32 position) {
void DeleteSavedGifClickHandler::onClickImpl() const { void DeleteSavedGifClickHandler::onClickImpl() const {
ChatHelpers::AddGifAction( ChatHelpers::AddGifAction(
[](QString, Fn<void()> &&done, const style::icon*) { done(); }, [](QString, Fn<void()> &&done, const style::icon*) { done(); },
nullptr,
_data); _data);
} }

View file

@ -353,7 +353,10 @@ void Inner::contextMenuEvent(QContextMenuEvent *e) {
const style::icon *icon) { const style::icon *icon) {
_menu->addAction(text, std::move(done), icon); _menu->addAction(text, std::move(done), icon);
}; };
ChatHelpers::AddGifAction(std::move(callback), previewDocument); ChatHelpers::AddGifAction(
std::move(callback),
_controller,
previewDocument);
} }
if (!_menu->empty()) { if (!_menu->empty()) {

View file

@ -8,6 +8,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "settings/settings_folders.h" #include "settings/settings_folders.h"
#include "apiwrap.h" #include "apiwrap.h"
#include "boxes/premium_limits_box.h"
#include "boxes/filters/edit_filter_box.h" #include "boxes/filters/edit_filter_box.h"
#include "core/application.h" #include "core/application.h"
#include "data/data_chat_filters.h" #include "data/data_chat_filters.h"
@ -39,8 +40,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
namespace Settings { namespace Settings {
namespace { namespace {
constexpr auto kFiltersLimit = 10;
using Flag = Data::ChatFilter::Flag; using Flag = Data::ChatFilter::Flag;
using Flags = Data::ChatFilter::Flags; using Flags = Data::ChatFilter::Flags;
@ -327,6 +326,14 @@ void FilterRowButton::paintEvent(QPaintEvent *e) {
auto &lifetime = container->lifetime(); auto &lifetime = container->lifetime();
const auto session = &controller->session(); 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); AddSkip(container, st::settingsSectionSkip);
AddSubsectionTitle(container, tr::lng_filters_subtitle()); AddSubsectionTitle(container, tr::lng_filters_subtitle());
@ -339,10 +346,10 @@ void FilterRowButton::paintEvent(QPaintEvent *e) {
}; };
const auto showLimitReached = [=] { const auto showLimitReached = [=] {
const auto removed = ranges::count_if(*rows, &FilterRow::removed); const auto removed = ranges::count_if(*rows, &FilterRow::removed);
if (rows->size() < kFiltersLimit + removed) { if (rows->size() < limit() + removed) {
return false; return false;
} }
controller->window().showToast(tr::lng_filters_limit(tr::now)); controller->show(Box(FiltersLimitBox, session));
return true; return true;
}; };
const auto wrap = container->add(object_ptr<Ui::VerticalLayout>( const auto wrap = container->add(object_ptr<Ui::VerticalLayout>(
@ -487,7 +494,7 @@ void FilterRowButton::paintEvent(QPaintEvent *e) {
auto showSuggestions = rpl::combine( auto showSuggestions = rpl::combine(
suggested->value(), suggested->value(),
rowsCount->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)); nonEmptyAbout->toggleOn(std::move(showSuggestions));
const auto prepareGoodIdsForNewFilters = [=] { const auto prepareGoodIdsForNewFilters = [=] {

View file

@ -249,13 +249,11 @@ bool PinnedLimitReached(
owner->setChatPinned(history, FilterId(), true); owner->setChatPinned(history, FilterId(), true);
history->session().api().savePinnedOrder(folder); history->session().api().savePinnedOrder(folder);
} else if (filterId) { } else if (filterId) {
controller->show( controller->show(Box(FilterPinsLimitBox, &history->session()));
Box(FilterPinsLimitBox, &history->session()), } else if (folder) {
Ui::LayerOption::CloseOther); controller->show(Box(FolderPinsLimitBox, &history->session()));
} else { } else {
controller->show( controller->show(Box(PinsLimitBox, &history->session()));
Box(PinsLimitBox, &history->session()),
Ui::LayerOption::CloseOther);
} }
return true; return true;
} }
@ -419,6 +417,7 @@ void Filler::addInfo() {
} }
void Filler::addToggleFolder() { void Filler::addToggleFolder() {
const auto controller = _controller;
const auto history = _request.key.history(); const auto history = _request.key.history();
if (!history || history->owner().chatsFilters().list().empty()) { if (!history || history->owner().chatsFilters().list().empty()) {
return; return;
@ -428,7 +427,7 @@ void Filler::addToggleFolder() {
.handler = nullptr, .handler = nullptr,
.icon = &st::menuIconAddToFolder, .icon = &st::menuIconAddToFolder,
.fillSubmenu = [=](not_null<Ui::PopupMenu*> menu) { .fillSubmenu = [=](not_null<Ui::PopupMenu*> menu) {
FillChooseFilterMenu(menu, history); FillChooseFilterMenu(controller, menu, history);
}, },
}); });
} }