From 997913be256a5b0c6e057f1ecf95259d12467186 Mon Sep 17 00:00:00 2001 From: John Preston Date: Fri, 19 Jun 2020 16:59:31 +0400 Subject: [PATCH] One Window::Notifications system for all sessions. --- Telegram/SourceFiles/apiwrap.cpp | 2 +- Telegram/SourceFiles/core/application.cpp | 6 + Telegram/SourceFiles/core/application.h | 13 ++ Telegram/SourceFiles/data/data_histories.cpp | 2 +- Telegram/SourceFiles/data/data_session.cpp | 6 +- .../dialogs/dialogs_inner_widget.cpp | 3 +- Telegram/SourceFiles/history/history.cpp | 7 +- .../history/history_inner_widget.cpp | 2 +- .../SourceFiles/history/history_service.cpp | 3 +- .../SourceFiles/history/history_widget.cpp | 2 +- Telegram/SourceFiles/main/main_domain.cpp | 82 ++++---- Telegram/SourceFiles/main/main_domain.h | 19 +- Telegram/SourceFiles/main/main_session.cpp | 7 - Telegram/SourceFiles/main/main_session.h | 9 +- Telegram/SourceFiles/mainwindow.cpp | 2 +- .../linux/notifications_manager_linux.cpp | 104 +++++++--- .../linux/notifications_manager_linux.h | 23 ++- .../platform/mac/notifications_manager_mac.h | 1 + .../platform/mac/notifications_manager_mac.mm | 9 + .../win/notifications_manager_win.cpp | 135 +++++++++---- .../platform/win/notifications_manager_win.h | 12 +- .../SourceFiles/settings/settings_codes.cpp | 7 +- .../settings/settings_notifications.cpp | 14 +- .../SourceFiles/storage/storage_domain.cpp | 26 ++- .../window/notifications_manager.cpp | 185 ++++++++++++------ .../window/notifications_manager.h | 83 ++++---- .../window/notifications_manager_default.cpp | 88 +++++++-- .../window/notifications_manager_default.h | 23 ++- 28 files changed, 578 insertions(+), 297 deletions(-) diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp index d75e20d3a..0a0df1e1f 100644 --- a/Telegram/SourceFiles/apiwrap.cpp +++ b/Telegram/SourceFiles/apiwrap.cpp @@ -2601,7 +2601,7 @@ void ApiWrap::applyNotifySettings( } } break; } - _session->notifications().checkDelayed(); + Core::App().notifications().checkDelayed(); } void ApiWrap::gotStickerSet(uint64 setId, const MTPmessages_StickerSet &result) { diff --git a/Telegram/SourceFiles/core/application.cpp b/Telegram/SourceFiles/core/application.cpp index 2a959d40d..1e5f90aa2 100644 --- a/Telegram/SourceFiles/core/application.cpp +++ b/Telegram/SourceFiles/core/application.cpp @@ -99,6 +99,7 @@ Application::Application(not_null launcher) , _fallbackProductionConfig( std::make_unique(MTP::Environment::Production)) , _domain(std::make_unique(cDataFile())) +, _notifications(std::make_unique()) , _langpack(std::make_unique()) , _langCloudManager(std::make_unique(langpack())) , _emojiKeywords(std::make_unique()) @@ -116,6 +117,11 @@ Application::Application(not_null launcher) _shouldLockAt = 0; }, _lifetime); + passcodeLockChanges( + ) | rpl::start_with_next([=] { + _notifications->updateAll(); + }, _lifetime); + _domain->activeSessionChanges( ) | rpl::start_with_next([=](Main::Session *session) { if (session && !UpdaterDisabled()) { // #TODO multi someSessionValue diff --git a/Telegram/SourceFiles/core/application.h b/Telegram/SourceFiles/core/application.h index 24da238bf..f3abac6c0 100644 --- a/Telegram/SourceFiles/core/application.h +++ b/Telegram/SourceFiles/core/application.h @@ -27,6 +27,12 @@ struct TermsLock; class Controller; } // namespace Window +namespace Window { +namespace Notifications { +class System; +} // namespace Notifications +} // namespace Window + namespace ChatHelpers { class EmojiKeywords; } // namespace ChatHelpers @@ -101,6 +107,9 @@ public: [[nodiscard]] Ui::Animations::Manager &animationManager() const { return *_animationsManager; } + [[nodiscard]] Window::Notifications::System ¬ifications() const { + return *_notifications; + } // Windows interface. bool hasActiveWindow(not_null session) const; @@ -294,6 +303,10 @@ private: QPointer _badProxyDisableBox; const std::unique_ptr _audio; + + // Notifications should be destroyed before _audio. + const std::unique_ptr _notifications; + const QImage _logo; const QImage _logoNoMargin; diff --git a/Telegram/SourceFiles/data/data_histories.cpp b/Telegram/SourceFiles/data/data_histories.cpp index a472fcb37..7345791b1 100644 --- a/Telegram/SourceFiles/data/data_histories.cpp +++ b/Telegram/SourceFiles/data/data_histories.cpp @@ -165,7 +165,7 @@ void Histories::readInboxTill( } }); - history->session().notifications().clearIncomingFromHistory(history); + Core::App().notifications().clearIncomingFromHistory(history); const auto needsRequest = history->readInboxTillNeedsRequest(tillId); if (!needsRequest && !force) { diff --git a/Telegram/SourceFiles/data/data_session.cpp b/Telegram/SourceFiles/data/data_session.cpp index cbc890b0d..59e46d523 100644 --- a/Telegram/SourceFiles/data/data_session.cpp +++ b/Telegram/SourceFiles/data/data_session.cpp @@ -1110,7 +1110,7 @@ void Session::setupUserIsContactViewer() { Session::~Session() { // Optimization: clear notifications before destroying items. - _session->notifications().clearAllFast(); + Core::App().notifications().clearFromSession(_session); clearLocalStorage(); } @@ -1980,7 +1980,7 @@ void Session::updateNotifySettingsLocal(not_null peer) { _mutedPeers.emplace(peer); unmuteByFinishedDelayed(changesIn); if (history) { - _session->notifications().clearIncomingFromHistory(history); + Core::App().notifications().clearIncomingFromHistory(history); } } else { _mutedPeers.erase(peer); @@ -3488,7 +3488,7 @@ void Session::removeChatListEntry(Dialogs::Key key) { } } if (const auto history = key.history()) { - session().notifications().clearFromHistory(history); + Core::App().notifications().clearFromHistory(history); } } diff --git a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp index 61d486522..8c98f29ef 100644 --- a/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp +++ b/Telegram/SourceFiles/dialogs/dialogs_inner_widget.cpp @@ -15,6 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/history.h" #include "history/history_item.h" #include "core/shortcuts.h" +#include "core/application.h" #include "ui/widgets/buttons.h" #include "ui/widgets/popup_menu.h" #include "ui/text/text_utilities.h" @@ -133,7 +134,7 @@ InnerWidget::InnerWidget( subscribe(session().downloaderTaskFinished(), [=] { update(); }); - subscribe(session().notifications().settingsChanged(), [=]( + subscribe(Core::App().notifications().settingsChanged(), [=]( Window::Notifications::ChangeType change) { if (change == Window::Notifications::ChangeType::CountMessages) { // Folder rows change their unread badge with this setting. diff --git a/Telegram/SourceFiles/history/history.cpp b/Telegram/SourceFiles/history/history.cpp index a5adc761d..10b06b58e 100644 --- a/Telegram/SourceFiles/history/history.cpp +++ b/Telegram/SourceFiles/history/history.cpp @@ -43,6 +43,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "ui/image/image.h" #include "ui/text_options.h" #include "core/crash_reports.h" +#include "core/application.h" #include "base/unixtime.h" #include "styles/style_dialogs.h" @@ -671,7 +672,7 @@ void History::destroyMessage(not_null item) { } owner().unregisterMessage(item); - session().notifications().clearFromItem(item); + Core::App().notifications().clearFromItem(item); auto hack = std::unique_ptr(item.get()); const auto i = _messages.find(hack); @@ -1290,7 +1291,7 @@ void History::newItemAdded(not_null item) { owner().notifyUnreadItemAdded(item); const auto stillShow = item->showNotification(); if (stillShow) { - session().notifications().schedule(item); + Core::App().notifications().schedule(item); if (!item->out() && item->unread()) { if (unreadCountKnown()) { setUnreadCount(unreadCount() + 1); @@ -1741,7 +1742,7 @@ void History::inboxRead(MsgId upTo, std::optional stillUnread) { } _firstUnreadView = nullptr; - session().notifications().clearIncomingFromHistory(this); + Core::App().notifications().clearIncomingFromHistory(this); } void History::inboxRead(not_null wasRead) { diff --git a/Telegram/SourceFiles/history/history_inner_widget.cpp b/Telegram/SourceFiles/history/history_inner_widget.cpp index ebdb0169d..33403d634 100644 --- a/Telegram/SourceFiles/history/history_inner_widget.cpp +++ b/Telegram/SourceFiles/history/history_inner_widget.cpp @@ -2057,7 +2057,7 @@ void HistoryInner::checkHistoryActivation() { adjustCurrent(_visibleAreaBottom); if (_history->loadedAtBottom() && _visibleAreaBottom >= height()) { // Clear possible scheduled messages notifications. - session().notifications().clearFromHistory(_history); + Core::App().notifications().clearFromHistory(_history); } if (_curHistory != _history || _history->isEmpty()) { return; diff --git a/Telegram/SourceFiles/history/history_service.cpp b/Telegram/SourceFiles/history/history_service.cpp index 63a2926d0..ac8875ad7 100644 --- a/Telegram/SourceFiles/history/history_service.cpp +++ b/Telegram/SourceFiles/history/history_service.cpp @@ -24,6 +24,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_channel.h" #include "data/data_user.h" #include "data/data_changes.h" +#include "core/application.h" #include "window/notifications_manager.h" #include "window/window_session_controller.h" #include "storage/storage_shared_media.h" @@ -355,7 +356,7 @@ bool HistoryService::updateDependent(bool force) { updateDependentText(); } if (force && gotDependencyItem) { - history()->session().notifications().checkDelayed(); + Core::App().notifications().checkDelayed(); } return (dependent->msg || !dependent->msgId); } diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp index 2d8eb53b9..2dc0c470b 100644 --- a/Telegram/SourceFiles/history/history_widget.cpp +++ b/Telegram/SourceFiles/history/history_widget.cpp @@ -2423,7 +2423,7 @@ void HistoryWidget::unreadMessageAdded(not_null item) { session().data().histories().readInboxOnNewMessage(item); // Also clear possible scheduled messages notifications. - session().notifications().clearFromHistory(_history); + Core::App().notifications().clearFromHistory(_history); } void HistoryWidget::unreadCountUpdated() { diff --git a/Telegram/SourceFiles/main/main_domain.cpp b/Telegram/SourceFiles/main/main_domain.cpp index ebdfed1f0..ce9457fdc 100644 --- a/Telegram/SourceFiles/main/main_domain.cpp +++ b/Telegram/SourceFiles/main/main_domain.cpp @@ -16,6 +16,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "mtproto/mtproto_dc_options.h" #include "storage/storage_domain.h" #include "storage/localstorage.h" +#include "window/notifications_manager.h" #include "facades.h" namespace Main { @@ -47,22 +48,25 @@ Storage::StartResult Domain::start(const QByteArray &passcode) { } void Domain::finish() { - _activeIndex = -1; + _accountToActivate = -1; _active = nullptr; base::take(_accounts); } -void Domain::accountAddedInStorage( - int index, - std::unique_ptr account) { - Expects(account != nullptr); - Expects(!_accounts.contains(index)); +void Domain::accountAddedInStorage(AccountWithIndex accountWithIndex) { + Expects(accountWithIndex.account != nullptr); - if (_accounts.empty()) { - _activeIndex = index; + for (const auto &[index, _] : _accounts) { + if (index == accountWithIndex.index) { + Unexpected("Repeated account index."); + } } - _accounts.emplace(index, std::move(account)); -}; + _accounts.push_back(std::move(accountWithIndex)); +} + +void Domain::activateFromStorage(int index) { + _accountToActivate = index; +} void Domain::resetWithForgottenPasscode() { if (_accounts.empty()) { @@ -78,15 +82,19 @@ void Domain::resetWithForgottenPasscode() { void Domain::activateAfterStarting() { Expects(started()); + auto toActivate = _accounts.front().account.get(); for (const auto &[index, account] : _accounts) { + if (index == _accountToActivate) { + toActivate = account.get(); + } watchSession(account.get()); } - activate(_activeIndex); + activate(toActivate); removePasscodeIfEmpty(); } -const base::flat_map> &Domain::accounts() const { +const std::vector &Domain::accounts() const { return _accounts; } @@ -94,12 +102,6 @@ rpl::producer Domain::activeValue() const { return _active.value(); } -int Domain::activeIndex() const { - Expects(_accounts.contains(_activeIndex)); - - return _activeIndex; -} - Account &Domain::active() const { Expects(!_accounts.empty()); @@ -170,8 +172,8 @@ void Domain::scheduleUpdateUnreadBadge() { })); } -int Domain::add(MTP::Environment environment) { - Expects(_active.current() != nullptr); +not_null Domain::add(MTP::Environment environment) { + Expects(started()); static const auto cloneConfig = [](const MTP::Config &config) { return std::make_unique(config); @@ -193,16 +195,17 @@ int Domain::add(MTP::Environment environment) { : std::make_unique(environment); }(); auto index = 0; - while (_accounts.contains(index)) { + while (ranges::contains(_accounts, index, &AccountWithIndex::index)) { ++index; } - const auto account = _accounts.emplace( - index, - std::make_unique(this, _dataName, index) - ).first->second.get(); + _accounts.push_back(AccountWithIndex{ + .index = index, + .account = std::make_unique(this, _dataName, index) + }); + const auto account = _accounts.back().account.get(); _local->startAdded(account, std::move(config)); watchSession(account); - return index; + return account; } void Domain::watchSession(not_null account) { @@ -237,8 +240,8 @@ void Domain::activateAuthedAccount() { return; } for (auto i = _accounts.begin(); i != _accounts.end(); ++i) { - if (i->second->sessionExists()) { - activate(i->first); + if (i->account->sessionExists()) { + activate(i->account.get()); return; } } @@ -264,12 +267,12 @@ void Domain::removeRedundantAccounts() { const auto was = _accounts.size(); activateAuthedAccount(); for (auto i = _accounts.begin(); i != _accounts.end();) { - if (i->second.get() == _active.current() - || i->second->sessionExists()) { + if (i->account.get() == _active.current() + || i->account->sessionExists()) { ++i; continue; } - checkForLastProductionConfig(i->second.get()); + checkForLastProductionConfig(i->account.get()); i = _accounts.erase(i); } @@ -293,13 +296,20 @@ void Domain::checkForLastProductionConfig( Core::App().refreshFallbackProductionConfig(mtp->config()); } -void Domain::activate(int index) { - Expects(_accounts.contains(index)); +void Domain::activate(not_null account) { + if (_active.current() == account.get()) { + return; + } + const auto i = ranges::find(_accounts, account.get(), []( + const AccountWithIndex &value) { + return value.account.get(); + }); + Assert(i != end(_accounts)); + const auto changed = (_accountToActivate != i->index); - const auto changed = (_activeIndex != index); _activeLifetime.destroy(); - _activeIndex = index; - _active = _accounts.find(index)->second.get(); + _accountToActivate = i->index; + _active = account.get(); _active.current()->sessionValue( ) | rpl::start_to_stream(_activeSessions, _activeLifetime); diff --git a/Telegram/SourceFiles/main/main_domain.h b/Telegram/SourceFiles/main/main_domain.h index 032dcbab8..9d572b123 100644 --- a/Telegram/SourceFiles/main/main_domain.h +++ b/Telegram/SourceFiles/main/main_domain.h @@ -25,6 +25,11 @@ class Session; class Domain final { public: + struct AccountWithIndex { + int index = 0; + std::unique_ptr account; + }; + static constexpr auto kMaxAccounts = 3; explicit Domain(const QString &dataName); @@ -40,11 +45,10 @@ public: } [[nodiscard]] auto accounts() const - -> const base::flat_map> &; + -> const std::vector &; [[nodiscard]] rpl::producer activeValue() const; // Expects(started()); - [[nodiscard]] int activeIndex() const; [[nodiscard]] Account &active() const; [[nodiscard]] rpl::producer> activeChanges() const; @@ -56,11 +60,12 @@ public: [[nodiscard]] rpl::producer<> unreadBadgeChanges() const; void notifyUnreadBadgeChanged(); - [[nodiscard]] int add(MTP::Environment environment); - void activate(int index); + [[nodiscard]] not_null add(MTP::Environment environment); + void activate(not_null account); // Interface for Storage::Domain. - void accountAddedInStorage(int index, std::unique_ptr account); + void accountAddedInStorage(AccountWithIndex accountWithIndex); + void activateFromStorage(int index); private: void activateAfterStarting(); @@ -76,9 +81,9 @@ private: const QString _dataName; const std::unique_ptr _local; - base::flat_map> _accounts; + std::vector _accounts; rpl::variable _active = nullptr; - int _activeIndex = 0; + int _accountToActivate = -1; bool _writeAccountsScheduled = false; rpl::event_stream _activeSessions; diff --git a/Telegram/SourceFiles/main/main_session.cpp b/Telegram/SourceFiles/main/main_session.cpp index ea4f432f2..39599e6d1 100644 --- a/Telegram/SourceFiles/main/main_session.cpp +++ b/Telegram/SourceFiles/main/main_session.cpp @@ -25,7 +25,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "data/data_session.h" #include "data/data_changes.h" #include "data/data_user.h" -#include "window/notifications_manager.h" #include "window/window_session_controller.h" #include "window/themes/window_theme.h" //#include "platform/platform_specific.h" @@ -75,7 +74,6 @@ Session::Session( , _downloader(std::make_unique(_api.get())) , _uploader(std::make_unique(_api.get())) , _storage(std::make_unique()) -, _notifications(std::make_unique(this)) , _changes(std::make_unique(this)) , _data(std::make_unique(this)) , _user(_data->processUser(user)) @@ -85,11 +83,6 @@ Session::Session( , _saveSettingsTimer([=] { saveSettings(); }) { Expects(_settings != nullptr); - Core::App().lockChanges( - ) | rpl::start_with_next([=] { - notifications().updateAll(); - }, _lifetime); - subscribe(Global::RefConnectionTypeChanged(), [=] { _api->refreshTopPromotion(); }); diff --git a/Telegram/SourceFiles/main/main_session.h b/Telegram/SourceFiles/main/main_session.h index 253d293d9..f23f8957d 100644 --- a/Telegram/SourceFiles/main/main_session.h +++ b/Telegram/SourceFiles/main/main_session.h @@ -42,9 +42,6 @@ class Domain; } // namespace Storage namespace Window { -namespace Notifications { -class System; -} // namespace Notifications class SessionController; } // namespace Window @@ -106,9 +103,6 @@ public: [[nodiscard]] Stickers::DicePacks &diceStickersPacks() const { return *_diceStickersPacks; } - [[nodiscard]] Window::Notifications::System ¬ifications() const { - return *_notifications; - } [[nodiscard]] Data::Changes &changes() const { return *_changes; } @@ -168,9 +162,8 @@ private: const std::unique_ptr _downloader; const std::unique_ptr _uploader; const std::unique_ptr _storage; - const std::unique_ptr _notifications; - // _data depends on _downloader / _uploader / _notifications. + // _data depends on _downloader / _uploader. const std::unique_ptr _changes; const std::unique_ptr _data; const not_null _user; diff --git a/Telegram/SourceFiles/mainwindow.cpp b/Telegram/SourceFiles/mainwindow.cpp index 567153b14..215e23cfd 100644 --- a/Telegram/SourceFiles/mainwindow.cpp +++ b/Telegram/SourceFiles/mainwindow.cpp @@ -814,7 +814,7 @@ void MainWindow::toggleDisplayNotifyFromTray() { } account().session().saveSettings(); using Change = Window::Notifications::ChangeType; - auto &changes = account().session().notifications().settingsChanged(); + auto &changes = Core::App().notifications().settingsChanged(); changes.notify(Change::DesktopEnabled); if (soundNotifyChanged) { changes.notify(Change::SoundEnabled); diff --git a/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp b/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp index ddb381bef..5400eb326 100644 --- a/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp @@ -14,6 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "core/application.h" #include "core/core_settings.h" #include "history/history.h" +#include "main/main_session.h" #include "lang/lang_keys.h" #include @@ -201,7 +202,7 @@ QString GetImageKey(const QVersionNumber &specificationVersion) { return QString(); } -} +} // namespace NotificationData::NotificationData( const base::weak_ptr &manager, @@ -210,6 +211,7 @@ NotificationData::NotificationData( const QString &msg, PeerId peerId, MsgId msgId, + UserId selfId, bool hideReplyButton) : _dbusConnection(QDBusConnection::sessionBus()) , _manager(manager) @@ -217,7 +219,8 @@ NotificationData::NotificationData( , _imageKey(GetImageKey(ParseSpecificationVersion( GetServerInformation()))) , _peerId(peerId) -, _msgId(msgId) { +, _msgId(msgId) +, _selfId(selfId) { const auto capabilities = GetCapabilities(); if (capabilities.contains(qsl("body-markup"))) { @@ -374,11 +377,16 @@ void NotificationData::setImage(const QString &imagePath) { _hints[_imageKey] = QVariant::fromValue(imageData); } +NotificationData::NotificationId NotificationData::myId() const { + return { .peerId = _peerId, .msgId = _msgId, .selfId = _selfId }; +} + void NotificationData::notificationClosed(uint id) { if (id == _notificationId) { const auto manager = _manager; + const auto my = myId(); crl::on_main(manager, [=] { - manager->clearNotification(_peerId, _msgId); + manager->clearNotification(my); }); } } @@ -391,13 +399,15 @@ void NotificationData::actionInvoked(uint id, const QString &actionName) { if (actionName == qsl("default") || actionName == qsl("mail-reply-sender")) { const auto manager = _manager; + const auto my = myId(); crl::on_main(manager, [=] { - manager->notificationActivated(_peerId, _msgId); + manager->notificationActivated(my); }); } else if (actionName == qsl("mail-mark-read")) { const auto manager = _manager; + const auto my = myId(); crl::on_main(manager, [=] { - manager->notificationReplied(_peerId, _msgId, {}); + manager->notificationReplied(my, {}); }); } } @@ -405,8 +415,9 @@ void NotificationData::actionInvoked(uint id, const QString &actionName) { void NotificationData::notificationReplied(uint id, const QString &text) { if (id == _notificationId) { const auto manager = _manager; + const auto my = myId(); crl::on_main(manager, [=] { - manager->notificationReplied(_peerId, _msgId, { text, {} }); + manager->notificationReplied(my, { text, {} }); }); } } @@ -530,6 +541,9 @@ void Manager::Private::showNotification( bool hideReplyButton) { if (!Supported()) return; + const auto peerId = peer->id; + const auto selfId = peer->session().userId(); + const auto key = FullPeer{ peerId, selfId }; auto notification = std::make_shared( _manager, title, @@ -537,32 +551,38 @@ void Manager::Private::showNotification( msg, peer->id, msgId, + peer->session().userId(), hideReplyButton); if (!hideNameAndPhoto) { - const auto key = peer->userpicUniqueKey(userpicView); - notification->setImage(_cachedUserpics.get(key, peer, userpicView)); + const auto userpicKey = peer->userpicUniqueKey(userpicView); + notification->setImage( + _cachedUserpics.get(userpicKey, peer, userpicView)); } - auto i = _notifications.find(peer->id); + auto i = _notifications.find(key); if (i != _notifications.cend()) { - auto j = i->find(msgId); - if (j != i->cend()) { - auto oldNotification = j.value(); - i->erase(j); + auto j = i->second.find(msgId); + if (j != i->second.end()) { + auto oldNotification = j->second; + i->second.erase(j); oldNotification->close(); - i = _notifications.find(peer->id); + i = _notifications.find(key); } } if (i == _notifications.cend()) { - i = _notifications.insert(peer->id, QMap()); + i = _notifications.emplace( + key, + base::flat_map()).first; } - _notifications[peer->id].insert(msgId, notification); + i->second.emplace(msgId, notification); if (!notification->show()) { - i = _notifications.find(peer->id); + i = _notifications.find(key); if (i != _notifications.cend()) { - i->remove(msgId); - if (i->isEmpty()) _notifications.erase(i); + i->second.remove(msgId); + if (i->empty()) { + _notifications.erase(i); + } } } } @@ -570,9 +590,8 @@ void Manager::Private::showNotification( void Manager::Private::clearAll() { if (!Supported()) return; - auto temp = base::take(_notifications); - for_const (auto ¬ifications, temp) { - for_const (auto notification, notifications) { + for (const auto &[key, notifications] : base::take(_notifications)) { + for (const auto &[msgId, notification] : notifications) { notification->close(); } } @@ -581,24 +600,43 @@ void Manager::Private::clearAll() { void Manager::Private::clearFromHistory(not_null history) { if (!Supported()) return; - auto i = _notifications.find(history->peer->id); + const auto key = FullPeer{ + history->peer->id, + history->session().userId() + }; + auto i = _notifications.find(key); if (i != _notifications.cend()) { - auto temp = base::take(i.value()); + const auto temp = base::take(i->second); _notifications.erase(i); - for_const (auto notification, temp) { + for (const auto &[msgId, notification] : temp) { notification->close(); } } } -void Manager::Private::clearNotification(PeerId peerId, MsgId msgId) { +void Manager::Private::clearFromSession(not_null session) { if (!Supported()) return; - auto i = _notifications.find(peerId); + const auto selfId = session->userId(); + for (auto i = _notifications.begin(); i != _notifications.end();) { + if (i->first.second == selfId) { + const auto temp = base::take(i->second); + i = _notifications.erase(i); + + for (const auto &[msgId, notification] : temp) { + notification->close(); + } + } + } +} + +void Manager::Private::clearNotification(NotificationId id) { + if (!Supported()) return; + + auto i = _notifications.find(FullPeer{ id.peerId, id.selfId }); if (i != _notifications.cend()) { - i.value().remove(msgId); - if (i.value().isEmpty()) { + if (i->second.remove(id.msgId) && i->second.empty()) { _notifications.erase(i); } } @@ -613,8 +651,8 @@ Manager::Manager(not_null system) , _private(std::make_unique(this, Private::Type::Rounded)) { } -void Manager::clearNotification(PeerId peerId, MsgId msgId) { - _private->clearNotification(peerId, msgId); +void Manager::clearNotification(NotificationId id) { + _private->clearNotification(id); } Manager::~Manager() = default; @@ -646,6 +684,10 @@ void Manager::doClearAllFast() { void Manager::doClearFromHistory(not_null history) { _private->clearFromHistory(history); } + +void Manager::doClearFromSession(not_null session) { + _private->clearFromSession(session); +} #endif // !TDESKTOP_DISABLE_DBUS_INTEGRATION } // namespace Notifications diff --git a/Telegram/SourceFiles/platform/linux/notifications_manager_linux.h b/Telegram/SourceFiles/platform/linux/notifications_manager_linux.h index ff9a68b6f..c24ec30cb 100644 --- a/Telegram/SourceFiles/platform/linux/notifications_manager_linux.h +++ b/Telegram/SourceFiles/platform/linux/notifications_manager_linux.h @@ -24,6 +24,8 @@ class NotificationData : public QObject { Q_OBJECT public: + using NotificationId = Window::Notifications::Manager::NotificationId; + NotificationData( const base::weak_ptr &manager, const QString &title, @@ -31,6 +33,7 @@ public: const QString &msg, PeerId peerId, MsgId msgId, + UserId selfId, bool hideReplyButton); NotificationData(const NotificationData &other) = delete; @@ -50,6 +53,8 @@ public: }; private: + [[nodiscard]] NotificationId myId() const; + QDBusConnection _dbusConnection; base::weak_ptr _manager; @@ -59,9 +64,10 @@ private: QVariantMap _hints; QString _imageKey; - uint _notificationId; - PeerId _peerId; - MsgId _msgId; + uint _notificationId = 0; + PeerId _peerId = 0; + MsgId _msgId = 0; + UserId _selfId = 0; private slots: void notificationClosed(uint id); @@ -84,7 +90,7 @@ class Manager , public base::has_weak_ptr { public: Manager(not_null system); - void clearNotification(PeerId peerId, MsgId msgId); + void clearNotification(NotificationId id); ~Manager(); protected: @@ -99,6 +105,7 @@ protected: bool hideReplyButton) override; void doClearAllFast() override; void doClearFromHistory(not_null history) override; + void doClearFromSession(not_null session) override; private: class Private; @@ -122,13 +129,15 @@ public: bool hideReplyButton); void clearAll(); void clearFromHistory(not_null history); - void clearNotification(PeerId peerId, MsgId msgId); + void clearFromSession(not_null session); + void clearNotification(NotificationId id); ~Private(); private: - using Notifications = QMap>; - Notifications _notifications; + base::flat_map< + FullPeer, + base::flat_map> _notifications; Window::Notifications::CachedUserpics _cachedUserpics; base::weak_ptr _manager; diff --git a/Telegram/SourceFiles/platform/mac/notifications_manager_mac.h b/Telegram/SourceFiles/platform/mac/notifications_manager_mac.h index af0bffb0e..025305745 100644 --- a/Telegram/SourceFiles/platform/mac/notifications_manager_mac.h +++ b/Telegram/SourceFiles/platform/mac/notifications_manager_mac.h @@ -30,6 +30,7 @@ protected: bool hideReplyButton) override; void doClearAllFast() override; void doClearFromHistory(not_null history) override; + void doClearFromSession(not_null session) override; private: class Private; diff --git a/Telegram/SourceFiles/platform/mac/notifications_manager_mac.mm b/Telegram/SourceFiles/platform/mac/notifications_manager_mac.mm index 4bdcc0108..f499cc2b9 100644 --- a/Telegram/SourceFiles/platform/mac/notifications_manager_mac.mm +++ b/Telegram/SourceFiles/platform/mac/notifications_manager_mac.mm @@ -173,6 +173,7 @@ public: bool hideReplyButton); void clearAll(); void clearFromHistory(not_null history); + void clearFromSession(not_null session); void updateDelegate(); ~Private(); @@ -328,6 +329,10 @@ void Manager::Private::clearFromHistory(not_null history) { putClearTask(ClearFromHistory { history->peer->id }); } +void Manager::Private::clearFromSession(not_null session) { + putClearTask(); // #TODO multi +} + void Manager::Private::updateDelegate() { NSUserNotificationCenter *center = [NSUserNotificationCenter defaultUserNotificationCenter]; [center setDelegate:_delegate]; @@ -377,5 +382,9 @@ void Manager::doClearFromHistory(not_null history) { _private->clearFromHistory(history); } +void Manager::doClearFromSession(not_null session) { + _private->clearFromSession(session); +} + } // namespace Notifications } // namespace Platform diff --git a/Telegram/SourceFiles/platform/win/notifications_manager_win.cpp b/Telegram/SourceFiles/platform/win/notifications_manager_win.cpp index a8febba65..d5d9d93c6 100644 --- a/Telegram/SourceFiles/platform/win/notifications_manager_win.cpp +++ b/Telegram/SourceFiles/platform/win/notifications_manager_win.cpp @@ -14,6 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "history/history.h" #include "core/application.h" #include "core/core_settings.h" +#include "main/main_session.h" #include "mainwindow.h" #include @@ -208,13 +209,17 @@ class ToastEventHandler final : public Implements< DesktopToastDismissedEventHandler, DesktopToastFailedEventHandler> { public: + using NotificationId = Manager::NotificationId; + // We keep a weak pointer to a member field of native notifications manager. ToastEventHandler( const std::shared_ptr &guarded, - const PeerId &peer, - MsgId msg) + PeerId peer, + MsgId msg, + UserId selfId) : _peerId(peer) , _msgId(msg) + , _selfId(selfId) , _weak(guarded) { } @@ -227,8 +232,9 @@ public: // DesktopToastActivatedEventHandler IFACEMETHODIMP Invoke(_In_ IToastNotification *sender, _In_ IInspectable* args) { - performOnMainQueue([peerId = _peerId, msgId = _msgId](Manager *manager) { - manager->notificationActivated(peerId, msgId); + const auto my = myId(); + performOnMainQueue([my](Manager *manager) { + manager->notificationActivated(my); }); return S_OK; } @@ -243,8 +249,9 @@ public: case ToastDismissalReason_UserCanceled: case ToastDismissalReason_TimedOut: default: - performOnMainQueue([peerId = _peerId, msgId = _msgId](Manager *manager) { - manager->clearNotification(peerId, msgId); + const auto my = myId(); + performOnMainQueue([my](Manager *manager) { + manager->clearNotification(my); }); break; } @@ -254,8 +261,9 @@ public: // DesktopToastFailedEventHandler IFACEMETHODIMP Invoke(_In_ IToastNotification *sender, _In_ IToastFailedEventArgs *e) { - performOnMainQueue([peerId = _peerId, msgId = _msgId](Manager *manager) { - manager->clearNotification(peerId, msgId); + const auto my = myId(); + performOnMainQueue([my](Manager *manager) { + manager->clearNotification(my); }); return S_OK; } @@ -293,9 +301,14 @@ public: } private: + [[nodiscard]] NotificationId myId() const { + return { .peerId = _peerId, .msgId = _msgId, .selfId = _selfId }; + } + ULONG _refCount = 0; PeerId _peerId = 0; MsgId _msgId = 0; + UserId _selfId = 0; std::weak_ptr _weak; }; @@ -353,9 +366,10 @@ public: bool hideReplyButton); void clearAll(); void clearFromHistory(not_null history); - void beforeNotificationActivated(PeerId peerId, MsgId msgId); - void afterNotificationActivated(PeerId peerId, MsgId msgId); - void clearNotification(PeerId peerId, MsgId msgId); + void clearFromSession(not_null session); + void beforeNotificationActivated(NotificationId id); + void afterNotificationActivated(NotificationId id); + void clearNotification(NotificationId id); ~Private(); @@ -376,7 +390,7 @@ private: ComPtr p; }; - QMap> _notifications; + base::flat_map> _notifications; }; @@ -414,8 +428,8 @@ void Manager::Private::clearAll() { if (!_notifier) return; auto temp = base::take(_notifications); - for_const (auto ¬ifications, temp) { - for_const (auto ¬ification, notifications) { + for (const auto &[key, notifications] : base::take(_notifications)) { + for (const auto &[msgId, notification] : notifications) { _notifier->Hide(notification.p.Get()); } } @@ -424,32 +438,51 @@ void Manager::Private::clearAll() { void Manager::Private::clearFromHistory(not_null history) { if (!_notifier) return; - auto i = _notifications.find(history->peer->id); + auto i = _notifications.find(FullPeer{ + history->peer->id, + history->session().userId() + }); if (i != _notifications.cend()) { - auto temp = base::take(i.value()); + auto temp = base::take(i->second); _notifications.erase(i); - for_const (auto ¬ification, temp) { + for (const auto &[msgId, notification] : temp) { _notifier->Hide(notification.p.Get()); } } } -void Manager::Private::beforeNotificationActivated(PeerId peerId, MsgId msgId) { - clearNotification(peerId, msgId); +void Manager::Private::clearFromSession(not_null session) { + if (!_notifier) return; + + const auto selfId = session->userId(); + for (auto i = _notifications.begin(); i != _notifications.end();) { + if (i->first.selfId == selfId) { + const auto temp = base::take(i->second); + _notifications.erase(i); + + for (const auto &[msgId, notification] : temp) { + _notifier->Hide(notification.p.Get()); + } + } + } } -void Manager::Private::afterNotificationActivated(PeerId peerId, MsgId msgId) { +void Manager::Private::beforeNotificationActivated(NotificationId id) { + clearNotification(id); +} + +void Manager::Private::afterNotificationActivated(NotificationId id) { if (auto window = App::wnd()) { SetForegroundWindow(window->psHwnd()); } } -void Manager::Private::clearNotification(PeerId peerId, MsgId msgId) { - auto i = _notifications.find(peerId); +void Manager::Private::clearNotification(NotificationId id) { + auto i = _notifications.find(FullPeer{ id.peerId, id.selfId }); if (i != _notifications.cend()) { - i.value().remove(msgId); - if (i.value().isEmpty()) { + i->second.remove(id.msgId); + if (i->second.empty()) { _notifications.erase(i); } } @@ -481,10 +514,10 @@ bool Manager::Private::showNotification( hr = SetAudioSilent(toastXml.Get()); if (!SUCCEEDED(hr)) return false; - const auto key = hideNameAndPhoto + const auto userpicKey = hideNameAndPhoto ? InMemoryKey() : peer->userpicUniqueKey(userpicView); - const auto userpicPath = _cachedUserpics.get(key, peer, userpicView); + const auto userpicPath = _cachedUserpics.get(userpicKey, peer, userpicView); const auto userpicPathWide = QDir::toNativeSeparators(userpicPath).toStdWString(); hr = SetImageSrc(userpicPathWide.c_str(), toastXml.Get()); @@ -533,7 +566,11 @@ bool Manager::Private::showNotification( if (!SUCCEEDED(hr)) return false; EventRegistrationToken activatedToken, dismissedToken, failedToken; - ComPtr eventHandler(new ToastEventHandler(_guarded, peer->id, msgId)); + ComPtr eventHandler(new ToastEventHandler( + _guarded, + peer->id, + msgId, + peer->session().userId())); hr = toast->add_Activated(eventHandler.Get(), &activatedToken); if (!SUCCEEDED(hr)) return false; @@ -544,26 +581,34 @@ bool Manager::Private::showNotification( hr = toast->add_Failed(eventHandler.Get(), &failedToken); if (!SUCCEEDED(hr)) return false; - auto i = _notifications.find(peer->id); + const auto key = FullPeer{ + peer->id, + peer->session().userId() + }; + auto i = _notifications.find(key); if (i != _notifications.cend()) { - auto j = i->find(msgId); - if (j != i->cend()) { - ComPtr notify = j->p; - i->erase(j); + auto j = i->second.find(msgId); + if (j != i->second.end()) { + ComPtr notify = j->second.p; + i->second.erase(j); _notifier->Hide(notify.Get()); - i = _notifications.find(peer->id); + i = _notifications.find(key); } } if (i == _notifications.cend()) { - i = _notifications.insert(peer->id, QMap()); + i = _notifications.emplace( + key, + base::flat_map()).first; } hr = _notifier->Show(toast.Get()); if (!SUCCEEDED(hr)) { - i = _notifications.find(peer->id); - if (i != _notifications.cend() && i->isEmpty()) _notifications.erase(i); + i = _notifications.find(key); + if (i != _notifications.cend() && i->second.empty()) { + _notifications.erase(i); + } return false; } - _notifications[peer->id].insert(msgId, toast); + i->second.emplace(msgId, toast); return true; } @@ -576,8 +621,8 @@ bool Manager::init() { return _private->init(); } -void Manager::clearNotification(PeerId peerId, MsgId msgId) { - _private->clearNotification(peerId, msgId); +void Manager::clearNotification(NotificationId id) { + _private->clearNotification(id); } Manager::~Manager() = default; @@ -610,12 +655,16 @@ void Manager::doClearFromHistory(not_null history) { _private->clearFromHistory(history); } -void Manager::onBeforeNotificationActivated(PeerId peerId, MsgId msgId) { - _private->beforeNotificationActivated(peerId, msgId); +void Manager::doClearFromSession(not_null session) { + _private->clearFromSession(session); } -void Manager::onAfterNotificationActivated(PeerId peerId, MsgId msgId) { - _private->afterNotificationActivated(peerId, msgId); +void Manager::onBeforeNotificationActivated(NotificationId id) { + _private->beforeNotificationActivated(id); +} + +void Manager::onAfterNotificationActivated(NotificationId id) { + _private->afterNotificationActivated(id); } #endif // !__MINGW32__ diff --git a/Telegram/SourceFiles/platform/win/notifications_manager_win.h b/Telegram/SourceFiles/platform/win/notifications_manager_win.h index 6068270fd..7aa53b7bc 100644 --- a/Telegram/SourceFiles/platform/win/notifications_manager_win.h +++ b/Telegram/SourceFiles/platform/win/notifications_manager_win.h @@ -13,15 +13,14 @@ namespace Platform { namespace Notifications { #ifndef __MINGW32__ + class Manager : public Window::Notifications::NativeManager { public: Manager(Window::Notifications::System *system); + ~Manager(); bool init(); - - void clearNotification(PeerId peerId, MsgId msgId); - - ~Manager(); + void clearNotification(NotificationId id); protected: void doShowNativeNotification( @@ -35,8 +34,9 @@ protected: bool hideReplyButton) override; void doClearAllFast() override; void doClearFromHistory(not_null history) override; - void onBeforeNotificationActivated(PeerId peerId, MsgId msgId) override; - void onAfterNotificationActivated(PeerId peerId, MsgId msgId) override; + void doClearFromSession(not_null session) override; + void onBeforeNotificationActivated(NotificationId id) override; + void onAfterNotificationActivated(NotificationId id) override; private: class Private; diff --git a/Telegram/SourceFiles/settings/settings_codes.cpp b/Telegram/SourceFiles/settings/settings_codes.cpp index f0a3fc972..bdd22c2e2 100644 --- a/Telegram/SourceFiles/settings/settings_codes.cpp +++ b/Telegram/SourceFiles/settings/settings_codes.cpp @@ -150,11 +150,8 @@ auto GenerateCodes() { SessionController *window) { crl::on_main(&Core::App(), [=] { const auto &list = Core::App().domain().accounts(); - const auto j = list.find(i); - if (j != list.end() && !Core::App().locked()) { - if (&Core::App().activeAccount() != j->second.get()) { - Core::App().domain().activate(i); - } + if (i < list.size()) { + Core::App().domain().activate(list[i].account.get()); } }); }); diff --git a/Telegram/SourceFiles/settings/settings_notifications.cpp b/Telegram/SourceFiles/settings/settings_notifications.cpp index d9390aa57..8e220759a 100644 --- a/Telegram/SourceFiles/settings/settings_notifications.cpp +++ b/Telegram/SourceFiles/settings/settings_notifications.cpp @@ -191,7 +191,7 @@ void NotificationsCount::setCount(int count) { if (count != Core::App().settings().notificationsCount()) { Core::App().settings().setNotificationsCount(count); Core::App().saveSettingsDelayed(); - _controller->session().notifications().settingsChanged().notify( + Core::App().notifications().settingsChanged().notify( ChangeType::MaxCount); } } @@ -355,7 +355,7 @@ void NotificationsCount::setOverCorner(ScreenCorner corner) { _isOverCorner = true; setCursor(style::cur_pointer); Global::SetNotificationsDemoIsShown(true); - _controller->session().notifications().settingsChanged().notify( + Core::App().notifications().settingsChanged().notify( ChangeType::DemoIsShown); } _overCorner = corner; @@ -391,7 +391,7 @@ void NotificationsCount::clearOverCorner() { _isOverCorner = false; setCursor(style::cur_default); Global::SetNotificationsDemoIsShown(false); - _controller->session().notifications().settingsChanged().notify(ChangeType::DemoIsShown); + Core::App().notifications().settingsChanged().notify(ChangeType::DemoIsShown); for_const (const auto &samples, _cornerSamples) { for_const (const auto widget, samples) { @@ -418,7 +418,7 @@ void NotificationsCount::mouseReleaseEvent(QMouseEvent *e) { if (_chosenCorner != Core::App().settings().notificationsCorner()) { Core::App().settings().setNotificationsCorner(_chosenCorner); Core::App().saveSettingsDelayed(); - _controller->session().notifications().settingsChanged().notify( + Core::App().notifications().settingsChanged().notify( ChangeType::Corner); } } @@ -685,7 +685,7 @@ void SetupNotificationsContent( using Change = Window::Notifications::ChangeType; const auto changed = [=](Change change) { Core::App().saveSettingsDelayed(); - session->notifications().settingsChanged().notify(change); + Core::App().notifications().settingsChanged().notify(change); }; desktop->checkedChanges( ) | rpl::filter([](bool checked) { @@ -758,7 +758,7 @@ void SetupNotificationsContent( }, count->lifetime()); base::ObservableViewer( - session->notifications().settingsChanged() + Core::App().notifications().settingsChanged() ) | rpl::start_with_next([=](Change change) { if (change == Change::DesktopEnabled) { desktop->setChecked(Core::App().settings().desktopNotify()); @@ -785,7 +785,7 @@ void SetupNotificationsContent( }) | rpl::start_with_next([=](bool checked) { Core::App().settings().setNativeNotifications(checked); Core::App().saveSettingsDelayed(); - session->notifications().createManager(); + Core::App().notifications().createManager(); if (advancedSlide) { advancedSlide->toggle( diff --git a/Telegram/SourceFiles/storage/storage_domain.cpp b/Telegram/SourceFiles/storage/storage_domain.cpp index 32170246a..c3ac61a36 100644 --- a/Telegram/SourceFiles/storage/storage_domain.cpp +++ b/Telegram/SourceFiles/storage/storage_domain.cpp @@ -87,7 +87,7 @@ void Domain::startWithSingleAccount( generateLocalKey(); account->start(account->prepareToStart(_localKey)); } - _owner->accountAddedInStorage(0, std::move(account)); + _owner->accountAddedInStorage({ .account = std::move(account) }); writeAccounts(); } @@ -170,6 +170,7 @@ Domain::StartModernResult Domain::startModern( auto tried = base::flat_set(); auto users = base::flat_set(); + auto active = 0; for (auto i = 0; i != count; ++i) { auto index = qint32(); info.stream >> index; @@ -184,12 +185,22 @@ Domain::StartModernResult Domain::startModern( const auto userId = account->willHaveUserId(); if (!users.contains(userId) && (userId != 0 || (users.empty() && i + 1 == count))) { + if (users.empty()) { + active = index; + } account->start(std::move(config)); - _owner->accountAddedInStorage(index, std::move(account)); + _owner->accountAddedInStorage({ + .index = index, + .account = std::move(account) + }); users.emplace(userId); } } } + if (!info.stream.atEnd()) { + info.stream >> active; + } + _owner->activateFromStorage(active); Ensures(!users.empty()); return StartModernResult::Success; @@ -208,18 +219,21 @@ void Domain::writeAccounts() { key.writeData(_passcodeKeyEncrypted); const auto &list = _owner->accounts(); - const auto active = _owner->activeIndex(); auto keySize = sizeof(qint32) + sizeof(qint32) * list.size(); + const auto active = &_owner->active(); + auto activeIndex = -1; + EncryptedDescriptor keyData(keySize); keyData.stream << qint32(list.size()); - keyData.stream << qint32(active); for (const auto &[index, account] : list) { - if (index != active) { - keyData.stream << qint32(index); + if (active == account.get()) { + activeIndex = index; } + keyData.stream << qint32(index); } + keyData.stream << qint32(activeIndex); key.writeEncrypted(keyData, _localKey); } diff --git a/Telegram/SourceFiles/window/notifications_manager.cpp b/Telegram/SourceFiles/window/notifications_manager.cpp index e527268e6..87e859304 100644 --- a/Telegram/SourceFiles/window/notifications_manager.cpp +++ b/Telegram/SourceFiles/window/notifications_manager.cpp @@ -24,6 +24,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "mainwindow.h" #include "api/api_updates.h" #include "apiwrap.h" +#include "main/main_account.h" #include "main/main_session.h" #include "main/main_domain.h" #include "facades.h" @@ -47,9 +48,8 @@ constexpr auto kSystemAlertDuration = crl::time(0); } // namespace -System::System(not_null session) -: _session(session) -, _waitTimer([=] { showNext(); }) +System::System() +: _waitTimer([=] { showNext(); }) , _waitForAllGroupedTimer([=] { showGrouped(); }) { createManager(); @@ -73,6 +73,16 @@ void System::createManager() { } } +Main::Session *System::findSession(UserId selfId) const { + for (const auto &[index, account] : Core::App().domain().accounts()) { + if (account->sessionExists() + && account->session().userId() == selfId) { + return &account->session(); + } + } + return nullptr; +} + System::SkipState System::skipNotification( not_null item) const { const auto history = item->history(); @@ -135,19 +145,23 @@ void System::schedule(not_null item) { auto when = ms + delay; if (!skip.silent) { - _whenAlerts[history].insert(when, notifyBy); + _whenAlerts[history].emplace(when, notifyBy); } if (Core::App().settings().desktopNotify() && !Platform::Notifications::SkipToast()) { auto &whenMap = _whenMaps[history]; - if (whenMap.constFind(item->id) == whenMap.cend()) { - whenMap.insert(item->id, when); + if (whenMap.find(item->id) == whenMap.end()) { + whenMap.emplace(item->id, when); } auto &addTo = ready ? _waiters : _settingWaiters; - const auto it = addTo.constFind(history); - if (it == addTo.cend() || it->when > when) { - addTo.insert(history, Waiter(item->id, when, notifyBy)); + const auto it = addTo.find(history); + if (it == addTo.end() || it->second.when > when) { + addTo.emplace(history, Waiter{ + .msg = item->id, + .when = when, + .notifyBy = notifyBy + }); } } if (ready) { @@ -161,7 +175,7 @@ void System::clearAll() { _manager->clearAll(); for (auto i = _whenMaps.cbegin(), e = _whenMaps.cend(); i != e; ++i) { - i.key()->clearNotifications(); + i->first->clearNotifications(); } _whenMaps.clear(); _whenAlerts.clear(); @@ -182,6 +196,35 @@ void System::clearFromHistory(not_null history) { showNext(); } +void System::clearFromSession(not_null session) { + _manager->clearFromSession(session); + + for (auto i = _whenMaps.begin(), e = _whenMaps.end(); i != e;) { + const auto history = i->first; + if (&history->session() == session) { + history->clearNotifications(); + i = _whenMaps.erase(i); + _whenAlerts.remove(history); + _waiters.remove(history); + _settingWaiters.remove(history); + } else { + ++i; + } + } + const auto clearFrom = [&](auto &map) { + for (auto i = map.begin(); i != map.end();) { + if (&i->first->session() == session) { + i = map.erase(i); + } else { + ++i; + } + } + }; + clearFrom(_whenAlerts); + clearFrom(_waiters); + clearFrom(_settingWaiters); +} + void System::clearIncomingFromHistory(not_null history) { _manager->clearFromHistory(history); history->clearIncomingNotifications(); @@ -203,14 +246,14 @@ void System::clearAllFast() { void System::checkDelayed() { for (auto i = _settingWaiters.begin(); i != _settingWaiters.end();) { - const auto history = i.key(); + const auto history = i->first; const auto peer = history->peer; auto loaded = false; auto muted = false; if (!peer->owner().notifyMuteUnknown(peer)) { if (!peer->owner().notifyIsMuted(peer)) { loaded = true; - } else if (const auto from = i.value().notifyBy) { + } else if (const auto from = i->second.notifyBy) { if (!peer->owner().notifyMuteUnknown(from)) { if (!peer->owner().notifyIsMuted(from)) { loaded = true; @@ -225,7 +268,7 @@ void System::checkDelayed() { if (loaded) { const auto fullId = FullMsgId( history->channelId(), - i.value().msg); + i->second.msg); if (const auto item = peer->owner().message(fullId)) { if (!item->notificationReady()) { loaded = false; @@ -236,7 +279,7 @@ void System::checkDelayed() { } if (loaded) { if (!muted) { - _waiters.insert(i.key(), i.value()); + _waiters.emplace(i->first, i->second); } i = _settingWaiters.erase(i); } else { @@ -248,11 +291,13 @@ void System::checkDelayed() { } void System::showGrouped() { - if (const auto lastItem = session().data().message(_lastHistoryItemId)) { - _waitForAllGroupedTimer.cancel(); - _manager->showNotification(lastItem, _lastForwardedCount); - _lastForwardedCount = 0; - _lastHistoryItemId = FullMsgId(); + if (const auto session = findSession(_lastHistorySelfId)) { + if (const auto lastItem = session->data().message(_lastHistoryItemId)) { + _waitForAllGroupedTimer.cancel(); + _manager->showNotification(lastItem, _lastForwardedCount); + _lastForwardedCount = 0; + _lastHistoryItemId = FullMsgId(); + } } } @@ -275,12 +320,12 @@ void System::showNext() { bool alert = false; int32 now = base::unixtime::now(); for (auto i = _whenAlerts.begin(); i != _whenAlerts.end();) { - while (!i.value().isEmpty() && i.value().begin().key() <= ms) { - const auto peer = i.key()->peer; + while (!i->second.empty() && i->second.begin()->first <= ms) { + const auto peer = i->first->peer; const auto peerUnknown = peer->owner().notifyMuteUnknown(peer); const auto peerAlert = !peerUnknown && !peer->owner().notifyIsMuted(peer); - const auto from = i.value().begin().value(); + const auto from = i->second.begin()->second; const auto fromUnknown = (!from || peer->owner().notifyMuteUnknown(from)); const auto fromAlert = !fromUnknown @@ -288,16 +333,16 @@ void System::showNext() { if (peerAlert || fromAlert) { alert = true; } - while (!i.value().isEmpty() - && i.value().begin().key() <= ms + kMinimalAlertDelay) { - i.value().erase(i.value().begin()); + while (!i->second.empty() + && i->second.begin()->first <= ms + kMinimalAlertDelay) { + i->second.erase(i->second.begin()); } } - if (i.value().isEmpty()) { + if (i->second.empty()) { i = _whenAlerts.erase(i); } else { - if (!nextAlert || nextAlert > i.value().begin().key()) { - nextAlert = i.value().begin().key(); + if (!nextAlert || nextAlert > i->second.begin()->first) { + nextAlert = i->second.begin()->first; } ++i; } @@ -320,7 +365,7 @@ void System::showNext() { } } - if (_waiters.isEmpty() || !settings.desktopNotify() || Platform::Notifications::SkipToast()) { + if (_waiters.empty() || !settings.desktopNotify() || Platform::Notifications::SkipToast()) { if (nextAlert) { _waitTimer.callOnce(nextAlert - ms); } @@ -332,8 +377,8 @@ void System::showNext() { HistoryItem *notifyItem = nullptr; History *notifyHistory = nullptr; for (auto i = _waiters.begin(); i != _waiters.end();) { - History *history = i.key(); - if (history->currentNotification() && history->currentNotification()->id != i.value().msg) { + const auto history = i->first; + if (history->currentNotification() && history->currentNotification()->id != i->second.msg) { auto j = _whenMaps.find(history); if (j == _whenMaps.end()) { history->clearNotifications(); @@ -341,10 +386,10 @@ void System::showNext() { continue; } do { - auto k = j.value().constFind(history->currentNotification()->id); - if (k != j.value().cend()) { - i.value().msg = k.key(); - i.value().when = k.value(); + auto k = j->second.find(history->currentNotification()->id); + if (k != j->second.cend()) { + i->second.msg = k->first; + i->second.when = k->second; break; } history->skipNotification(); @@ -355,7 +400,7 @@ void System::showNext() { i = _waiters.erase(i); continue; } - auto when = i.value().when; + auto when = i->second.when; if (!notifyItem || next > when) { next = when; notifyItem = history->currentNotification(); @@ -390,12 +435,15 @@ void System::showNext() { break; } - j.value().remove((groupedItem ? groupedItem : notifyItem)->id); + j->second.remove((groupedItem ? groupedItem : notifyItem)->id); do { - const auto k = j.value().constFind(history->currentNotification()->id); - if (k != j.value().cend()) { + const auto k = j->second.find(history->currentNotification()->id); + if (k != j->second.cend()) { nextNotify = history->currentNotification(); - _waiters.insert(notifyHistory, Waiter(k.key(), k.value(), 0)); + _waiters.emplace(notifyHistory, Waiter{ + .msg = k->first, + .when = k->second + }); break; } history->skipNotification(); @@ -482,8 +530,8 @@ void System::updateAll() { _manager->updateAll(); } -Manager::DisplayOptions Manager::getNotificationOptions(HistoryItem *item) { - const auto hideEverything = Core::App().locked() +Manager::DisplayOptions Manager::GetNotificationOptions(HistoryItem *item) { + const auto hideEverything = Core::App().passcodeLocked() || Global::ScreenIsLocked(); const auto view = Core::App().settings().notifyView(); @@ -499,20 +547,26 @@ Manager::DisplayOptions Manager::getNotificationOptions(HistoryItem *item) { return result; } -void Manager::notificationActivated(PeerId peerId, MsgId msgId) { - onBeforeNotificationActivated(peerId, msgId); - if (auto window = App::wnd()) { - auto history = system()->session().data().history(peerId); - window->showFromTray(); - window->reActivateWindow(); - if (Core::App().locked()) { - window->setInnerFocus(); - system()->clearAll(); - } else { - openNotificationMessage(history, msgId); +void Manager::notificationActivated(NotificationId id) { + onBeforeNotificationActivated(id); + if (const auto session = system()->findSession(id.selfId)) { + if (session->windows().empty()) { + Core::App().domain().activate(&session->account()); + } + if (!session->windows().empty()) { + const auto window = session->windows().front(); + const auto history = session->data().history(id.peerId); + window->widget()->showFromTray(); + window->widget()->reActivateWindow(); + if (Core::App().passcodeLocked()) { + window->widget()->setInnerFocus(); + system()->clearAll(); + } else { + openNotificationMessage(history, id.msgId); + } } } - onAfterNotificationActivated(peerId, msgId); + onAfterNotificationActivated(id); } void Manager::openNotificationMessage( @@ -548,20 +602,29 @@ void Manager::openNotificationMessage( } void Manager::notificationReplied( - PeerId peerId, - MsgId msgId, + NotificationId id, const TextWithTags &reply) { - if (!peerId) return; + if (!id.selfId || !id.peerId) { + return; + } - const auto history = system()->session().data().history(peerId); + const auto session = system()->findSession(id.selfId); + if (!session) { + return; + } + const auto history = session->data().history(id.peerId); auto message = Api::MessageToSend(history); message.textWithTags = reply; - message.action.replyTo = (msgId > 0 && !history->peer->isUser()) ? msgId : 0; + message.action.replyTo = (id.msgId > 0 && !history->peer->isUser()) + ? id.msgId + : 0; message.action.clearDraft = false; history->session().api().sendMessage(std::move(message)); - const auto item = history->owner().message(history->channelId(), msgId); + const auto item = history->owner().message( + history->channelId(), + id.msgId); if (item && item->isUnreadMention() && !item->isUnreadMedia()) { history->session().api().markMediaRead(item); } @@ -570,7 +633,7 @@ void Manager::notificationReplied( void NativeManager::doShowNotification( not_null item, int forwardedCount) { - const auto options = getNotificationOptions(item); + const auto options = GetNotificationOptions(item); const auto peer = item->history()->peer; const auto scheduled = !options.hideNameAndPhoto diff --git a/Telegram/SourceFiles/window/notifications_manager.h b/Telegram/SourceFiles/window/notifications_manager.h index ba9b737ca..22f00f1cc 100644 --- a/Telegram/SourceFiles/window/notifications_manager.h +++ b/Telegram/SourceFiles/window/notifications_manager.h @@ -64,7 +64,10 @@ class Manager; class System final : private base::Subscriber { public: - explicit System(not_null session); + System(); + ~System(); + + [[nodiscard]] Main::Session *findSession(UserId selfId) const; void createManager(); @@ -72,6 +75,7 @@ public: void schedule(not_null item); void clearFromHistory(not_null history); void clearIncomingFromHistory(not_null history); + void clearFromSession(not_null session); void clearFromItem(not_null item); void clearAll(); void clearAllFast(); @@ -81,12 +85,6 @@ public: return _settingsChanged; } - Main::Session &session() const { - return *_session; - } - - ~System(); - private: struct SkipState { enum Value { @@ -97,34 +95,29 @@ private: Value value = Value::Unknown; bool silent = false; }; + struct Waiter { + MsgId msg; + crl::time when; + PeerData *notifyBy = nullptr; + }; - SkipState skipNotification(not_null item) const; + [[nodiscard]] SkipState skipNotification( + not_null item) const; void showNext(); void showGrouped(); void ensureSoundCreated(); - not_null _session; + base::flat_map< + not_null, + base::flat_map> _whenMaps; - QMap> _whenMaps; - - struct Waiter { - Waiter(MsgId msg, crl::time when, PeerData *notifyBy) - : msg(msg) - , when(when) - , notifyBy(notifyBy) { - } - MsgId msg; - crl::time when; - PeerData *notifyBy; - }; - using Waiters = QMap; - Waiters _waiters; - Waiters _settingWaiters; + base::flat_map, Waiter> _waiters; + base::flat_map, Waiter> _settingWaiters; base::Timer _waitTimer; base::Timer _waitForAllGroupedTimer; - QMap> _whenAlerts; + base::flat_map, base::flat_map> _whenAlerts; std::unique_ptr _manager; @@ -133,12 +126,28 @@ private: std::unique_ptr _soundTrack; int _lastForwardedCount = 0; + UserId _lastHistorySelfId = 0; FullMsgId _lastHistoryItemId; }; class Manager { public: + struct NotificationId { + PeerId peerId = 0; + MsgId msgId = 0; + UserId selfId = 0; + }; + struct FullPeer { + PeerId peerId = 0; + UserId selfId = 0; + + friend inline bool operator<(const FullPeer &a, const FullPeer &b) { + return std::tie(a.peerId, a.selfId) + < std::tie(b.peerId, b.selfId); + } + }; + explicit Manager(not_null system) : _system(system) { } @@ -162,19 +171,20 @@ public: void clearFromHistory(not_null history) { doClearFromHistory(history); } + void clearFromSession(not_null session) { + doClearFromSession(session); + } - void notificationActivated(PeerId peerId, MsgId msgId); - void notificationReplied( - PeerId peerId, - MsgId msgId, - const TextWithTags &reply); + void notificationActivated(NotificationId id); + void notificationReplied(NotificationId id, const TextWithTags &reply); struct DisplayOptions { - bool hideNameAndPhoto; - bool hideMessageText; - bool hideReplyButton; + bool hideNameAndPhoto = false; + bool hideMessageText = false; + bool hideReplyButton = false; }; - static DisplayOptions getNotificationOptions(HistoryItem *item); + [[nodiscard]] static DisplayOptions GetNotificationOptions( + HistoryItem *item); virtual ~Manager() = default; @@ -191,9 +201,10 @@ protected: virtual void doClearAllFast() = 0; virtual void doClearFromItem(not_null item) = 0; virtual void doClearFromHistory(not_null history) = 0; - virtual void onBeforeNotificationActivated(PeerId peerId, MsgId msgId) { + virtual void doClearFromSession(not_null session) = 0; + virtual void onBeforeNotificationActivated(NotificationId id) { } - virtual void onAfterNotificationActivated(PeerId peerId, MsgId msgId) { + virtual void onAfterNotificationActivated(NotificationId id) { } private: diff --git a/Telegram/SourceFiles/window/notifications_manager_default.cpp b/Telegram/SourceFiles/window/notifications_manager_default.cpp index c528fe774..ea6d829a7 100644 --- a/Telegram/SourceFiles/window/notifications_manager_default.cpp +++ b/Telegram/SourceFiles/window/notifications_manager_default.cpp @@ -67,11 +67,6 @@ std::unique_ptr Create(System *system) { Manager::Manager(System *system) : Notifications::Manager(system) , _inputCheckTimer([=] { checkLastInput(); }) { - subscribe(system->session().downloaderTaskFinished(), [this] { - for (const auto ¬ification : _notifications) { - notification->updatePeerPhoto(); - } - }); subscribe(system->settingsChanged(), [this](ChangeType change) { settingsChanged(change); }); @@ -212,7 +207,8 @@ void Manager::showNextFromQueue() { auto queued = _queuedNotifications.front(); _queuedNotifications.pop_front(); - auto notification = std::make_unique( + subscribeToSession(&queued.history->session()); + _notifications.push_back(std::make_unique( this, queued.history, queued.peer, @@ -222,8 +218,7 @@ void Manager::showNextFromQueue() { queued.fromScheduled, startPosition, startShift, - shiftDirection); - _notifications.push_back(std::move(notification)); + shiftDirection)); --count; } while (count > 0 && !_queuedNotifications.empty()); @@ -231,6 +226,32 @@ void Manager::showNextFromQueue() { checkLastInput(); } +void Manager::subscribeToSession(not_null session) { + auto i = _subscriptions.find(session); + if (i == _subscriptions.end()) { + i = _subscriptions.emplace(session, base::Subscription()).first; + session->lifetime().add([=] { + _subscriptions.remove(session); + }); + } else if (i->second) { + return; + } + i->second = session->downloaderTaskFinished().add_subscription([=] { + auto found = false; + for (const auto ¬ification : _notifications) { + if (const auto history = notification->maybeHistory()) { + if (&history->session() == session) { + notification->updatePeerPhoto(); + found = true; + } + } + } + if (!found) { + _subscriptions[session].destroy(); + } + }); +} + void Manager::moveWidgets() { auto shift = st::notifyDeltaY; int lastShift = 0, lastShiftCurrent = 0, count = 0; @@ -334,7 +355,7 @@ void Manager::doClearFromHistory(not_null history) { ++i; } } - for_const (auto ¬ification, _notifications) { + for (const auto ¬ification : _notifications) { if (notification->unlinkHistory(history)) { _positionsOutdated = true; } @@ -342,6 +363,22 @@ void Manager::doClearFromHistory(not_null history) { showNextFromQueue(); } +void Manager::doClearFromSession(not_null session) { + for (auto i = _queuedNotifications.begin(); i != _queuedNotifications.cend();) { + if (&i->history->session() == session) { + i = _queuedNotifications.erase(i); + } else { + ++i; + } + } + for (const auto ¬ification : _notifications) { + if (notification->unlinkSession(session)) { + _positionsOutdated = true; + } + } + showNextFromQueue(); +} + void Manager::doClearFromItem(not_null item) { _queuedNotifications.erase(std::remove_if(_queuedNotifications.begin(), _queuedNotifications.end(), [&](auto &queued) { return (queued.item == item); @@ -678,7 +715,7 @@ void Notification::actionsOpacityCallback() { void Notification::updateNotifyDisplay() { if (!_history || (!_item && _forwardedCount < 2)) return; - const auto options = Manager::getNotificationOptions(_item); + const auto options = Manager::GetNotificationOptions(_item); _hideReplyButton = options.hideReplyButton; int32 w = width(), h = height(); @@ -832,7 +869,7 @@ bool Notification::unlinkItem(HistoryItem *deleted) { bool Notification::canReply() const { return !_hideReplyButton && (_item != nullptr) - && !Core::App().locked() + && !Core::App().passcodeLocked() && (Core::App().settings().notifyView() <= dbinvShowPreview); } @@ -901,16 +938,23 @@ void Notification::showReplyField() { void Notification::sendReply() { if (!_history) return; - auto peerId = _history->peer->id; - auto msgId = _item ? _item->id : ShowAtUnreadMsgId; manager()->notificationReplied( - peerId, - msgId, + myId(), _replyArea->getTextWithAppliedMarkdown()); manager()->startAllHiding(); } +Notifications::Manager::NotificationId Notification::myId() const { + if (!_history) { + return {}; + } + const auto selfId = _history->session().userId(); + const auto peerId = _history->peer->id; + const auto msgId = _item ? _item->id : ShowAtUnreadMsgId; + return { .peerId = peerId, .msgId = msgId, .selfId = selfId }; +} + void Notification::changeHeight(int newHeight) { manager()->changeNotificationHeight(this, newHeight); } @@ -925,6 +969,16 @@ bool Notification::unlinkHistory(History *history) { return unlink; } +bool Notification::unlinkSession(not_null session) { + const auto unlink = _history && (&_history->session() == session); + if (unlink) { + hideFast(); + _history = nullptr; + _item = nullptr; + } + return unlink; +} + void Notification::enterEventHook(QEvent *e) { if (!_history) return; manager()->stopAllHiding(); @@ -951,9 +1005,7 @@ void Notification::mousePressEvent(QMouseEvent *e) { unlinkHistoryInManager(); } else { e->ignore(); - auto peerId = _history->peer->id; - auto msgId = _item ? _item->id : ShowAtUnreadMsgId; - manager()->notificationActivated(peerId, msgId); + manager()->notificationActivated(myId()); } } diff --git a/Telegram/SourceFiles/window/notifications_manager_default.h b/Telegram/SourceFiles/window/notifications_manager_default.h index 06e2d5af3..10fb3426c 100644 --- a/Telegram/SourceFiles/window/notifications_manager_default.h +++ b/Telegram/SourceFiles/window/notifications_manager_default.h @@ -38,19 +38,18 @@ class HideAllButton; class Manager; std::unique_ptr Create(System *system); -class Manager : public Notifications::Manager, private base::Subscriber { +class Manager final : public Notifications::Manager, private base::Subscriber { public: Manager(System *system); + ~Manager(); template void enumerateNotifications(Method method) { - for_const (auto ¬ification, _notifications) { + for (const auto ¬ification : _notifications) { method(notification); } } - ~Manager(); - private: friend class internal::Notification; friend class internal::HideAllButton; @@ -58,7 +57,7 @@ private: using Notification = internal::Notification; using HideAllButton = internal::HideAllButton; - QPixmap hiddenUserpicPlaceholder() const; + [[nodiscard]] QPixmap hiddenUserpicPlaceholder() const; void doUpdateAll() override; void doShowNotification( @@ -67,6 +66,7 @@ private: void doClearAll() override; void doClearAllFast() override; void doClearFromHistory(not_null history) override; + void doClearFromSession(not_null session) override; void doClearFromItem(not_null item) override; void showNextFromQueue(); @@ -86,7 +86,12 @@ private: bool hasReplyingNotification() const; + void subscribeToSession(not_null session); + std::vector> _notifications; + base::flat_map< + not_null, + base::Subscription> _subscriptions; std::unique_ptr _hideAll; @@ -181,7 +186,7 @@ protected: }; -class Notification : public Widget { +class Notification final : public Widget { public: Notification( not_null manager, @@ -207,10 +212,14 @@ public: bool isReplying() const { return _replyArea && !isUnlinked(); } + [[nodiscard]] History *maybeHistory() const { + return _history; + } // Called only by Manager. bool unlinkItem(HistoryItem *del); bool unlinkHistory(History *history = nullptr); + bool unlinkSession(not_null session); bool checkLastInput(bool hasReplyingNotifications); protected: @@ -236,6 +245,8 @@ private: void updateGeometry(int x, int y, int width, int height) override; void actionsOpacityCallback(); + [[nodiscard]] Notifications::Manager::NotificationId myId() const; + const not_null _peer; QPixmap _cache;