Migrate all notification settings to builder.

This commit is contained in:
John Preston
2026-01-02 17:19:03 +04:00
parent 74f204e8e8
commit 0c5c5d3c42
8 changed files with 1029 additions and 632 deletions
+2
View File
@@ -1571,6 +1571,8 @@ PRIVATE
settings/builder/settings_builder.h settings/builder/settings_builder.h
settings/builder/settings_main_builder.cpp settings/builder/settings_main_builder.cpp
settings/builder/settings_main_builder.h settings/builder/settings_main_builder.h
settings/builder/settings_notifications_builder.cpp
settings/builder/settings_notifications_builder.h
settings/settings_notifications.cpp settings/settings_notifications.cpp
settings/settings_notifications.h settings/settings_notifications.h
settings/settings_notifications_type.cpp settings/settings_notifications_type.cpp
@@ -250,6 +250,75 @@ Ui::SettingsButton *SectionBuilder::addPremiumButton(PremiumButtonArgs &&args) {
}); });
} }
Ui::SettingsButton *SectionBuilder::addToggle(ToggleArgs &&args) {
return v::match(_context, [&](const WidgetContext &ctx)
-> Ui::SettingsButton* {
const auto &st = args.st ? *args.st : st::settingsButton;
const auto button = ctx.container->add(CreateButtonWithIcon(
ctx.container,
rpl::duplicate(args.title),
st,
std::move(args.icon)));
button->toggleOn(std::move(args.toggled));
return button;
}, [&](const SearchContext &ctx) -> Ui::SettingsButton* {
if (!args.id.isEmpty()) {
ctx.entries->push_back({
.id = std::move(args.id),
.title = ResolveTitle(std::move(args.title)),
.keywords = std::move(args.keywords),
});
}
return nullptr;
});
}
Ui::SlideWrap<Ui::SettingsButton> *SectionBuilder::addSlideToggle(
SlideToggleArgs &&args) {
return v::match(_context, [&](const WidgetContext &ctx)
-> Ui::SlideWrap<Ui::SettingsButton>* {
const auto &st = args.st ? *args.st : st::settingsButton;
const auto wrap = ctx.container->add(
object_ptr<Ui::SlideWrap<Ui::SettingsButton>>(
ctx.container,
CreateButtonWithIcon(
ctx.container,
rpl::duplicate(args.title),
st,
std::move(args.icon))));
if (args.shown) {
wrap->toggleOn(std::move(args.shown));
}
const auto button = wrap->entity();
button->toggleOn(std::move(args.toggled));
return wrap;
}, [&](const SearchContext &ctx) -> Ui::SlideWrap<Ui::SettingsButton>* {
if (!args.id.isEmpty()) {
ctx.entries->push_back({
.id = std::move(args.id),
.title = ResolveTitle(std::move(args.title)),
.keywords = std::move(args.keywords),
});
}
return nullptr;
});
}
void SectionBuilder::addSubsectionTitle(rpl::producer<QString> text) {
v::match(_context, [&](const WidgetContext &ctx) {
AddSubsectionTitle(ctx.container, std::move(text));
}, [](const SearchContext &) {
});
}
Ui::VerticalLayout *SectionBuilder::container() const {
return v::match(_context, [](const WidgetContext &ctx) {
return ctx.container.get();
}, [](const SearchContext &) -> Ui::VerticalLayout* {
return nullptr;
});
}
Window::SessionController *SectionBuilder::controller() const { Window::SessionController *SectionBuilder::controller() const {
return v::match(_context, [](const WidgetContext &ctx) { return v::match(_context, [](const WidgetContext &ctx) {
return ctx.controller.get(); return ctx.controller.get();
@@ -121,11 +121,34 @@ public:
}; };
Ui::SettingsButton *addPremiumButton(PremiumButtonArgs &&args); Ui::SettingsButton *addPremiumButton(PremiumButtonArgs &&args);
struct ToggleArgs {
QString id;
rpl::producer<QString> title;
const style::SettingsButton *st = nullptr;
IconDescriptor icon;
rpl::producer<bool> toggled;
QStringList keywords;
};
Ui::SettingsButton *addToggle(ToggleArgs &&args);
struct SlideToggleArgs {
QString id;
rpl::producer<QString> title;
const style::SettingsButton *st = nullptr;
IconDescriptor icon;
rpl::producer<bool> toggled;
rpl::producer<bool> shown;
QStringList keywords;
};
Ui::SlideWrap<Ui::SettingsButton> *addSlideToggle(SlideToggleArgs &&args);
void addSubsectionTitle(rpl::producer<QString> text);
void addDivider(); void addDivider();
void addDividerText(rpl::producer<QString> text); void addDividerText(rpl::producer<QString> text);
void addSkip(); void addSkip();
void addSkip(int height); void addSkip(int height);
[[nodiscard]] Ui::VerticalLayout *container() const;
[[nodiscard]] Window::SessionController *controller() const; [[nodiscard]] Window::SessionController *controller() const;
[[nodiscard]] Fn<void(Type)> showOther() const; [[nodiscard]] Fn<void(Type)> showOther() const;
@@ -0,0 +1,770 @@
/*
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/builder/settings_notifications_builder.h"
#include "api/api_authorizations.h"
#include "api/api_ringtones.h"
#include "apiwrap.h"
#include "base/platform/base_platform_info.h"
#include "boxes/ringtones_box.h"
#include "core/application.h"
#include "data/data_chat_filters.h"
#include "data/data_session.h"
#include "data/notify/data_notify_settings.h"
#include "data/notify/data_peer_notify_volume.h"
#include "lang/lang_keys.h"
#include "main/main_account.h"
#include "main/main_domain.h"
#include "main/main_session.h"
#include "mainwindow.h"
#include "platform/platform_notifications_manager.h"
#include "platform/platform_specific.h"
#include "settings/builder/settings_builder.h"
#include "settings/settings_notifications_common.h"
#include "settings/settings_notifications_type.h"
#include "ui/vertical_list.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/checkbox.h"
#include "ui/widgets/continuous_sliders.h"
#include "ui/widgets/discrete_sliders.h"
#include "ui/wrap/slide_wrap.h"
#include "ui/wrap/vertical_layout.h"
#include "window/notifications_manager.h"
#include "window/window_session_controller.h"
#include "styles/style_settings.h"
#include "styles/style_menu_icons.h"
#include <QtGui/QGuiApplication>
#include <QtGui/QScreen>
namespace Settings::Builder {
namespace {
constexpr auto kDefaultDisplayIndex = -1;
using NotifyView = Core::Settings::NotifyView;
using ChangeType = Window::Notifications::ChangeType;
void BuildMultiAccountSection(
SectionBuilder &builder,
Window::SessionController *controller) {
if (Core::App().domain().accounts().size() < 2) {
return;
}
builder.addSubsectionTitle(tr::lng_settings_show_from());
const auto container = builder.container();
if (!container) {
builder.addToggle({
.id = u"notifications/multi_account"_q,
.title = tr::lng_settings_notify_all(),
.toggled = rpl::single(Core::App().settings().notifyFromAll()),
.keywords = { u"all accounts"_q, u"multiple"_q },
});
builder.addSkip();
builder.addDividerText(tr::lng_settings_notify_all_about());
builder.addSkip();
return;
}
const auto fromAll = builder.addToggle({
.id = u"notifications/multi_account"_q,
.title = tr::lng_settings_notify_all(),
.st = &st::settingsButtonNoIcon,
.toggled = rpl::single(Core::App().settings().notifyFromAll()),
.keywords = { u"all accounts"_q, u"multiple"_q },
});
if (fromAll) {
fromAll->toggledChanges(
) | rpl::filter([](bool checked) {
return (checked != Core::App().settings().notifyFromAll());
}) | rpl::on_next([=](bool checked) {
Core::App().settings().setNotifyFromAll(checked);
Core::App().saveSettingsDelayed();
if (!checked) {
auto &notifications = Core::App().notifications();
const auto &list = Core::App().domain().accounts();
for (const auto &[index, account] : list) {
if (account.get() == &Core::App().domain().active()) {
continue;
} else if (const auto session = account->maybeSession()) {
notifications.clearFromSession(session);
}
}
}
}, fromAll->lifetime());
}
builder.addSkip();
builder.addDividerText(tr::lng_settings_notify_all_about());
builder.addSkip();
}
void BuildGlobalNotificationsSection(
SectionBuilder &builder,
Window::SessionController *controller) {
builder.addSubsectionTitle(tr::lng_settings_notify_global());
const auto container = builder.container();
const auto &settings = Core::App().settings();
const auto desktopToggles = container
? container->lifetime().make_state<rpl::event_stream<bool>>()
: nullptr;
const auto desktop = builder.addToggle({
.id = u"notifications/desktop"_q,
.title = tr::lng_settings_desktop_notify(),
.icon = { &st::menuIconNotifications },
.toggled = desktopToggles
? desktopToggles->events_starting_with(settings.desktopNotify())
: rpl::single(settings.desktopNotify()),
.keywords = { u"desktop"_q, u"popup"_q, u"show"_q },
});
const auto flashbounceToggles = container
? container->lifetime().make_state<rpl::event_stream<bool>>()
: nullptr;
const auto flashbounce = builder.addToggle({
.id = u"notifications/flash"_q,
.title = (Platform::IsWindows()
? tr::lng_settings_alert_windows
: Platform::IsMac()
? tr::lng_settings_alert_mac
: tr::lng_settings_alert_linux)(),
.icon = { &st::menuIconDockBounce },
.toggled = flashbounceToggles
? flashbounceToggles->events_starting_with(settings.flashBounceNotify())
: rpl::single(settings.flashBounceNotify()),
.keywords = { u"flash"_q, u"bounce"_q, u"taskbar"_q },
});
const auto soundAllowed = container
? container->lifetime().make_state<rpl::event_stream<bool>>()
: nullptr;
const auto allowed = [=] {
return Core::App().settings().soundNotify();
};
const auto sound = builder.addToggle({
.id = u"notifications/sound"_q,
.title = tr::lng_settings_sound_allowed(),
.icon = { &st::menuIconUnmute },
.toggled = soundAllowed
? soundAllowed->events_starting_with(allowed())
: rpl::single(allowed()),
.keywords = { u"sound"_q, u"audio"_q, u"mute"_q },
});
if (container && controller) {
const auto session = &controller->session();
Ui::AddRingtonesVolumeSlider(
container,
rpl::single(true),
tr::lng_settings_master_volume_notifications(),
Data::VolumeController{
.volume = []() -> ushort {
const auto volume
= Core::App().settings().notificationsVolume();
return volume ? volume : 100;
},
.saveVolume = [=](ushort volume) {
Core::App().notifications().playSound(
session,
0,
volume / 100.);
Core::App().settings().setNotificationsVolume(volume);
Core::App().saveSettingsDelayed();
}});
}
builder.addSkip();
if (desktop) {
const auto changed = [=](ChangeType change) {
Core::App().saveSettingsDelayed();
Core::App().notifications().notifySettingsChanged(change);
};
desktop->toggledChanges(
) | rpl::filter([](bool checked) {
return (checked != Core::App().settings().desktopNotify());
}) | rpl::on_next([=](bool checked) {
Core::App().settings().setDesktopNotify(checked);
changed(ChangeType::DesktopEnabled);
}, desktop->lifetime());
if (sound) {
sound->toggledChanges(
) | rpl::filter([](bool checked) {
return (checked != Core::App().settings().soundNotify());
}) | rpl::on_next([=](bool checked) {
Core::App().settings().setSoundNotify(checked);
changed(ChangeType::SoundEnabled);
}, sound->lifetime());
}
if (flashbounce) {
flashbounce->toggledChanges(
) | rpl::filter([](bool checked) {
return (checked != Core::App().settings().flashBounceNotify());
}) | rpl::on_next([=](bool checked) {
Core::App().settings().setFlashBounceNotify(checked);
changed(ChangeType::FlashBounceEnabled);
}, flashbounce->lifetime());
}
Core::App().notifications().settingsChanged(
) | rpl::on_next([=](ChangeType change) {
if (change == ChangeType::DesktopEnabled) {
desktopToggles->fire(Core::App().settings().desktopNotify());
} else if (change == ChangeType::SoundEnabled) {
soundAllowed->fire(allowed());
} else if (change == ChangeType::FlashBounceEnabled) {
flashbounceToggles->fire(
Core::App().settings().flashBounceNotify());
}
}, desktop->lifetime());
}
}
void BuildNotifyViewSection(
SectionBuilder &builder,
Window::SessionController *controller,
rpl::lifetime &lifetime) {
const auto container = builder.container();
if (!container || !controller) {
return;
}
const auto &settings = Core::App().settings();
const auto checkboxes = SetupNotifyViewOptions(
controller,
container,
(settings.notifyView() <= NotifyView::ShowName),
(settings.notifyView() <= NotifyView::ShowPreview));
const auto name = checkboxes.name;
const auto preview = checkboxes.preview;
const auto previewWrap = checkboxes.wrap;
const auto previewDivider = container->add(
object_ptr<Ui::SlideWrap<Ui::BoxContentDivider>>(
container,
object_ptr<Ui::BoxContentDivider>(container)));
previewWrap->toggle(settings.desktopNotify(), anim::type::instant);
previewDivider->toggle(!settings.desktopNotify(), anim::type::instant);
const auto changed = [=](ChangeType change) {
Core::App().saveSettingsDelayed();
Core::App().notifications().notifySettingsChanged(change);
};
name->checkedChanges(
) | rpl::map([=](bool checked) {
if (!checked) {
preview->setChecked(false);
return NotifyView::ShowNothing;
} else if (!preview->checked()) {
return NotifyView::ShowName;
}
return NotifyView::ShowPreview;
}) | rpl::filter([=](NotifyView value) {
return (value != Core::App().settings().notifyView());
}) | rpl::on_next([=](NotifyView value) {
Core::App().settings().setNotifyView(value);
changed(ChangeType::ViewParams);
}, name->lifetime());
preview->checkedChanges(
) | rpl::map([=](bool checked) {
if (checked) {
name->setChecked(true);
return NotifyView::ShowPreview;
} else if (name->checked()) {
return NotifyView::ShowName;
}
return NotifyView::ShowNothing;
}) | rpl::filter([=](NotifyView value) {
return (value != Core::App().settings().notifyView());
}) | rpl::on_next([=](NotifyView value) {
Core::App().settings().setNotifyView(value);
changed(ChangeType::ViewParams);
}, preview->lifetime());
Core::App().notifications().settingsChanged(
) | rpl::on_next([=](ChangeType change) {
if (change == ChangeType::DesktopEnabled) {
previewWrap->toggle(
Core::App().settings().desktopNotify(),
anim::type::normal);
previewDivider->toggle(
!Core::App().settings().desktopNotify(),
anim::type::normal);
}
}, lifetime);
}
void BuildNotifyTypeSection(
SectionBuilder &builder,
Window::SessionController *controller) {
const auto container = builder.container();
const auto showOther = builder.showOther();
builder.addSkip(st::notifyPreviewBottomSkip);
builder.addSubsectionTitle(tr::lng_settings_notify_title());
if (container && controller && showOther) {
controller->session().data().notifySettings().loadExceptions();
AddTypeButton(container, controller, Data::DefaultNotify::User, showOther);
AddTypeButton(container, controller, Data::DefaultNotify::Group, showOther);
AddTypeButton(container, controller, Data::DefaultNotify::Broadcast, showOther);
} else {
builder.addSettingsButton({
.id = u"notifications/private"_q,
.title = tr::lng_notification_private_chats(),
.icon = { &st::menuIconProfile },
.keywords = { u"private"_q, u"chats"_q, u"direct"_q },
});
builder.addSettingsButton({
.id = u"notifications/groups"_q,
.title = tr::lng_notification_groups(),
.icon = { &st::menuIconGroups },
.keywords = { u"groups"_q, u"chats"_q },
});
builder.addSettingsButton({
.id = u"notifications/channels"_q,
.title = tr::lng_notification_channels(),
.icon = { &st::menuIconChannel },
.keywords = { u"channels"_q, u"broadcast"_q },
});
}
}
void BuildEventNotificationsSection(
SectionBuilder &builder,
Window::SessionController *controller) {
builder.addSkip(st::settingsCheckboxesSkip);
builder.addDivider();
builder.addSkip(st::settingsCheckboxesSkip);
builder.addSubsectionTitle(tr::lng_settings_events_title());
if (!controller) {
builder.addToggle({
.id = u"notifications/events/joined"_q,
.title = tr::lng_settings_events_joined(),
.icon = { &st::menuIconInvite },
.toggled = rpl::single(false),
.keywords = { u"joined"_q, u"contacts"_q, u"signup"_q },
});
builder.addToggle({
.id = u"notifications/events/pinned"_q,
.title = tr::lng_settings_events_pinned(),
.icon = { &st::menuIconPin },
.toggled = rpl::single(Core::App().settings().notifyAboutPinned()),
.keywords = { u"pinned"_q, u"message"_q },
});
return;
}
const auto session = &controller->session();
const auto &settings = Core::App().settings();
auto joinSilent = rpl::single(
session->api().contactSignupSilentCurrent().value_or(false)
) | rpl::then(session->api().contactSignupSilent());
const auto joined = builder.addToggle({
.id = u"notifications/events/joined"_q,
.title = tr::lng_settings_events_joined(),
.icon = { &st::menuIconInvite },
.toggled = std::move(joinSilent) | rpl::map([](bool s) { return !s; }),
.keywords = { u"joined"_q, u"contacts"_q, u"signup"_q },
});
if (joined) {
joined->toggledChanges(
) | rpl::filter([=](bool enabled) {
const auto silent = session->api().contactSignupSilentCurrent();
return (enabled == silent.value_or(false));
}) | rpl::on_next([=](bool enabled) {
session->api().saveContactSignupSilent(!enabled);
}, joined->lifetime());
}
const auto pinned = builder.addToggle({
.id = u"notifications/events/pinned"_q,
.title = tr::lng_settings_events_pinned(),
.icon = { &st::menuIconPin },
.toggled = rpl::single(
settings.notifyAboutPinned()
) | rpl::then(settings.notifyAboutPinnedChanges()),
.keywords = { u"pinned"_q, u"message"_q },
});
if (pinned) {
pinned->toggledChanges(
) | rpl::filter([=](bool notify) {
return (notify != Core::App().settings().notifyAboutPinned());
}) | rpl::on_next([=](bool notify) {
Core::App().settings().setNotifyAboutPinned(notify);
Core::App().saveSettingsDelayed();
}, pinned->lifetime());
}
}
void BuildCallNotificationsSection(
SectionBuilder &builder,
Window::SessionController *controller) {
builder.addSkip(st::settingsCheckboxesSkip);
builder.addDivider();
builder.addSkip(st::settingsCheckboxesSkip);
builder.addSubsectionTitle(tr::lng_settings_notifications_calls_title());
if (!controller) {
builder.addToggle({
.id = u"notifications/calls/accept"_q,
.title = tr::lng_settings_call_accept_calls(),
.icon = { &st::menuIconCallsReceive },
.toggled = rpl::single(true),
.keywords = { u"calls"_q, u"receive"_q, u"incoming"_q },
});
return;
}
const auto session = &controller->session();
const auto authorizations = &session->api().authorizations();
authorizations->reload();
const auto acceptCalls = builder.addToggle({
.id = u"notifications/calls/accept"_q,
.title = tr::lng_settings_call_accept_calls(),
.icon = { &st::menuIconCallsReceive },
.toggled = authorizations->callsDisabledHereValue()
| rpl::map([](bool disabled) { return !disabled; }),
.keywords = { u"calls"_q, u"receive"_q, u"incoming"_q },
});
if (acceptCalls) {
const auto container = builder.container();
acceptCalls->toggledChanges(
) | rpl::filter([=](bool toggled) {
return (toggled == authorizations->callsDisabledHere());
}) | rpl::on_next([=](bool toggled) {
authorizations->toggleCallsDisabledHere(!toggled);
}, container->lifetime());
}
}
void BuildBadgeCounterSection(
SectionBuilder &builder,
Window::SessionController *controller) {
builder.addSkip(st::settingsCheckboxesSkip);
builder.addDivider();
builder.addSkip(st::settingsCheckboxesSkip);
builder.addSubsectionTitle(tr::lng_settings_badge_title());
const auto &settings = Core::App().settings();
const auto muted = builder.addToggle({
.id = u"notifications/badge/muted"_q,
.title = tr::lng_settings_include_muted(),
.st = &st::settingsButtonNoIcon,
.toggled = rpl::single(settings.includeMutedCounter()),
.keywords = { u"muted"_q, u"badge"_q, u"counter"_q },
});
const auto hasFolders = controller
&& controller->session().data().chatsFilters().has();
const auto mutedFolders = hasFolders ? builder.addToggle({
.id = u"notifications/badge/muted_folders"_q,
.title = tr::lng_settings_include_muted_folders(),
.st = &st::settingsButtonNoIcon,
.toggled = rpl::single(settings.includeMutedCounterFolders()),
.keywords = { u"muted"_q, u"folders"_q },
}) : nullptr;
const auto count = builder.addToggle({
.id = u"notifications/badge/count"_q,
.title = tr::lng_settings_count_unread(),
.st = &st::settingsButtonNoIcon,
.toggled = rpl::single(settings.countUnreadMessages()),
.keywords = { u"unread"_q, u"messages"_q, u"count"_q },
});
const auto changed = [=](ChangeType change) {
Core::App().saveSettingsDelayed();
Core::App().notifications().notifySettingsChanged(change);
};
if (muted) {
muted->toggledChanges(
) | rpl::filter([=](bool checked) {
return (checked != Core::App().settings().includeMutedCounter());
}) | rpl::on_next([=](bool checked) {
Core::App().settings().setIncludeMutedCounter(checked);
changed(ChangeType::IncludeMuted);
}, muted->lifetime());
}
if (mutedFolders) {
mutedFolders->toggledChanges(
) | rpl::filter([=](bool checked) {
return (checked
!= Core::App().settings().includeMutedCounterFolders());
}) | rpl::on_next([=](bool checked) {
Core::App().settings().setIncludeMutedCounterFolders(checked);
changed(ChangeType::IncludeMuted);
}, mutedFolders->lifetime());
}
if (count) {
count->toggledChanges(
) | rpl::filter([=](bool checked) {
return (checked != Core::App().settings().countUnreadMessages());
}) | rpl::on_next([=](bool checked) {
Core::App().settings().setCountUnreadMessages(checked);
changed(ChangeType::CountMessages);
}, count->lifetime());
}
}
void BuildSystemIntegrationAndAdvancedSection(
SectionBuilder &builder,
Window::SessionController *controller) {
const auto container = builder.container();
auto nativeText = [&]() -> rpl::producer<QString> {
if (!Platform::Notifications::Supported()
|| Core::App().notifications().nativeEnforced()) {
return rpl::producer<QString>();
} else if (Platform::IsWindows()) {
return tr::lng_settings_use_windows();
}
return tr::lng_settings_use_native_notifications();
}();
if (nativeText) {
builder.addSkip(st::settingsCheckboxesSkip);
builder.addDivider();
builder.addSkip(st::settingsCheckboxesSkip);
builder.addSubsectionTitle(tr::lng_settings_native_title());
}
const auto &settings = Core::App().settings();
const auto native = nativeText ? builder.addToggle({
.id = u"notifications/native"_q,
.title = std::move(nativeText),
.st = &st::settingsButtonNoIcon,
.toggled = rpl::single(settings.nativeNotifications()),
.keywords = { u"native"_q, u"system"_q, u"windows"_q },
}) : nullptr;
if (Core::App().notifications().nativeEnforced()) {
return;
}
if (!container || !controller) {
return;
}
const auto advancedSlide = container->add(
object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
container,
object_ptr<Ui::VerticalLayout>(container)));
const auto advancedWrap = advancedSlide->entity();
if (native) {
native->toggledChanges(
) | rpl::filter([](bool checked) {
return (checked != Core::App().settings().nativeNotifications());
}) | rpl::on_next([=](bool checked) {
Core::App().settings().setNativeNotifications(checked);
Core::App().saveSettingsDelayed();
Core::App().notifications().createManager();
advancedSlide->toggle(!checked, anim::type::normal);
}, native->lifetime());
}
if (Platform::IsWindows()) {
const auto skipInFocus = advancedWrap->add(object_ptr<Ui::SettingsButton>(
advancedWrap,
tr::lng_settings_skip_in_focus(),
st::settingsButtonNoIcon
))->toggleOn(rpl::single(Core::App().settings().skipToastsInFocus()));
skipInFocus->toggledChanges(
) | rpl::filter([](bool checked) {
return (checked != Core::App().settings().skipToastsInFocus());
}) | rpl::on_next([=](bool checked) {
Core::App().settings().setSkipToastsInFocus(checked);
Core::App().saveSettingsDelayed();
if (checked && Platform::Notifications::SkipToastForCustom()) {
Core::App().notifications().notifySettingsChanged(
ChangeType::DesktopEnabled);
}
}, skipInFocus->lifetime());
}
const auto screens = QGuiApplication::screens();
if (screens.size() > 1) {
Ui::AddSkip(advancedWrap, st::settingsCheckboxesSkip);
Ui::AddDivider(advancedWrap);
Ui::AddSkip(advancedWrap, st::settingsCheckboxesSkip);
Ui::AddSubsectionTitle(
advancedWrap,
tr::lng_settings_notifications_display());
const auto currentChecksum
= Core::App().settings().notificationsDisplayChecksum();
auto currentIndex = (currentChecksum == 0)
? kDefaultDisplayIndex
: 0;
for (auto i = 0; i < screens.size(); ++i) {
if (Platform::ScreenNameChecksum(screens[i]) == currentChecksum) {
currentIndex = i;
break;
}
}
const auto group = std::make_shared<Ui::RadiobuttonGroup>(
currentIndex);
advancedWrap->add(
object_ptr<Ui::Radiobutton>(
advancedWrap,
group,
kDefaultDisplayIndex,
tr::lng_settings_notifications_display_default(tr::now),
st::settingsSendType),
st::settingsSendTypePadding);
for (auto i = 0; i < screens.size(); ++i) {
const auto &screen = screens[i];
const auto name = Platform::ScreenDisplayLabel(screen);
const auto geometry = screen->geometry();
const auto resolution = QString::number(geometry.width())
+ QChar(0x00D7)
+ QString::number(geometry.height());
const auto label = name.isEmpty()
? QString("Display (%1)").arg(resolution)
: QString("%1 (%2)").arg(name).arg(resolution);
advancedWrap->add(
object_ptr<Ui::Radiobutton>(
advancedWrap,
group,
i,
label,
st::settingsSendType),
st::settingsSendTypePadding);
}
group->setChangedCallback([=](int selectedIndex) {
if (selectedIndex == kDefaultDisplayIndex) {
Core::App().settings().setNotificationsDisplayChecksum(0);
Core::App().saveSettings();
Core::App().notifications().notifySettingsChanged(
ChangeType::Corner);
} else {
const auto screens = QGuiApplication::screens();
if (selectedIndex >= 0 && selectedIndex < screens.size()) {
const auto checksum = Platform::ScreenNameChecksum(
screens[selectedIndex]);
Core::App().settings().setNotificationsDisplayChecksum(
checksum);
Core::App().saveSettings();
Core::App().notifications().notifySettingsChanged(
ChangeType::Corner);
}
}
});
}
Ui::AddSkip(advancedWrap, st::settingsCheckboxesSkip);
Ui::AddDivider(advancedWrap);
Ui::AddSkip(advancedWrap, st::settingsCheckboxesSkip);
Ui::AddSubsectionTitle(
advancedWrap,
tr::lng_settings_notifications_position());
Ui::AddSkip(advancedWrap, st::settingsCheckboxesSkip);
const auto position = advancedWrap->add(
object_ptr<NotificationsCount>(advancedWrap, controller));
Ui::AddSkip(advancedWrap, st::settingsCheckboxesSkip);
Ui::AddSubsectionTitle(advancedWrap, tr::lng_settings_notifications_count());
const auto countSlider = advancedWrap->add(
object_ptr<Ui::SettingsSlider>(advancedWrap, st::settingsSlider),
st::settingsBigScalePadding);
for (int i = 0; i != kMaxNotificationsCount; ++i) {
countSlider->addSection(QString::number(i + 1));
}
countSlider->setActiveSectionFast(CurrentNotificationsCount() - 1);
countSlider->sectionActivated(
) | rpl::on_next([=](int section) {
position->setCount(section + 1);
}, countSlider->lifetime());
Ui::AddSkip(advancedWrap, st::settingsCheckboxesSkip);
if (Core::App().settings().nativeNotifications()) {
advancedSlide->hide(anim::type::instant);
}
Core::App().notifications().settingsChanged(
) | rpl::on_next([=](ChangeType change) {
if (change == ChangeType::DesktopEnabled) {
const auto native = Core::App().settings().nativeNotifications();
advancedSlide->toggle(!native, anim::type::normal);
}
}, advancedSlide->lifetime());
}
void BuildNotificationsSectionContent(
SectionBuilder &builder,
Window::SessionController *controller) {
const auto container = builder.container();
builder.addSkip(st::settingsPrivacySkip);
BuildMultiAccountSection(builder, controller);
BuildGlobalNotificationsSection(builder, controller);
if (container && controller) {
BuildNotifyViewSection(builder, controller, container->lifetime());
}
BuildNotifyTypeSection(builder, controller);
BuildEventNotificationsSection(builder, controller);
BuildCallNotificationsSection(builder, controller);
BuildBadgeCounterSection(builder, controller);
BuildSystemIntegrationAndAdvancedSection(builder, controller);
}
} // namespace
void BuildNotificationsSection(
not_null<Ui::VerticalLayout*> container,
not_null<Window::SessionController*> controller,
Fn<void(Type)> showOther) {
const auto isPaused = Window::PausedIn(
controller,
Window::GifPauseReason::Layer);
auto builder = SectionBuilder(WidgetContext{
.container = container,
.controller = controller,
.showOther = std::move(showOther),
.isPaused = isPaused,
});
BuildNotificationsSectionContent(builder, controller);
}
std::vector<SearchEntry> BuildNotificationsSectionForSearch() {
auto entries = std::vector<SearchEntry>();
auto builder = SectionBuilder(SearchContext{
.entries = &entries,
});
BuildNotificationsSectionContent(builder, nullptr);
return entries;
}
} // namespace Settings::Builder
@@ -0,0 +1,29 @@
/*
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
*/
#pragma once
#include "settings/builder/settings_builder.h"
namespace Ui {
class VerticalLayout;
} // namespace Ui
namespace Window {
class SessionController;
} // namespace Window
namespace Settings::Builder {
void BuildNotificationsSection(
not_null<Ui::VerticalLayout*> container,
not_null<Window::SessionController*> controller,
Fn<void(Type)> showOther);
[[nodiscard]] std::vector<SearchEntry> BuildNotificationsSectionForSearch();
} // namespace Settings::Builder
@@ -7,124 +7,50 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
*/ */
#include "settings/settings_notifications.h" #include "settings/settings_notifications.h"
#include "ui/widgets/continuous_sliders.h" #include "settings/builder/settings_notifications_builder.h"
#include "settings/settings_notifications_common.h"
#include "settings/settings_notifications_type.h" #include "settings/settings_notifications_type.h"
#include "ui/boxes/confirm_box.h" #include "ui/boxes/confirm_box.h"
#include "ui/chat/chat_theme.h"
#include "ui/controls/chat_service_checkbox.h" #include "ui/controls/chat_service_checkbox.h"
#include "ui/effects/animations.h" #include "ui/effects/animations.h"
#include "data/notify/data_peer_notify_volume.h"
#include "ui/chat/chat_theme.h"
#include "ui/text/text_utilities.h"
#include "ui/wrap/vertical_layout.h"
#include "ui/wrap/slide_wrap.h"
#include "ui/widgets/box_content_divider.h"
#include "ui/widgets/checkbox.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/discrete_sliders.h"
#include "ui/painter.h" #include "ui/painter.h"
#include "ui/vertical_list.h"
#include "ui/ui_utility.h" #include "ui/ui_utility.h"
#include "ui/widgets/buttons.h"
#include "ui/widgets/checkbox.h"
#include "ui/wrap/slide_wrap.h"
#include "ui/wrap/vertical_layout.h"
#include "core/application.h"
#include "data/data_session.h"
#include "data/notify/data_notify_settings.h"
#include "lang/lang_keys.h" #include "lang/lang_keys.h"
#include "main/main_session.h"
#include "mainwindow.h"
#include "window/notifications_manager.h" #include "window/notifications_manager.h"
#include "window/section_widget.h" #include "window/section_widget.h"
#include "window/themes/window_theme.h" #include "window/themes/window_theme.h"
#include "window/window_session_controller.h" #include "window/window_session_controller.h"
#include "platform/platform_specific.h"
#include "platform/platform_notifications_manager.h"
#include "base/platform/base_platform_info.h"
#include "mainwindow.h"
#include "core/application.h"
#include "main/main_session.h"
#include "main/main_account.h"
#include "main/main_domain.h"
#include "api/api_authorizations.h"
#include "api/api_ringtones.h"
#include "data/data_chat_filters.h"
#include "data/data_session.h"
#include "data/data_document.h"
#include "data/notify/data_notify_settings.h"
#include "boxes/ringtones_box.h"
#include "apiwrap.h"
#include "styles/style_settings.h"
#include "styles/style_boxes.h" #include "styles/style_boxes.h"
#include "styles/style_chat.h"
#include "styles/style_dialogs.h"
#include "styles/style_layers.h" #include "styles/style_layers.h"
#include "styles/style_menu_icons.h" #include "styles/style_menu_icons.h"
#include "styles/style_chat.h" #include "styles/style_settings.h"
#include "styles/style_window.h" #include "styles/style_window.h"
#include "styles/style_dialogs.h"
#include <QSvgRenderer> #include <QSvgRenderer>
#include <QtGui/QGuiApplication>
#include <QtGui/QScreen>
namespace Settings { namespace Settings {
namespace {
constexpr auto kMaxNotificationsCount = 5; using ChangeType = Window::Notifications::ChangeType;
constexpr auto kDefaultDisplayIndex = -1;
[[nodiscard]] int CurrentCount() { int CurrentNotificationsCount() {
return std::clamp( return std::clamp(
Core::App().settings().notificationsCount(), Core::App().settings().notificationsCount(),
1, 1,
kMaxNotificationsCount); kMaxNotificationsCount);
} }
using ChangeType = Window::Notifications::ChangeType;
class NotificationsCount : public Ui::RpWidget {
public:
NotificationsCount(
QWidget *parent,
not_null<Window::SessionController*> controller);
void setCount(int count);
~NotificationsCount();
protected:
void paintEvent(QPaintEvent *e) override;
void mousePressEvent(QMouseEvent *e) override;
void mouseMoveEvent(QMouseEvent *e) override;
void leaveEventHook(QEvent *e) override;
void mouseReleaseEvent(QMouseEvent *e) override;
int resizeGetHeight(int newWidth) override;
private:
using ScreenCorner = Core::Settings::ScreenCorner;
void setOverCorner(ScreenCorner corner);
void clearOverCorner();
class SampleWidget;
void removeSample(SampleWidget *widget);
QRect getScreenRect() const;
QRect getScreenRect(int width) const;
int getContentLeft() const;
void prepareNotificationSampleSmall();
void prepareNotificationSampleLarge();
void prepareNotificationSampleUserpic();
const not_null<Window::SessionController*> _controller;
QPixmap _notificationSampleUserpic;
QPixmap _notificationSampleSmall;
QPixmap _notificationSampleLarge;
ScreenCorner _chosenCorner;
std::vector<Ui::Animations::Simple> _sampleOpacities;
bool _isOverCorner = false;
ScreenCorner _overCorner = ScreenCorner::TopLeft;
bool _isDownCorner = false;
ScreenCorner _downCorner = ScreenCorner::TopLeft;
int _oldCount;
std::vector<SampleWidget*> _cornerSamples[4];
};
class NotificationsCount::SampleWidget : public QWidget { class NotificationsCount::SampleWidget : public QWidget {
public: public:
SampleWidget(NotificationsCount *owner, const QPixmap &cache); SampleWidget(NotificationsCount *owner, const QPixmap &cache);
@@ -306,7 +232,7 @@ NotificationsCount::NotificationsCount(
not_null<Window::SessionController*> controller) not_null<Window::SessionController*> controller)
: _controller(controller) : _controller(controller)
, _chosenCorner(Core::App().settings().notificationsCorner()) , _chosenCorner(Core::App().settings().notificationsCorner())
, _oldCount(CurrentCount()) { , _oldCount(CurrentNotificationsCount()) {
setMouseTracking(true); setMouseTracking(true);
_sampleOpacities.resize(kMaxNotificationsCount); _sampleOpacities.resize(kMaxNotificationsCount);
@@ -780,12 +706,6 @@ void NotifyPreview::paint(Painter &p, int x, int y) {
2); 2);
} }
struct NotifyViewCheckboxes {
not_null<Ui::SlideWrap<>*> wrap;
not_null<Ui::Checkbox*> name;
not_null<Ui::Checkbox*> preview;
};
NotifyViewCheckboxes SetupNotifyViewOptions( NotifyViewCheckboxes SetupNotifyViewOptions(
not_null<Window::SessionController*> controller, not_null<Window::SessionController*> controller,
not_null<Ui::VerticalLayout*> container, not_null<Ui::VerticalLayout*> container,
@@ -873,539 +793,6 @@ NotifyViewCheckboxes SetupNotifyViewOptions(
}; };
} }
void SetupAdvancedNotifications(
not_null<Window::SessionController*> controller,
not_null<Ui::VerticalLayout*> container) {
if (Platform::IsWindows()) {
const auto skipInFocus = container->add(object_ptr<Button>(
container,
tr::lng_settings_skip_in_focus(),
st::settingsButtonNoIcon
))->toggleOn(rpl::single(Core::App().settings().skipToastsInFocus()));
skipInFocus->toggledChanges(
) | rpl::filter([](bool checked) {
return (checked != Core::App().settings().skipToastsInFocus());
}) | rpl::on_next([=](bool checked) {
Core::App().settings().setSkipToastsInFocus(checked);
Core::App().saveSettingsDelayed();
if (checked && Platform::Notifications::SkipToastForCustom()) {
using Change = Window::Notifications::ChangeType;
Core::App().notifications().notifySettingsChanged(
Change::DesktopEnabled);
}
}, skipInFocus->lifetime());
}
const auto screens = QGuiApplication::screens();
if (screens.size() > 1) {
Ui::AddSkip(container, st::settingsCheckboxesSkip);
Ui::AddDivider(container);
Ui::AddSkip(container, st::settingsCheckboxesSkip);
Ui::AddSubsectionTitle(
container,
tr::lng_settings_notifications_display());
const auto currentChecksum
= Core::App().settings().notificationsDisplayChecksum();
auto currentIndex = (currentChecksum == 0)
? kDefaultDisplayIndex
: 0;
for (auto i = 0; i < screens.size(); ++i) {
if (Platform::ScreenNameChecksum(screens[i]) == currentChecksum) {
currentIndex = i;
break;
}
}
const auto group = std::make_shared<Ui::RadiobuttonGroup>(
currentIndex);
container->add(
object_ptr<Ui::Radiobutton>(
container,
group,
kDefaultDisplayIndex,
tr::lng_settings_notifications_display_default(tr::now),
st::settingsSendType),
st::settingsSendTypePadding);
for (auto i = 0; i < screens.size(); ++i) {
const auto &screen = screens[i];
const auto name = Platform::ScreenDisplayLabel(screen);
const auto geometry = screen->geometry();
const auto resolution = QString::number(geometry.width())
+ QChar(0x00D7)
+ QString::number(geometry.height());
const auto label = name.isEmpty()
? QString("Display (%1)").arg(resolution)
: QString("%1 (%2)").arg(name).arg(resolution);
container->add(
object_ptr<Ui::Radiobutton>(
container,
group,
i,
label,
st::settingsSendType),
st::settingsSendTypePadding);
}
group->setChangedCallback([=](int selectedIndex) {
if (selectedIndex == kDefaultDisplayIndex) {
Core::App().settings().setNotificationsDisplayChecksum(0);
Core::App().saveSettings();
Core::App().notifications().notifySettingsChanged(
ChangeType::Corner);
} else {
const auto screens = QGuiApplication::screens();
if (selectedIndex >= 0 && selectedIndex < screens.size()) {
const auto checksum = Platform::ScreenNameChecksum(
screens[selectedIndex]);
Core::App().settings().setNotificationsDisplayChecksum(
checksum);
Core::App().saveSettings();
Core::App().notifications().notifySettingsChanged(
ChangeType::Corner);
}
}
});
}
Ui::AddSkip(container, st::settingsCheckboxesSkip);
Ui::AddDivider(container);
Ui::AddSkip(container, st::settingsCheckboxesSkip);
Ui::AddSubsectionTitle(
container,
tr::lng_settings_notifications_position());
Ui::AddSkip(container, st::settingsCheckboxesSkip);
const auto position = container->add(
object_ptr<NotificationsCount>(container, controller));
Ui::AddSkip(container, st::settingsCheckboxesSkip);
Ui::AddSubsectionTitle(container, tr::lng_settings_notifications_count());
const auto count = container->add(
object_ptr<Ui::SettingsSlider>(container, st::settingsSlider),
st::settingsBigScalePadding);
for (int i = 0; i != kMaxNotificationsCount; ++i) {
count->addSection(QString::number(i + 1));
}
count->setActiveSectionFast(CurrentCount() - 1);
count->sectionActivated(
) | rpl::on_next([=](int section) {
position->setCount(section + 1);
}, count->lifetime());
Ui::AddSkip(container, st::settingsCheckboxesSkip);
}
void SetupMultiAccountNotifications(
not_null<Window::SessionController*> controller,
not_null<Ui::VerticalLayout*> container) {
if (Core::App().domain().accounts().size() < 2) {
return;
}
Ui::AddSubsectionTitle(container, tr::lng_settings_show_from());
const auto fromAll = container->add(object_ptr<Button>(
container,
tr::lng_settings_notify_all(),
st::settingsButtonNoIcon
))->toggleOn(rpl::single(Core::App().settings().notifyFromAll()));
fromAll->toggledChanges(
) | rpl::filter([](bool checked) {
return (checked != Core::App().settings().notifyFromAll());
}) | rpl::on_next([=](bool checked) {
Core::App().settings().setNotifyFromAll(checked);
Core::App().saveSettingsDelayed();
if (!checked) {
auto &notifications = Core::App().notifications();
const auto &list = Core::App().domain().accounts();
for (const auto &[index, account] : list) {
if (account.get() == &Core::App().domain().active()) {
continue;
} else if (const auto session = account->maybeSession()) {
notifications.clearFromSession(session);
}
}
}
}, fromAll->lifetime());
Ui::AddSkip(container);
Ui::AddDividerText(container, tr::lng_settings_notify_all_about());
Ui::AddSkip(container);
}
void SetupNotificationsContent(
not_null<Window::SessionController*> controller,
not_null<Ui::VerticalLayout*> container,
Fn<void(Type)> showOther) {
using namespace rpl::mappers;
Ui::AddSkip(container, st::settingsPrivacySkip);
using NotifyView = Core::Settings::NotifyView;
SetupMultiAccountNotifications(controller, container);
AddSubsectionTitle(container, tr::lng_settings_notify_global());
const auto session = &controller->session();
const auto checkbox = [&](
rpl::producer<QString> label,
IconDescriptor &&descriptor,
rpl::producer<bool> checked) {
auto result = CreateButtonWithIcon(
container,
std::move(label),
st::settingsButton,
std::move(descriptor)
);
result->toggleOn(std::move(checked));
return result;
};
const auto addCheckbox = [&](
rpl::producer<QString> label,
IconDescriptor &&descriptor,
rpl::producer<bool> checked) {
return container->add(
checkbox(
std::move(label),
std::move(descriptor),
std::move(checked)));
};
const auto &settings = Core::App().settings();
const auto desktopToggles = container->lifetime(
).make_state<rpl::event_stream<bool>>();
const auto desktop = addCheckbox(
tr::lng_settings_desktop_notify(),
{ &st::menuIconNotifications },
desktopToggles->events_starting_with(settings.desktopNotify()));
const auto flashbounceToggles = container->lifetime(
).make_state<rpl::event_stream<bool>>();
const auto flashbounce = addCheckbox(
(Platform::IsWindows()
? tr::lng_settings_alert_windows
: Platform::IsMac()
? tr::lng_settings_alert_mac
: tr::lng_settings_alert_linux)(),
{ &st::menuIconDockBounce },
flashbounceToggles->events_starting_with(
settings.flashBounceNotify()));
const auto soundAllowed = container->lifetime(
).make_state<rpl::event_stream<bool>>();
const auto allowed = [=] {
return Core::App().settings().soundNotify();
};
const auto sound = addCheckbox(
tr::lng_settings_sound_allowed(),
{ &st::menuIconUnmute },
soundAllowed->events_starting_with(allowed()));
Ui::AddRingtonesVolumeSlider(
container,
rpl::single(true),
tr::lng_settings_master_volume_notifications(),
Data::VolumeController{
.volume = []() -> ushort {
const auto volume
= Core::App().settings().notificationsVolume();
return volume ? volume : 100;
},
.saveVolume = [=](ushort volume) {
Core::App().notifications().playSound(
session,
0,
volume / 100.);
Core::App().settings().setNotificationsVolume(volume);
Core::App().saveSettingsDelayed();
}});
Ui::AddSkip(container);
const auto checkboxes = SetupNotifyViewOptions(
controller,
container,
(settings.notifyView() <= NotifyView::ShowName),
(settings.notifyView() <= NotifyView::ShowPreview));
const auto name = checkboxes.name;
const auto preview = checkboxes.preview;
const auto previewWrap = checkboxes.wrap;
const auto previewDivider = container->add(
object_ptr<Ui::SlideWrap<Ui::BoxContentDivider>>(
container,
object_ptr<Ui::BoxContentDivider>(container)));
previewWrap->toggle(settings.desktopNotify(), anim::type::instant);
previewDivider->toggle(!settings.desktopNotify(), anim::type::instant);
controller->session().data().notifySettings().loadExceptions();
Ui::AddSkip(container, st::notifyPreviewBottomSkip);
Ui::AddSubsectionTitle(container, tr::lng_settings_notify_title());
const auto addType = [&](Data::DefaultNotify type) {
AddTypeButton(container, controller, type, showOther);
};
addType(Data::DefaultNotify::User);
addType(Data::DefaultNotify::Group);
addType(Data::DefaultNotify::Broadcast);
Ui::AddSkip(container, st::settingsCheckboxesSkip);
Ui::AddDivider(container);
Ui::AddSkip(container, st::settingsCheckboxesSkip);
Ui::AddSubsectionTitle(container, tr::lng_settings_events_title());
auto joinSilent = rpl::single(
session->api().contactSignupSilentCurrent().value_or(false)
) | rpl::then(session->api().contactSignupSilent());
const auto joined = addCheckbox(
tr::lng_settings_events_joined(),
{ &st::menuIconInvite },
std::move(joinSilent) | rpl::map(!_1));
joined->toggledChanges(
) | rpl::filter([=](bool enabled) {
const auto silent = session->api().contactSignupSilentCurrent();
return (enabled == silent.value_or(false));
}) | rpl::on_next([=](bool enabled) {
session->api().saveContactSignupSilent(!enabled);
}, joined->lifetime());
const auto pinned = addCheckbox(
tr::lng_settings_events_pinned(),
{ &st::menuIconPin },
rpl::single(
settings.notifyAboutPinned()
) | rpl::then(settings.notifyAboutPinnedChanges()));
pinned->toggledChanges(
) | rpl::filter([=](bool notify) {
return (notify != Core::App().settings().notifyAboutPinned());
}) | rpl::on_next([=](bool notify) {
Core::App().settings().setNotifyAboutPinned(notify);
Core::App().saveSettingsDelayed();
}, joined->lifetime());
Ui::AddSkip(container, st::settingsCheckboxesSkip);
Ui::AddDivider(container);
Ui::AddSkip(container, st::settingsCheckboxesSkip);
Ui::AddSubsectionTitle(
container,
tr::lng_settings_notifications_calls_title());
const auto authorizations = &session->api().authorizations();
// Request valid value of calls disabled flag.
authorizations->reload();
const auto acceptCalls = addCheckbox(
tr::lng_settings_call_accept_calls(),
{ &st::menuIconCallsReceive },
authorizations->callsDisabledHereValue() | rpl::map(!_1));
acceptCalls->toggledChanges(
) | rpl::filter([=](bool toggled) {
return (toggled == authorizations->callsDisabledHere());
}) | rpl::on_next([=](bool toggled) {
authorizations->toggleCallsDisabledHere(!toggled);
}, container->lifetime());
Ui::AddSkip(container, st::settingsCheckboxesSkip);
Ui::AddDivider(container);
Ui::AddSkip(container, st::settingsCheckboxesSkip);
Ui::AddSubsectionTitle(container, tr::lng_settings_badge_title());
const auto muted = container->add(object_ptr<Button>(
container,
tr::lng_settings_include_muted(),
st::settingsButtonNoIcon));
muted->toggleOn(rpl::single(settings.includeMutedCounter()));
const auto mutedFolders = session->data().chatsFilters().has()
? container->add(object_ptr<Button>(
container,
tr::lng_settings_include_muted_folders(),
st::settingsButtonNoIcon))
: nullptr;
if (mutedFolders) {
mutedFolders->toggleOn(
rpl::single(settings.includeMutedCounterFolders()));
}
const auto count = container->add(object_ptr<Button>(
container,
tr::lng_settings_count_unread(),
st::settingsButtonNoIcon));
count->toggleOn(rpl::single(settings.countUnreadMessages()));
auto nativeText = [&] {
if (!Platform::Notifications::Supported()
|| Core::App().notifications().nativeEnforced()) {
return rpl::producer<QString>();
} else if (Platform::IsWindows()) {
return tr::lng_settings_use_windows();
}
return tr::lng_settings_use_native_notifications();
}();
const auto native = [&]() -> Ui::SettingsButton* {
if (!nativeText) {
return nullptr;
}
Ui::AddSkip(container, st::settingsCheckboxesSkip);
Ui::AddDivider(container);
Ui::AddSkip(container, st::settingsCheckboxesSkip);
Ui::AddSubsectionTitle(container, tr::lng_settings_native_title());
return container->add(object_ptr<Button>(
container,
std::move(nativeText),
st::settingsButtonNoIcon
))->toggleOn(rpl::single(settings.nativeNotifications()));
}();
const auto advancedSlide = !Core::App().notifications().nativeEnforced()
? container->add(
object_ptr<Ui::SlideWrap<Ui::VerticalLayout>>(
container,
object_ptr<Ui::VerticalLayout>(container)))
: nullptr;
const auto advancedWrap = advancedSlide
? advancedSlide->entity()
: nullptr;
if (advancedWrap) {
SetupAdvancedNotifications(controller, advancedWrap);
}
if (native && advancedSlide && settings.nativeNotifications()) {
advancedSlide->hide(anim::type::instant);
}
using Change = Window::Notifications::ChangeType;
const auto changed = [=](Change change) {
Core::App().saveSettingsDelayed();
Core::App().notifications().notifySettingsChanged(change);
};
desktop->toggledChanges(
) | rpl::filter([](bool checked) {
return (checked != Core::App().settings().desktopNotify());
}) | rpl::on_next([=](bool checked) {
Core::App().settings().setDesktopNotify(checked);
changed(Change::DesktopEnabled);
}, desktop->lifetime());
sound->toggledChanges(
) | rpl::filter([](bool checked) {
return (checked != Core::App().settings().soundNotify());
}) | rpl::on_next([=](bool checked) {
Core::App().settings().setSoundNotify(checked);
changed(Change::SoundEnabled);
}, sound->lifetime());
name->checkedChanges(
) | rpl::map([=](bool checked) {
if (!checked) {
preview->setChecked(false);
return NotifyView::ShowNothing;
} else if (!preview->checked()) {
return NotifyView::ShowName;
}
return NotifyView::ShowPreview;
}) | rpl::filter([=](NotifyView value) {
return (value != Core::App().settings().notifyView());
}) | rpl::on_next([=](NotifyView value) {
Core::App().settings().setNotifyView(value);
changed(Change::ViewParams);
}, name->lifetime());
preview->checkedChanges(
) | rpl::map([=](bool checked) {
if (checked) {
name->setChecked(true);
return NotifyView::ShowPreview;
} else if (name->checked()) {
return NotifyView::ShowName;
}
return NotifyView::ShowNothing;
}) | rpl::filter([=](NotifyView value) {
return (value != Core::App().settings().notifyView());
}) | rpl::on_next([=](NotifyView value) {
Core::App().settings().setNotifyView(value);
changed(Change::ViewParams);
}, preview->lifetime());
flashbounce->toggledChanges(
) | rpl::filter([](bool checked) {
return (checked != Core::App().settings().flashBounceNotify());
}) | rpl::on_next([=](bool checked) {
Core::App().settings().setFlashBounceNotify(checked);
changed(Change::FlashBounceEnabled);
}, flashbounce->lifetime());
muted->toggledChanges(
) | rpl::filter([=](bool checked) {
return (checked != Core::App().settings().includeMutedCounter());
}) | rpl::on_next([=](bool checked) {
Core::App().settings().setIncludeMutedCounter(checked);
changed(Change::IncludeMuted);
}, muted->lifetime());
if (mutedFolders) {
mutedFolders->toggledChanges(
) | rpl::filter([=](bool checked) {
return (checked
!= Core::App().settings().includeMutedCounterFolders());
}) | rpl::on_next([=](bool checked) {
Core::App().settings().setIncludeMutedCounterFolders(checked);
changed(Change::IncludeMuted);
}, mutedFolders->lifetime());
}
count->toggledChanges(
) | rpl::filter([=](bool checked) {
return (checked != Core::App().settings().countUnreadMessages());
}) | rpl::on_next([=](bool checked) {
Core::App().settings().setCountUnreadMessages(checked);
changed(Change::CountMessages);
}, count->lifetime());
Core::App().notifications().settingsChanged(
) | rpl::on_next([=](Change change) {
if (change == Change::DesktopEnabled) {
desktopToggles->fire(Core::App().settings().desktopNotify());
previewWrap->toggle(
Core::App().settings().desktopNotify(),
anim::type::normal);
previewDivider->toggle(
!Core::App().settings().desktopNotify(),
anim::type::normal);
} else if (change == Change::ViewParams) {
//
} else if (change == Change::SoundEnabled) {
soundAllowed->fire(allowed());
} else if (change == Change::FlashBounceEnabled) {
flashbounceToggles->fire(
Core::App().settings().flashBounceNotify());
}
}, desktop->lifetime());
if (native) {
native->toggledChanges(
) | rpl::filter([](bool checked) {
return (checked != Core::App().settings().nativeNotifications());
}) | rpl::on_next([=](bool checked) {
Core::App().settings().setNativeNotifications(checked);
Core::App().saveSettingsDelayed();
Core::App().notifications().createManager();
if (advancedSlide) {
advancedSlide->toggle(
!Core::App().settings().nativeNotifications(),
anim::type::normal);
}
}, native->lifetime());
}
}
void SetupNotifications(
not_null<Window::SessionController*> controller,
not_null<Ui::VerticalLayout*> container,
Fn<void(Type)> showOther) {
SetupNotificationsContent(controller, container, std::move(showOther));
}
} // namespace
Notifications::Notifications( Notifications::Notifications(
QWidget *parent, QWidget *parent,
not_null<Window::SessionController*> controller) not_null<Window::SessionController*> controller)
@@ -1421,7 +808,10 @@ void Notifications::setupContent(
not_null<Window::SessionController*> controller) { not_null<Window::SessionController*> controller) {
const auto content = Ui::CreateChild<Ui::VerticalLayout>(this); const auto content = Ui::CreateChild<Ui::VerticalLayout>(this);
SetupNotifications(controller, content, showOtherMethod()); Builder::BuildNotificationsSection(
content,
controller,
showOtherMethod());
Ui::ResizeFitChild(this, content); Ui::ResizeFitChild(this, content);
} }
@@ -9,6 +9,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "settings/settings_common_session.h" #include "settings/settings_common_session.h"
namespace Ui {
class VerticalLayout;
} // namespace Ui
namespace Settings { namespace Settings {
class Notifications : public Section<Notifications> { class Notifications : public Section<Notifications> {
@@ -0,0 +1,110 @@
/*
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
*/
#pragma once
#include <cstdint>
#include "base/object_ptr.h"
#include "core/core_settings.h"
#include "settings/settings_type.h"
#include "ui/effects/animations.h"
#include "ui/rp_widget.h"
namespace Ui {
class Checkbox;
class VerticalLayout;
template <typename Widget>
class SlideWrap;
} // namespace Ui
namespace Window {
class SessionController;
} // namespace Window
namespace Data {
enum class DefaultNotify : uint8_t;
} // namespace Data
namespace Settings {
constexpr auto kMaxNotificationsCount = 5;
[[nodiscard]] int CurrentNotificationsCount();
class NotificationsCount : public Ui::RpWidget {
public:
NotificationsCount(
QWidget *parent,
not_null<Window::SessionController*> controller);
void setCount(int count);
~NotificationsCount();
protected:
void paintEvent(QPaintEvent *e) override;
void mousePressEvent(QMouseEvent *e) override;
void mouseMoveEvent(QMouseEvent *e) override;
void leaveEventHook(QEvent *e) override;
void mouseReleaseEvent(QMouseEvent *e) override;
int resizeGetHeight(int newWidth) override;
private:
using ScreenCorner = Core::Settings::ScreenCorner;
void setOverCorner(ScreenCorner corner);
void clearOverCorner();
class SampleWidget;
void removeSample(SampleWidget *widget);
QRect getScreenRect() const;
QRect getScreenRect(int width) const;
int getContentLeft() const;
void prepareNotificationSampleSmall();
void prepareNotificationSampleLarge();
void prepareNotificationSampleUserpic();
const not_null<Window::SessionController*> _controller;
QPixmap _notificationSampleUserpic;
QPixmap _notificationSampleSmall;
QPixmap _notificationSampleLarge;
ScreenCorner _chosenCorner;
std::vector<Ui::Animations::Simple> _sampleOpacities;
bool _isOverCorner = false;
ScreenCorner _overCorner = ScreenCorner::TopLeft;
bool _isDownCorner = false;
ScreenCorner _downCorner = ScreenCorner::TopLeft;
int _oldCount;
std::vector<SampleWidget*> _cornerSamples[4];
};
struct NotifyViewCheckboxes {
not_null<Ui::SlideWrap<Ui::RpWidget>*> wrap;
not_null<Ui::Checkbox*> name;
not_null<Ui::Checkbox*> preview;
};
[[nodiscard]] NotifyViewCheckboxes SetupNotifyViewOptions(
not_null<Window::SessionController*> controller,
not_null<Ui::VerticalLayout*> container,
bool nameShown,
bool previewShown);
void AddTypeButton(
not_null<Ui::VerticalLayout*> container,
not_null<Window::SessionController*> controller,
Data::DefaultNotify type,
Fn<void(Type)> showOther);
} // namespace Settings