One Window::Notifications system for all sessions.

This commit is contained in:
John Preston 2020-06-19 16:59:31 +04:00
parent 83538675ce
commit 997913be25
28 changed files with 578 additions and 297 deletions

View file

@ -2601,7 +2601,7 @@ void ApiWrap::applyNotifySettings(
} }
} break; } break;
} }
_session->notifications().checkDelayed(); Core::App().notifications().checkDelayed();
} }
void ApiWrap::gotStickerSet(uint64 setId, const MTPmessages_StickerSet &result) { void ApiWrap::gotStickerSet(uint64 setId, const MTPmessages_StickerSet &result) {

View file

@ -99,6 +99,7 @@ Application::Application(not_null<Launcher*> launcher)
, _fallbackProductionConfig( , _fallbackProductionConfig(
std::make_unique<MTP::Config>(MTP::Environment::Production)) std::make_unique<MTP::Config>(MTP::Environment::Production))
, _domain(std::make_unique<Main::Domain>(cDataFile())) , _domain(std::make_unique<Main::Domain>(cDataFile()))
, _notifications(std::make_unique<Window::Notifications::System>())
, _langpack(std::make_unique<Lang::Instance>()) , _langpack(std::make_unique<Lang::Instance>())
, _langCloudManager(std::make_unique<Lang::CloudManager>(langpack())) , _langCloudManager(std::make_unique<Lang::CloudManager>(langpack()))
, _emojiKeywords(std::make_unique<ChatHelpers::EmojiKeywords>()) , _emojiKeywords(std::make_unique<ChatHelpers::EmojiKeywords>())
@ -116,6 +117,11 @@ Application::Application(not_null<Launcher*> launcher)
_shouldLockAt = 0; _shouldLockAt = 0;
}, _lifetime); }, _lifetime);
passcodeLockChanges(
) | rpl::start_with_next([=] {
_notifications->updateAll();
}, _lifetime);
_domain->activeSessionChanges( _domain->activeSessionChanges(
) | rpl::start_with_next([=](Main::Session *session) { ) | rpl::start_with_next([=](Main::Session *session) {
if (session && !UpdaterDisabled()) { // #TODO multi someSessionValue if (session && !UpdaterDisabled()) { // #TODO multi someSessionValue

View file

@ -27,6 +27,12 @@ struct TermsLock;
class Controller; class Controller;
} // namespace Window } // namespace Window
namespace Window {
namespace Notifications {
class System;
} // namespace Notifications
} // namespace Window
namespace ChatHelpers { namespace ChatHelpers {
class EmojiKeywords; class EmojiKeywords;
} // namespace ChatHelpers } // namespace ChatHelpers
@ -101,6 +107,9 @@ public:
[[nodiscard]] Ui::Animations::Manager &animationManager() const { [[nodiscard]] Ui::Animations::Manager &animationManager() const {
return *_animationsManager; return *_animationsManager;
} }
[[nodiscard]] Window::Notifications::System &notifications() const {
return *_notifications;
}
// Windows interface. // Windows interface.
bool hasActiveWindow(not_null<Main::Session*> session) const; bool hasActiveWindow(not_null<Main::Session*> session) const;
@ -294,6 +303,10 @@ private:
QPointer<Ui::BoxContent> _badProxyDisableBox; QPointer<Ui::BoxContent> _badProxyDisableBox;
const std::unique_ptr<Media::Audio::Instance> _audio; const std::unique_ptr<Media::Audio::Instance> _audio;
// Notifications should be destroyed before _audio.
const std::unique_ptr<Window::Notifications::System> _notifications;
const QImage _logo; const QImage _logo;
const QImage _logoNoMargin; const QImage _logoNoMargin;

View file

@ -165,7 +165,7 @@ void Histories::readInboxTill(
} }
}); });
history->session().notifications().clearIncomingFromHistory(history); Core::App().notifications().clearIncomingFromHistory(history);
const auto needsRequest = history->readInboxTillNeedsRequest(tillId); const auto needsRequest = history->readInboxTillNeedsRequest(tillId);
if (!needsRequest && !force) { if (!needsRequest && !force) {

View file

@ -1110,7 +1110,7 @@ void Session::setupUserIsContactViewer() {
Session::~Session() { Session::~Session() {
// Optimization: clear notifications before destroying items. // Optimization: clear notifications before destroying items.
_session->notifications().clearAllFast(); Core::App().notifications().clearFromSession(_session);
clearLocalStorage(); clearLocalStorage();
} }
@ -1980,7 +1980,7 @@ void Session::updateNotifySettingsLocal(not_null<PeerData*> peer) {
_mutedPeers.emplace(peer); _mutedPeers.emplace(peer);
unmuteByFinishedDelayed(changesIn); unmuteByFinishedDelayed(changesIn);
if (history) { if (history) {
_session->notifications().clearIncomingFromHistory(history); Core::App().notifications().clearIncomingFromHistory(history);
} }
} else { } else {
_mutedPeers.erase(peer); _mutedPeers.erase(peer);
@ -3488,7 +3488,7 @@ void Session::removeChatListEntry(Dialogs::Key key) {
} }
} }
if (const auto history = key.history()) { if (const auto history = key.history()) {
session().notifications().clearFromHistory(history); Core::App().notifications().clearFromHistory(history);
} }
} }

View file

@ -15,6 +15,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/history.h" #include "history/history.h"
#include "history/history_item.h" #include "history/history_item.h"
#include "core/shortcuts.h" #include "core/shortcuts.h"
#include "core/application.h"
#include "ui/widgets/buttons.h" #include "ui/widgets/buttons.h"
#include "ui/widgets/popup_menu.h" #include "ui/widgets/popup_menu.h"
#include "ui/text/text_utilities.h" #include "ui/text/text_utilities.h"
@ -133,7 +134,7 @@ InnerWidget::InnerWidget(
subscribe(session().downloaderTaskFinished(), [=] { update(); }); subscribe(session().downloaderTaskFinished(), [=] { update(); });
subscribe(session().notifications().settingsChanged(), [=]( subscribe(Core::App().notifications().settingsChanged(), [=](
Window::Notifications::ChangeType change) { Window::Notifications::ChangeType change) {
if (change == Window::Notifications::ChangeType::CountMessages) { if (change == Window::Notifications::ChangeType::CountMessages) {
// Folder rows change their unread badge with this setting. // Folder rows change their unread badge with this setting.

View file

@ -43,6 +43,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "ui/image/image.h" #include "ui/image/image.h"
#include "ui/text_options.h" #include "ui/text_options.h"
#include "core/crash_reports.h" #include "core/crash_reports.h"
#include "core/application.h"
#include "base/unixtime.h" #include "base/unixtime.h"
#include "styles/style_dialogs.h" #include "styles/style_dialogs.h"
@ -671,7 +672,7 @@ void History::destroyMessage(not_null<HistoryItem*> item) {
} }
owner().unregisterMessage(item); owner().unregisterMessage(item);
session().notifications().clearFromItem(item); Core::App().notifications().clearFromItem(item);
auto hack = std::unique_ptr<HistoryItem>(item.get()); auto hack = std::unique_ptr<HistoryItem>(item.get());
const auto i = _messages.find(hack); const auto i = _messages.find(hack);
@ -1290,7 +1291,7 @@ void History::newItemAdded(not_null<HistoryItem*> item) {
owner().notifyUnreadItemAdded(item); owner().notifyUnreadItemAdded(item);
const auto stillShow = item->showNotification(); const auto stillShow = item->showNotification();
if (stillShow) { if (stillShow) {
session().notifications().schedule(item); Core::App().notifications().schedule(item);
if (!item->out() && item->unread()) { if (!item->out() && item->unread()) {
if (unreadCountKnown()) { if (unreadCountKnown()) {
setUnreadCount(unreadCount() + 1); setUnreadCount(unreadCount() + 1);
@ -1741,7 +1742,7 @@ void History::inboxRead(MsgId upTo, std::optional<int> stillUnread) {
} }
_firstUnreadView = nullptr; _firstUnreadView = nullptr;
session().notifications().clearIncomingFromHistory(this); Core::App().notifications().clearIncomingFromHistory(this);
} }
void History::inboxRead(not_null<const HistoryItem*> wasRead) { void History::inboxRead(not_null<const HistoryItem*> wasRead) {

View file

@ -2057,7 +2057,7 @@ void HistoryInner::checkHistoryActivation() {
adjustCurrent(_visibleAreaBottom); adjustCurrent(_visibleAreaBottom);
if (_history->loadedAtBottom() && _visibleAreaBottom >= height()) { if (_history->loadedAtBottom() && _visibleAreaBottom >= height()) {
// Clear possible scheduled messages notifications. // Clear possible scheduled messages notifications.
session().notifications().clearFromHistory(_history); Core::App().notifications().clearFromHistory(_history);
} }
if (_curHistory != _history || _history->isEmpty()) { if (_curHistory != _history || _history->isEmpty()) {
return; return;

View file

@ -24,6 +24,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_channel.h" #include "data/data_channel.h"
#include "data/data_user.h" #include "data/data_user.h"
#include "data/data_changes.h" #include "data/data_changes.h"
#include "core/application.h"
#include "window/notifications_manager.h" #include "window/notifications_manager.h"
#include "window/window_session_controller.h" #include "window/window_session_controller.h"
#include "storage/storage_shared_media.h" #include "storage/storage_shared_media.h"
@ -355,7 +356,7 @@ bool HistoryService::updateDependent(bool force) {
updateDependentText(); updateDependentText();
} }
if (force && gotDependencyItem) { if (force && gotDependencyItem) {
history()->session().notifications().checkDelayed(); Core::App().notifications().checkDelayed();
} }
return (dependent->msg || !dependent->msgId); return (dependent->msg || !dependent->msgId);
} }

View file

@ -2423,7 +2423,7 @@ void HistoryWidget::unreadMessageAdded(not_null<HistoryItem*> item) {
session().data().histories().readInboxOnNewMessage(item); session().data().histories().readInboxOnNewMessage(item);
// Also clear possible scheduled messages notifications. // Also clear possible scheduled messages notifications.
session().notifications().clearFromHistory(_history); Core::App().notifications().clearFromHistory(_history);
} }
void HistoryWidget::unreadCountUpdated() { void HistoryWidget::unreadCountUpdated() {

View file

@ -16,6 +16,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "mtproto/mtproto_dc_options.h" #include "mtproto/mtproto_dc_options.h"
#include "storage/storage_domain.h" #include "storage/storage_domain.h"
#include "storage/localstorage.h" #include "storage/localstorage.h"
#include "window/notifications_manager.h"
#include "facades.h" #include "facades.h"
namespace Main { namespace Main {
@ -47,22 +48,25 @@ Storage::StartResult Domain::start(const QByteArray &passcode) {
} }
void Domain::finish() { void Domain::finish() {
_activeIndex = -1; _accountToActivate = -1;
_active = nullptr; _active = nullptr;
base::take(_accounts); base::take(_accounts);
} }
void Domain::accountAddedInStorage( void Domain::accountAddedInStorage(AccountWithIndex accountWithIndex) {
int index, Expects(accountWithIndex.account != nullptr);
std::unique_ptr<Account> account) {
Expects(account != nullptr);
Expects(!_accounts.contains(index));
if (_accounts.empty()) { for (const auto &[index, _] : _accounts) {
_activeIndex = index; if (index == accountWithIndex.index) {
Unexpected("Repeated account index.");
}
}
_accounts.push_back(std::move(accountWithIndex));
}
void Domain::activateFromStorage(int index) {
_accountToActivate = index;
} }
_accounts.emplace(index, std::move(account));
};
void Domain::resetWithForgottenPasscode() { void Domain::resetWithForgottenPasscode() {
if (_accounts.empty()) { if (_accounts.empty()) {
@ -78,15 +82,19 @@ void Domain::resetWithForgottenPasscode() {
void Domain::activateAfterStarting() { void Domain::activateAfterStarting() {
Expects(started()); Expects(started());
auto toActivate = _accounts.front().account.get();
for (const auto &[index, account] : _accounts) { for (const auto &[index, account] : _accounts) {
if (index == _accountToActivate) {
toActivate = account.get();
}
watchSession(account.get()); watchSession(account.get());
} }
activate(_activeIndex); activate(toActivate);
removePasscodeIfEmpty(); removePasscodeIfEmpty();
} }
const base::flat_map<int, std::unique_ptr<Account>> &Domain::accounts() const { const std::vector<Domain::AccountWithIndex> &Domain::accounts() const {
return _accounts; return _accounts;
} }
@ -94,12 +102,6 @@ rpl::producer<Account*> Domain::activeValue() const {
return _active.value(); return _active.value();
} }
int Domain::activeIndex() const {
Expects(_accounts.contains(_activeIndex));
return _activeIndex;
}
Account &Domain::active() const { Account &Domain::active() const {
Expects(!_accounts.empty()); Expects(!_accounts.empty());
@ -170,8 +172,8 @@ void Domain::scheduleUpdateUnreadBadge() {
})); }));
} }
int Domain::add(MTP::Environment environment) { not_null<Main::Account*> Domain::add(MTP::Environment environment) {
Expects(_active.current() != nullptr); Expects(started());
static const auto cloneConfig = [](const MTP::Config &config) { static const auto cloneConfig = [](const MTP::Config &config) {
return std::make_unique<MTP::Config>(config); return std::make_unique<MTP::Config>(config);
@ -193,16 +195,17 @@ int Domain::add(MTP::Environment environment) {
: std::make_unique<MTP::Config>(environment); : std::make_unique<MTP::Config>(environment);
}(); }();
auto index = 0; auto index = 0;
while (_accounts.contains(index)) { while (ranges::contains(_accounts, index, &AccountWithIndex::index)) {
++index; ++index;
} }
const auto account = _accounts.emplace( _accounts.push_back(AccountWithIndex{
index, .index = index,
std::make_unique<Account>(this, _dataName, index) .account = std::make_unique<Account>(this, _dataName, index)
).first->second.get(); });
const auto account = _accounts.back().account.get();
_local->startAdded(account, std::move(config)); _local->startAdded(account, std::move(config));
watchSession(account); watchSession(account);
return index; return account;
} }
void Domain::watchSession(not_null<Account*> account) { void Domain::watchSession(not_null<Account*> account) {
@ -237,8 +240,8 @@ void Domain::activateAuthedAccount() {
return; return;
} }
for (auto i = _accounts.begin(); i != _accounts.end(); ++i) { for (auto i = _accounts.begin(); i != _accounts.end(); ++i) {
if (i->second->sessionExists()) { if (i->account->sessionExists()) {
activate(i->first); activate(i->account.get());
return; return;
} }
} }
@ -264,12 +267,12 @@ void Domain::removeRedundantAccounts() {
const auto was = _accounts.size(); const auto was = _accounts.size();
activateAuthedAccount(); activateAuthedAccount();
for (auto i = _accounts.begin(); i != _accounts.end();) { for (auto i = _accounts.begin(); i != _accounts.end();) {
if (i->second.get() == _active.current() if (i->account.get() == _active.current()
|| i->second->sessionExists()) { || i->account->sessionExists()) {
++i; ++i;
continue; continue;
} }
checkForLastProductionConfig(i->second.get()); checkForLastProductionConfig(i->account.get());
i = _accounts.erase(i); i = _accounts.erase(i);
} }
@ -293,13 +296,20 @@ void Domain::checkForLastProductionConfig(
Core::App().refreshFallbackProductionConfig(mtp->config()); Core::App().refreshFallbackProductionConfig(mtp->config());
} }
void Domain::activate(int index) { void Domain::activate(not_null<Main::Account*> account) {
Expects(_accounts.contains(index)); 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(); _activeLifetime.destroy();
_activeIndex = index; _accountToActivate = i->index;
_active = _accounts.find(index)->second.get(); _active = account.get();
_active.current()->sessionValue( _active.current()->sessionValue(
) | rpl::start_to_stream(_activeSessions, _activeLifetime); ) | rpl::start_to_stream(_activeSessions, _activeLifetime);

View file

@ -25,6 +25,11 @@ class Session;
class Domain final { class Domain final {
public: public:
struct AccountWithIndex {
int index = 0;
std::unique_ptr<Account> account;
};
static constexpr auto kMaxAccounts = 3; static constexpr auto kMaxAccounts = 3;
explicit Domain(const QString &dataName); explicit Domain(const QString &dataName);
@ -40,11 +45,10 @@ public:
} }
[[nodiscard]] auto accounts() const [[nodiscard]] auto accounts() const
-> const base::flat_map<int, std::unique_ptr<Account>> &; -> const std::vector<AccountWithIndex> &;
[[nodiscard]] rpl::producer<Account*> activeValue() const; [[nodiscard]] rpl::producer<Account*> activeValue() const;
// Expects(started()); // Expects(started());
[[nodiscard]] int activeIndex() const;
[[nodiscard]] Account &active() const; [[nodiscard]] Account &active() const;
[[nodiscard]] rpl::producer<not_null<Account*>> activeChanges() const; [[nodiscard]] rpl::producer<not_null<Account*>> activeChanges() const;
@ -56,11 +60,12 @@ public:
[[nodiscard]] rpl::producer<> unreadBadgeChanges() const; [[nodiscard]] rpl::producer<> unreadBadgeChanges() const;
void notifyUnreadBadgeChanged(); void notifyUnreadBadgeChanged();
[[nodiscard]] int add(MTP::Environment environment); [[nodiscard]] not_null<Main::Account*> add(MTP::Environment environment);
void activate(int index); void activate(not_null<Main::Account*> account);
// Interface for Storage::Domain. // Interface for Storage::Domain.
void accountAddedInStorage(int index, std::unique_ptr<Account> account); void accountAddedInStorage(AccountWithIndex accountWithIndex);
void activateFromStorage(int index);
private: private:
void activateAfterStarting(); void activateAfterStarting();
@ -76,9 +81,9 @@ private:
const QString _dataName; const QString _dataName;
const std::unique_ptr<Storage::Domain> _local; const std::unique_ptr<Storage::Domain> _local;
base::flat_map<int, std::unique_ptr<Account>> _accounts; std::vector<AccountWithIndex> _accounts;
rpl::variable<Account*> _active = nullptr; rpl::variable<Account*> _active = nullptr;
int _activeIndex = 0; int _accountToActivate = -1;
bool _writeAccountsScheduled = false; bool _writeAccountsScheduled = false;
rpl::event_stream<Session*> _activeSessions; rpl::event_stream<Session*> _activeSessions;

View file

@ -25,7 +25,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_session.h" #include "data/data_session.h"
#include "data/data_changes.h" #include "data/data_changes.h"
#include "data/data_user.h" #include "data/data_user.h"
#include "window/notifications_manager.h"
#include "window/window_session_controller.h" #include "window/window_session_controller.h"
#include "window/themes/window_theme.h" #include "window/themes/window_theme.h"
//#include "platform/platform_specific.h" //#include "platform/platform_specific.h"
@ -75,7 +74,6 @@ Session::Session(
, _downloader(std::make_unique<Storage::DownloadManagerMtproto>(_api.get())) , _downloader(std::make_unique<Storage::DownloadManagerMtproto>(_api.get()))
, _uploader(std::make_unique<Storage::Uploader>(_api.get())) , _uploader(std::make_unique<Storage::Uploader>(_api.get()))
, _storage(std::make_unique<Storage::Facade>()) , _storage(std::make_unique<Storage::Facade>())
, _notifications(std::make_unique<Window::Notifications::System>(this))
, _changes(std::make_unique<Data::Changes>(this)) , _changes(std::make_unique<Data::Changes>(this))
, _data(std::make_unique<Data::Session>(this)) , _data(std::make_unique<Data::Session>(this))
, _user(_data->processUser(user)) , _user(_data->processUser(user))
@ -85,11 +83,6 @@ Session::Session(
, _saveSettingsTimer([=] { saveSettings(); }) { , _saveSettingsTimer([=] { saveSettings(); }) {
Expects(_settings != nullptr); Expects(_settings != nullptr);
Core::App().lockChanges(
) | rpl::start_with_next([=] {
notifications().updateAll();
}, _lifetime);
subscribe(Global::RefConnectionTypeChanged(), [=] { subscribe(Global::RefConnectionTypeChanged(), [=] {
_api->refreshTopPromotion(); _api->refreshTopPromotion();
}); });

View file

@ -42,9 +42,6 @@ class Domain;
} // namespace Storage } // namespace Storage
namespace Window { namespace Window {
namespace Notifications {
class System;
} // namespace Notifications
class SessionController; class SessionController;
} // namespace Window } // namespace Window
@ -106,9 +103,6 @@ public:
[[nodiscard]] Stickers::DicePacks &diceStickersPacks() const { [[nodiscard]] Stickers::DicePacks &diceStickersPacks() const {
return *_diceStickersPacks; return *_diceStickersPacks;
} }
[[nodiscard]] Window::Notifications::System &notifications() const {
return *_notifications;
}
[[nodiscard]] Data::Changes &changes() const { [[nodiscard]] Data::Changes &changes() const {
return *_changes; return *_changes;
} }
@ -168,9 +162,8 @@ private:
const std::unique_ptr<Storage::DownloadManagerMtproto> _downloader; const std::unique_ptr<Storage::DownloadManagerMtproto> _downloader;
const std::unique_ptr<Storage::Uploader> _uploader; const std::unique_ptr<Storage::Uploader> _uploader;
const std::unique_ptr<Storage::Facade> _storage; const std::unique_ptr<Storage::Facade> _storage;
const std::unique_ptr<Window::Notifications::System> _notifications;
// _data depends on _downloader / _uploader / _notifications. // _data depends on _downloader / _uploader.
const std::unique_ptr<Data::Changes> _changes; const std::unique_ptr<Data::Changes> _changes;
const std::unique_ptr<Data::Session> _data; const std::unique_ptr<Data::Session> _data;
const not_null<UserData*> _user; const not_null<UserData*> _user;

View file

@ -814,7 +814,7 @@ void MainWindow::toggleDisplayNotifyFromTray() {
} }
account().session().saveSettings(); account().session().saveSettings();
using Change = Window::Notifications::ChangeType; using Change = Window::Notifications::ChangeType;
auto &changes = account().session().notifications().settingsChanged(); auto &changes = Core::App().notifications().settingsChanged();
changes.notify(Change::DesktopEnabled); changes.notify(Change::DesktopEnabled);
if (soundNotifyChanged) { if (soundNotifyChanged) {
changes.notify(Change::SoundEnabled); changes.notify(Change::SoundEnabled);

View file

@ -14,6 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "core/application.h" #include "core/application.h"
#include "core/core_settings.h" #include "core/core_settings.h"
#include "history/history.h" #include "history/history.h"
#include "main/main_session.h"
#include "lang/lang_keys.h" #include "lang/lang_keys.h"
#include <QtCore/QVersionNumber> #include <QtCore/QVersionNumber>
@ -201,7 +202,7 @@ QString GetImageKey(const QVersionNumber &specificationVersion) {
return QString(); return QString();
} }
} } // namespace
NotificationData::NotificationData( NotificationData::NotificationData(
const base::weak_ptr<Manager> &manager, const base::weak_ptr<Manager> &manager,
@ -210,6 +211,7 @@ NotificationData::NotificationData(
const QString &msg, const QString &msg,
PeerId peerId, PeerId peerId,
MsgId msgId, MsgId msgId,
UserId selfId,
bool hideReplyButton) bool hideReplyButton)
: _dbusConnection(QDBusConnection::sessionBus()) : _dbusConnection(QDBusConnection::sessionBus())
, _manager(manager) , _manager(manager)
@ -217,7 +219,8 @@ NotificationData::NotificationData(
, _imageKey(GetImageKey(ParseSpecificationVersion( , _imageKey(GetImageKey(ParseSpecificationVersion(
GetServerInformation()))) GetServerInformation())))
, _peerId(peerId) , _peerId(peerId)
, _msgId(msgId) { , _msgId(msgId)
, _selfId(selfId) {
const auto capabilities = GetCapabilities(); const auto capabilities = GetCapabilities();
if (capabilities.contains(qsl("body-markup"))) { if (capabilities.contains(qsl("body-markup"))) {
@ -374,11 +377,16 @@ void NotificationData::setImage(const QString &imagePath) {
_hints[_imageKey] = QVariant::fromValue(imageData); _hints[_imageKey] = QVariant::fromValue(imageData);
} }
NotificationData::NotificationId NotificationData::myId() const {
return { .peerId = _peerId, .msgId = _msgId, .selfId = _selfId };
}
void NotificationData::notificationClosed(uint id) { void NotificationData::notificationClosed(uint id) {
if (id == _notificationId) { if (id == _notificationId) {
const auto manager = _manager; const auto manager = _manager;
const auto my = myId();
crl::on_main(manager, [=] { 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") if (actionName == qsl("default")
|| actionName == qsl("mail-reply-sender")) { || actionName == qsl("mail-reply-sender")) {
const auto manager = _manager; const auto manager = _manager;
const auto my = myId();
crl::on_main(manager, [=] { crl::on_main(manager, [=] {
manager->notificationActivated(_peerId, _msgId); manager->notificationActivated(my);
}); });
} else if (actionName == qsl("mail-mark-read")) { } else if (actionName == qsl("mail-mark-read")) {
const auto manager = _manager; const auto manager = _manager;
const auto my = myId();
crl::on_main(manager, [=] { 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) { void NotificationData::notificationReplied(uint id, const QString &text) {
if (id == _notificationId) { if (id == _notificationId) {
const auto manager = _manager; const auto manager = _manager;
const auto my = myId();
crl::on_main(manager, [=] { crl::on_main(manager, [=] {
manager->notificationReplied(_peerId, _msgId, { text, {} }); manager->notificationReplied(my, { text, {} });
}); });
} }
} }
@ -530,6 +541,9 @@ void Manager::Private::showNotification(
bool hideReplyButton) { bool hideReplyButton) {
if (!Supported()) return; 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<NotificationData>( auto notification = std::make_shared<NotificationData>(
_manager, _manager,
title, title,
@ -537,32 +551,38 @@ void Manager::Private::showNotification(
msg, msg,
peer->id, peer->id,
msgId, msgId,
peer->session().userId(),
hideReplyButton); hideReplyButton);
if (!hideNameAndPhoto) { if (!hideNameAndPhoto) {
const auto key = peer->userpicUniqueKey(userpicView); const auto userpicKey = peer->userpicUniqueKey(userpicView);
notification->setImage(_cachedUserpics.get(key, peer, userpicView)); notification->setImage(
_cachedUserpics.get(userpicKey, peer, userpicView));
} }
auto i = _notifications.find(peer->id); auto i = _notifications.find(key);
if (i != _notifications.cend()) { if (i != _notifications.cend()) {
auto j = i->find(msgId); auto j = i->second.find(msgId);
if (j != i->cend()) { if (j != i->second.end()) {
auto oldNotification = j.value(); auto oldNotification = j->second;
i->erase(j); i->second.erase(j);
oldNotification->close(); oldNotification->close();
i = _notifications.find(peer->id); i = _notifications.find(key);
} }
} }
if (i == _notifications.cend()) { if (i == _notifications.cend()) {
i = _notifications.insert(peer->id, QMap<MsgId, Notification>()); i = _notifications.emplace(
key,
base::flat_map<MsgId, Notification>()).first;
} }
_notifications[peer->id].insert(msgId, notification); i->second.emplace(msgId, notification);
if (!notification->show()) { if (!notification->show()) {
i = _notifications.find(peer->id); i = _notifications.find(key);
if (i != _notifications.cend()) { if (i != _notifications.cend()) {
i->remove(msgId); i->second.remove(msgId);
if (i->isEmpty()) _notifications.erase(i); if (i->empty()) {
_notifications.erase(i);
}
} }
} }
} }
@ -570,9 +590,8 @@ void Manager::Private::showNotification(
void Manager::Private::clearAll() { void Manager::Private::clearAll() {
if (!Supported()) return; if (!Supported()) return;
auto temp = base::take(_notifications); for (const auto &[key, notifications] : base::take(_notifications)) {
for_const (auto &notifications, temp) { for (const auto &[msgId, notification] : notifications) {
for_const (auto notification, notifications) {
notification->close(); notification->close();
} }
} }
@ -581,24 +600,43 @@ void Manager::Private::clearAll() {
void Manager::Private::clearFromHistory(not_null<History*> history) { void Manager::Private::clearFromHistory(not_null<History*> history) {
if (!Supported()) return; 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()) { if (i != _notifications.cend()) {
auto temp = base::take(i.value()); const auto temp = base::take(i->second);
_notifications.erase(i); _notifications.erase(i);
for_const (auto notification, temp) { for (const auto &[msgId, notification] : temp) {
notification->close(); notification->close();
} }
} }
} }
void Manager::Private::clearNotification(PeerId peerId, MsgId msgId) { void Manager::Private::clearFromSession(not_null<Main::Session*> session) {
if (!Supported()) return; 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()) { if (i != _notifications.cend()) {
i.value().remove(msgId); if (i->second.remove(id.msgId) && i->second.empty()) {
if (i.value().isEmpty()) {
_notifications.erase(i); _notifications.erase(i);
} }
} }
@ -613,8 +651,8 @@ Manager::Manager(not_null<Window::Notifications::System*> system)
, _private(std::make_unique<Private>(this, Private::Type::Rounded)) { , _private(std::make_unique<Private>(this, Private::Type::Rounded)) {
} }
void Manager::clearNotification(PeerId peerId, MsgId msgId) { void Manager::clearNotification(NotificationId id) {
_private->clearNotification(peerId, msgId); _private->clearNotification(id);
} }
Manager::~Manager() = default; Manager::~Manager() = default;
@ -646,6 +684,10 @@ void Manager::doClearAllFast() {
void Manager::doClearFromHistory(not_null<History*> history) { void Manager::doClearFromHistory(not_null<History*> history) {
_private->clearFromHistory(history); _private->clearFromHistory(history);
} }
void Manager::doClearFromSession(not_null<Main::Session*> session) {
_private->clearFromSession(session);
}
#endif // !TDESKTOP_DISABLE_DBUS_INTEGRATION #endif // !TDESKTOP_DISABLE_DBUS_INTEGRATION
} // namespace Notifications } // namespace Notifications

View file

@ -24,6 +24,8 @@ class NotificationData : public QObject {
Q_OBJECT Q_OBJECT
public: public:
using NotificationId = Window::Notifications::Manager::NotificationId;
NotificationData( NotificationData(
const base::weak_ptr<Manager> &manager, const base::weak_ptr<Manager> &manager,
const QString &title, const QString &title,
@ -31,6 +33,7 @@ public:
const QString &msg, const QString &msg,
PeerId peerId, PeerId peerId,
MsgId msgId, MsgId msgId,
UserId selfId,
bool hideReplyButton); bool hideReplyButton);
NotificationData(const NotificationData &other) = delete; NotificationData(const NotificationData &other) = delete;
@ -50,6 +53,8 @@ public:
}; };
private: private:
[[nodiscard]] NotificationId myId() const;
QDBusConnection _dbusConnection; QDBusConnection _dbusConnection;
base::weak_ptr<Manager> _manager; base::weak_ptr<Manager> _manager;
@ -59,9 +64,10 @@ private:
QVariantMap _hints; QVariantMap _hints;
QString _imageKey; QString _imageKey;
uint _notificationId; uint _notificationId = 0;
PeerId _peerId; PeerId _peerId = 0;
MsgId _msgId; MsgId _msgId = 0;
UserId _selfId = 0;
private slots: private slots:
void notificationClosed(uint id); void notificationClosed(uint id);
@ -84,7 +90,7 @@ class Manager
, public base::has_weak_ptr { , public base::has_weak_ptr {
public: public:
Manager(not_null<Window::Notifications::System*> system); Manager(not_null<Window::Notifications::System*> system);
void clearNotification(PeerId peerId, MsgId msgId); void clearNotification(NotificationId id);
~Manager(); ~Manager();
protected: protected:
@ -99,6 +105,7 @@ protected:
bool hideReplyButton) override; bool hideReplyButton) override;
void doClearAllFast() override; void doClearAllFast() override;
void doClearFromHistory(not_null<History*> history) override; void doClearFromHistory(not_null<History*> history) override;
void doClearFromSession(not_null<Main::Session*> session) override;
private: private:
class Private; class Private;
@ -122,13 +129,15 @@ public:
bool hideReplyButton); bool hideReplyButton);
void clearAll(); void clearAll();
void clearFromHistory(not_null<History*> history); void clearFromHistory(not_null<History*> history);
void clearNotification(PeerId peerId, MsgId msgId); void clearFromSession(not_null<Main::Session*> session);
void clearNotification(NotificationId id);
~Private(); ~Private();
private: private:
using Notifications = QMap<PeerId, QMap<MsgId, Notification>>; base::flat_map<
Notifications _notifications; FullPeer,
base::flat_map<MsgId, Notification>> _notifications;
Window::Notifications::CachedUserpics _cachedUserpics; Window::Notifications::CachedUserpics _cachedUserpics;
base::weak_ptr<Manager> _manager; base::weak_ptr<Manager> _manager;

View file

@ -30,6 +30,7 @@ protected:
bool hideReplyButton) override; bool hideReplyButton) override;
void doClearAllFast() override; void doClearAllFast() override;
void doClearFromHistory(not_null<History*> history) override; void doClearFromHistory(not_null<History*> history) override;
void doClearFromSession(not_null<Main::Session*> session) override;
private: private:
class Private; class Private;

View file

@ -173,6 +173,7 @@ public:
bool hideReplyButton); bool hideReplyButton);
void clearAll(); void clearAll();
void clearFromHistory(not_null<History*> history); void clearFromHistory(not_null<History*> history);
void clearFromSession(not_null<Main::Session*> session);
void updateDelegate(); void updateDelegate();
~Private(); ~Private();
@ -328,6 +329,10 @@ void Manager::Private::clearFromHistory(not_null<History*> history) {
putClearTask(ClearFromHistory { history->peer->id }); putClearTask(ClearFromHistory { history->peer->id });
} }
void Manager::Private::clearFromSession(not_null<Main::Session*> session) {
putClearTask(); // #TODO multi
}
void Manager::Private::updateDelegate() { void Manager::Private::updateDelegate() {
NSUserNotificationCenter *center = [NSUserNotificationCenter defaultUserNotificationCenter]; NSUserNotificationCenter *center = [NSUserNotificationCenter defaultUserNotificationCenter];
[center setDelegate:_delegate]; [center setDelegate:_delegate];
@ -377,5 +382,9 @@ void Manager::doClearFromHistory(not_null<History*> history) {
_private->clearFromHistory(history); _private->clearFromHistory(history);
} }
void Manager::doClearFromSession(not_null<Main::Session*> session) {
_private->clearFromSession(session);
}
} // namespace Notifications } // namespace Notifications
} // namespace Platform } // namespace Platform

View file

@ -14,6 +14,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "history/history.h" #include "history/history.h"
#include "core/application.h" #include "core/application.h"
#include "core/core_settings.h" #include "core/core_settings.h"
#include "main/main_session.h"
#include "mainwindow.h" #include "mainwindow.h"
#include <Shobjidl.h> #include <Shobjidl.h>
@ -208,13 +209,17 @@ class ToastEventHandler final : public Implements<
DesktopToastDismissedEventHandler, DesktopToastDismissedEventHandler,
DesktopToastFailedEventHandler> { DesktopToastFailedEventHandler> {
public: public:
using NotificationId = Manager::NotificationId;
// We keep a weak pointer to a member field of native notifications manager. // We keep a weak pointer to a member field of native notifications manager.
ToastEventHandler( ToastEventHandler(
const std::shared_ptr<Manager*> &guarded, const std::shared_ptr<Manager*> &guarded,
const PeerId &peer, PeerId peer,
MsgId msg) MsgId msg,
UserId selfId)
: _peerId(peer) : _peerId(peer)
, _msgId(msg) , _msgId(msg)
, _selfId(selfId)
, _weak(guarded) { , _weak(guarded) {
} }
@ -227,8 +232,9 @@ public:
// DesktopToastActivatedEventHandler // DesktopToastActivatedEventHandler
IFACEMETHODIMP Invoke(_In_ IToastNotification *sender, _In_ IInspectable* args) { IFACEMETHODIMP Invoke(_In_ IToastNotification *sender, _In_ IInspectable* args) {
performOnMainQueue([peerId = _peerId, msgId = _msgId](Manager *manager) { const auto my = myId();
manager->notificationActivated(peerId, msgId); performOnMainQueue([my](Manager *manager) {
manager->notificationActivated(my);
}); });
return S_OK; return S_OK;
} }
@ -243,8 +249,9 @@ public:
case ToastDismissalReason_UserCanceled: case ToastDismissalReason_UserCanceled:
case ToastDismissalReason_TimedOut: case ToastDismissalReason_TimedOut:
default: default:
performOnMainQueue([peerId = _peerId, msgId = _msgId](Manager *manager) { const auto my = myId();
manager->clearNotification(peerId, msgId); performOnMainQueue([my](Manager *manager) {
manager->clearNotification(my);
}); });
break; break;
} }
@ -254,8 +261,9 @@ public:
// DesktopToastFailedEventHandler // DesktopToastFailedEventHandler
IFACEMETHODIMP Invoke(_In_ IToastNotification *sender, _In_ IToastFailedEventArgs *e) { IFACEMETHODIMP Invoke(_In_ IToastNotification *sender, _In_ IToastFailedEventArgs *e) {
performOnMainQueue([peerId = _peerId, msgId = _msgId](Manager *manager) { const auto my = myId();
manager->clearNotification(peerId, msgId); performOnMainQueue([my](Manager *manager) {
manager->clearNotification(my);
}); });
return S_OK; return S_OK;
} }
@ -293,9 +301,14 @@ public:
} }
private: private:
[[nodiscard]] NotificationId myId() const {
return { .peerId = _peerId, .msgId = _msgId, .selfId = _selfId };
}
ULONG _refCount = 0; ULONG _refCount = 0;
PeerId _peerId = 0; PeerId _peerId = 0;
MsgId _msgId = 0; MsgId _msgId = 0;
UserId _selfId = 0;
std::weak_ptr<Manager*> _weak; std::weak_ptr<Manager*> _weak;
}; };
@ -353,9 +366,10 @@ public:
bool hideReplyButton); bool hideReplyButton);
void clearAll(); void clearAll();
void clearFromHistory(not_null<History*> history); void clearFromHistory(not_null<History*> history);
void beforeNotificationActivated(PeerId peerId, MsgId msgId); void clearFromSession(not_null<Main::Session*> session);
void afterNotificationActivated(PeerId peerId, MsgId msgId); void beforeNotificationActivated(NotificationId id);
void clearNotification(PeerId peerId, MsgId msgId); void afterNotificationActivated(NotificationId id);
void clearNotification(NotificationId id);
~Private(); ~Private();
@ -376,7 +390,7 @@ private:
ComPtr<IToastNotification> p; ComPtr<IToastNotification> p;
}; };
QMap<PeerId, QMap<MsgId, NotificationPtr>> _notifications; base::flat_map<FullPeer, base::flat_map<MsgId, NotificationPtr>> _notifications;
}; };
@ -414,8 +428,8 @@ void Manager::Private::clearAll() {
if (!_notifier) return; if (!_notifier) return;
auto temp = base::take(_notifications); auto temp = base::take(_notifications);
for_const (auto &notifications, temp) { for (const auto &[key, notifications] : base::take(_notifications)) {
for_const (auto &notification, notifications) { for (const auto &[msgId, notification] : notifications) {
_notifier->Hide(notification.p.Get()); _notifier->Hide(notification.p.Get());
} }
} }
@ -424,32 +438,51 @@ void Manager::Private::clearAll() {
void Manager::Private::clearFromHistory(not_null<History*> history) { void Manager::Private::clearFromHistory(not_null<History*> history) {
if (!_notifier) return; 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()) { if (i != _notifications.cend()) {
auto temp = base::take(i.value()); auto temp = base::take(i->second);
_notifications.erase(i); _notifications.erase(i);
for_const (auto &notification, temp) { for (const auto &[msgId, notification] : temp) {
_notifier->Hide(notification.p.Get()); _notifier->Hide(notification.p.Get());
} }
} }
} }
void Manager::Private::beforeNotificationActivated(PeerId peerId, MsgId msgId) { void Manager::Private::clearFromSession(not_null<Main::Session*> session) {
clearNotification(peerId, msgId); 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()) { if (auto window = App::wnd()) {
SetForegroundWindow(window->psHwnd()); SetForegroundWindow(window->psHwnd());
} }
} }
void Manager::Private::clearNotification(PeerId peerId, MsgId msgId) { void Manager::Private::clearNotification(NotificationId id) {
auto i = _notifications.find(peerId); auto i = _notifications.find(FullPeer{ id.peerId, id.selfId });
if (i != _notifications.cend()) { if (i != _notifications.cend()) {
i.value().remove(msgId); i->second.remove(id.msgId);
if (i.value().isEmpty()) { if (i->second.empty()) {
_notifications.erase(i); _notifications.erase(i);
} }
} }
@ -481,10 +514,10 @@ bool Manager::Private::showNotification(
hr = SetAudioSilent(toastXml.Get()); hr = SetAudioSilent(toastXml.Get());
if (!SUCCEEDED(hr)) return false; if (!SUCCEEDED(hr)) return false;
const auto key = hideNameAndPhoto const auto userpicKey = hideNameAndPhoto
? InMemoryKey() ? InMemoryKey()
: peer->userpicUniqueKey(userpicView); : 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(); const auto userpicPathWide = QDir::toNativeSeparators(userpicPath).toStdWString();
hr = SetImageSrc(userpicPathWide.c_str(), toastXml.Get()); hr = SetImageSrc(userpicPathWide.c_str(), toastXml.Get());
@ -533,7 +566,11 @@ bool Manager::Private::showNotification(
if (!SUCCEEDED(hr)) return false; if (!SUCCEEDED(hr)) return false;
EventRegistrationToken activatedToken, dismissedToken, failedToken; EventRegistrationToken activatedToken, dismissedToken, failedToken;
ComPtr<ToastEventHandler> eventHandler(new ToastEventHandler(_guarded, peer->id, msgId)); ComPtr<ToastEventHandler> eventHandler(new ToastEventHandler(
_guarded,
peer->id,
msgId,
peer->session().userId()));
hr = toast->add_Activated(eventHandler.Get(), &activatedToken); hr = toast->add_Activated(eventHandler.Get(), &activatedToken);
if (!SUCCEEDED(hr)) return false; if (!SUCCEEDED(hr)) return false;
@ -544,26 +581,34 @@ bool Manager::Private::showNotification(
hr = toast->add_Failed(eventHandler.Get(), &failedToken); hr = toast->add_Failed(eventHandler.Get(), &failedToken);
if (!SUCCEEDED(hr)) return false; 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()) { if (i != _notifications.cend()) {
auto j = i->find(msgId); auto j = i->second.find(msgId);
if (j != i->cend()) { if (j != i->second.end()) {
ComPtr<IToastNotification> notify = j->p; ComPtr<IToastNotification> notify = j->second.p;
i->erase(j); i->second.erase(j);
_notifier->Hide(notify.Get()); _notifier->Hide(notify.Get());
i = _notifications.find(peer->id); i = _notifications.find(key);
} }
} }
if (i == _notifications.cend()) { if (i == _notifications.cend()) {
i = _notifications.insert(peer->id, QMap<MsgId, NotificationPtr>()); i = _notifications.emplace(
key,
base::flat_map<MsgId, NotificationPtr>()).first;
} }
hr = _notifier->Show(toast.Get()); hr = _notifier->Show(toast.Get());
if (!SUCCEEDED(hr)) { if (!SUCCEEDED(hr)) {
i = _notifications.find(peer->id); i = _notifications.find(key);
if (i != _notifications.cend() && i->isEmpty()) _notifications.erase(i); if (i != _notifications.cend() && i->second.empty()) {
_notifications.erase(i);
}
return false; return false;
} }
_notifications[peer->id].insert(msgId, toast); i->second.emplace(msgId, toast);
return true; return true;
} }
@ -576,8 +621,8 @@ bool Manager::init() {
return _private->init(); return _private->init();
} }
void Manager::clearNotification(PeerId peerId, MsgId msgId) { void Manager::clearNotification(NotificationId id) {
_private->clearNotification(peerId, msgId); _private->clearNotification(id);
} }
Manager::~Manager() = default; Manager::~Manager() = default;
@ -610,12 +655,16 @@ void Manager::doClearFromHistory(not_null<History*> history) {
_private->clearFromHistory(history); _private->clearFromHistory(history);
} }
void Manager::onBeforeNotificationActivated(PeerId peerId, MsgId msgId) { void Manager::doClearFromSession(not_null<Main::Session*> session) {
_private->beforeNotificationActivated(peerId, msgId); _private->clearFromSession(session);
} }
void Manager::onAfterNotificationActivated(PeerId peerId, MsgId msgId) { void Manager::onBeforeNotificationActivated(NotificationId id) {
_private->afterNotificationActivated(peerId, msgId); _private->beforeNotificationActivated(id);
}
void Manager::onAfterNotificationActivated(NotificationId id) {
_private->afterNotificationActivated(id);
} }
#endif // !__MINGW32__ #endif // !__MINGW32__

View file

@ -13,15 +13,14 @@ namespace Platform {
namespace Notifications { namespace Notifications {
#ifndef __MINGW32__ #ifndef __MINGW32__
class Manager : public Window::Notifications::NativeManager { class Manager : public Window::Notifications::NativeManager {
public: public:
Manager(Window::Notifications::System *system); Manager(Window::Notifications::System *system);
~Manager();
bool init(); bool init();
void clearNotification(NotificationId id);
void clearNotification(PeerId peerId, MsgId msgId);
~Manager();
protected: protected:
void doShowNativeNotification( void doShowNativeNotification(
@ -35,8 +34,9 @@ protected:
bool hideReplyButton) override; bool hideReplyButton) override;
void doClearAllFast() override; void doClearAllFast() override;
void doClearFromHistory(not_null<History*> history) override; void doClearFromHistory(not_null<History*> history) override;
void onBeforeNotificationActivated(PeerId peerId, MsgId msgId) override; void doClearFromSession(not_null<Main::Session*> session) override;
void onAfterNotificationActivated(PeerId peerId, MsgId msgId) override; void onBeforeNotificationActivated(NotificationId id) override;
void onAfterNotificationActivated(NotificationId id) override;
private: private:
class Private; class Private;

View file

@ -150,11 +150,8 @@ auto GenerateCodes() {
SessionController *window) { SessionController *window) {
crl::on_main(&Core::App(), [=] { crl::on_main(&Core::App(), [=] {
const auto &list = Core::App().domain().accounts(); const auto &list = Core::App().domain().accounts();
const auto j = list.find(i); if (i < list.size()) {
if (j != list.end() && !Core::App().locked()) { Core::App().domain().activate(list[i].account.get());
if (&Core::App().activeAccount() != j->second.get()) {
Core::App().domain().activate(i);
}
} }
}); });
}); });

View file

@ -191,7 +191,7 @@ void NotificationsCount::setCount(int count) {
if (count != Core::App().settings().notificationsCount()) { if (count != Core::App().settings().notificationsCount()) {
Core::App().settings().setNotificationsCount(count); Core::App().settings().setNotificationsCount(count);
Core::App().saveSettingsDelayed(); Core::App().saveSettingsDelayed();
_controller->session().notifications().settingsChanged().notify( Core::App().notifications().settingsChanged().notify(
ChangeType::MaxCount); ChangeType::MaxCount);
} }
} }
@ -355,7 +355,7 @@ void NotificationsCount::setOverCorner(ScreenCorner corner) {
_isOverCorner = true; _isOverCorner = true;
setCursor(style::cur_pointer); setCursor(style::cur_pointer);
Global::SetNotificationsDemoIsShown(true); Global::SetNotificationsDemoIsShown(true);
_controller->session().notifications().settingsChanged().notify( Core::App().notifications().settingsChanged().notify(
ChangeType::DemoIsShown); ChangeType::DemoIsShown);
} }
_overCorner = corner; _overCorner = corner;
@ -391,7 +391,7 @@ void NotificationsCount::clearOverCorner() {
_isOverCorner = false; _isOverCorner = false;
setCursor(style::cur_default); setCursor(style::cur_default);
Global::SetNotificationsDemoIsShown(false); 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 &samples, _cornerSamples) {
for_const (const auto widget, samples) { for_const (const auto widget, samples) {
@ -418,7 +418,7 @@ void NotificationsCount::mouseReleaseEvent(QMouseEvent *e) {
if (_chosenCorner != Core::App().settings().notificationsCorner()) { if (_chosenCorner != Core::App().settings().notificationsCorner()) {
Core::App().settings().setNotificationsCorner(_chosenCorner); Core::App().settings().setNotificationsCorner(_chosenCorner);
Core::App().saveSettingsDelayed(); Core::App().saveSettingsDelayed();
_controller->session().notifications().settingsChanged().notify( Core::App().notifications().settingsChanged().notify(
ChangeType::Corner); ChangeType::Corner);
} }
} }
@ -685,7 +685,7 @@ void SetupNotificationsContent(
using Change = Window::Notifications::ChangeType; using Change = Window::Notifications::ChangeType;
const auto changed = [=](Change change) { const auto changed = [=](Change change) {
Core::App().saveSettingsDelayed(); Core::App().saveSettingsDelayed();
session->notifications().settingsChanged().notify(change); Core::App().notifications().settingsChanged().notify(change);
}; };
desktop->checkedChanges( desktop->checkedChanges(
) | rpl::filter([](bool checked) { ) | rpl::filter([](bool checked) {
@ -758,7 +758,7 @@ void SetupNotificationsContent(
}, count->lifetime()); }, count->lifetime());
base::ObservableViewer( base::ObservableViewer(
session->notifications().settingsChanged() Core::App().notifications().settingsChanged()
) | rpl::start_with_next([=](Change change) { ) | rpl::start_with_next([=](Change change) {
if (change == Change::DesktopEnabled) { if (change == Change::DesktopEnabled) {
desktop->setChecked(Core::App().settings().desktopNotify()); desktop->setChecked(Core::App().settings().desktopNotify());
@ -785,7 +785,7 @@ void SetupNotificationsContent(
}) | rpl::start_with_next([=](bool checked) { }) | rpl::start_with_next([=](bool checked) {
Core::App().settings().setNativeNotifications(checked); Core::App().settings().setNativeNotifications(checked);
Core::App().saveSettingsDelayed(); Core::App().saveSettingsDelayed();
session->notifications().createManager(); Core::App().notifications().createManager();
if (advancedSlide) { if (advancedSlide) {
advancedSlide->toggle( advancedSlide->toggle(

View file

@ -87,7 +87,7 @@ void Domain::startWithSingleAccount(
generateLocalKey(); generateLocalKey();
account->start(account->prepareToStart(_localKey)); account->start(account->prepareToStart(_localKey));
} }
_owner->accountAddedInStorage(0, std::move(account)); _owner->accountAddedInStorage({ .account = std::move(account) });
writeAccounts(); writeAccounts();
} }
@ -170,6 +170,7 @@ Domain::StartModernResult Domain::startModern(
auto tried = base::flat_set<int>(); auto tried = base::flat_set<int>();
auto users = base::flat_set<UserId>(); auto users = base::flat_set<UserId>();
auto active = 0;
for (auto i = 0; i != count; ++i) { for (auto i = 0; i != count; ++i) {
auto index = qint32(); auto index = qint32();
info.stream >> index; info.stream >> index;
@ -184,12 +185,22 @@ Domain::StartModernResult Domain::startModern(
const auto userId = account->willHaveUserId(); const auto userId = account->willHaveUserId();
if (!users.contains(userId) if (!users.contains(userId)
&& (userId != 0 || (users.empty() && i + 1 == count))) { && (userId != 0 || (users.empty() && i + 1 == count))) {
if (users.empty()) {
active = index;
}
account->start(std::move(config)); account->start(std::move(config));
_owner->accountAddedInStorage(index, std::move(account)); _owner->accountAddedInStorage({
.index = index,
.account = std::move(account)
});
users.emplace(userId); users.emplace(userId);
} }
} }
} }
if (!info.stream.atEnd()) {
info.stream >> active;
}
_owner->activateFromStorage(active);
Ensures(!users.empty()); Ensures(!users.empty());
return StartModernResult::Success; return StartModernResult::Success;
@ -208,18 +219,21 @@ void Domain::writeAccounts() {
key.writeData(_passcodeKeyEncrypted); key.writeData(_passcodeKeyEncrypted);
const auto &list = _owner->accounts(); const auto &list = _owner->accounts();
const auto active = _owner->activeIndex();
auto keySize = sizeof(qint32) + sizeof(qint32) * list.size(); auto keySize = sizeof(qint32) + sizeof(qint32) * list.size();
const auto active = &_owner->active();
auto activeIndex = -1;
EncryptedDescriptor keyData(keySize); EncryptedDescriptor keyData(keySize);
keyData.stream << qint32(list.size()); keyData.stream << qint32(list.size());
keyData.stream << qint32(active);
for (const auto &[index, account] : list) { for (const auto &[index, account] : list) {
if (index != active) { if (active == account.get()) {
activeIndex = index;
}
keyData.stream << qint32(index); keyData.stream << qint32(index);
} }
} keyData.stream << qint32(activeIndex);
key.writeEncrypted(keyData, _localKey); key.writeEncrypted(keyData, _localKey);
} }

View file

@ -24,6 +24,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "mainwindow.h" #include "mainwindow.h"
#include "api/api_updates.h" #include "api/api_updates.h"
#include "apiwrap.h" #include "apiwrap.h"
#include "main/main_account.h"
#include "main/main_session.h" #include "main/main_session.h"
#include "main/main_domain.h" #include "main/main_domain.h"
#include "facades.h" #include "facades.h"
@ -47,9 +48,8 @@ constexpr auto kSystemAlertDuration = crl::time(0);
} // namespace } // namespace
System::System(not_null<Main::Session*> session) System::System()
: _session(session) : _waitTimer([=] { showNext(); })
, _waitTimer([=] { showNext(); })
, _waitForAllGroupedTimer([=] { showGrouped(); }) { , _waitForAllGroupedTimer([=] { showGrouped(); }) {
createManager(); 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( System::SkipState System::skipNotification(
not_null<HistoryItem*> item) const { not_null<HistoryItem*> item) const {
const auto history = item->history(); const auto history = item->history();
@ -135,19 +145,23 @@ void System::schedule(not_null<HistoryItem*> item) {
auto when = ms + delay; auto when = ms + delay;
if (!skip.silent) { if (!skip.silent) {
_whenAlerts[history].insert(when, notifyBy); _whenAlerts[history].emplace(when, notifyBy);
} }
if (Core::App().settings().desktopNotify() if (Core::App().settings().desktopNotify()
&& !Platform::Notifications::SkipToast()) { && !Platform::Notifications::SkipToast()) {
auto &whenMap = _whenMaps[history]; auto &whenMap = _whenMaps[history];
if (whenMap.constFind(item->id) == whenMap.cend()) { if (whenMap.find(item->id) == whenMap.end()) {
whenMap.insert(item->id, when); whenMap.emplace(item->id, when);
} }
auto &addTo = ready ? _waiters : _settingWaiters; auto &addTo = ready ? _waiters : _settingWaiters;
const auto it = addTo.constFind(history); const auto it = addTo.find(history);
if (it == addTo.cend() || it->when > when) { if (it == addTo.end() || it->second.when > when) {
addTo.insert(history, Waiter(item->id, when, notifyBy)); addTo.emplace(history, Waiter{
.msg = item->id,
.when = when,
.notifyBy = notifyBy
});
} }
} }
if (ready) { if (ready) {
@ -161,7 +175,7 @@ void System::clearAll() {
_manager->clearAll(); _manager->clearAll();
for (auto i = _whenMaps.cbegin(), e = _whenMaps.cend(); i != e; ++i) { for (auto i = _whenMaps.cbegin(), e = _whenMaps.cend(); i != e; ++i) {
i.key()->clearNotifications(); i->first->clearNotifications();
} }
_whenMaps.clear(); _whenMaps.clear();
_whenAlerts.clear(); _whenAlerts.clear();
@ -182,6 +196,35 @@ void System::clearFromHistory(not_null<History*> history) {
showNext(); showNext();
} }
void System::clearFromSession(not_null<Main::Session*> 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*> history) { void System::clearIncomingFromHistory(not_null<History*> history) {
_manager->clearFromHistory(history); _manager->clearFromHistory(history);
history->clearIncomingNotifications(); history->clearIncomingNotifications();
@ -203,14 +246,14 @@ void System::clearAllFast() {
void System::checkDelayed() { void System::checkDelayed() {
for (auto i = _settingWaiters.begin(); i != _settingWaiters.end();) { for (auto i = _settingWaiters.begin(); i != _settingWaiters.end();) {
const auto history = i.key(); const auto history = i->first;
const auto peer = history->peer; const auto peer = history->peer;
auto loaded = false; auto loaded = false;
auto muted = false; auto muted = false;
if (!peer->owner().notifyMuteUnknown(peer)) { if (!peer->owner().notifyMuteUnknown(peer)) {
if (!peer->owner().notifyIsMuted(peer)) { if (!peer->owner().notifyIsMuted(peer)) {
loaded = true; 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().notifyMuteUnknown(from)) {
if (!peer->owner().notifyIsMuted(from)) { if (!peer->owner().notifyIsMuted(from)) {
loaded = true; loaded = true;
@ -225,7 +268,7 @@ void System::checkDelayed() {
if (loaded) { if (loaded) {
const auto fullId = FullMsgId( const auto fullId = FullMsgId(
history->channelId(), history->channelId(),
i.value().msg); i->second.msg);
if (const auto item = peer->owner().message(fullId)) { if (const auto item = peer->owner().message(fullId)) {
if (!item->notificationReady()) { if (!item->notificationReady()) {
loaded = false; loaded = false;
@ -236,7 +279,7 @@ void System::checkDelayed() {
} }
if (loaded) { if (loaded) {
if (!muted) { if (!muted) {
_waiters.insert(i.key(), i.value()); _waiters.emplace(i->first, i->second);
} }
i = _settingWaiters.erase(i); i = _settingWaiters.erase(i);
} else { } else {
@ -248,13 +291,15 @@ void System::checkDelayed() {
} }
void System::showGrouped() { void System::showGrouped() {
if (const auto lastItem = session().data().message(_lastHistoryItemId)) { if (const auto session = findSession(_lastHistorySelfId)) {
if (const auto lastItem = session->data().message(_lastHistoryItemId)) {
_waitForAllGroupedTimer.cancel(); _waitForAllGroupedTimer.cancel();
_manager->showNotification(lastItem, _lastForwardedCount); _manager->showNotification(lastItem, _lastForwardedCount);
_lastForwardedCount = 0; _lastForwardedCount = 0;
_lastHistoryItemId = FullMsgId(); _lastHistoryItemId = FullMsgId();
} }
} }
}
void System::showNext() { void System::showNext() {
if (App::quitting()) return; if (App::quitting()) return;
@ -275,12 +320,12 @@ void System::showNext() {
bool alert = false; bool alert = false;
int32 now = base::unixtime::now(); int32 now = base::unixtime::now();
for (auto i = _whenAlerts.begin(); i != _whenAlerts.end();) { for (auto i = _whenAlerts.begin(); i != _whenAlerts.end();) {
while (!i.value().isEmpty() && i.value().begin().key() <= ms) { while (!i->second.empty() && i->second.begin()->first <= ms) {
const auto peer = i.key()->peer; const auto peer = i->first->peer;
const auto peerUnknown = peer->owner().notifyMuteUnknown(peer); const auto peerUnknown = peer->owner().notifyMuteUnknown(peer);
const auto peerAlert = !peerUnknown const auto peerAlert = !peerUnknown
&& !peer->owner().notifyIsMuted(peer); && !peer->owner().notifyIsMuted(peer);
const auto from = i.value().begin().value(); const auto from = i->second.begin()->second;
const auto fromUnknown = (!from const auto fromUnknown = (!from
|| peer->owner().notifyMuteUnknown(from)); || peer->owner().notifyMuteUnknown(from));
const auto fromAlert = !fromUnknown const auto fromAlert = !fromUnknown
@ -288,16 +333,16 @@ void System::showNext() {
if (peerAlert || fromAlert) { if (peerAlert || fromAlert) {
alert = true; alert = true;
} }
while (!i.value().isEmpty() while (!i->second.empty()
&& i.value().begin().key() <= ms + kMinimalAlertDelay) { && i->second.begin()->first <= ms + kMinimalAlertDelay) {
i.value().erase(i.value().begin()); i->second.erase(i->second.begin());
} }
} }
if (i.value().isEmpty()) { if (i->second.empty()) {
i = _whenAlerts.erase(i); i = _whenAlerts.erase(i);
} else { } else {
if (!nextAlert || nextAlert > i.value().begin().key()) { if (!nextAlert || nextAlert > i->second.begin()->first) {
nextAlert = i.value().begin().key(); nextAlert = i->second.begin()->first;
} }
++i; ++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) { if (nextAlert) {
_waitTimer.callOnce(nextAlert - ms); _waitTimer.callOnce(nextAlert - ms);
} }
@ -332,8 +377,8 @@ void System::showNext() {
HistoryItem *notifyItem = nullptr; HistoryItem *notifyItem = nullptr;
History *notifyHistory = nullptr; History *notifyHistory = nullptr;
for (auto i = _waiters.begin(); i != _waiters.end();) { for (auto i = _waiters.begin(); i != _waiters.end();) {
History *history = i.key(); const auto history = i->first;
if (history->currentNotification() && history->currentNotification()->id != i.value().msg) { if (history->currentNotification() && history->currentNotification()->id != i->second.msg) {
auto j = _whenMaps.find(history); auto j = _whenMaps.find(history);
if (j == _whenMaps.end()) { if (j == _whenMaps.end()) {
history->clearNotifications(); history->clearNotifications();
@ -341,10 +386,10 @@ void System::showNext() {
continue; continue;
} }
do { do {
auto k = j.value().constFind(history->currentNotification()->id); auto k = j->second.find(history->currentNotification()->id);
if (k != j.value().cend()) { if (k != j->second.cend()) {
i.value().msg = k.key(); i->second.msg = k->first;
i.value().when = k.value(); i->second.when = k->second;
break; break;
} }
history->skipNotification(); history->skipNotification();
@ -355,7 +400,7 @@ void System::showNext() {
i = _waiters.erase(i); i = _waiters.erase(i);
continue; continue;
} }
auto when = i.value().when; auto when = i->second.when;
if (!notifyItem || next > when) { if (!notifyItem || next > when) {
next = when; next = when;
notifyItem = history->currentNotification(); notifyItem = history->currentNotification();
@ -390,12 +435,15 @@ void System::showNext() {
break; break;
} }
j.value().remove((groupedItem ? groupedItem : notifyItem)->id); j->second.remove((groupedItem ? groupedItem : notifyItem)->id);
do { do {
const auto k = j.value().constFind(history->currentNotification()->id); const auto k = j->second.find(history->currentNotification()->id);
if (k != j.value().cend()) { if (k != j->second.cend()) {
nextNotify = history->currentNotification(); nextNotify = history->currentNotification();
_waiters.insert(notifyHistory, Waiter(k.key(), k.value(), 0)); _waiters.emplace(notifyHistory, Waiter{
.msg = k->first,
.when = k->second
});
break; break;
} }
history->skipNotification(); history->skipNotification();
@ -482,8 +530,8 @@ void System::updateAll() {
_manager->updateAll(); _manager->updateAll();
} }
Manager::DisplayOptions Manager::getNotificationOptions(HistoryItem *item) { Manager::DisplayOptions Manager::GetNotificationOptions(HistoryItem *item) {
const auto hideEverything = Core::App().locked() const auto hideEverything = Core::App().passcodeLocked()
|| Global::ScreenIsLocked(); || Global::ScreenIsLocked();
const auto view = Core::App().settings().notifyView(); const auto view = Core::App().settings().notifyView();
@ -499,20 +547,26 @@ Manager::DisplayOptions Manager::getNotificationOptions(HistoryItem *item) {
return result; return result;
} }
void Manager::notificationActivated(PeerId peerId, MsgId msgId) { void Manager::notificationActivated(NotificationId id) {
onBeforeNotificationActivated(peerId, msgId); onBeforeNotificationActivated(id);
if (auto window = App::wnd()) { if (const auto session = system()->findSession(id.selfId)) {
auto history = system()->session().data().history(peerId); if (session->windows().empty()) {
window->showFromTray(); Core::App().domain().activate(&session->account());
window->reActivateWindow(); }
if (Core::App().locked()) { if (!session->windows().empty()) {
window->setInnerFocus(); 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(); system()->clearAll();
} else { } else {
openNotificationMessage(history, msgId); openNotificationMessage(history, id.msgId);
} }
} }
onAfterNotificationActivated(peerId, msgId); }
onAfterNotificationActivated(id);
} }
void Manager::openNotificationMessage( void Manager::openNotificationMessage(
@ -548,20 +602,29 @@ void Manager::openNotificationMessage(
} }
void Manager::notificationReplied( void Manager::notificationReplied(
PeerId peerId, NotificationId id,
MsgId msgId,
const TextWithTags &reply) { 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); auto message = Api::MessageToSend(history);
message.textWithTags = reply; 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; message.action.clearDraft = false;
history->session().api().sendMessage(std::move(message)); 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()) { if (item && item->isUnreadMention() && !item->isUnreadMedia()) {
history->session().api().markMediaRead(item); history->session().api().markMediaRead(item);
} }
@ -570,7 +633,7 @@ void Manager::notificationReplied(
void NativeManager::doShowNotification( void NativeManager::doShowNotification(
not_null<HistoryItem*> item, not_null<HistoryItem*> item,
int forwardedCount) { int forwardedCount) {
const auto options = getNotificationOptions(item); const auto options = GetNotificationOptions(item);
const auto peer = item->history()->peer; const auto peer = item->history()->peer;
const auto scheduled = !options.hideNameAndPhoto const auto scheduled = !options.hideNameAndPhoto

View file

@ -64,7 +64,10 @@ class Manager;
class System final : private base::Subscriber { class System final : private base::Subscriber {
public: public:
explicit System(not_null<Main::Session*> session); System();
~System();
[[nodiscard]] Main::Session *findSession(UserId selfId) const;
void createManager(); void createManager();
@ -72,6 +75,7 @@ public:
void schedule(not_null<HistoryItem*> item); void schedule(not_null<HistoryItem*> item);
void clearFromHistory(not_null<History*> history); void clearFromHistory(not_null<History*> history);
void clearIncomingFromHistory(not_null<History*> history); void clearIncomingFromHistory(not_null<History*> history);
void clearFromSession(not_null<Main::Session*> session);
void clearFromItem(not_null<HistoryItem*> item); void clearFromItem(not_null<HistoryItem*> item);
void clearAll(); void clearAll();
void clearAllFast(); void clearAllFast();
@ -81,12 +85,6 @@ public:
return _settingsChanged; return _settingsChanged;
} }
Main::Session &session() const {
return *_session;
}
~System();
private: private:
struct SkipState { struct SkipState {
enum Value { enum Value {
@ -97,34 +95,29 @@ private:
Value value = Value::Unknown; Value value = Value::Unknown;
bool silent = false; bool silent = false;
}; };
struct Waiter {
MsgId msg;
crl::time when;
PeerData *notifyBy = nullptr;
};
SkipState skipNotification(not_null<HistoryItem*> item) const; [[nodiscard]] SkipState skipNotification(
not_null<HistoryItem*> item) const;
void showNext(); void showNext();
void showGrouped(); void showGrouped();
void ensureSoundCreated(); void ensureSoundCreated();
not_null<Main::Session*> _session; base::flat_map<
not_null<History*>,
base::flat_map<MsgId, crl::time>> _whenMaps;
QMap<History*, QMap<MsgId, crl::time>> _whenMaps; base::flat_map<not_null<History*>, Waiter> _waiters;
base::flat_map<not_null<History*>, Waiter> _settingWaiters;
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<History*, Waiter>;
Waiters _waiters;
Waiters _settingWaiters;
base::Timer _waitTimer; base::Timer _waitTimer;
base::Timer _waitForAllGroupedTimer; base::Timer _waitForAllGroupedTimer;
QMap<History*, QMap<crl::time, PeerData*>> _whenAlerts; base::flat_map<not_null<History*>, base::flat_map<crl::time, PeerData*>> _whenAlerts;
std::unique_ptr<Manager> _manager; std::unique_ptr<Manager> _manager;
@ -133,12 +126,28 @@ private:
std::unique_ptr<Media::Audio::Track> _soundTrack; std::unique_ptr<Media::Audio::Track> _soundTrack;
int _lastForwardedCount = 0; int _lastForwardedCount = 0;
UserId _lastHistorySelfId = 0;
FullMsgId _lastHistoryItemId; FullMsgId _lastHistoryItemId;
}; };
class Manager { class Manager {
public: 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(system) { explicit Manager(not_null<System*> system) : _system(system) {
} }
@ -162,19 +171,20 @@ public:
void clearFromHistory(not_null<History*> history) { void clearFromHistory(not_null<History*> history) {
doClearFromHistory(history); doClearFromHistory(history);
} }
void clearFromSession(not_null<Main::Session*> session) {
doClearFromSession(session);
}
void notificationActivated(PeerId peerId, MsgId msgId); void notificationActivated(NotificationId id);
void notificationReplied( void notificationReplied(NotificationId id, const TextWithTags &reply);
PeerId peerId,
MsgId msgId,
const TextWithTags &reply);
struct DisplayOptions { struct DisplayOptions {
bool hideNameAndPhoto; bool hideNameAndPhoto = false;
bool hideMessageText; bool hideMessageText = false;
bool hideReplyButton; bool hideReplyButton = false;
}; };
static DisplayOptions getNotificationOptions(HistoryItem *item); [[nodiscard]] static DisplayOptions GetNotificationOptions(
HistoryItem *item);
virtual ~Manager() = default; virtual ~Manager() = default;
@ -191,9 +201,10 @@ protected:
virtual void doClearAllFast() = 0; virtual void doClearAllFast() = 0;
virtual void doClearFromItem(not_null<HistoryItem*> item) = 0; virtual void doClearFromItem(not_null<HistoryItem*> item) = 0;
virtual void doClearFromHistory(not_null<History*> history) = 0; virtual void doClearFromHistory(not_null<History*> history) = 0;
virtual void onBeforeNotificationActivated(PeerId peerId, MsgId msgId) { virtual void doClearFromSession(not_null<Main::Session*> session) = 0;
virtual void onBeforeNotificationActivated(NotificationId id) {
} }
virtual void onAfterNotificationActivated(PeerId peerId, MsgId msgId) { virtual void onAfterNotificationActivated(NotificationId id) {
} }
private: private:

View file

@ -67,11 +67,6 @@ std::unique_ptr<Manager> Create(System *system) {
Manager::Manager(System *system) Manager::Manager(System *system)
: Notifications::Manager(system) : Notifications::Manager(system)
, _inputCheckTimer([=] { checkLastInput(); }) { , _inputCheckTimer([=] { checkLastInput(); }) {
subscribe(system->session().downloaderTaskFinished(), [this] {
for (const auto &notification : _notifications) {
notification->updatePeerPhoto();
}
});
subscribe(system->settingsChanged(), [this](ChangeType change) { subscribe(system->settingsChanged(), [this](ChangeType change) {
settingsChanged(change); settingsChanged(change);
}); });
@ -212,7 +207,8 @@ void Manager::showNextFromQueue() {
auto queued = _queuedNotifications.front(); auto queued = _queuedNotifications.front();
_queuedNotifications.pop_front(); _queuedNotifications.pop_front();
auto notification = std::make_unique<Notification>( subscribeToSession(&queued.history->session());
_notifications.push_back(std::make_unique<Notification>(
this, this,
queued.history, queued.history,
queued.peer, queued.peer,
@ -222,8 +218,7 @@ void Manager::showNextFromQueue() {
queued.fromScheduled, queued.fromScheduled,
startPosition, startPosition,
startShift, startShift,
shiftDirection); shiftDirection));
_notifications.push_back(std::move(notification));
--count; --count;
} while (count > 0 && !_queuedNotifications.empty()); } while (count > 0 && !_queuedNotifications.empty());
@ -231,6 +226,32 @@ void Manager::showNextFromQueue() {
checkLastInput(); checkLastInput();
} }
void Manager::subscribeToSession(not_null<Main::Session*> 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 &notification : _notifications) {
if (const auto history = notification->maybeHistory()) {
if (&history->session() == session) {
notification->updatePeerPhoto();
found = true;
}
}
}
if (!found) {
_subscriptions[session].destroy();
}
});
}
void Manager::moveWidgets() { void Manager::moveWidgets() {
auto shift = st::notifyDeltaY; auto shift = st::notifyDeltaY;
int lastShift = 0, lastShiftCurrent = 0, count = 0; int lastShift = 0, lastShiftCurrent = 0, count = 0;
@ -334,7 +355,7 @@ void Manager::doClearFromHistory(not_null<History*> history) {
++i; ++i;
} }
} }
for_const (auto &notification, _notifications) { for (const auto &notification : _notifications) {
if (notification->unlinkHistory(history)) { if (notification->unlinkHistory(history)) {
_positionsOutdated = true; _positionsOutdated = true;
} }
@ -342,6 +363,22 @@ void Manager::doClearFromHistory(not_null<History*> history) {
showNextFromQueue(); showNextFromQueue();
} }
void Manager::doClearFromSession(not_null<Main::Session*> session) {
for (auto i = _queuedNotifications.begin(); i != _queuedNotifications.cend();) {
if (&i->history->session() == session) {
i = _queuedNotifications.erase(i);
} else {
++i;
}
}
for (const auto &notification : _notifications) {
if (notification->unlinkSession(session)) {
_positionsOutdated = true;
}
}
showNextFromQueue();
}
void Manager::doClearFromItem(not_null<HistoryItem*> item) { void Manager::doClearFromItem(not_null<HistoryItem*> item) {
_queuedNotifications.erase(std::remove_if(_queuedNotifications.begin(), _queuedNotifications.end(), [&](auto &queued) { _queuedNotifications.erase(std::remove_if(_queuedNotifications.begin(), _queuedNotifications.end(), [&](auto &queued) {
return (queued.item == item); return (queued.item == item);
@ -678,7 +715,7 @@ void Notification::actionsOpacityCallback() {
void Notification::updateNotifyDisplay() { void Notification::updateNotifyDisplay() {
if (!_history || (!_item && _forwardedCount < 2)) return; if (!_history || (!_item && _forwardedCount < 2)) return;
const auto options = Manager::getNotificationOptions(_item); const auto options = Manager::GetNotificationOptions(_item);
_hideReplyButton = options.hideReplyButton; _hideReplyButton = options.hideReplyButton;
int32 w = width(), h = height(); int32 w = width(), h = height();
@ -832,7 +869,7 @@ bool Notification::unlinkItem(HistoryItem *deleted) {
bool Notification::canReply() const { bool Notification::canReply() const {
return !_hideReplyButton return !_hideReplyButton
&& (_item != nullptr) && (_item != nullptr)
&& !Core::App().locked() && !Core::App().passcodeLocked()
&& (Core::App().settings().notifyView() <= dbinvShowPreview); && (Core::App().settings().notifyView() <= dbinvShowPreview);
} }
@ -901,16 +938,23 @@ void Notification::showReplyField() {
void Notification::sendReply() { void Notification::sendReply() {
if (!_history) return; if (!_history) return;
auto peerId = _history->peer->id;
auto msgId = _item ? _item->id : ShowAtUnreadMsgId;
manager()->notificationReplied( manager()->notificationReplied(
peerId, myId(),
msgId,
_replyArea->getTextWithAppliedMarkdown()); _replyArea->getTextWithAppliedMarkdown());
manager()->startAllHiding(); 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) { void Notification::changeHeight(int newHeight) {
manager()->changeNotificationHeight(this, newHeight); manager()->changeNotificationHeight(this, newHeight);
} }
@ -925,6 +969,16 @@ bool Notification::unlinkHistory(History *history) {
return unlink; return unlink;
} }
bool Notification::unlinkSession(not_null<Main::Session*> session) {
const auto unlink = _history && (&_history->session() == session);
if (unlink) {
hideFast();
_history = nullptr;
_item = nullptr;
}
return unlink;
}
void Notification::enterEventHook(QEvent *e) { void Notification::enterEventHook(QEvent *e) {
if (!_history) return; if (!_history) return;
manager()->stopAllHiding(); manager()->stopAllHiding();
@ -951,9 +1005,7 @@ void Notification::mousePressEvent(QMouseEvent *e) {
unlinkHistoryInManager(); unlinkHistoryInManager();
} else { } else {
e->ignore(); e->ignore();
auto peerId = _history->peer->id; manager()->notificationActivated(myId());
auto msgId = _item ? _item->id : ShowAtUnreadMsgId;
manager()->notificationActivated(peerId, msgId);
} }
} }

View file

@ -38,19 +38,18 @@ class HideAllButton;
class Manager; class Manager;
std::unique_ptr<Manager> Create(System *system); std::unique_ptr<Manager> Create(System *system);
class Manager : public Notifications::Manager, private base::Subscriber { class Manager final : public Notifications::Manager, private base::Subscriber {
public: public:
Manager(System *system); Manager(System *system);
~Manager();
template <typename Method> template <typename Method>
void enumerateNotifications(Method method) { void enumerateNotifications(Method method) {
for_const (auto &notification, _notifications) { for (const auto &notification : _notifications) {
method(notification); method(notification);
} }
} }
~Manager();
private: private:
friend class internal::Notification; friend class internal::Notification;
friend class internal::HideAllButton; friend class internal::HideAllButton;
@ -58,7 +57,7 @@ private:
using Notification = internal::Notification; using Notification = internal::Notification;
using HideAllButton = internal::HideAllButton; using HideAllButton = internal::HideAllButton;
QPixmap hiddenUserpicPlaceholder() const; [[nodiscard]] QPixmap hiddenUserpicPlaceholder() const;
void doUpdateAll() override; void doUpdateAll() override;
void doShowNotification( void doShowNotification(
@ -67,6 +66,7 @@ private:
void doClearAll() override; void doClearAll() override;
void doClearAllFast() override; void doClearAllFast() override;
void doClearFromHistory(not_null<History*> history) override; void doClearFromHistory(not_null<History*> history) override;
void doClearFromSession(not_null<Main::Session*> session) override;
void doClearFromItem(not_null<HistoryItem*> item) override; void doClearFromItem(not_null<HistoryItem*> item) override;
void showNextFromQueue(); void showNextFromQueue();
@ -86,7 +86,12 @@ private:
bool hasReplyingNotification() const; bool hasReplyingNotification() const;
void subscribeToSession(not_null<Main::Session*> session);
std::vector<std::unique_ptr<Notification>> _notifications; std::vector<std::unique_ptr<Notification>> _notifications;
base::flat_map<
not_null<Main::Session*>,
base::Subscription> _subscriptions;
std::unique_ptr<HideAllButton> _hideAll; std::unique_ptr<HideAllButton> _hideAll;
@ -181,7 +186,7 @@ protected:
}; };
class Notification : public Widget { class Notification final : public Widget {
public: public:
Notification( Notification(
not_null<Manager*> manager, not_null<Manager*> manager,
@ -207,10 +212,14 @@ public:
bool isReplying() const { bool isReplying() const {
return _replyArea && !isUnlinked(); return _replyArea && !isUnlinked();
} }
[[nodiscard]] History *maybeHistory() const {
return _history;
}
// Called only by Manager. // Called only by Manager.
bool unlinkItem(HistoryItem *del); bool unlinkItem(HistoryItem *del);
bool unlinkHistory(History *history = nullptr); bool unlinkHistory(History *history = nullptr);
bool unlinkSession(not_null<Main::Session*> session);
bool checkLastInput(bool hasReplyingNotifications); bool checkLastInput(bool hasReplyingNotifications);
protected: protected:
@ -236,6 +245,8 @@ private:
void updateGeometry(int x, int y, int width, int height) override; void updateGeometry(int x, int y, int width, int height) override;
void actionsOpacityCallback(); void actionsOpacityCallback();
[[nodiscard]] Notifications::Manager::NotificationId myId() const;
const not_null<PeerData*> _peer; const not_null<PeerData*> _peer;
QPixmap _cache; QPixmap _cache;