| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433 |
- /*
- This file is part of Telegram Desktop,
- the official desktop application for the Telegram messaging service.
- For license and copyright information please follow this link:
- https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
- */
- #include "settings/business/settings_recipients_helper.h"
- #include "boxes/filters/edit_filter_chats_list.h"
- #include "boxes/filters/edit_filter_chats_preview.h"
- #include "data/business/data_shortcut_messages.h"
- #include "data/data_session.h"
- #include "data/data_user.h"
- #include "history/history.h"
- #include "lang/lang_keys.h"
- #include "main/main_app_config.h"
- #include "main/main_session.h"
- #include "settings/settings_common.h"
- #include "ui/widgets/checkbox.h"
- #include "ui/wrap/slide_wrap.h"
- #include "ui/wrap/vertical_layout.h"
- #include "ui/vertical_list.h"
- #include "window/window_session_controller.h"
- #include "styles/style_settings.h"
- namespace Settings {
- namespace {
- constexpr auto kAllExcept = 0;
- constexpr auto kSelectedOnly = 1;
- using Flag = Data::ChatFilter::Flag;
- using Flags = Data::ChatFilter::Flags;
- [[nodiscard]] Flags TypesToFlags(Data::BusinessChatTypes types) {
- using Type = Data::BusinessChatType;
- return ((types & Type::Contacts) ? Flag::Contacts : Flag())
- | ((types & Type::NonContacts) ? Flag::NonContacts : Flag())
- | ((types & Type::NewChats) ? Flag::NewChats : Flag())
- | ((types & Type::ExistingChats) ? Flag::ExistingChats : Flag());
- }
- [[nodiscard]] Data::BusinessChatTypes FlagsToTypes(Flags flags) {
- using Type = Data::BusinessChatType;
- return ((flags & Flag::Contacts) ? Type::Contacts : Type())
- | ((flags & Flag::NonContacts) ? Type::NonContacts : Type())
- | ((flags & Flag::NewChats) ? Type::NewChats : Type())
- | ((flags & Flag::ExistingChats) ? Type::ExistingChats : Type());
- }
- } // namespace
- void EditBusinessChats(
- not_null<Window::SessionController*> window,
- BusinessChatsDescriptor &&descriptor) {
- const auto session = &window->session();
- const auto options = Flag::ExistingChats
- | Flag::NewChats
- | Flag::Contacts
- | Flag::NonContacts;
- auto &&peers = descriptor.current.list | ranges::views::transform([=](
- not_null<UserData*> user) {
- return user->owner().history(user);
- });
- auto controller = std::make_unique<EditFilterChatsListController>(
- session,
- (descriptor.include
- ? tr::lng_filters_include_title()
- : tr::lng_filters_exclude_title()),
- (descriptor.usersOnly ? Flag() : options),
- TypesToFlags(descriptor.current.types) & options,
- base::flat_set<not_null<History*>>(begin(peers), end(peers)),
- 100,
- nullptr);
- const auto rawController = controller.get();
- const auto save = descriptor.save;
- auto initBox = [=](not_null<PeerListBox*> box) {
- box->setCloseByOutsideClick(false);
- box->addButton(tr::lng_settings_save(), crl::guard(box, [=] {
- const auto peers = box->collectSelectedRows();
- auto &&users = ranges::views::all(
- peers
- ) | ranges::views::transform([=](not_null<PeerData*> peer) {
- return not_null(peer->asUser());
- }) | ranges::to_vector;
- save(Data::BusinessChats{
- .types = FlagsToTypes(rawController->chosenOptions()),
- .list = std::move(users),
- });
- box->closeBox();
- }));
- box->addButton(tr::lng_cancel(), [=] { box->closeBox(); });
- };
- window->show(
- Box<PeerListBox>(std::move(controller), std::move(initBox)));
- }
- not_null<FilterChatsPreview*> SetupBusinessChatsPreview(
- not_null<Ui::VerticalLayout*> container,
- not_null<rpl::variable<Data::BusinessChats>*> data) {
- const auto rules = data->current();
- const auto locked = std::make_shared<bool>();
- auto &&peers = data->current().list | ranges::views::transform([=](
- not_null<UserData*> user) {
- return user->owner().history(user);
- });
- const auto preview = container->add(object_ptr<FilterChatsPreview>(
- container,
- TypesToFlags(data->current().types),
- base::flat_set<not_null<History*>>(begin(peers), end(peers))));
- preview->flagRemoved(
- ) | rpl::start_with_next([=](Flag flag) {
- *locked = true;
- *data = Data::BusinessChats{
- data->current().types & ~FlagsToTypes(flag),
- data->current().list
- };
- *locked = false;
- }, preview->lifetime());
- preview->peerRemoved(
- ) | rpl::start_with_next([=](not_null<History*> history) {
- auto list = data->current().list;
- list.erase(
- ranges::remove(list, not_null(history->peer->asUser())),
- end(list));
- *locked = true;
- *data = Data::BusinessChats{
- data->current().types,
- std::move(list)
- };
- *locked = false;
- }, preview->lifetime());
- data->changes(
- ) | rpl::filter([=] {
- return !*locked;
- }) | rpl::start_with_next([=](const Data::BusinessChats &rules) {
- auto &&peers = rules.list | ranges::views::transform([=](
- not_null<UserData*> user) {
- return user->owner().history(user);
- });
- preview->updateData(
- TypesToFlags(rules.types),
- base::flat_set<not_null<History*>>(begin(peers), end(peers)));
- }, preview->lifetime());
- return preview;
- }
- void AddBusinessRecipientsSelector(
- not_null<Ui::VerticalLayout*> container,
- BusinessRecipientsSelectorDescriptor &&descriptor) {
- Ui::AddSkip(container);
- Ui::AddSubsectionTitle(container, std::move(descriptor.title));
- auto &lifetime = container->lifetime();
- const auto controller = descriptor.controller;
- const auto data = descriptor.data;
- const auto includeWithExcluded = (descriptor.type
- == Data::BusinessRecipientsType::Bots);
- const auto change = [=](Fn<void(Data::BusinessRecipients&)> modify) {
- auto now = data->current();
- modify(now);
- *data = std::move(now);
- };
- const auto ¤t = data->current();
- const auto all = current.allButExcluded || current.included.empty();
- const auto group = std::make_shared<Ui::RadiobuttonGroup>(
- all ? kAllExcept : kSelectedOnly);
- container->add(
- object_ptr<Ui::Radiobutton>(
- container,
- group,
- kAllExcept,
- tr::lng_chatbots_all_except(tr::now),
- st::settingsChatbotsAccess),
- st::settingsChatbotsAccessMargins);
- container->add(
- object_ptr<Ui::Radiobutton>(
- container,
- group,
- kSelectedOnly,
- tr::lng_chatbots_selected(tr::now),
- st::settingsChatbotsAccess),
- st::settingsChatbotsAccessMargins);
- Ui::AddSkip(container, st::settingsChatbotsAccessSkip);
- Ui::AddDivider(container);
- const auto includeWrap = container->add(
- object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
- container,
- object_ptr<Ui::VerticalLayout>(container))
- )->setDuration(0);
- const auto excludeWrap = container->add(
- object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
- container,
- object_ptr<Ui::VerticalLayout>(container))
- )->setDuration(0);
- const auto excludeInner = excludeWrap->entity();
- Ui::AddSkip(excludeInner);
- Ui::AddSubsectionTitle(excludeInner, tr::lng_chatbots_excluded_title());
- const auto excludeAdd = AddButtonWithIcon(
- excludeInner,
- tr::lng_chatbots_exclude_button(),
- st::settingsChatbotsAdd,
- { &st::settingsIconRemove, IconType::Round, &st::windowBgActive });
- const auto addExcluded = [=] {
- const auto save = [=](Data::BusinessChats value) {
- change([&](Data::BusinessRecipients &data) {
- if (includeWithExcluded) {
- if (!data.allButExcluded) {
- value.types = {};
- }
- for (const auto &user : value.list) {
- data.included.list.erase(
- ranges::remove(data.included.list, user),
- end(data.included.list));
- }
- }
- if (!value.empty()) {
- data.included = {};
- }
- data.excluded = std::move(value);
- });
- };
- EditBusinessChats(controller, {
- .current = data->current().excluded,
- .save = crl::guard(excludeAdd, save),
- .usersOnly = (includeWithExcluded
- && !data->current().allButExcluded),
- .include = false,
- });
- };
- excludeAdd->setClickedCallback(addExcluded);
- const auto excluded = lifetime.make_state<
- rpl::variable<Data::BusinessChats>
- >(data->current().excluded);
- data->changes(
- ) | rpl::start_with_next([=](const Data::BusinessRecipients &value) {
- *excluded = value.excluded;
- }, lifetime);
- excluded->changes(
- ) | rpl::start_with_next([=](Data::BusinessChats &&value) {
- change([&](Data::BusinessRecipients &data) {
- data.excluded = std::move(value);
- });
- }, lifetime);
- SetupBusinessChatsPreview(excludeInner, excluded);
- excludeWrap->toggleOn(data->value(
- ) | rpl::map([=](const Data::BusinessRecipients &value) {
- return value.allButExcluded || includeWithExcluded;
- }));
- excludeWrap->finishAnimating();
- const auto includeInner = includeWrap->entity();
- Ui::AddSkip(includeInner);
- Ui::AddSubsectionTitle(includeInner, tr::lng_chatbots_included_title());
- const auto includeAdd = AddButtonWithIcon(
- includeInner,
- tr::lng_chatbots_include_button(),
- st::settingsChatbotsAdd,
- { &st::settingsIconAdd, IconType::Round, &st::windowBgActive });
- const auto addIncluded = [=] {
- const auto save = [=](Data::BusinessChats value) {
- change([&](Data::BusinessRecipients &data) {
- if (includeWithExcluded) {
- for (const auto &user : value.list) {
- data.excluded.list.erase(
- ranges::remove(data.excluded.list, user),
- end(data.excluded.list));
- }
- }
- if (!value.empty()) {
- data.excluded.types = {};
- }
- data.included = std::move(value);
- });
- if (!data->current().included.empty()) {
- group->setValue(kSelectedOnly);
- }
- };
- EditBusinessChats(controller, {
- .current = data->current().included,
- .save = crl::guard(includeAdd, save),
- .include = true,
- });
- };
- includeAdd->setClickedCallback(addIncluded);
- const auto included = lifetime.make_state<
- rpl::variable<Data::BusinessChats>
- >(data->current().included);
- data->changes(
- ) | rpl::start_with_next([=](const Data::BusinessRecipients &value) {
- *included = value.included;
- }, lifetime);
- included->changes(
- ) | rpl::start_with_next([=](Data::BusinessChats &&value) {
- change([&](Data::BusinessRecipients &data) {
- data.included = std::move(value);
- });
- }, lifetime);
- SetupBusinessChatsPreview(includeInner, included);
- included->value(
- ) | rpl::start_with_next([=](const Data::BusinessChats &value) {
- if (value.empty() && group->current() == kSelectedOnly) {
- group->setValue(kAllExcept);
- }
- }, lifetime);
- includeWrap->toggleOn(data->value(
- ) | rpl::map([](const Data::BusinessRecipients &value) {
- return !value.allButExcluded;
- }));
- includeWrap->finishAnimating();
- group->setChangedCallback([=](int value) {
- if (value == kSelectedOnly && data->current().included.empty()) {
- group->setValue(kAllExcept);
- addIncluded();
- return;
- }
- change([&](Data::BusinessRecipients &data) {
- data.allButExcluded = (value == kAllExcept);
- });
- });
- }
- int ShortcutsCount(not_null<Main::Session*> session) {
- const auto &shortcuts = session->data().shortcutMessages().shortcuts();
- auto result = 0;
- for (const auto &[_, shortcut] : shortcuts.list) {
- if (shortcut.count > 0) {
- ++result;
- }
- }
- return result;
- }
- rpl::producer<int> ShortcutsCountValue(not_null<Main::Session*> session) {
- const auto messages = &session->data().shortcutMessages();
- return rpl::single(rpl::empty) | rpl::then(
- messages->shortcutsChanged()
- ) | rpl::map([=] {
- return ShortcutsCount(session);
- });
- }
- int ShortcutMessagesCount(
- not_null<Main::Session*> session,
- const QString &name) {
- const auto &shortcuts = session->data().shortcutMessages().shortcuts();
- for (const auto &[_, shortcut] : shortcuts.list) {
- if (shortcut.name == name) {
- return shortcut.count;
- }
- }
- return 0;
- }
- rpl::producer<int> ShortcutMessagesCountValue(
- not_null<Main::Session*> session,
- const QString &name) {
- const auto messages = &session->data().shortcutMessages();
- return rpl::single(rpl::empty) | rpl::then(
- messages->shortcutsChanged()
- ) | rpl::map([=] {
- return ShortcutMessagesCount(session, name);
- });
- }
- bool ShortcutExists(not_null<Main::Session*> session, const QString &name) {
- return ShortcutMessagesCount(session, name) > 0;
- }
- rpl::producer<bool> ShortcutExistsValue(
- not_null<Main::Session*> session,
- const QString &name) {
- return ShortcutMessagesCountValue(session, name)
- | rpl::map(rpl::mappers::_1 > 0);
- }
- int ShortcutsLimit(not_null<Main::Session*> session) {
- const auto appConfig = &session->appConfig();
- return appConfig->get<int>("quick_replies_limit", 100);
- }
- rpl::producer<int> ShortcutsLimitValue(not_null<Main::Session*> session) {
- const auto appConfig = &session->appConfig();
- return appConfig->value() | rpl::map([=] {
- return ShortcutsLimit(session);
- });
- }
- int ShortcutMessagesLimit(not_null<Main::Session*> session) {
- const auto appConfig = &session->appConfig();
- return appConfig->get<int>("quick_reply_messages_limit", 20);
- }
- rpl::producer<int> ShortcutMessagesLimitValue(
- not_null<Main::Session*> session) {
- const auto appConfig = &session->appConfig();
- return appConfig->value() | rpl::map([=] {
- return ShortcutMessagesLimit(session);
- });
- }
- BusinessShortcutId LookupShortcutId(
- not_null<Main::Session*> session,
- const QString &name) {
- const auto messages = &session->data().shortcutMessages();
- for (const auto &[id, shortcut] : messages->shortcuts().list) {
- if (shortcut.name == name) {
- return id;
- }
- }
- return {};
- }
- } // namespace Settings
|