From 0615f21deb6b52cbc76ea73faf188e1ff0a15fc5 Mon Sep 17 00:00:00 2001 From: John Preston Date: Wed, 6 Apr 2022 19:23:41 +0400 Subject: [PATCH] Allow editing general notification sound. --- Telegram/Resources/langs/lang.strings | 1 + Telegram/SourceFiles/apiwrap.cpp | 24 +++ Telegram/SourceFiles/apiwrap.h | 3 + Telegram/SourceFiles/data/data_session.cpp | 2 +- Telegram/SourceFiles/data/data_session.h | 2 +- .../data/notify/data_notify_settings.cpp | 155 ++++++++++-------- .../data/notify/data_notify_settings.h | 38 +++-- .../SourceFiles/history/history_widget.cpp | 7 +- .../info/profile/info_profile_values.cpp | 2 +- Telegram/SourceFiles/menu/menu_mute.cpp | 4 +- .../settings/settings_notifications.cpp | 74 +++++++-- 11 files changed, 210 insertions(+), 102 deletions(-) diff --git a/Telegram/Resources/langs/lang.strings b/Telegram/Resources/langs/lang.strings index 9d1c4e6c5..6cbc49457 100644 --- a/Telegram/Resources/langs/lang.strings +++ b/Telegram/Resources/langs/lang.strings @@ -354,6 +354,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL "lng_settings_notifications_position" = "Location on the screen"; "lng_settings_notifications_count" = "Notifications count"; "lng_settings_sound_notify" = "Play sound"; +"lng_settings_sound_notify_off" = "Off"; "lng_settings_alert_windows" = "Flash the taskbar icon"; "lng_settings_alert_mac" = "Bounce the dock icon"; "lng_settings_alert_linux" = "Draw attention to the window"; diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp index 811540539..f651d08e2 100644 --- a/Telegram/SourceFiles/apiwrap.cpp +++ b/Telegram/SourceFiles/apiwrap.cpp @@ -1763,6 +1763,11 @@ void ApiWrap::updateNotifySettingsDelayed(not_null peer) { _updateNotifySettingsTimer.callOnce(kNotifySettingSaveTimeout); } +void ApiWrap::updateDefaultNotifySettingsDelayed(Data::DefaultNotify type) { + _updateNotifySettingsDefaults.emplace(type); + _updateNotifySettingsTimer.callOnce(kNotifySettingSaveTimeout); +} + void ApiWrap::sendNotifySettingsUpdates() { while (!_updateNotifySettingsPeers.empty()) { const auto peer = *_updateNotifySettingsPeers.begin(); @@ -1772,6 +1777,25 @@ void ApiWrap::sendNotifySettingsUpdates() { peer->notifySerialize() )).afterDelay(_updateNotifySettingsPeers.empty() ? 0 : 10).send(); } + const auto &settings = session().data().notifySettings(); + while (!_updateNotifySettingsDefaults.empty()) { + const auto type = *_updateNotifySettingsDefaults.begin(); + _updateNotifySettingsDefaults.erase( + _updateNotifySettingsDefaults.begin()); + const auto input = [&] { + switch (type) { + case Data::DefaultNotify::User: return MTP_inputNotifyUsers(); + case Data::DefaultNotify::Group: return MTP_inputNotifyChats(); + case Data::DefaultNotify::Broadcast: + return MTP_inputNotifyBroadcasts(); + } + Unexpected("Default notify type in sendNotifySettingsUpdates"); + }(); + request(MTPaccount_UpdateNotifySettings( + input, + settings.defaultSettings(type).serialize() + )).afterDelay(_updateNotifySettingsDefaults.empty() ? 0 : 10).send(); + } } void ApiWrap::saveDraftToCloudDelayed(not_null history) { diff --git a/Telegram/SourceFiles/apiwrap.h b/Telegram/SourceFiles/apiwrap.h index 7535336ba..467a2828c 100644 --- a/Telegram/SourceFiles/apiwrap.h +++ b/Telegram/SourceFiles/apiwrap.h @@ -30,6 +30,7 @@ namespace Data { struct UpdatedFileReferences; class WallPaper; struct ResolvedForwardDraft; +enum class DefaultNotify; } // namespace Data namespace InlineBots { @@ -239,6 +240,7 @@ public: void requestNotifySettings(const MTPInputNotifyPeer &peer); void updateNotifySettingsDelayed(not_null peer); + void updateDefaultNotifySettingsDelayed(Data::DefaultNotify type); void saveDraftToCloudDelayed(not_null history); static int OnlineTillFromStatus( @@ -595,6 +597,7 @@ private: base::Timer _topPromotionTimer; base::flat_set> _updateNotifySettingsPeers; + base::flat_set _updateNotifySettingsDefaults; base::Timer _updateNotifySettingsTimer; std::map< diff --git a/Telegram/SourceFiles/data/data_session.cpp b/Telegram/SourceFiles/data/data_session.cpp index ad28ca5cb..7d7bb82ca 100644 --- a/Telegram/SourceFiles/data/data_session.cpp +++ b/Telegram/SourceFiles/data/data_session.cpp @@ -951,7 +951,7 @@ void Session::enumerateGroups(Fn)> action) const { } } -void Session::enumerateChannels( +void Session::enumerateBroadcasts( Fn)> action) const { for (const auto &[peerId, peer] : _peers) { if (const auto channel = peer->asChannel()) { diff --git a/Telegram/SourceFiles/data/data_session.h b/Telegram/SourceFiles/data/data_session.h index 9d0ba0925..45ccb1592 100644 --- a/Telegram/SourceFiles/data/data_session.h +++ b/Telegram/SourceFiles/data/data_session.h @@ -192,7 +192,7 @@ public: void enumerateUsers(Fn)> action) const; void enumerateGroups(Fn)> action) const; - void enumerateChannels(Fn)> action) const; + void enumerateBroadcasts(Fn)> action) const; [[nodiscard]] UserData *userByPhone(const QString &phone) const; [[nodiscard]] PeerData *peerByUsername(const QString &username) const; diff --git a/Telegram/SourceFiles/data/notify/data_notify_settings.cpp b/Telegram/SourceFiles/data/notify/data_notify_settings.cpp index b387b599b..49ea4a973 100644 --- a/Telegram/SourceFiles/data/notify/data_notify_settings.cpp +++ b/Telegram/SourceFiles/data/notify/data_notify_settings.cpp @@ -33,8 +33,8 @@ constexpr auto kMaxNotifyCheckDelay = 24 * 3600 * crl::time(1000); } // namespace NotifySettings::NotifySettings(not_null owner) -: _owner(owner) -, _unmuteByFinishedTimer([=] { unmuteByFinished(); }) { + : _owner(owner) + , _unmuteByFinishedTimer([=] { unmuteByFinished(); }) { } void NotifySettings::request(not_null peer) { @@ -42,7 +42,7 @@ void NotifySettings::request(not_null peer) { peer->session().api().requestNotifySettings( MTP_inputNotifyPeer(peer->input)); } - if (defaultNotifySettings(peer).settingsUnknown()) { + if (defaultSettings(peer).settingsUnknown()) { peer->session().api().requestNotifySettings(peer->isUser() ? MTP_inputNotifyUsers() : (peer->isChat() || peer->isMegagroup()) @@ -54,49 +54,15 @@ void NotifySettings::request(not_null peer) { void NotifySettings::apply( const MTPNotifyPeer ¬ifyPeer, const MTPPeerNotifySettings &settings) { - const auto goodForUpdate = [&]( - not_null peer, - const PeerNotifySettings &settings) { - return !peer->notifySettingsUnknown() - && ((!peer->notifyMuteUntil() && settings.muteUntil()) - || (!peer->notifySilentPosts() && settings.silentPosts()) - || (!peer->notifySound() && settings.sound())); + const auto set = [&](DefaultNotify type) { + if (defaultValue(type).settings.change(settings)) { + updateLocal(type); + } }; - switch (notifyPeer.type()) { - case mtpc_notifyUsers: { - if (_defaultUser.change(settings)) { - _defaultUserUpdates.fire({}); - - _owner->enumerateUsers([&](not_null user) { - if (goodForUpdate(user, _defaultUser)) { - updateLocal(user); - } - }); - } - } break; - case mtpc_notifyChats: { - if (_defaultChat.change(settings)) { - _defaultChatUpdates.fire({}); - - _owner->enumerateGroups([&](not_null peer) { - if (goodForUpdate(peer, _defaultChat)) { - updateLocal(peer); - } - }); - } - } break; - case mtpc_notifyBroadcasts: { - if (_defaultBroadcast.change(settings)) { - _defaultBroadcastUpdates.fire({}); - - _owner->enumerateChannels([&](not_null channel) { - if (goodForUpdate(channel, _defaultBroadcast)) { - updateLocal(channel); - } - }); - } - } break; + case mtpc_notifyUsers: set(DefaultNotify::User); break; + case mtpc_notifyChats: set(DefaultNotify::Group); break; + case mtpc_notifyBroadcasts: set(DefaultNotify::Broadcast); break; case mtpc_notifyPeer: { const auto &data = notifyPeer.c_notifyPeer(); if (const auto peer = _owner->peerLoaded(peerFromMTP(data.vpeer()))) { @@ -134,13 +100,44 @@ void NotifySettings::resetToDefault(not_null peer) { } } -const PeerNotifySettings &NotifySettings::defaultNotifySettings( +auto NotifySettings::defaultValue(DefaultNotify type) +-> DefaultValue & { + const auto index = static_cast(type); + Assert(index >= 0 && index < base::array_size(_defaultValues)); + return _defaultValues[index]; +} + +auto NotifySettings::defaultValue(DefaultNotify type) const +-> const DefaultValue & { + const auto index = static_cast(type); + Assert(index >= 0 && index < base::array_size(_defaultValues)); + return _defaultValues[index]; +} + +const PeerNotifySettings &NotifySettings::defaultSettings( not_null peer) const { - return peer->isUser() - ? _defaultUser + return defaultSettings(peer->isUser() + ? DefaultNotify::User : (peer->isChat() || peer->isMegagroup()) - ? _defaultChat - : _defaultBroadcast; + ? DefaultNotify::Group + : DefaultNotify::Broadcast); +} + +const PeerNotifySettings &NotifySettings::defaultSettings( + DefaultNotify type) const { + return defaultValue(type).settings; +} + +void NotifySettings::defaultUpdate( + DefaultNotify type, + std::optional muteForSeconds, + std::optional silentPosts, + std::optional sound) { + auto &settings = defaultValue(type).settings; + if (settings.change(muteForSeconds, silentPosts, sound)) { + updateLocal(type); + _owner->session().api().updateDefaultNotifySettingsDelayed(type); + } } void NotifySettings::updateLocal(not_null peer) { @@ -193,6 +190,32 @@ void NotifySettings::updateLocal(not_null peer) { } } +void NotifySettings::updateLocal(DefaultNotify type) { + defaultValue(type).updates.fire({}); + + const auto goodForUpdate = [&]( + not_null peer, + const PeerNotifySettings &settings) { + return !peer->notifySettingsUnknown() + && ((!peer->notifyMuteUntil() && settings.muteUntil()) + || (!peer->notifySilentPosts() && settings.silentPosts()) + || (!peer->notifySound() && settings.sound())); + }; + + const auto callback = [&](not_null peer) { + if (goodForUpdate(peer, defaultSettings(type))) { + updateLocal(peer); + } + }; + switch (type) { + case DefaultNotify::User: _owner->enumerateUsers(callback); break; + case DefaultNotify::Group: _owner->enumerateGroups(callback); break; + case DefaultNotify::Broadcast: + _owner->enumerateBroadcasts(callback); + break; + } +} + std::shared_ptr NotifySettings::lookupRingtone( DocumentId id) const { if (!id) { @@ -252,7 +275,7 @@ bool NotifySettings::isMuted( if (const auto until = peer->notifyMuteUntil()) { return resultFromUntil(*until); } - const auto &settings = defaultNotifySettings(peer); + const auto &settings = defaultSettings(peer); if (const auto until = settings.muteUntil()) { return resultFromUntil(*until); } @@ -267,7 +290,7 @@ bool NotifySettings::silentPosts(not_null peer) const { if (const auto silent = peer->notifySilentPosts()) { return *silent; } - const auto &settings = defaultNotifySettings(peer); + const auto &settings = defaultSettings(peer); if (const auto silent = settings.silentPosts()) { return *silent; } @@ -278,7 +301,7 @@ NotifySound NotifySettings::sound(not_null peer) const { if (const auto sound = peer->notifySound()) { return *sound; } - const auto &settings = defaultNotifySettings(peer); + const auto &settings = defaultSettings(peer); if (const auto sound = settings.sound()) { return *sound; } @@ -291,7 +314,7 @@ bool NotifySettings::muteUnknown(not_null peer) const { } else if (const auto nonDefault = peer->notifyMuteUntil()) { return false; } - return defaultNotifySettings(peer).settingsUnknown(); + return defaultSettings(peer).settingsUnknown(); } bool NotifySettings::silentPostsUnknown( @@ -301,7 +324,7 @@ bool NotifySettings::silentPostsUnknown( } else if (const auto nonDefault = peer->notifySilentPosts()) { return false; } - return defaultNotifySettings(peer).settingsUnknown(); + return defaultSettings(peer).settingsUnknown(); } bool NotifySettings::soundUnknown( @@ -311,7 +334,7 @@ bool NotifySettings::soundUnknown( } else if (const auto nonDefault = peer->notifySound()) { return false; } - return defaultNotifySettings(peer).settingsUnknown(); + return defaultSettings(peer).settingsUnknown(); } bool NotifySettings::settingsUnknown(not_null peer) const { @@ -320,25 +343,17 @@ bool NotifySettings::settingsUnknown(not_null peer) const { || soundUnknown(peer); } -rpl::producer<> NotifySettings::defaultUserNotifyUpdates() const { - return _defaultUserUpdates.events(); +rpl::producer<> NotifySettings::defaultUpdates(DefaultNotify type) const { + return defaultValue(type).updates.events(); } -rpl::producer<> NotifySettings::defaultChatNotifyUpdates() const { - return _defaultChatUpdates.events(); -} - -rpl::producer<> NotifySettings::defaultBroadcastNotifyUpdates() const { - return _defaultBroadcastUpdates.events(); -} - -rpl::producer<> NotifySettings::defaultNotifyUpdates( +rpl::producer<> NotifySettings::defaultUpdates( not_null peer) const { - return peer->isUser() - ? defaultUserNotifyUpdates() + return defaultUpdates(peer->isUser() + ? DefaultNotify::User : (peer->isChat() || peer->isMegagroup()) - ? defaultChatNotifyUpdates() - : defaultBroadcastNotifyUpdates(); + ? DefaultNotify::Group + : DefaultNotify::Broadcast); } } // namespace Data diff --git a/Telegram/SourceFiles/data/notify/data_notify_settings.h b/Telegram/SourceFiles/data/notify/data_notify_settings.h index e174b3a2c..3f2dc1068 100644 --- a/Telegram/SourceFiles/data/notify/data_notify_settings.h +++ b/Telegram/SourceFiles/data/notify/data_notify_settings.h @@ -18,6 +18,12 @@ namespace Data { class DocumentMedia; class Session; +enum class DefaultNotify { + User, + Group, + Broadcast, +}; + class NotifySettings final { public: NotifySettings(not_null owner); @@ -35,12 +41,19 @@ public: std::shared_ptr lookupRingtone(DocumentId id) const; - [[nodiscard]] rpl::producer<> defaultUserNotifyUpdates() const; - [[nodiscard]] rpl::producer<> defaultChatNotifyUpdates() const; - [[nodiscard]] rpl::producer<> defaultBroadcastNotifyUpdates() const; - [[nodiscard]] rpl::producer<> defaultNotifyUpdates( + [[nodiscard]] rpl::producer<> defaultUpdates(DefaultNotify type) const; + [[nodiscard]] rpl::producer<> defaultUpdates( not_null peer) const; + [[nodiscard]] const PeerNotifySettings &defaultSettings( + DefaultNotify type) const; + + void defaultUpdate( + DefaultNotify type, + std::optional muteForSeconds, + std::optional silentPosts = std::nullopt, + std::optional sound = std::nullopt); + [[nodiscard]] bool isMuted(not_null peer) const; [[nodiscard]] bool silentPosts(not_null peer) const; [[nodiscard]] NotifySound sound(not_null peer) const; @@ -50,26 +63,29 @@ public: [[nodiscard]] bool soundUnknown(not_null peer) const; private: + struct DefaultValue { + PeerNotifySettings settings; + rpl::event_stream<> updates; + }; + [[nodiscard]] bool isMuted( not_null peer, crl::time *changesIn) const; - [[nodiscard]] const PeerNotifySettings &defaultNotifySettings( + [[nodiscard]] DefaultValue &defaultValue(DefaultNotify type); + [[nodiscard]] const DefaultValue &defaultValue(DefaultNotify type) const; + [[nodiscard]] const PeerNotifySettings &defaultSettings( not_null peer) const; [[nodiscard]] bool settingsUnknown(not_null peer) const; void unmuteByFinished(); void unmuteByFinishedDelayed(crl::time delay); void updateLocal(not_null peer); + void updateLocal(DefaultNotify type); const not_null _owner; - PeerNotifySettings _defaultUser; - PeerNotifySettings _defaultChat; - PeerNotifySettings _defaultBroadcast; - rpl::event_stream<> _defaultUserUpdates; - rpl::event_stream<> _defaultChatUpdates; - rpl::event_stream<> _defaultBroadcastUpdates; + DefaultValue _defaultValues[3]; std::unordered_set> _mutedPeers; base::Timer _unmuteByFinishedTimer; diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp index 8df631fec..64251a8c3 100644 --- a/Telegram/SourceFiles/history/history_widget.cpp +++ b/Telegram/SourceFiles/history/history_widget.cpp @@ -793,10 +793,11 @@ HistoryWidget::HistoryWidget( } }, lifetime()); + using Type = Data::DefaultNotify; rpl::merge( - session().data().notifySettings().defaultUserNotifyUpdates(), - session().data().notifySettings().defaultChatNotifyUpdates(), - session().data().notifySettings().defaultBroadcastNotifyUpdates() + session().data().notifySettings().defaultUpdates(Type::User), + session().data().notifySettings().defaultUpdates(Type::Group), + session().data().notifySettings().defaultUpdates(Type::Broadcast) ) | rpl::start_with_next([=] { updateNotifyControls(); }, lifetime()); diff --git a/Telegram/SourceFiles/info/profile/info_profile_values.cpp b/Telegram/SourceFiles/info/profile/info_profile_values.cpp index 814a98ff4..e22b9585e 100644 --- a/Telegram/SourceFiles/info/profile/info_profile_values.cpp +++ b/Telegram/SourceFiles/info/profile/info_profile_values.cpp @@ -172,7 +172,7 @@ rpl::producer NotificationsEnabledValue(not_null peer) { peer, UpdateFlag::Notifications ) | rpl::to_empty, - peer->owner().notifySettings().defaultNotifyUpdates(peer) + peer->owner().notifySettings().defaultUpdates(peer) ) | rpl::map([=] { return !peer->owner().notifySettings().isMuted(peer); }) | rpl::distinct_until_changed(); diff --git a/Telegram/SourceFiles/menu/menu_mute.cpp b/Telegram/SourceFiles/menu/menu_mute.cpp index 49c41cee8..232a31fd6 100644 --- a/Telegram/SourceFiles/menu/menu_mute.cpp +++ b/Telegram/SourceFiles/menu/menu_mute.cpp @@ -231,7 +231,9 @@ void FillMuteMenu( menu->addAction( tr::lng_mute_menu_sound_select(tr::now), - [=, show = args.show] { show->showBox(Box(RingtonesBox, peer)); }, + [=, show = args.show] { + show->showBox(Box(PeerRingtonesBox, peer)); + }, &st::menuIconSoundSelect); const auto soundIsNone = peer->owner().notifySettings().sound(peer).none; diff --git a/Telegram/SourceFiles/settings/settings_notifications.cpp b/Telegram/SourceFiles/settings/settings_notifications.cpp index a2ff8cc8c..861393915 100644 --- a/Telegram/SourceFiles/settings/settings_notifications.cpp +++ b/Telegram/SourceFiles/settings/settings_notifications.cpp @@ -30,6 +30,11 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "main/main_account.h" #include "main/main_domain.h" #include "api/api_authorizations.h" +#include "api/api_ringtones.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 "facades.h" #include "styles/style_settings.h" @@ -824,12 +829,6 @@ void SetupNotificationsContent( { &st::settingsIconNotifications, kIconRed }, desktopToggles->events_starting_with(settings.desktopNotify())); - const auto soundToggles = container->lifetime( - ).make_state>(); - const auto sound = addCheckbox( - tr::lng_settings_sound_notify(), - { &st::settingsIconSound, kIconLightBlue }, - soundToggles->events_starting_with(settings.soundNotify())); const auto flashbounceToggles = container->lifetime( ).make_state>(); const auto flashbounce = addCheckbox( @@ -842,6 +841,42 @@ void SetupNotificationsContent( flashbounceToggles->events_starting_with( settings.flashBounceNotify())); + const auto soundLabel = container->lifetime( + ).make_state>(); + const auto soundValue = [=] { + const auto owner = &controller->session().data(); + const auto &settings = owner->notifySettings().defaultSettings( + Data::DefaultNotify::User); + return !Core::App().settings().soundNotify() + ? Data::NotifySound{ .none = true } + : settings.sound().value_or(Data::NotifySound()); + }; + const auto label = [=] { + const auto now = soundValue(); + const auto owner = &controller->session().data(); + return now.none + ? tr::lng_settings_sound_notify_off(tr::now) + : !now.id + ? tr::lng_ringtones_box_default(tr::now) + : ExtractRingtoneName(owner->document(now.id)); + }; + controller->session().data().notifySettings().defaultUpdates( + Data::DefaultNotify::User + ) | rpl::start_with_next([=] { + soundLabel->fire(label()); + }, container->lifetime()); + controller->session().api().ringtones().listUpdates( + ) | rpl::start_with_next([=] { + soundLabel->fire(label()); + }, container->lifetime()); + + const auto sound = AddButtonWithLabel( + container, + tr::lng_settings_sound_notify(), + soundLabel->events_starting_with(label()), + st::settingsButton, + { &st::settingsIconSound, kIconLightBlue }); + AddSkip(container); const auto checkboxes = SetupNotifyViewOptions( @@ -1012,13 +1047,24 @@ void SetupNotificationsContent( changed(Change::ViewParams); }, preview->lifetime()); - sound->toggledChanges( - ) | rpl::filter([](bool checked) { - return (checked != Core::App().settings().soundNotify()); - }) | rpl::start_with_next([=](bool checked) { - Core::App().settings().setSoundNotify(checked); - changed(Change::SoundEnabled); - }, sound->lifetime()); + sound->setClickedCallback([=] { + controller->show(Box(RingtonesBox, session, soundValue(), [=]( + Data::NotifySound sound) { + Core::App().settings().setSoundNotify(!sound.none); + if (!sound.none) { + using Type = Data::DefaultNotify; + const auto owner = &controller->session().data(); + auto &settings = owner->notifySettings(); + const auto updateType = [&](Type type) { + settings.defaultUpdate(type, {}, {}, sound); + }; + updateType(Type::User); + updateType(Type::Group); + updateType(Type::Broadcast); + } + changed(Change::SoundEnabled); + })); + }); flashbounce->toggledChanges( ) | rpl::filter([](bool checked) { @@ -1057,7 +1103,7 @@ void SetupNotificationsContent( } else if (change == Change::ViewParams) { // } else if (change == Change::SoundEnabled) { - soundToggles->fire(Core::App().settings().soundNotify()); + soundLabel->fire(label()); } else if (change == Change::FlashBounceEnabled) { flashbounceToggles->fire( Core::App().settings().flashBounceNotify());