mirror of
https://github.com/AyuGram/AyuGramDesktop.git
synced 2025-04-22 09:07:05 +02:00
One Window::Notifications system for all sessions.
This commit is contained in:
parent
83538675ce
commit
997913be25
28 changed files with 578 additions and 297 deletions
|
@ -2601,7 +2601,7 @@ void ApiWrap::applyNotifySettings(
|
|||
}
|
||||
} break;
|
||||
}
|
||||
_session->notifications().checkDelayed();
|
||||
Core::App().notifications().checkDelayed();
|
||||
}
|
||||
|
||||
void ApiWrap::gotStickerSet(uint64 setId, const MTPmessages_StickerSet &result) {
|
||||
|
|
|
@ -99,6 +99,7 @@ Application::Application(not_null<Launcher*> launcher)
|
|||
, _fallbackProductionConfig(
|
||||
std::make_unique<MTP::Config>(MTP::Environment::Production))
|
||||
, _domain(std::make_unique<Main::Domain>(cDataFile()))
|
||||
, _notifications(std::make_unique<Window::Notifications::System>())
|
||||
, _langpack(std::make_unique<Lang::Instance>())
|
||||
, _langCloudManager(std::make_unique<Lang::CloudManager>(langpack()))
|
||||
, _emojiKeywords(std::make_unique<ChatHelpers::EmojiKeywords>())
|
||||
|
@ -116,6 +117,11 @@ Application::Application(not_null<Launcher*> 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
|
||||
|
|
|
@ -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<Main::Session*> session) const;
|
||||
|
@ -294,6 +303,10 @@ private:
|
|||
QPointer<Ui::BoxContent> _badProxyDisableBox;
|
||||
|
||||
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 _logoNoMargin;
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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<PeerData*> 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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<HistoryItem*> item) {
|
|||
}
|
||||
|
||||
owner().unregisterMessage(item);
|
||||
session().notifications().clearFromItem(item);
|
||||
Core::App().notifications().clearFromItem(item);
|
||||
|
||||
auto hack = std::unique_ptr<HistoryItem>(item.get());
|
||||
const auto i = _messages.find(hack);
|
||||
|
@ -1290,7 +1291,7 @@ void History::newItemAdded(not_null<HistoryItem*> 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<int> stillUnread) {
|
|||
}
|
||||
|
||||
_firstUnreadView = nullptr;
|
||||
session().notifications().clearIncomingFromHistory(this);
|
||||
Core::App().notifications().clearIncomingFromHistory(this);
|
||||
}
|
||||
|
||||
void History::inboxRead(not_null<const HistoryItem*> wasRead) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -2423,7 +2423,7 @@ void HistoryWidget::unreadMessageAdded(not_null<HistoryItem*> item) {
|
|||
session().data().histories().readInboxOnNewMessage(item);
|
||||
|
||||
// Also clear possible scheduled messages notifications.
|
||||
session().notifications().clearFromHistory(_history);
|
||||
Core::App().notifications().clearFromHistory(_history);
|
||||
}
|
||||
|
||||
void HistoryWidget::unreadCountUpdated() {
|
||||
|
|
|
@ -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> 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<int, std::unique_ptr<Account>> &Domain::accounts() const {
|
||||
const std::vector<Domain::AccountWithIndex> &Domain::accounts() const {
|
||||
return _accounts;
|
||||
}
|
||||
|
||||
|
@ -94,12 +102,6 @@ rpl::producer<Account*> 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<Main::Account*> Domain::add(MTP::Environment environment) {
|
||||
Expects(started());
|
||||
|
||||
static const auto cloneConfig = [](const 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);
|
||||
}();
|
||||
auto index = 0;
|
||||
while (_accounts.contains(index)) {
|
||||
while (ranges::contains(_accounts, index, &AccountWithIndex::index)) {
|
||||
++index;
|
||||
}
|
||||
const auto account = _accounts.emplace(
|
||||
index,
|
||||
std::make_unique<Account>(this, _dataName, index)
|
||||
).first->second.get();
|
||||
_accounts.push_back(AccountWithIndex{
|
||||
.index = index,
|
||||
.account = std::make_unique<Account>(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*> 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<Main::Account*> 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);
|
||||
|
||||
|
|
|
@ -25,6 +25,11 @@ class Session;
|
|||
|
||||
class Domain final {
|
||||
public:
|
||||
struct AccountWithIndex {
|
||||
int index = 0;
|
||||
std::unique_ptr<Account> account;
|
||||
};
|
||||
|
||||
static constexpr auto kMaxAccounts = 3;
|
||||
|
||||
explicit Domain(const QString &dataName);
|
||||
|
@ -40,11 +45,10 @@ public:
|
|||
}
|
||||
|
||||
[[nodiscard]] auto accounts() const
|
||||
-> const base::flat_map<int, std::unique_ptr<Account>> &;
|
||||
-> const std::vector<AccountWithIndex> &;
|
||||
[[nodiscard]] rpl::producer<Account*> activeValue() const;
|
||||
|
||||
// Expects(started());
|
||||
[[nodiscard]] int activeIndex() const;
|
||||
[[nodiscard]] Account &active() const;
|
||||
[[nodiscard]] rpl::producer<not_null<Account*>> 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<Main::Account*> add(MTP::Environment environment);
|
||||
void activate(not_null<Main::Account*> account);
|
||||
|
||||
// Interface for Storage::Domain.
|
||||
void accountAddedInStorage(int index, std::unique_ptr<Account> account);
|
||||
void accountAddedInStorage(AccountWithIndex accountWithIndex);
|
||||
void activateFromStorage(int index);
|
||||
|
||||
private:
|
||||
void activateAfterStarting();
|
||||
|
@ -76,9 +81,9 @@ private:
|
|||
const QString _dataName;
|
||||
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;
|
||||
int _activeIndex = 0;
|
||||
int _accountToActivate = -1;
|
||||
bool _writeAccountsScheduled = false;
|
||||
|
||||
rpl::event_stream<Session*> _activeSessions;
|
||||
|
|
|
@ -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<Storage::DownloadManagerMtproto>(_api.get()))
|
||||
, _uploader(std::make_unique<Storage::Uploader>(_api.get()))
|
||||
, _storage(std::make_unique<Storage::Facade>())
|
||||
, _notifications(std::make_unique<Window::Notifications::System>(this))
|
||||
, _changes(std::make_unique<Data::Changes>(this))
|
||||
, _data(std::make_unique<Data::Session>(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();
|
||||
});
|
||||
|
|
|
@ -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<Storage::DownloadManagerMtproto> _downloader;
|
||||
const std::unique_ptr<Storage::Uploader> _uploader;
|
||||
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::Session> _data;
|
||||
const not_null<UserData*> _user;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 <QtCore/QVersionNumber>
|
||||
|
@ -201,7 +202,7 @@ QString GetImageKey(const QVersionNumber &specificationVersion) {
|
|||
return QString();
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace
|
||||
|
||||
NotificationData::NotificationData(
|
||||
const base::weak_ptr<Manager> &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<NotificationData>(
|
||||
_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<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()) {
|
||||
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*> 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<Main::Session*> 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<Window::Notifications::System*> system)
|
|||
, _private(std::make_unique<Private>(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*> history) {
|
||||
_private->clearFromHistory(history);
|
||||
}
|
||||
|
||||
void Manager::doClearFromSession(not_null<Main::Session*> session) {
|
||||
_private->clearFromSession(session);
|
||||
}
|
||||
#endif // !TDESKTOP_DISABLE_DBUS_INTEGRATION
|
||||
|
||||
} // namespace Notifications
|
||||
|
|
|
@ -24,6 +24,8 @@ class NotificationData : public QObject {
|
|||
Q_OBJECT
|
||||
|
||||
public:
|
||||
using NotificationId = Window::Notifications::Manager::NotificationId;
|
||||
|
||||
NotificationData(
|
||||
const base::weak_ptr<Manager> &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> _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<Window::Notifications::System*> 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*> history) override;
|
||||
void doClearFromSession(not_null<Main::Session*> session) override;
|
||||
|
||||
private:
|
||||
class Private;
|
||||
|
@ -122,13 +129,15 @@ public:
|
|||
bool hideReplyButton);
|
||||
void clearAll();
|
||||
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:
|
||||
using Notifications = QMap<PeerId, QMap<MsgId, Notification>>;
|
||||
Notifications _notifications;
|
||||
base::flat_map<
|
||||
FullPeer,
|
||||
base::flat_map<MsgId, Notification>> _notifications;
|
||||
|
||||
Window::Notifications::CachedUserpics _cachedUserpics;
|
||||
base::weak_ptr<Manager> _manager;
|
||||
|
|
|
@ -30,6 +30,7 @@ protected:
|
|||
bool hideReplyButton) override;
|
||||
void doClearAllFast() override;
|
||||
void doClearFromHistory(not_null<History*> history) override;
|
||||
void doClearFromSession(not_null<Main::Session*> session) override;
|
||||
|
||||
private:
|
||||
class Private;
|
||||
|
|
|
@ -173,6 +173,7 @@ public:
|
|||
bool hideReplyButton);
|
||||
void clearAll();
|
||||
void clearFromHistory(not_null<History*> history);
|
||||
void clearFromSession(not_null<Main::Session*> session);
|
||||
void updateDelegate();
|
||||
|
||||
~Private();
|
||||
|
@ -328,6 +329,10 @@ void Manager::Private::clearFromHistory(not_null<History*> history) {
|
|||
putClearTask(ClearFromHistory { history->peer->id });
|
||||
}
|
||||
|
||||
void Manager::Private::clearFromSession(not_null<Main::Session*> 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*> history) {
|
|||
_private->clearFromHistory(history);
|
||||
}
|
||||
|
||||
void Manager::doClearFromSession(not_null<Main::Session*> session) {
|
||||
_private->clearFromSession(session);
|
||||
}
|
||||
|
||||
} // namespace Notifications
|
||||
} // namespace Platform
|
||||
|
|
|
@ -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 <Shobjidl.h>
|
||||
|
@ -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<Manager*> &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<Manager*> _weak;
|
||||
|
||||
};
|
||||
|
@ -353,9 +366,10 @@ public:
|
|||
bool hideReplyButton);
|
||||
void clearAll();
|
||||
void clearFromHistory(not_null<History*> history);
|
||||
void beforeNotificationActivated(PeerId peerId, MsgId msgId);
|
||||
void afterNotificationActivated(PeerId peerId, MsgId msgId);
|
||||
void clearNotification(PeerId peerId, MsgId msgId);
|
||||
void clearFromSession(not_null<Main::Session*> session);
|
||||
void beforeNotificationActivated(NotificationId id);
|
||||
void afterNotificationActivated(NotificationId id);
|
||||
void clearNotification(NotificationId id);
|
||||
|
||||
~Private();
|
||||
|
||||
|
@ -376,7 +390,7 @@ private:
|
|||
|
||||
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;
|
||||
|
||||
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*> 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<Main::Session*> 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<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);
|
||||
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<IToastNotification> notify = j->p;
|
||||
i->erase(j);
|
||||
auto j = i->second.find(msgId);
|
||||
if (j != i->second.end()) {
|
||||
ComPtr<IToastNotification> 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<MsgId, NotificationPtr>());
|
||||
i = _notifications.emplace(
|
||||
key,
|
||||
base::flat_map<MsgId, NotificationPtr>()).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*> history) {
|
|||
_private->clearFromHistory(history);
|
||||
}
|
||||
|
||||
void Manager::onBeforeNotificationActivated(PeerId peerId, MsgId msgId) {
|
||||
_private->beforeNotificationActivated(peerId, msgId);
|
||||
void Manager::doClearFromSession(not_null<Main::Session*> 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__
|
||||
|
||||
|
|
|
@ -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*> history) override;
|
||||
void onBeforeNotificationActivated(PeerId peerId, MsgId msgId) override;
|
||||
void onAfterNotificationActivated(PeerId peerId, MsgId msgId) override;
|
||||
void doClearFromSession(not_null<Main::Session*> session) override;
|
||||
void onBeforeNotificationActivated(NotificationId id) override;
|
||||
void onAfterNotificationActivated(NotificationId id) override;
|
||||
|
||||
private:
|
||||
class Private;
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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<int>();
|
||||
auto users = base::flat_set<UserId>();
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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<Main::Session*> 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<HistoryItem*> item) const {
|
||||
const auto history = item->history();
|
||||
|
@ -135,19 +145,23 @@ void System::schedule(not_null<HistoryItem*> 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*> history) {
|
|||
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) {
|
||||
_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<HistoryItem*> item,
|
||||
int forwardedCount) {
|
||||
const auto options = getNotificationOptions(item);
|
||||
const auto options = GetNotificationOptions(item);
|
||||
|
||||
const auto peer = item->history()->peer;
|
||||
const auto scheduled = !options.hideNameAndPhoto
|
||||
|
|
|
@ -64,7 +64,10 @@ class Manager;
|
|||
|
||||
class System final : private base::Subscriber {
|
||||
public:
|
||||
explicit System(not_null<Main::Session*> session);
|
||||
System();
|
||||
~System();
|
||||
|
||||
[[nodiscard]] Main::Session *findSession(UserId selfId) const;
|
||||
|
||||
void createManager();
|
||||
|
||||
|
@ -72,6 +75,7 @@ public:
|
|||
void schedule(not_null<HistoryItem*> item);
|
||||
void clearFromHistory(not_null<History*> history);
|
||||
void clearIncomingFromHistory(not_null<History*> history);
|
||||
void clearFromSession(not_null<Main::Session*> session);
|
||||
void clearFromItem(not_null<HistoryItem*> 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<HistoryItem*> item) const;
|
||||
[[nodiscard]] SkipState skipNotification(
|
||||
not_null<HistoryItem*> item) const;
|
||||
|
||||
void showNext();
|
||||
void showGrouped();
|
||||
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;
|
||||
|
||||
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::flat_map<not_null<History*>, Waiter> _waiters;
|
||||
base::flat_map<not_null<History*>, Waiter> _settingWaiters;
|
||||
base::Timer _waitTimer;
|
||||
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;
|
||||
|
||||
|
@ -133,12 +126,28 @@ private:
|
|||
std::unique_ptr<Media::Audio::Track> _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(system) {
|
||||
}
|
||||
|
||||
|
@ -162,19 +171,20 @@ public:
|
|||
void clearFromHistory(not_null<History*> history) {
|
||||
doClearFromHistory(history);
|
||||
}
|
||||
void clearFromSession(not_null<Main::Session*> 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<HistoryItem*> item) = 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:
|
||||
|
|
|
@ -67,11 +67,6 @@ std::unique_ptr<Manager> 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<Notification>(
|
||||
subscribeToSession(&queued.history->session());
|
||||
_notifications.push_back(std::make_unique<Notification>(
|
||||
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<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 ¬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*> 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*> history) {
|
|||
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 ¬ification : _notifications) {
|
||||
if (notification->unlinkSession(session)) {
|
||||
_positionsOutdated = true;
|
||||
}
|
||||
}
|
||||
showNextFromQueue();
|
||||
}
|
||||
|
||||
void Manager::doClearFromItem(not_null<HistoryItem*> 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<Main::Session*> 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());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -38,19 +38,18 @@ class HideAllButton;
|
|||
class Manager;
|
||||
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:
|
||||
Manager(System *system);
|
||||
~Manager();
|
||||
|
||||
template <typename Method>
|
||||
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*> history) override;
|
||||
void doClearFromSession(not_null<Main::Session*> session) override;
|
||||
void doClearFromItem(not_null<HistoryItem*> item) override;
|
||||
|
||||
void showNextFromQueue();
|
||||
|
@ -86,7 +86,12 @@ private:
|
|||
|
||||
bool hasReplyingNotification() const;
|
||||
|
||||
void subscribeToSession(not_null<Main::Session*> session);
|
||||
|
||||
std::vector<std::unique_ptr<Notification>> _notifications;
|
||||
base::flat_map<
|
||||
not_null<Main::Session*>,
|
||||
base::Subscription> _subscriptions;
|
||||
|
||||
std::unique_ptr<HideAllButton> _hideAll;
|
||||
|
||||
|
@ -181,7 +186,7 @@ protected:
|
|||
|
||||
};
|
||||
|
||||
class Notification : public Widget {
|
||||
class Notification final : public Widget {
|
||||
public:
|
||||
Notification(
|
||||
not_null<Manager*> 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<Main::Session*> 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<PeerData*> _peer;
|
||||
|
||||
QPixmap _cache;
|
||||
|
|
Loading…
Add table
Reference in a new issue