From 81790b2271d23f29d61e766e02f7ff5c44231494 Mon Sep 17 00:00:00 2001 From: John Preston Date: Sat, 4 Mar 2017 22:36:59 +0300 Subject: [PATCH] Notifications management moved to AuthSession. Also implemented Global::WorkMode() as an base::Variable. --- Telegram/SourceFiles/apiwrap.cpp | 3 +- Telegram/SourceFiles/app.cpp | 17 +- Telegram/SourceFiles/application.cpp | 4 - Telegram/SourceFiles/auth_session.cpp | 37 +- Telegram/SourceFiles/auth_session.h | 38 +- Telegram/SourceFiles/boxes/addcontactbox.cpp | 3 +- Telegram/SourceFiles/boxes/backgroundbox.cpp | 3 +- Telegram/SourceFiles/boxes/confirmbox.cpp | 3 +- Telegram/SourceFiles/boxes/contactsbox.cpp | 5 +- .../SourceFiles/boxes/localstoragebox.cpp | 3 +- Telegram/SourceFiles/boxes/members_box.cpp | 7 +- .../SourceFiles/boxes/notifications_box.cpp | 12 +- Telegram/SourceFiles/boxes/sharebox.cpp | 6 +- Telegram/SourceFiles/boxes/stickers_box.cpp | 3 +- Telegram/SourceFiles/boxes/stickersetbox.cpp | 3 +- Telegram/SourceFiles/core/lambda.h | 2 +- Telegram/SourceFiles/core/observer.h | 14 +- Telegram/SourceFiles/core/task_queue.cpp | 72 ++-- Telegram/SourceFiles/core/task_queue.h | 7 +- Telegram/SourceFiles/dialogswidget.cpp | 7 +- Telegram/SourceFiles/facades.cpp | 6 +- Telegram/SourceFiles/facades.h | 20 +- Telegram/SourceFiles/history.cpp | 3 +- Telegram/SourceFiles/history.h | 1 + .../history/field_autocomplete.cpp | 3 +- .../SourceFiles/history/history_message.cpp | 7 +- Telegram/SourceFiles/historywidget.cpp | 21 +- Telegram/SourceFiles/intro/introwidget.cpp | 9 + Telegram/SourceFiles/layerwidget.cpp | 3 +- Telegram/SourceFiles/mainwidget.cpp | 44 +-- Telegram/SourceFiles/mainwidget.h | 5 +- Telegram/SourceFiles/mainwindow.cpp | 336 +----------------- Telegram/SourceFiles/mainwindow.h | 32 +- Telegram/SourceFiles/mediaview.cpp | 18 +- Telegram/SourceFiles/messenger.cpp | 14 +- Telegram/SourceFiles/messenger.h | 6 +- Telegram/SourceFiles/mtproto/mtp_instance.cpp | 2 +- Telegram/SourceFiles/overviewwidget.cpp | 6 +- Telegram/SourceFiles/passcodewidget.cpp | 2 +- .../platform/linux/main_window_linux.cpp | 21 +- .../platform/linux/main_window_linux.h | 9 +- .../linux/notifications_manager_linux.cpp | 198 ++++++----- .../linux/notifications_manager_linux.h | 14 +- .../platform/linux/specific_linux.cpp | 3 + .../platform/mac/main_window_mac.h | 20 +- .../platform/mac/main_window_mac.mm | 62 ++-- .../platform/mac/notifications_manager_mac.h | 16 +- .../platform/mac/notifications_manager_mac.mm | 68 ++-- .../SourceFiles/platform/mac/specific_mac_p.h | 1 - .../platform/mac/specific_mac_p.mm | 5 - .../platform/platform_notifications_manager.h | 5 +- .../platform/win/main_window_win.cpp | 22 +- .../platform/win/main_window_win.h | 9 +- .../win/notifications_manager_win.cpp | 205 +++++++---- .../platform/win/notifications_manager_win.h | 8 +- .../profile/profile_block_peer_list.cpp | 3 +- .../profile/profile_userpic_button.cpp | 4 +- Telegram/SourceFiles/settings.cpp | 1 - Telegram/SourceFiles/settings.h | 1 - .../settings/settings_general_widget.cpp | 12 +- .../settings_notifications_widget.cpp | 27 +- Telegram/SourceFiles/shortcuts.cpp | 2 +- Telegram/SourceFiles/stickers/emoji_pan.cpp | 3 +- .../SourceFiles/storage/file_download.cpp | 86 ++--- Telegram/SourceFiles/storage/file_download.h | 45 +-- .../SourceFiles/storage/localimageloader.h | 2 + Telegram/SourceFiles/storage/localstorage.cpp | 15 +- Telegram/SourceFiles/storage/localstorage.h | 1 + Telegram/SourceFiles/structs.cpp | 1 + Telegram/SourceFiles/structs.h | 28 ++ Telegram/SourceFiles/ui/images.cpp | 4 + Telegram/SourceFiles/ui/images.h | 34 +- Telegram/SourceFiles/window/main_window.cpp | 3 +- Telegram/SourceFiles/window/main_window.h | 3 + .../window/notifications_manager.cpp | 331 ++++++++++++++++- .../window/notifications_manager.h | 98 ++++- .../window/notifications_manager_default.cpp | 243 ++++++------- .../window/notifications_manager_default.h | 40 +-- .../window/notifications_utilities.cpp | 8 +- .../SourceFiles/window/window_main_menu.cpp | 3 +- 80 files changed, 1299 insertions(+), 1152 deletions(-) diff --git a/Telegram/SourceFiles/apiwrap.cpp b/Telegram/SourceFiles/apiwrap.cpp index 15a2a565c..7bf8db354 100644 --- a/Telegram/SourceFiles/apiwrap.cpp +++ b/Telegram/SourceFiles/apiwrap.cpp @@ -31,6 +31,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "auth_session.h" #include "boxes/confirmbox.h" #include "window/themes/window_theme.h" +#include "window/notifications_manager.h" ApiWrap::ApiWrap(QObject *parent) : QObject(parent) , _messageDataResolveDelayed(new SingleDelayedCall(this, "resolveMessageDatas")) { @@ -1016,7 +1017,7 @@ PeerData *ApiWrap::notifySettingReceived(MTPInputNotifyPeer notifyPeer, const MT } } break; } - App::wnd()->notifySettingGot(); + AuthSession::Current().notifications()->checkDelayed(); return requestedPeer; } diff --git a/Telegram/SourceFiles/app.cpp b/Telegram/SourceFiles/app.cpp index e866e10b5..1ae11252a 100644 --- a/Telegram/SourceFiles/app.cpp +++ b/Telegram/SourceFiles/app.cpp @@ -203,15 +203,14 @@ namespace { Media::Player::mixer()->stopAndClear(); if (auto w = wnd()) { w->tempDirDelete(Local::ClearManagerAll); - w->notifyClearFast(); w->setupIntro(); } + histories().clear(); Messenger::Instance().authSessionDestroy(); Local::reset(); Window::Theme::Background()->reset(); cSetOtherOnline(0); - histories().clear(); globalNotifyAllPtr = UnknownNotifySettings; globalNotifyUsersPtr = UnknownNotifySettings; globalNotifyChatsPtr = UnknownNotifySettings; @@ -2014,9 +2013,7 @@ namespace { dependent->dependencyItemRemoved(item); } } - if (auto manager = Window::Notifications::GetManager()) { - manager->clearFromItem(item); - } + AuthSession::Current().notifications()->clearFromItem(item); if (Global::started() && !App::quitting()) { Global::RefItemRemoved().notify(item, true); } @@ -2038,13 +2035,13 @@ namespace { ::dependentItems.clear(); QVector toDelete; - for_const (HistoryItem *item, msgsData) { + for_const (auto item, msgsData) { if (item->detached()) { toDelete.push_back(item); } } - for_const (const MsgsData &chMsgsData, channelMsgsData) { - for_const (HistoryItem *item, chMsgsData) { + for_const (auto &chMsgsData, channelMsgsData) { + for_const (auto item, chMsgsData) { if (item->detached()) { toDelete.push_back(item); } @@ -2052,8 +2049,8 @@ namespace { } msgsData.clear(); channelMsgsData.clear(); - for (int32 i = 0, l = toDelete.size(); i < l; ++i) { - delete toDelete[i]; + for_const (auto item, toDelete) { + delete item; } clearMousedItems(); diff --git a/Telegram/SourceFiles/application.cpp b/Telegram/SourceFiles/application.cpp index 2748e6764..abc2e8b24 100644 --- a/Telegram/SourceFiles/application.cpp +++ b/Telegram/SourceFiles/application.cpp @@ -319,10 +319,6 @@ void Application::closeApplication() { if (App::launchState() == App::QuitProcessed) return; App::setLaunchState(App::QuitProcessed); - if (auto manager = Window::Notifications::GetManager()) { - manager->clearAllFast(); - } - if (_messengerInstance) { Messenger::Instance().prepareToDestroy(); _messengerInstance.reset(); diff --git a/Telegram/SourceFiles/auth_session.cpp b/Telegram/SourceFiles/auth_session.cpp index ab31a9689..bd03e5665 100644 --- a/Telegram/SourceFiles/auth_session.cpp +++ b/Telegram/SourceFiles/auth_session.cpp @@ -21,18 +21,41 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "auth_session.h" #include "messenger.h" +#include "storage/file_download.h" +#include "window/notifications_manager.h" -AuthSession::AuthSession(UserId userId) : _userId(userId) { +AuthSession::AuthSession(UserId userId) +: _userId(userId) +, _downloader(std::make_unique()) +, _notifications(std::make_unique(this)) { t_assert(_userId != 0); } -AuthSession *AuthSession::Current() { - return Messenger::Instance().authSession(); +bool AuthSession::Exists() { + return (Messenger::Instance().authSession() != nullptr); +} + +AuthSession &AuthSession::Current() { + auto result = Messenger::Instance().authSession(); + t_assert(result != nullptr); + return *result; } UserData *AuthSession::CurrentUser() { - if (auto userId = CurrentUserId()) { - return App::user(userId); - } - return nullptr; + return App::user(CurrentUserId()); } + +base::Observable &AuthSession::CurrentDownloaderTaskFinished() { + return Current().downloader()->taskFinished(); +} + +bool AuthSession::validateSelf(const MTPUser &user) { + if (user.type() != mtpc_user || !user.c_user().is_self() || user.c_user().vid.v != userId()) { + LOG(("Auth Error: wrong self user received.")); + App::logOutDelayed(); + return false; + } + return true; +} + +AuthSession::~AuthSession() = default; diff --git a/Telegram/SourceFiles/auth_session.h b/Telegram/SourceFiles/auth_session.h index 8ac526c0c..1ff2dd6a2 100644 --- a/Telegram/SourceFiles/auth_session.h +++ b/Telegram/SourceFiles/auth_session.h @@ -20,29 +20,55 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org */ #pragma once -class AuthSession { +namespace Storage { +class Downloader; +} // namespace Storage + +namespace Window { +namespace Notifications { +class System; +} // namespace Notifications +} // namespace Window + +class AuthSession final { public: AuthSession(UserId userId); AuthSession(const AuthSession &other) = delete; AuthSession &operator=(const AuthSession &other) = delete; - static AuthSession *Current(); + static bool Exists(); + + static AuthSession &Current(); static UserId CurrentUserId() { - auto current = Current(); - return current ? current->userId() : 0; + return Current().userId(); } static PeerId CurrentUserPeerId() { - auto userId = CurrentUserId(); - return userId ? peerFromUser(userId) : 0; + return peerFromUser(CurrentUserId()); } static UserData *CurrentUser(); UserId userId() const { return _userId; } + bool validateSelf(const MTPUser &user); + + Storage::Downloader *downloader() { + return _downloader.get(); + } + + static base::Observable &CurrentDownloaderTaskFinished(); + + Window::Notifications::System *notifications() { + return _notifications.get(); + } + + ~AuthSession(); private: UserId _userId = 0; + const std::unique_ptr _downloader; + const std::unique_ptr _notifications; + }; diff --git a/Telegram/SourceFiles/boxes/addcontactbox.cpp b/Telegram/SourceFiles/boxes/addcontactbox.cpp index b50ec9625..52d1171d2 100644 --- a/Telegram/SourceFiles/boxes/addcontactbox.cpp +++ b/Telegram/SourceFiles/boxes/addcontactbox.cpp @@ -38,6 +38,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "mainwindow.h" #include "apiwrap.h" #include "observer_peer.h" +#include "auth_session.h" AddContactBox::AddContactBox(QWidget*, QString fname, QString lname, QString phone) : _first(this, st::defaultInputField, lang(lng_signup_firstname), fname) @@ -1119,7 +1120,7 @@ void RevokePublicLinkBox::prepare() { addButton(lang(lng_cancel), [this] { closeBox(); }); - subscribe(FileDownload::ImageLoaded(), [this] { update(); }); + subscribe(AuthSession::CurrentDownloaderTaskFinished(), [this] { update(); }); updateMaxHeight(); } diff --git a/Telegram/SourceFiles/boxes/backgroundbox.cpp b/Telegram/SourceFiles/boxes/backgroundbox.cpp index 237bb4752..d4df90ffe 100644 --- a/Telegram/SourceFiles/boxes/backgroundbox.cpp +++ b/Telegram/SourceFiles/boxes/backgroundbox.cpp @@ -27,6 +27,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "styles/style_overview.h" #include "styles/style_boxes.h" #include "ui/effects/round_checkbox.h" +#include "auth_session.h" BackgroundBox::BackgroundBox(QWidget*) { } @@ -63,7 +64,7 @@ BackgroundBox::Inner::Inner(QWidget *parent) : TWidget(parent) updateWallpapers(); } - subscribe(FileDownload::ImageLoaded(), [this] { update(); }); + subscribe(AuthSession::CurrentDownloaderTaskFinished(), [this] { update(); }); subscribe(Window::Theme::Background(), [this](const Window::Theme::BackgroundUpdate &update) { if (update.paletteChanged()) { _check->invalidateCache(); diff --git a/Telegram/SourceFiles/boxes/confirmbox.cpp b/Telegram/SourceFiles/boxes/confirmbox.cpp index cbe0f5ea4..cc0ea625a 100644 --- a/Telegram/SourceFiles/boxes/confirmbox.cpp +++ b/Telegram/SourceFiles/boxes/confirmbox.cpp @@ -32,6 +32,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "ui/toast/toast.h" #include "core/click_handler_types.h" #include "storage/localstorage.h" +#include "auth_session.h" TextParseOptions _confirmBoxTextOptions = { TextParseLinks | TextParseMultiline | TextParseRichText, // flags @@ -569,7 +570,7 @@ ConfirmInviteBox::ConfirmInviteBox(QWidget*, const QString &title, const MTPChat if (!location.isNull()) { _photo = ImagePtr(location); if (!_photo->loaded()) { - subscribe(FileDownload::ImageLoaded(), [this] { update(); }); + subscribe(AuthSession::CurrentDownloaderTaskFinished(), [this] { update(); }); _photo->load(); } } diff --git a/Telegram/SourceFiles/boxes/contactsbox.cpp b/Telegram/SourceFiles/boxes/contactsbox.cpp index 6e5abb2c7..50aba9103 100644 --- a/Telegram/SourceFiles/boxes/contactsbox.cpp +++ b/Telegram/SourceFiles/boxes/contactsbox.cpp @@ -43,6 +43,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "observer_peer.h" #include "apiwrap.h" #include "auth_session.h" +#include "storage/file_download.h" QString PeerFloodErrorText(PeerFloodType type) { auto link = textcmdLink(CreateInternalLinkHttps(qsl("spambot")), lang(lng_cant_more_info)); @@ -623,7 +624,7 @@ ContactsBox::Inner::Inner(QWidget *parent, UserData *bot) : TWidget(parent) } void ContactsBox::Inner::init() { - subscribe(FileDownload::ImageLoaded(), [this] { update(); }); + subscribe(AuthSession::CurrentDownloaderTaskFinished(), [this] { update(); }); connect(_addContactLnk, SIGNAL(clicked()), App::wnd(), SLOT(onShowAddContact())); connect(_allAdmins, SIGNAL(changed()), this, SLOT(onAllAdminsChanged())); @@ -848,7 +849,7 @@ void ContactsBox::Inner::loadProfilePhotos() { auto yFrom = _visibleTop - _rowsTop; auto yTo = yFrom + (_visibleBottom - _visibleTop) * 5; - MTP::clearLoaderPriorities(); + AuthSession::Current().downloader()->clearPriorities(); if (yTo < 0) return; if (yFrom < 0) yFrom = 0; diff --git a/Telegram/SourceFiles/boxes/localstoragebox.cpp b/Telegram/SourceFiles/boxes/localstoragebox.cpp index b6af89009..505ea29ae 100644 --- a/Telegram/SourceFiles/boxes/localstoragebox.cpp +++ b/Telegram/SourceFiles/boxes/localstoragebox.cpp @@ -25,6 +25,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "storage/localstorage.h" #include "lang.h" #include "mainwindow.h" +#include "auth_session.h" LocalStorageBox::LocalStorageBox(QWidget *parent) : _clear(this, lang(lng_local_storage_clear), st::boxLinkButton) { @@ -40,7 +41,7 @@ void LocalStorageBox::prepare() { connect(App::wnd(), SIGNAL(tempDirCleared(int)), this, SLOT(onTempDirCleared(int))); connect(App::wnd(), SIGNAL(tempDirClearFailed(int)), this, SLOT(onTempDirClearFailed(int))); - subscribe(FileDownload::ImageLoaded(), [this] { update(); }); + subscribe(AuthSession::CurrentDownloaderTaskFinished(), [this] { update(); }); updateControls(); diff --git a/Telegram/SourceFiles/boxes/members_box.cpp b/Telegram/SourceFiles/boxes/members_box.cpp index 05c4e1215..df6e6dfef 100644 --- a/Telegram/SourceFiles/boxes/members_box.cpp +++ b/Telegram/SourceFiles/boxes/members_box.cpp @@ -31,7 +31,8 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "ui/widgets/scroll_area.h" #include "ui/effects/ripple_animation.h" #include "observer_peer.h" - +#include "auth_session.h" +#include "storage/file_download.h" MembersAddButton::MembersAddButton(QWidget *parent, const style::TwoIconButton &st) : RippleButton(parent, st.ripple) , _st(st) { @@ -132,7 +133,7 @@ MembersBox::Inner::Inner(QWidget *parent, ChannelData *channel, MembersFilter fi , _kickWidth(st::normalFont->width(_kickText)) , _aboutWidth(st::boxWideWidth - st::contactsPadding.left() - st::contactsPadding.right()) , _about(_aboutWidth) { - subscribe(FileDownload::ImageLoaded(), [this] { update(); }); + subscribe(AuthSession::CurrentDownloaderTaskFinished(), [this] { update(); }); connect(App::main(), SIGNAL(peerNameChanged(PeerData*,const PeerData::Names&,const PeerData::NameFirstChars&)), this, SLOT(onPeerNameChanged(PeerData*, const PeerData::Names&, const PeerData::NameFirstChars&))); connect(App::main(), SIGNAL(peerPhotoChanged(PeerData*)), this, SLOT(peerUpdated(PeerData*))); @@ -341,7 +342,7 @@ void MembersBox::Inner::loadProfilePhotos() { auto yFrom = _visibleTop; auto yTo = yFrom + (_visibleBottom - _visibleTop) * 5; - MTP::clearLoaderPriorities(); + AuthSession::Current().downloader()->clearPriorities(); if (yTo < 0) return; if (yFrom < 0) yFrom = 0; diff --git a/Telegram/SourceFiles/boxes/notifications_box.cpp b/Telegram/SourceFiles/boxes/notifications_box.cpp index 5328e230d..9550463d3 100644 --- a/Telegram/SourceFiles/boxes/notifications_box.cpp +++ b/Telegram/SourceFiles/boxes/notifications_box.cpp @@ -28,11 +28,15 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "styles/style_window.h" #include "mainwindow.h" #include "storage/localstorage.h" +#include "auth_session.h" +#include "window/notifications_manager.h" namespace { constexpr int kMaxNotificationsCount = 5; +using ChangeType = Window::Notifications::ChangeType; + } // namespace class NotificationsBox::SampleWidget : public QWidget { @@ -190,7 +194,7 @@ void NotificationsBox::countChanged() { if (currentCount() != Global::NotificationsCount()) { Global::SetNotificationsCount(currentCount()); - Global::RefNotifySettingsChanged().notify(Notify::ChangeType::MaxCount); + AuthSession::Current().notifications()->settingsChanged().notify(ChangeType::MaxCount); Local::writeUserSettings(); } } @@ -347,7 +351,7 @@ void NotificationsBox::setOverCorner(Notify::ScreenCorner corner) { _isOverCorner = true; setCursor(style::cur_pointer); Global::SetNotificationsDemoIsShown(true); - Global::RefNotifySettingsChanged().notify(Notify::ChangeType::DemoIsShown); + AuthSession::Current().notifications()->settingsChanged().notify(ChangeType::DemoIsShown); } _overCorner = corner; @@ -382,7 +386,7 @@ void NotificationsBox::clearOverCorner() { _isOverCorner = false; setCursor(style::cur_default); Global::SetNotificationsDemoIsShown(false); - Global::RefNotifySettingsChanged().notify(Notify::ChangeType::DemoIsShown); + AuthSession::Current().notifications()->settingsChanged().notify(ChangeType::DemoIsShown); for_const (auto &samples, _cornerSamples) { for_const (auto widget, samples) { @@ -409,7 +413,7 @@ void NotificationsBox::mouseReleaseEvent(QMouseEvent *e) { if (_chosenCorner != Global::NotificationsCorner()) { Global::SetNotificationsCorner(_chosenCorner); - Global::RefNotifySettingsChanged().notify(Notify::ChangeType::Corner); + AuthSession::Current().notifications()->settingsChanged().notify(ChangeType::Corner); Local::writeUserSettings(); } } diff --git a/Telegram/SourceFiles/boxes/sharebox.cpp b/Telegram/SourceFiles/boxes/sharebox.cpp index 0525c114d..235bfe5f6 100644 --- a/Telegram/SourceFiles/boxes/sharebox.cpp +++ b/Telegram/SourceFiles/boxes/sharebox.cpp @@ -294,7 +294,7 @@ ShareBox::Inner::Inner(QWidget *parent, ShareBox::FilterCallback &&filterCallbac subscribe(Notify::PeerUpdated(), Notify::PeerUpdatedHandler(observeEvents, [this](const Notify::PeerUpdate &update) { notifyPeerUpdated(update); })); - subscribe(FileDownload::ImageLoaded(), [this] { update(); }); + subscribe(AuthSession::CurrentDownloaderTaskFinished(), [this] { update(); }); subscribe(Window::Theme::Background(), [this](const Window::Theme::BackgroundUpdate &update) { if (update.paletteChanged()) { @@ -435,7 +435,7 @@ void ShareBox::Inner::loadProfilePhotos(int yFrom) { yFrom *= _columnCount; yTo *= _columnCount; - MTP::clearLoaderPriorities(); + AuthSession::Current().downloader()->clearPriorities(); if (_filter.isEmpty()) { if (!_chatsIndexed->isEmpty()) { auto i = _chatsIndexed->cfind(yFrom, _rowHeight); @@ -953,7 +953,7 @@ void shareGameScoreByHash(const QString &hash) { } auto hashDataInts = reinterpret_cast(hashData.data()); - if (hashDataInts[0] != AuthSession::CurrentUserId()) { + if (!AuthSession::Exists() || hashDataInts[0] != AuthSession::CurrentUserId()) { Ui::show(Box(lang(lng_share_wrong_user))); return; } diff --git a/Telegram/SourceFiles/boxes/stickers_box.cpp b/Telegram/SourceFiles/boxes/stickers_box.cpp index f91f55c79..972dadb30 100644 --- a/Telegram/SourceFiles/boxes/stickers_box.cpp +++ b/Telegram/SourceFiles/boxes/stickers_box.cpp @@ -35,6 +35,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "ui/effects/ripple_animation.h" #include "ui/effects/slide_animation.h" #include "ui/widgets/discrete_sliders.h" +#include "auth_session.h" namespace { @@ -576,7 +577,7 @@ StickersBox::Inner::Inner(QWidget *parent, const Stickers::Order &archivedIds) : } void StickersBox::Inner::setup() { - subscribe(FileDownload::ImageLoaded(), [this] { + subscribe(AuthSession::CurrentDownloaderTaskFinished(), [this] { update(); readVisibleSets(); }); diff --git a/Telegram/SourceFiles/boxes/stickersetbox.cpp b/Telegram/SourceFiles/boxes/stickersetbox.cpp index 62986e032..30d69c63f 100644 --- a/Telegram/SourceFiles/boxes/stickersetbox.cpp +++ b/Telegram/SourceFiles/boxes/stickersetbox.cpp @@ -32,6 +32,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "styles/style_stickers.h" #include "ui/widgets/buttons.h" #include "ui/widgets/scroll_area.h" +#include "auth_session.h" StickerSetBox::StickerSetBox(QWidget*, const MTPInputStickerSet &set) : _set(set) { @@ -109,7 +110,7 @@ StickerSetBox::Inner::Inner(QWidget *parent, const MTPInputStickerSet &set) : TW MTP::send(MTPmessages_GetStickerSet(_input), rpcDone(&Inner::gotSet), rpcFail(&Inner::failedSet)); App::main()->updateStickers(); - subscribe(FileDownload::ImageLoaded(), [this] { update(); }); + subscribe(AuthSession::CurrentDownloaderTaskFinished(), [this] { update(); }); setMouseTracking(true); diff --git a/Telegram/SourceFiles/core/lambda.h b/Telegram/SourceFiles/core/lambda.h index 2990a7928..6438aea66 100644 --- a/Telegram/SourceFiles/core/lambda.h +++ b/Telegram/SourceFiles/core/lambda.h @@ -176,7 +176,7 @@ struct vtable_once_impl : public vtable_base, + &bad_const_call, &vtable_once_impl::call_method, &vtable_once_impl::destruct_method) { } diff --git a/Telegram/SourceFiles/core/observer.h b/Telegram/SourceFiles/core/observer.h index 57dd964da..b83400544 100644 --- a/Telegram/SourceFiles/core/observer.h +++ b/Telegram/SourceFiles/core/observer.h @@ -376,7 +376,7 @@ public: void setForced(parameter_type newValue, bool sync = false) { _value = newValue; - _observable.notify(_value, sync); + changed().notify(_value, sync); } void set(parameter_type newValue, bool sync = false) { @@ -388,16 +388,16 @@ public: template void process(Callback callback, bool sync = false) { callback(_value); - _observable.notify(_value, sync); + changed().notify(_value, sync); } - Observable &observable() { - return _observable; + Observable &changed() { + return _changed; } private: Type _value; - Observable _observable; + Observable _changed; }; @@ -416,12 +416,12 @@ protected: template int subscribe(base::Variable &variable, Lambda &&handler) { - return subscribe(variable.observable(), std::forward(handler)); + return subscribe(variable.changed(), std::forward(handler)); } template int subscribe(base::Variable *variable, Lambda &&handler) { - return subscribe(variable->observable(), std::forward(handler)); + return subscribe(variable->changed(), std::forward(handler)); } void unsubscribe(int index) { diff --git a/Telegram/SourceFiles/core/task_queue.cpp b/Telegram/SourceFiles/core/task_queue.cpp index 0e0c973ec..59508ac7c 100644 --- a/Telegram/SourceFiles/core/task_queue.cpp +++ b/Telegram/SourceFiles/core/task_queue.cpp @@ -20,30 +20,14 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org */ #include "core/task_queue.h" +#include +#include + namespace base { namespace { -auto MainThreadId = QThread::currentThreadId(); -const auto MaxThreadsCount = qMax(QThread::idealThreadCount(), 2); - -template -class Thread : public QThread { -public: - Thread(Lambda code) : _code(std::move(code)) { - } - void run() override { - _code(); - } - -private: - Lambda _code; - -}; - -template -object_ptr> MakeThread(Lambda code) { - return object_ptr>(std::move(code)); -} +auto MainThreadId = std::this_thread::get_id(); +const auto MaxThreadsCount = qMax(std::thread::hardware_concurrency(), 2U); } // namespace @@ -76,7 +60,7 @@ class TaskQueue::TaskThreadPool { public: TaskThreadPool(const Private &) { } - static const QSharedPointer &Instance(); + static const std::shared_ptr &Instance(); void AddQueueTask(TaskQueue *queue, Task &&task); void RemoveQueue(TaskQueue *queue); @@ -84,16 +68,15 @@ public: ~TaskThreadPool(); private: - void ThreadFunction(); - std::vector> threads_; - QMutex queues_mutex_; + std::vector threads_; + std::mutex queues_mutex_; // queues_mutex_ must be locked when working with the list. TaskQueueList queue_list_; - QWaitCondition thread_condition_; + std::condition_variable thread_condition_; bool stopped_ = false; int tasks_in_process_ = 0; int background_tasks_in_process_ = 0; @@ -192,7 +175,7 @@ TaskQueue *TaskQueue::TaskQueueList::TakeFirst(int list_index_) { } void TaskQueue::TaskThreadPool::AddQueueTask(TaskQueue *queue, Task &&task) { - QMutexLocker lock(&queues_mutex_); + std::unique_lock lock(queues_mutex_); queue->tasks_.push_back(std::move(task)); auto list_was_empty = queue_list_.Empty(kAllQueuesList); @@ -207,18 +190,17 @@ void TaskQueue::TaskThreadPool::AddQueueTask(TaskQueue *queue, Task &&task) { } } if (will_create_thread) { - threads_.push_back(MakeThread([this]() { + threads_.emplace_back([this]() { ThreadFunction(); - })); - threads_.back()->start(); + }); } else if (some_threads_are_vacant) { t_assert(threads_count > tasks_in_process_); - thread_condition_.wakeOne(); + thread_condition_.notify_one(); } } void TaskQueue::TaskThreadPool::RemoveQueue(TaskQueue *queue) { - QMutexLocker lock(&queues_mutex_); + std::unique_lock lock(queues_mutex_); if (queue_list_.IsInList(queue)) { queue_list_.Unregister(queue); } @@ -229,18 +211,18 @@ void TaskQueue::TaskThreadPool::RemoveQueue(TaskQueue *queue) { TaskQueue::TaskThreadPool::~TaskThreadPool() { { - QMutexLocker lock(&queues_mutex_); + std::unique_lock lock(queues_mutex_); queue_list_.Clear(); stopped_ = true; } - thread_condition_.wakeAll(); + thread_condition_.notify_all(); for (auto &thread : threads_) { - thread->wait(); + thread.join(); } } -const QSharedPointer &TaskQueue::TaskThreadPool::Instance() { // static - static auto Pool = MakeShared(Private()); +const std::shared_ptr &TaskQueue::TaskThreadPool::Instance() { // static + static auto Pool = std::make_shared(Private()); return Pool; } @@ -259,7 +241,7 @@ void TaskQueue::TaskThreadPool::ThreadFunction() { while (true) { Task task; { - QMutexLocker lock(&queues_mutex_); + std::unique_lock lock(queues_mutex_); // Finish the previous task processing. if (task_was_processed) { @@ -285,7 +267,7 @@ void TaskQueue::TaskThreadPool::ThreadFunction() { if (stopped_) { return; } - thread_condition_.wait(&queues_mutex_); + thread_condition_.wait(lock); } // Select a task we will be processing. @@ -331,7 +313,7 @@ TaskQueue::TaskQueue(Type type, Priority priority) TaskQueue::~TaskQueue() { if (type_ != Type::Main && type_ != Type::Special) { - if (auto thread_pool = weak_thread_pool_.toStrongRef()) { + if (auto thread_pool = weak_thread_pool_.lock()) { thread_pool->RemoveQueue(this); } } @@ -339,7 +321,7 @@ TaskQueue::~TaskQueue() { void TaskQueue::Put(Task &&task) { if (type_ == Type::Main) { - QMutexLocker lock(&tasks_mutex_); + std::unique_lock lock(tasks_mutex_); tasks_.push_back(std::move(task)); Sandbox::MainThreadTaskAdded(); @@ -350,14 +332,14 @@ void TaskQueue::Put(Task &&task) { } void TaskQueue::ProcessMainTasks() { // static - t_assert(QThread::currentThreadId() == MainThreadId); + t_assert(std::this_thread::get_id() == MainThreadId); while (ProcessOneMainTask()) { } } void TaskQueue::ProcessMainTasks(TimeMs max_time_spent) { // static - t_assert(QThread::currentThreadId() == MainThreadId); + t_assert(std::this_thread::get_id() == MainThreadId); auto start_time = getms(); while (ProcessOneMainTask()) { @@ -370,7 +352,7 @@ void TaskQueue::ProcessMainTasks(TimeMs max_time_spent) { // static bool TaskQueue::ProcessOneMainTask() { // static Task task; { - QMutexLocker lock(&Main().tasks_mutex_); + std::unique_lock lock(Main().tasks_mutex_); auto &tasks = Main().tasks_; if (tasks.empty()) { return false; @@ -386,7 +368,7 @@ bool TaskQueue::ProcessOneMainTask() { // static bool TaskQueue::IsMyThread() const { if (type_ == Type::Main) { - return (QThread::currentThreadId() == MainThreadId); + return (std::this_thread::get_id() == MainThreadId); } t_assert(type_ != Type::Special); return false; diff --git a/Telegram/SourceFiles/core/task_queue.h b/Telegram/SourceFiles/core/task_queue.h index d2e57baa9..33897d641 100644 --- a/Telegram/SourceFiles/core/task_queue.h +++ b/Telegram/SourceFiles/core/task_queue.h @@ -20,6 +20,9 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org */ #pragma once +#include +#include + namespace base { using Task = lambda_once; @@ -70,11 +73,11 @@ private: const Priority priority_; std::deque tasks_; - QMutex tasks_mutex_; // Only for the main queue. + std::mutex tasks_mutex_; // Only for the main queue. // Only for the other queues, not main. class TaskThreadPool; - QWeakPointer weak_thread_pool_; + std::weak_ptr weak_thread_pool_; class TaskQueueList; diff --git a/Telegram/SourceFiles/dialogswidget.cpp b/Telegram/SourceFiles/dialogswidget.cpp index 83615bd04..45749dba6 100644 --- a/Telegram/SourceFiles/dialogswidget.cpp +++ b/Telegram/SourceFiles/dialogswidget.cpp @@ -45,6 +45,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "autoupdater.h" #include "observer_peer.h" #include "auth_session.h" +#include "window/notifications_manager.h" namespace { @@ -95,7 +96,7 @@ DialogsInner::DialogsInner(QWidget *parent, QWidget *main) : SplittedWidget(pare connect(_cancelSearchInPeer, SIGNAL(clicked()), this, SIGNAL(cancelSearchInPeer())); _cancelSearchInPeer->hide(); - subscribe(FileDownload::ImageLoaded(), [this] { update(); }); + subscribe(AuthSession::CurrentDownloaderTaskFinished(), [this] { update(); }); subscribe(Global::RefItemRemoved(), [this](HistoryItem *item) { itemRemoved(item); }); @@ -999,7 +1000,7 @@ void DialogsInner::removeDialog(History *history) { if (_dialogsImportant) { history->removeFromChatList(Dialogs::Mode::Important, _dialogsImportant.get()); } - if (App::wnd()) App::wnd()->notifyClear(history); + AuthSession::Current().notifications()->clearFromHistory(history); if (_contacts->contains(history->peer->id)) { if (!_contactsNoDialogs->contains(history->peer->id)) { _contactsNoDialogs->addByName(history); @@ -1889,7 +1890,7 @@ void DialogsInner::loadPeerPhotos() { auto yFrom = _visibleTop; auto yTo = _visibleTop + (_visibleBottom - _visibleTop) * (PreloadHeightsCount + 1); - MTP::clearLoaderPriorities(); + AuthSession::Current().downloader()->clearPriorities(); if (_state == DefaultState) { auto otherStart = shownDialogs()->size() * st::dialogsRowHeight; if (yFrom < otherStart) { diff --git a/Telegram/SourceFiles/facades.cpp b/Telegram/SourceFiles/facades.cpp index 09954cd3f..e17f980da 100644 --- a/Telegram/SourceFiles/facades.cpp +++ b/Telegram/SourceFiles/facades.cpp @@ -686,7 +686,6 @@ struct Data { int NotificationsCount = 3; Notify::ScreenCorner NotificationsCorner = Notify::ScreenCorner::BottomRight; bool NotificationsDemoIsShown = false; - base::Observable NotifySettingsChanged; DBIConnectionType ConnectionType = dbictAuto; bool TryIPv6 = (cPlatform() == dbipWindows) ? false : true; @@ -699,6 +698,8 @@ struct Data { bool LocalPasscode = false; base::Observable LocalPasscodeChanged; + base::Variable WorkMode = { dbiwmWindowAndTray }; + base::Observable ItemRemoved; base::Observable UnreadCounterUpdate; base::Observable PeerChooseCancel; @@ -807,7 +808,6 @@ DefineVar(Global, bool, NativeNotifications); DefineVar(Global, int, NotificationsCount); DefineVar(Global, Notify::ScreenCorner, NotificationsCorner); DefineVar(Global, bool, NotificationsDemoIsShown); -DefineRefVar(Global, base::Observable, NotifySettingsChanged); DefineVar(Global, DBIConnectionType, ConnectionType); DefineVar(Global, bool, TryIPv6); @@ -820,6 +820,8 @@ DefineVar(Global, int, AutoLock); DefineVar(Global, bool, LocalPasscode); DefineRefVar(Global, base::Observable, LocalPasscodeChanged); +DefineRefVar(Global, base::Variable, WorkMode); + DefineRefVar(Global, base::Observable, ItemRemoved); DefineRefVar(Global, base::Observable, UnreadCounterUpdate); DefineRefVar(Global, base::Observable, PeerChooseCancel); diff --git a/Telegram/SourceFiles/facades.h b/Telegram/SourceFiles/facades.h index 6a6546170..97eaa7c17 100644 --- a/Telegram/SourceFiles/facades.h +++ b/Telegram/SourceFiles/facades.h @@ -187,15 +187,6 @@ void historyMuteUpdated(History *history); void handlePendingHistoryUpdate(); void unreadCounterUpdated(); -enum class ChangeType { - SoundEnabled, - IncludeMuted, - DesktopEnabled, - ViewParams, - MaxCount, - Corner, - DemoIsShown, -}; enum class ScreenCorner { TopLeft = 0, @@ -214,14 +205,6 @@ inline bool IsTopCorner(ScreenCorner corner) { } // namespace Notify -namespace base { - -template <> -struct custom_is_fast_copy_type : public std::true_type { -}; - -} // namespace base - #define DeclareReadOnlyVar(Type, Name) const Type &Name(); #define DeclareRefVar(Type, Name) DeclareReadOnlyVar(Type, Name) \ Type &Ref##Name(); @@ -395,7 +378,6 @@ DeclareVar(bool, NativeNotifications); DeclareVar(int, NotificationsCount); DeclareVar(Notify::ScreenCorner, NotificationsCorner); DeclareVar(bool, NotificationsDemoIsShown); -DeclareRefVar(base::Observable, NotifySettingsChanged); DeclareVar(DBIConnectionType, ConnectionType); DeclareVar(bool, TryIPv6); @@ -408,6 +390,8 @@ DeclareVar(int, AutoLock); DeclareVar(bool, LocalPasscode); DeclareRefVar(base::Observable, LocalPasscodeChanged); +DeclareRefVar(base::Variable, WorkMode); + DeclareRefVar(base::Observable, ItemRemoved); DeclareRefVar(base::Observable, UnreadCounterUpdate); DeclareRefVar(base::Observable, PeerChooseCancel); diff --git a/Telegram/SourceFiles/history.cpp b/Telegram/SourceFiles/history.cpp index c28f6fd21..443001a8e 100644 --- a/Telegram/SourceFiles/history.cpp +++ b/Telegram/SourceFiles/history.cpp @@ -32,6 +32,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "window/top_bar_widget.h" #include "observer_peer.h" #include "auth_session.h" +#include "window/notifications_manager.h" namespace { @@ -1503,7 +1504,7 @@ MsgId History::inboxRead(MsgId upTo) { } showFrom = nullptr; - App::wnd()->notifyClear(this); + AuthSession::Current().notifications()->clearFromHistory(this); return upTo; } diff --git a/Telegram/SourceFiles/history.h b/Telegram/SourceFiles/history.h index 698dd8e36..1a45f866c 100644 --- a/Telegram/SourceFiles/history.h +++ b/Telegram/SourceFiles/history.h @@ -23,6 +23,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "structs.h" #include "dialogs/dialogs_common.h" #include "ui/effects/send_action_animations.h" +#include "core/observer.h" void historyInit(); diff --git a/Telegram/SourceFiles/history/field_autocomplete.cpp b/Telegram/SourceFiles/history/field_autocomplete.cpp index 6582a990b..a5a0ce4d2 100644 --- a/Telegram/SourceFiles/history/field_autocomplete.cpp +++ b/Telegram/SourceFiles/history/field_autocomplete.cpp @@ -27,6 +27,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "styles/style_history.h" #include "styles/style_widgets.h" #include "styles/style_stickers.h" +#include "auth_session.h" FieldAutocomplete::FieldAutocomplete(QWidget *parent) : TWidget(parent) , _scroll(this, st::mentionScroll) { @@ -533,7 +534,7 @@ FieldAutocompleteInner::FieldAutocompleteInner(FieldAutocomplete *parent, Mentio , _previewShown(false) { _previewTimer.setSingleShot(true); connect(&_previewTimer, SIGNAL(timeout()), this, SLOT(onPreview())); - subscribe(FileDownload::ImageLoaded(), [this] { update(); }); + subscribe(AuthSession::CurrentDownloaderTaskFinished(), [this] { update(); }); } void FieldAutocompleteInner::paintEvent(QPaintEvent *e) { diff --git a/Telegram/SourceFiles/history/history_message.cpp b/Telegram/SourceFiles/history/history_message.cpp index 65b7848c4..05928514b 100644 --- a/Telegram/SourceFiles/history/history_message.cpp +++ b/Telegram/SourceFiles/history/history_message.cpp @@ -31,6 +31,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "styles/style_dialogs.h" #include "styles/style_widgets.h" #include "styles/style_history.h" +#include "window/notifications_manager.h" namespace { @@ -2032,10 +2033,8 @@ bool HistoryService::updateDependent(bool force) { } updateDependentText(); } - if (force) { - if (gotDependencyItem && App::wnd()) { - App::wnd()->notifySettingGot(); - } + if (force && gotDependencyItem) { + AuthSession::Current().notifications()->checkDelayed(); } return (dependent->msg || !dependent->msgId); } diff --git a/Telegram/SourceFiles/historywidget.cpp b/Telegram/SourceFiles/historywidget.cpp index bd9acc29a..90f39c4d4 100644 --- a/Telegram/SourceFiles/historywidget.cpp +++ b/Telegram/SourceFiles/historywidget.cpp @@ -61,6 +61,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "ui/widgets/popup_menu.h" #include "platform/platform_file_utilities.h" #include "auth_session.h" +#include "window/notifications_manager.h" namespace { @@ -3130,7 +3131,7 @@ HistoryWidget::HistoryWidget(QWidget *parent) : TWidget(parent) , _topShadow(this, st::shadowFg) { setAcceptDrops(true); - subscribe(FileDownload::ImageLoaded(), [this] { update(); }); + subscribe(AuthSession::CurrentDownloaderTaskFinished(), [this] { update(); }); connect(_scroll, SIGNAL(scrolled()), this, SLOT(onScroll())); connect(_reportSpamPanel, SIGNAL(reportClicked()), this, SLOT(onReportSpamClicked())); connect(_reportSpamPanel, SIGNAL(hideClicked()), this, SLOT(onReportSpamHide())); @@ -4341,7 +4342,7 @@ void HistoryWidget::showHistory(const PeerId &peerId, MsgId showAtMsgId, bool re if (_peer) { App::forgetMedia(); _serviceImageCacheSize = imageCacheSize(); - MTP::clearLoaderPriorities(); + AuthSession::Current().downloader()->clearPriorities(); _history = App::history(_peer->id); _migrated = _peer->migrateFrom() ? App::history(_peer->migrateFrom()->id) : 0; @@ -4797,7 +4798,7 @@ void HistoryWidget::newUnreadMsg(History *history, HistoryItem *item) { return; } } - App::wnd()->notifySchedule(history, item); + AuthSession::Current().notifications()->schedule(history, item); history->setUnreadCount(history->unreadCount() + 1); } @@ -6803,8 +6804,6 @@ void HistoryWidget::sendFileConfirmed(const FileLoadResultPtr &file) { } void HistoryWidget::onPhotoUploaded(const FullMsgId &newId, bool silent, const MTPInputFile &file) { - if (!AuthSession::Current()) return; - if (auto item = App::histItemById(newId)) { uint64 randomId = rand_value(); App::historyRegRandom(randomId, newId); @@ -6853,8 +6852,6 @@ namespace { } void HistoryWidget::onDocumentUploaded(const FullMsgId &newId, bool silent, const MTPInputFile &file) { - if (!AuthSession::Current()) return; - if (auto item = dynamic_cast(App::histItemById(newId))) { auto media = item->getMedia(); if (auto document = media ? media->getDocument() : nullptr) { @@ -6881,8 +6878,6 @@ void HistoryWidget::onDocumentUploaded(const FullMsgId &newId, bool silent, cons } void HistoryWidget::onThumbDocumentUploaded(const FullMsgId &newId, bool silent, const MTPInputFile &file, const MTPInputFile &thumb) { - if (!AuthSession::Current()) return; - if (auto item = dynamic_cast(App::histItemById(newId))) { auto media = item->getMedia(); if (auto document = media ? media->getDocument() : nullptr) { @@ -6909,8 +6904,6 @@ void HistoryWidget::onThumbDocumentUploaded(const FullMsgId &newId, bool silent, } void HistoryWidget::onPhotoProgress(const FullMsgId &newId) { - if (!AuthSession::Current()) return; - if (auto item = App::histItemById(newId)) { auto photo = (item->getMedia() && item->getMedia()->type() == MediaTypePhoto) ? static_cast(item->getMedia())->photo() : nullptr; if (!item->isPost()) { @@ -6921,8 +6914,6 @@ void HistoryWidget::onPhotoProgress(const FullMsgId &newId) { } void HistoryWidget::onDocumentProgress(const FullMsgId &newId) { - if (!AuthSession::Current()) return; - if (auto item = App::histItemById(newId)) { auto media = item->getMedia(); auto document = media ? media->getDocument() : nullptr; @@ -6934,8 +6925,6 @@ void HistoryWidget::onDocumentProgress(const FullMsgId &newId) { } void HistoryWidget::onPhotoFailed(const FullMsgId &newId) { - if (!AuthSession::Current()) return; - HistoryItem *item = App::histItemById(newId); if (item) { if (!item->isPost()) { @@ -6946,8 +6935,6 @@ void HistoryWidget::onPhotoFailed(const FullMsgId &newId) { } void HistoryWidget::onDocumentFailed(const FullMsgId &newId) { - if (!AuthSession::Current()) return; - if (auto item = App::histItemById(newId)) { auto media = item->getMedia(); auto document = media ? media->getDocument() : nullptr; diff --git a/Telegram/SourceFiles/intro/introwidget.cpp b/Telegram/SourceFiles/intro/introwidget.cpp index 74b1e2559..89e827175 100644 --- a/Telegram/SourceFiles/intro/introwidget.cpp +++ b/Telegram/SourceFiles/intro/introwidget.cpp @@ -425,6 +425,15 @@ QString Widget::Step::nextButtonText() const { } void Widget::Step::finish(const MTPUser &user, QImage photo) { + if (user.type() != mtpc_user || !user.c_user().is_self()) { + // No idea what to do here. + // We could've reset intro and MTP, but this really should not happen. + Ui::show(Box("Internal error: bad user.is_self() after sign in.")); + return; + } + + Messenger::Instance().authSessionCreate(user.c_user().vid.v); + App::wnd()->setupMain(&user); // "this" is already deleted here by creating the main widget. diff --git a/Telegram/SourceFiles/layerwidget.cpp b/Telegram/SourceFiles/layerwidget.cpp index 87e14f144..7c76cbdbe 100644 --- a/Telegram/SourceFiles/layerwidget.cpp +++ b/Telegram/SourceFiles/layerwidget.cpp @@ -32,6 +32,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "styles/style_stickers.h" #include "ui/widgets/shadow.h" #include "window/window_main_menu.h" +#include "auth_session.h" namespace { @@ -720,7 +721,7 @@ LayerStackWidget::~LayerStackWidget() { MediaPreviewWidget::MediaPreviewWidget(QWidget *parent) : TWidget(parent) , _emojiSize(Ui::Emoji::Size(Ui::Emoji::Index() + 1) / cIntRetinaFactor()) { setAttribute(Qt::WA_TransparentForMouseEvents); - subscribe(FileDownload::ImageLoaded(), [this] { update(); }); + subscribe(AuthSession::CurrentDownloaderTaskFinished(), [this] { update(); }); } void MediaPreviewWidget::paintEvent(QPaintEvent *e) { diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index ff7daaf38..cd7174fc2 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -63,6 +63,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "mtproto/dc_options.h" #include "core/file_utilities.h" #include "auth_session.h" +#include "window/notifications_manager.h" StackItemSection::StackItemSection(std::unique_ptr &&memento) : StackItem(nullptr) , _memento(std::move(memento)) { @@ -810,7 +811,6 @@ void MainWidget::deleteHistoryPart(DeleteHistoryRequest request, const MTPmessag } int32 offset = d.voffset.v; - if (!AuthSession::Current()) return; if (offset <= 0) { cRefReportSpamStatuses().remove(peer->id); Local::writeReportSpamStatuses(); @@ -909,7 +909,6 @@ void MainWidget::deleteAllFromUserPart(DeleteAllFromUserParams params, const MTP } int32 offset = d.voffset.v; - if (!AuthSession::Current()) return; if (offset > 0) { MTP::send(MTPchannels_DeleteUserHistory(params.channel->inputChannel, params.from->inputUser), rpcDone(&MainWidget::deleteAllFromUserPart, params)); } else if (History *h = App::historyLoaded(params.channel)) { @@ -1504,7 +1503,6 @@ void MainWidget::overviewLoaded(History *history, const MTPmessages_Messages &re } void MainWidget::sendReadRequest(PeerData *peer, MsgId upTo) { - if (!AuthSession::Current()) return; if (peer->isChannel()) { _readRequests.insert(peer, qMakePair(MTP::send(MTPchannels_ReadHistory(peer->asChannel()->inputChannel, MTP_int(upTo)), rpcDone(&MainWidget::channelReadDone, peer), rpcFail(&MainWidget::readRequestFail, peer)), upTo)); } else { @@ -2106,8 +2104,6 @@ void MainWidget::fillPeerMenu(PeerData *peer, base::lambda now) { @@ -3655,8 +3649,6 @@ void MainWidget::onGetDifferenceTimeByPts() { } void MainWidget::onGetDifferenceTimeAfterFail() { - if (!AuthSession::Current()) return; - auto now = getms(true), wait = 0LL; if (_getDifferenceTimeAfterFail) { if (_getDifferenceTimeAfterFail > now) { @@ -3731,24 +3723,21 @@ void MainWidget::mtpPing() { MTP::ping(); } -void MainWidget::start(const MTPUser &user) { - int32 uid = user.c_user().vid.v; - if (!uid) { - LOG(("MTP Error: incorrect user received")); - App::logOut(); +void MainWidget::start(const MTPUser *self) { + if (!self) { + MTP::send(MTPusers_GetUsers(MTP_vector(1, MTP_inputUserSelf())), rpcDone(&MainWidget::startWithSelf)); return; } - if (AuthSession::CurrentUserId() != uid) { - Messenger::Instance().authSessionCreate(uid); - Local::writeMtpData(); + if (!AuthSession::Current().validateSelf(*self)) { + return; } Local::readSavedPeers(); - cSetOtherOnline(0); - if (auto self = App::feedUsers(MTP_vector(1, user))) { - self->loadUserpic(); + if (auto user = App::feedUsers(MTP_vector(1, *self))) { + user->loadUserpic(); } + MTP::send(MTPupdates_GetState(), rpcDone(&MainWidget::gotState)); update(); @@ -4039,12 +4028,13 @@ bool MainWidget::inviteImportFail(const RPCError &error) { return true; } -void MainWidget::startFull(const MTPVector &users) { - const auto &v(users.c_vector().v); - if (v.isEmpty() || v[0].type() != mtpc_user || !v[0].c_user().is_self()) { // wtf?.. +void MainWidget::startWithSelf(const MTPVector &users) { + auto &v = users.c_vector().v; + if (v.isEmpty()) { + LOG(("Auth Error: self user not received.")); return App::logOutDelayed(); } - start(v[0]); + start(&v[0]); } void MainWidget::applyNotifySetting(const MTPNotifyPeer &peer, const MTPPeerNotifySettings &settings, History *h) { @@ -4100,7 +4090,7 @@ void MainWidget::applyNotifySetting(const MTPNotifyPeer &peer, const MTPPeerNoti if (!h) h = App::history(updatePeer->id); int32 changeIn = 0; if (isNotifyMuted(setTo, &changeIn)) { - App::wnd()->notifyClear(h); + AuthSession::Current().notifications()->clearFromHistory(h); h->setMute(true); App::regMuted(updatePeer, changeIn); } else { @@ -4379,7 +4369,7 @@ void MainWidget::checkIdleFinish() { } void MainWidget::updateReceived(const mtpPrime *from, const mtpPrime *end) { - if (end <= from || !AuthSession::Current()) return; + if (end <= from) return; App::wnd()->checkAutoLock(); @@ -4638,8 +4628,6 @@ void MainWidget::feedUpdates(const MTPUpdates &updates, uint64 randomId) { } void MainWidget::feedUpdate(const MTPUpdate &update) { - if (!AuthSession::Current()) return; - switch (update.type()) { case mtpc_updateNewMessage: { auto &d = update.c_updateNewMessage(); diff --git a/Telegram/SourceFiles/mainwidget.h b/Telegram/SourceFiles/mainwidget.h index f95bf62f0..89805c347 100644 --- a/Telegram/SourceFiles/mainwidget.h +++ b/Telegram/SourceFiles/mainwidget.h @@ -158,7 +158,7 @@ public: void showAnimated(const QPixmap &bgAnimCache, bool back = false); - void start(const MTPUser &user); + void start(const MTPUser *self = nullptr); void checkStartUrl(); void openLocalUrl(const QString &str); @@ -166,7 +166,6 @@ public: void joinGroupByHash(const QString &hash); void stickersBox(const MTPInputStickerSet &set); - void startFull(const MTPVector &users); bool started(); void applyNotifySetting(const MTPNotifyPeer &peer, const MTPPeerNotifySettings &settings, History *history = 0); @@ -518,6 +517,8 @@ private: Window::SectionSlideParams prepareOverviewAnimation(); Window::SectionSlideParams prepareDialogsAnimation(); + void startWithSelf(const MTPVector &users); + void saveSectionInStack(); bool _started = false; diff --git a/Telegram/SourceFiles/mainwindow.cpp b/Telegram/SourceFiles/mainwindow.cpp index a5798ff27..839637405 100644 --- a/Telegram/SourceFiles/mainwindow.cpp +++ b/Telegram/SourceFiles/mainwindow.cpp @@ -100,17 +100,6 @@ MainWindow::MainWindow() { iconbig32 = iconbig256.scaledToWidth(32, Qt::SmoothTransformation); iconbig64 = iconbig256.scaledToWidth(64, Qt::SmoothTransformation); - subscribe(Global::RefNotifySettingsChanged(), [this](Notify::ChangeType type) { - if (type == Notify::ChangeType::DesktopEnabled) { - updateTrayMenu(); - notifyClear(); - } else if (type == Notify::ChangeType::ViewParams) { - notifyUpdateAll(); - } else if (type == Notify::ChangeType::IncludeMuted) { - Notify::unreadCounterUpdated(); - } - }); - resize(st::windowDefaultWidth, st::windowDefaultHeight); setLocale(QLocale(QLocale::English, QLocale::UnitedStates)); @@ -118,8 +107,6 @@ MainWindow::MainWindow() { _inactiveTimer.setSingleShot(true); connect(&_inactiveTimer, SIGNAL(timeout()), this, SLOT(onInactiveTimer())); - connect(&_notifyWaitTimer, SIGNAL(timeout()), this, SLOT(notifyShowNext())); - connect(&_autoLockTimer, SIGNAL(timeout()), this, SLOT(checkAutoLock())); subscribe(Global::RefSelfChanged(), [this]() { updateGlobalMenu(); }); @@ -156,7 +143,7 @@ void MainWindow::onStateChanged(Qt::WindowState state) { updateIsActive((state == Qt::WindowMinimized) ? Global::OfflineBlurTimeout() : Global::OnlineFocusTimeout()); psUpdateSysMenu(state); - if (state == Qt::WindowMinimized && cWorkMode() == dbiwmTrayOnly) { + if (state == Qt::WindowMinimized && Global::WorkMode().value() == dbiwmTrayOnly) { App::wnd()->minimizeToTray(); } savePosition(state); @@ -200,7 +187,7 @@ void MainWindow::firstShow() { trayIconMenu->addAction(notificationActionText, this, SLOT(toggleDisplayNotifyFromTray()))->setEnabled(true); trayIconMenu->addAction(lang(lng_quit_from_tray), this, SLOT(quitFromTray()))->setEnabled(true); } - psUpdateWorkmode(); + workmodeUpdated(Global::WorkMode().value()); psFirstShow(); updateTrayMenu(); @@ -241,7 +228,7 @@ void MainWindow::clearPasscode() { } else { _main->showAnimated(bg, true); } - notifyUpdateAll(); + AuthSession::Current().notifications()->updateAll(); updateGlobalMenu(); if (_main) { @@ -265,7 +252,9 @@ void MainWindow::setupPasscode() { setInnerFocus(); } _shouldLockAt = 0; - notifyUpdateAll(); + if (AuthSession::Exists()) { + AuthSession::Current().notifications()->updateAll(); + } updateGlobalMenu(); } @@ -360,6 +349,9 @@ void MainWindow::setupMain(const MTPUser *self) { auto bg = animated ? grabInner() : QPixmap(); clearWidgets(); + + t_assert(AuthSession::Exists()); + _main.create(bodyWidget()); _main->show(); updateControlsGeometry(); @@ -369,11 +361,7 @@ void MainWindow::setupMain(const MTPUser *self) { } else { _main->activate(); } - if (self) { - _main->start(*self); - } else { - MTP::send(MTPusers_GetUsers(MTP_vector(1, MTP_inputUserSelf())), _main->rpcDone(&MainWidget::startFull)); - } + _main->start(self); fixOrder(); @@ -573,7 +561,7 @@ bool MainWindow::doWeReadServerHistory() { } void MainWindow::checkHistoryActivation() { - if (_main && AuthSession::Current() && doWeReadServerHistory()) { + if (_main && doWeReadServerHistory()) { _main->markActiveHistoryAsRead(); } } @@ -842,9 +830,9 @@ void MainWindow::toggleDisplayNotifyFromTray() { } } Local::writeUserSettings(); - Global::RefNotifySettingsChanged().notify(Notify::ChangeType::DesktopEnabled); + AuthSession::Current().notifications()->settingsChanged().notify(Window::Notifications::ChangeType::DesktopEnabled); if (soundNotifyChanged) { - Global::RefNotifySettingsChanged().notify(Notify::ChangeType::SoundEnabled); + AuthSession::Current().notifications()->settingsChanged().notify(Window::Notifications::ChangeType::SoundEnabled); } } @@ -854,7 +842,7 @@ void MainWindow::closeEvent(QCloseEvent *e) { App::quit(); } else { e->ignore(); - if (!AuthSession::Current() || !Ui::hideWindowNoQuit()) { + if (!AuthSession::Exists() || !Ui::hideWindowNoQuit()) { App::quit(); } } @@ -919,305 +907,10 @@ void MainWindow::onClearFailed(int task, void *manager) { emit tempDirClearFailed(task); } -void MainWindow::notifySchedule(History *history, HistoryItem *item) { - if (App::quitting() || !history->currentNotification() || !App::api()) return; - - PeerData *notifyByFrom = (!history->peer->isUser() && item->mentionsMe()) ? item->from() : 0; - - if (item->isSilent()) { - history->popNotification(item); - return; - } - - bool haveSetting = (history->peer->notify != UnknownNotifySettings); - if (haveSetting) { - if (history->peer->notify != EmptyNotifySettings && history->peer->notify->mute > unixtime()) { - if (notifyByFrom) { - haveSetting = (item->from()->notify != UnknownNotifySettings); - if (haveSetting) { - if (notifyByFrom->notify != EmptyNotifySettings && notifyByFrom->notify->mute > unixtime()) { - history->popNotification(item); - return; - } - } else { - App::api()->requestNotifySetting(notifyByFrom); - } - } else { - history->popNotification(item); - return; - } - } - } else { - if (notifyByFrom && notifyByFrom->notify == UnknownNotifySettings) { - App::api()->requestNotifySetting(notifyByFrom); - } - App::api()->requestNotifySetting(history->peer); - } - if (!item->notificationReady()) { - haveSetting = false; - } - - int delay = item->Has() ? 500 : 100, t = unixtime(); - auto ms = getms(true); - bool isOnline = _main->lastWasOnline(), otherNotOld = ((cOtherOnline() * 1000LL) + Global::OnlineCloudTimeout() > t * 1000LL); - bool otherLaterThanMe = (cOtherOnline() * 1000LL + (ms - _main->lastSetOnline()) > t * 1000LL); - if (!isOnline && otherNotOld && otherLaterThanMe) { - delay = Global::NotifyCloudDelay(); - } else if (cOtherOnline() >= t) { - delay = Global::NotifyDefaultDelay(); - } - - auto when = ms + delay; - _notifyWhenAlerts[history].insert(when, notifyByFrom); - if (Global::DesktopNotify() && !Platform::Notifications::SkipToast()) { - NotifyWhenMaps::iterator i = _notifyWhenMaps.find(history); - if (i == _notifyWhenMaps.end()) { - i = _notifyWhenMaps.insert(history, NotifyWhenMap()); - } - if (i.value().constFind(item->id) == i.value().cend()) { - i.value().insert(item->id, when); - } - NotifyWaiters *addTo = haveSetting ? &_notifyWaiters : &_notifySettingWaiters; - NotifyWaiters::const_iterator it = addTo->constFind(history); - if (it == addTo->cend() || it->when > when) { - addTo->insert(history, NotifyWaiter(item->id, when, notifyByFrom)); - } - } - if (haveSetting) { - if (!_notifyWaitTimer.isActive() || _notifyWaitTimer.remainingTime() > delay) { - _notifyWaitTimer.start(delay); - } - } -} - -void MainWindow::notifyClear(History *history) { - if (!history) { - Window::Notifications::GetManager()->clearAll(); - - for (auto i = _notifyWhenMaps.cbegin(), e = _notifyWhenMaps.cend(); i != e; ++i) { - i.key()->clearNotifications(); - } - _notifyWhenMaps.clear(); - _notifyWhenAlerts.clear(); - _notifyWaiters.clear(); - _notifySettingWaiters.clear(); - return; - } - - Window::Notifications::GetManager()->clearFromHistory(history); - - history->clearNotifications(); - _notifyWhenMaps.remove(history); - _notifyWhenAlerts.remove(history); - _notifyWaiters.remove(history); - _notifySettingWaiters.remove(history); - - _notifyWaitTimer.stop(); - notifyShowNext(); -} - -void MainWindow::notifyClearFast() { - Window::Notifications::GetManager()->clearAllFast(); - - _notifyWhenMaps.clear(); - _notifyWhenAlerts.clear(); - _notifyWaiters.clear(); - _notifySettingWaiters.clear(); -} - -void MainWindow::notifySettingGot() { - int32 t = unixtime(); - for (NotifyWaiters::iterator i = _notifySettingWaiters.begin(); i != _notifySettingWaiters.end();) { - History *history = i.key(); - bool loaded = false, muted = false; - if (history->peer->notify != UnknownNotifySettings) { - if (history->peer->notify == EmptyNotifySettings || history->peer->notify->mute <= t) { - loaded = true; - } else if (PeerData *from = i.value().notifyByFrom) { - if (from->notify != UnknownNotifySettings) { - if (from->notify == EmptyNotifySettings || from->notify->mute <= t) { - loaded = true; - } else { - loaded = muted = true; - } - } - } else { - loaded = muted = true; - } - } - if (loaded) { - if (HistoryItem *item = App::histItemById(history->channelId(), i.value().msg)) { - if (!item->notificationReady()) { - loaded = false; - } - } else { - muted = true; - } - } - if (loaded) { - if (!muted) { - _notifyWaiters.insert(i.key(), i.value()); - } - i = _notifySettingWaiters.erase(i); - } else { - ++i; - } - } - _notifyWaitTimer.stop(); - notifyShowNext(); -} - -void MainWindow::notifyShowNext() { - if (App::quitting()) return; - - auto ms = getms(true), nextAlert = 0LL; - bool alert = false; - int32 now = unixtime(); - for (NotifyWhenAlerts::iterator i = _notifyWhenAlerts.begin(); i != _notifyWhenAlerts.end();) { - while (!i.value().isEmpty() && i.value().begin().key() <= ms) { - NotifySettingsPtr n = i.key()->peer->notify, f = i.value().begin().value() ? i.value().begin().value()->notify : UnknownNotifySettings; - while (!i.value().isEmpty() && i.value().begin().key() <= ms + 500) { // not more than one sound in 500ms from one peer - grouping - i.value().erase(i.value().begin()); - } - if (n == EmptyNotifySettings || (n != UnknownNotifySettings && n->mute <= now)) { - alert = true; - } else if (f == EmptyNotifySettings || (f != UnknownNotifySettings && f->mute <= now)) { // notify by from() - alert = true; - } - } - if (i.value().isEmpty()) { - i = _notifyWhenAlerts.erase(i); - } else { - if (!nextAlert || nextAlert > i.value().begin().key()) { - nextAlert = i.value().begin().key(); - } - ++i; - } - } - if (alert) { - psFlash(); - App::playSound(); - } - - if (_notifyWaiters.isEmpty() || !Global::DesktopNotify() || Platform::Notifications::SkipToast()) { - if (nextAlert) { - _notifyWaitTimer.start(nextAlert - ms); - } - return; - } - - while (true) { - auto next = 0LL; - HistoryItem *notifyItem = 0; - History *notifyHistory = 0; - for (NotifyWaiters::iterator i = _notifyWaiters.begin(); i != _notifyWaiters.end();) { - History *history = i.key(); - if (history->currentNotification() && history->currentNotification()->id != i.value().msg) { - NotifyWhenMaps::iterator j = _notifyWhenMaps.find(history); - if (j == _notifyWhenMaps.end()) { - history->clearNotifications(); - i = _notifyWaiters.erase(i); - continue; - } - do { - NotifyWhenMap::const_iterator k = j.value().constFind(history->currentNotification()->id); - if (k != j.value().cend()) { - i.value().msg = k.key(); - i.value().when = k.value(); - break; - } - history->skipNotification(); - } while (history->currentNotification()); - } - if (!history->currentNotification()) { - _notifyWhenMaps.remove(history); - i = _notifyWaiters.erase(i); - continue; - } - auto when = i.value().when; - if (!notifyItem || next > when) { - next = when; - notifyItem = history->currentNotification(); - notifyHistory = history; - } - ++i; - } - if (notifyItem) { - if (next > ms) { - if (nextAlert && nextAlert < next) { - next = nextAlert; - nextAlert = 0; - } - _notifyWaitTimer.start(next - ms); - break; - } else { - HistoryItem *fwd = notifyItem->Has() ? notifyItem : nullptr; // forwarded notify grouping - int32 fwdCount = 1; - - auto ms = getms(true); - History *history = notifyItem->history(); - NotifyWhenMaps::iterator j = _notifyWhenMaps.find(history); - if (j == _notifyWhenMaps.cend()) { - history->clearNotifications(); - } else { - HistoryItem *nextNotify = 0; - do { - history->skipNotification(); - if (!history->hasNotification()) { - break; - } - - j.value().remove((fwd ? fwd : notifyItem)->id); - do { - NotifyWhenMap::const_iterator k = j.value().constFind(history->currentNotification()->id); - if (k != j.value().cend()) { - nextNotify = history->currentNotification(); - _notifyWaiters.insert(notifyHistory, NotifyWaiter(k.key(), k.value(), 0)); - break; - } - history->skipNotification(); - } while (history->hasNotification()); - if (nextNotify) { - if (fwd) { - HistoryItem *nextFwd = nextNotify->Has() ? nextNotify : nullptr; - if (nextFwd && fwd->author() == nextFwd->author() && qAbs(int64(nextFwd->date.toTime_t()) - int64(fwd->date.toTime_t())) < 2) { - fwd = nextFwd; - ++fwdCount; - } else { - nextNotify = 0; - } - } else { - nextNotify = 0; - } - } - } while (nextNotify); - } - - Window::Notifications::GetManager()->showNotification(notifyItem, fwdCount); - - if (!history->hasNotification()) { - _notifyWaiters.remove(history); - _notifyWhenMaps.remove(history); - continue; - } - } - } else { - break; - } - } - if (nextAlert) { - _notifyWaitTimer.start(nextAlert - ms); - } -} - void MainWindow::app_activateClickHandler(ClickHandlerPtr handler, Qt::MouseButton button) { handler->onClick(button); } -void MainWindow::notifyUpdateAll() { - Window::Notifications::GetManager()->updateAll(); -} - QImage MainWindow::iconLarge() const { return iconbig256; } @@ -1346,7 +1039,6 @@ void MainWindow::updateIsActiveHook() { } MainWindow::~MainWindow() { - notifyClearFast(); if (_clearManager) { _clearManager->stop(); _clearManager = nullptr; diff --git a/Telegram/SourceFiles/mainwindow.h b/Telegram/SourceFiles/mainwindow.h index 7d36a99c2..cb4056893 100644 --- a/Telegram/SourceFiles/mainwindow.h +++ b/Telegram/SourceFiles/mainwindow.h @@ -87,7 +87,7 @@ public: void clearPasscode(); void checkAutoLockIn(int msec); void setupIntro(); - void setupMain(const MTPUser *user = 0); + void setupMain(const MTPUser *user = nullptr); void serviceNotification(const TextWithEntities &message, const MTPMessageMedia &media = MTP_messageMediaEmpty(), int32 date = 0, bool force = false); void serviceNotificationLocal(QString text); void sendServiceHistoryRequest(); @@ -120,12 +120,6 @@ public: TempDirState localStorageState(); void tempDirDelete(int task); - void notifySettingGot(); - void notifySchedule(History *history, HistoryItem *item); - void notifyClear(History *history = 0); - void notifyClearFast(); - void notifyUpdateAll(); - QImage iconLarge() const; void sendPaths(); @@ -183,8 +177,6 @@ public slots: void onClearFinished(int task, void *manager); void onClearFailed(int task, void *manager); - void notifyShowNext(); - void onShowAddContact(); void onShowNewGroup(); void onShowNewChannel(); @@ -240,28 +232,6 @@ private: SingleTimer _autoLockTimer; TimeMs _shouldLockAt = 0; - using NotifyWhenMap = QMap; - using NotifyWhenMaps = QMap; - NotifyWhenMaps _notifyWhenMaps; - struct NotifyWaiter { - NotifyWaiter(MsgId msg, TimeMs when, PeerData *notifyByFrom) - : msg(msg) - , when(when) - , notifyByFrom(notifyByFrom) { - } - MsgId msg; - TimeMs when; - PeerData *notifyByFrom; - }; - using NotifyWaiters = QMap; - NotifyWaiters _notifyWaiters; - NotifyWaiters _notifySettingWaiters; - SingleTimer _notifyWaitTimer; - - using NotifyWhenAlert = QMap; - using NotifyWhenAlerts = QMap; - NotifyWhenAlerts _notifyWhenAlerts; - }; class PreLaunchWindow : public TWidget { diff --git a/Telegram/SourceFiles/mediaview.cpp b/Telegram/SourceFiles/mediaview.cpp index 36374f457..1924fb232 100644 --- a/Telegram/SourceFiles/mediaview.cpp +++ b/Telegram/SourceFiles/mediaview.cpp @@ -36,6 +36,9 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "window/themes/window_theme_preview.h" #include "core/task_queue.h" #include "observer_peer.h" +#include "auth_session.h" +#include "messenger.h" +#include "storage/file_download.h" namespace { @@ -86,10 +89,15 @@ MediaView::MediaView(QWidget*) : TWidget(nullptr) connect(QApplication::desktop(), SIGNAL(resized(int)), this, SLOT(onScreenResized(int))); - subscribe(FileDownload::ImageLoaded(), [this] { - if (!isHidden()) { - updateControls(); - } + // While we have one mediaview for all authsessions we have to do this. + subscribe(Messenger::Instance().authSessionChanged(), [this] { + if (!AuthSession::Exists()) return; + + subscribe(AuthSession::CurrentDownloaderTaskFinished(), [this] { + if (!isHidden()) { + updateControls(); + } + }); }); auto observeEvents = Notify::PeerUpdate::Flag::SharedMediaChanged; subscribe(Notify::PeerUpdated(), Notify::PeerUpdatedHandler(observeEvents, [this](const Notify::PeerUpdate &update) { @@ -1155,7 +1163,7 @@ void MediaView::displayPhoto(PhotoData *photo, HistoryItem *item) { } _zoomToScreen = 0; - MTP::clearLoaderPriorities(); + AuthSession::Current().downloader()->clearPriorities(); _full = -1; _current = QPixmap(); _down = OverNone; diff --git a/Telegram/SourceFiles/messenger.cpp b/Telegram/SourceFiles/messenger.cpp index b7ad0efa5..3024bd2a2 100644 --- a/Telegram/SourceFiles/messenger.cpp +++ b/Telegram/SourceFiles/messenger.cpp @@ -86,7 +86,6 @@ Messenger::Messenger() : QObject() anim::startManager(); historyInit(); Media::Player::start(); - Window::Notifications::Start(); DEBUG_LOG(("Application Info: inited...")); @@ -130,7 +129,7 @@ Messenger::Messenger() : QObject() if (state == Local::ReadMapPassNeeded) { _window->setupPasscode(); } else { - if (AuthSession::Current()) { + if (AuthSession::Exists()) { _window->setupMain(); } else { _window->setupIntro(); @@ -196,7 +195,7 @@ QByteArray Messenger::serializeMtpAuthorization() const { QDataStream stream(&buffer); stream.setVersion(QDataStream::Qt_5_1); - stream << qint32(AuthSession::CurrentUserId()) << qint32(mainDcId); + stream << qint32(AuthSession::Exists() ? AuthSession::CurrentUserId() : 0) << qint32(mainDcId); writeKeys(stream, keys); writeKeys(stream, keysToDestroy); } @@ -433,7 +432,7 @@ bool Messenger::peerPhotoFail(PeerId peer, const RPCError &error) { } void Messenger::peerClearPhoto(PeerId id) { - if (!AuthSession::Current()) return; + if (!AuthSession::Exists()) return; if (id == AuthSession::CurrentUserPeerId()) { MTP::send(MTPphotos_UpdateProfilePhoto(MTP_inputPhotoEmpty()), rpcDone(&Messenger::selfPhotoCleared), rpcFail(&Messenger::peerPhotoFail, id)); @@ -525,7 +524,7 @@ void Messenger::killDownloadSessions() { } void Messenger::photoUpdated(const FullMsgId &msgId, bool silent, const MTPInputFile &file) { - if (!AuthSession::Current()) return; + if (!AuthSession::Exists()) return; auto i = photoUpdates.find(msgId); if (i != photoUpdates.end()) { @@ -583,10 +582,12 @@ void Messenger::onSwitchTestMode() { void Messenger::authSessionCreate(UserId userId) { _authSession = std::make_unique(userId); + authSessionChanged().notify(); } void Messenger::authSessionDestroy() { _authSession.reset(); + authSessionChanged().notify(); } FileUploader *Messenger::uploader() { @@ -656,6 +657,7 @@ void Messenger::prepareToDestroy() { // Some MTP requests can be cancelled from data clearing. App::clearHistories(); + authSessionDestroy(); _delayedDestroyedLoaders.clear(); _mtproto.reset(); @@ -668,8 +670,6 @@ Messenger::~Messenger() { Shortcuts::finish(); - Window::Notifications::Finish(); - anim::stopManager(); stopWebLoadManager(); diff --git a/Telegram/SourceFiles/messenger.h b/Telegram/SourceFiles/messenger.h index 3d5463909..87cb104e7 100644 --- a/Telegram/SourceFiles/messenger.h +++ b/Telegram/SourceFiles/messenger.h @@ -32,7 +32,7 @@ class MainWidget; class FileUploader; class Translator; -class Messenger : public QObject, public RPCSender, private base::Subscriber { +class Messenger final : public QObject, public RPCSender, private base::Subscriber { Q_OBJECT public: @@ -73,6 +73,9 @@ public: } void authSessionCreate(UserId userId); void authSessionDestroy(); + base::Observable &authSessionChanged() { + return _authSessionChanged; + } FileUploader *uploader(); void uploadProfilePhoto(const QImage &tosend, const PeerId &peerId); @@ -145,6 +148,7 @@ private: std::unique_ptr _mtproto; std::unique_ptr _mtprotoForKeysDestroy; std::unique_ptr _authSession; + base::Observable _authSessionChanged; SingleDelayedCall _delayedLoadersDestroyer; std::vector> _delayedDestroyedLoaders; diff --git a/Telegram/SourceFiles/mtproto/mtp_instance.cpp b/Telegram/SourceFiles/mtproto/mtp_instance.cpp index 37760afb8..1d4e43a1f 100644 --- a/Telegram/SourceFiles/mtproto/mtp_instance.cpp +++ b/Telegram/SourceFiles/mtproto/mtp_instance.cpp @@ -796,7 +796,7 @@ bool Instance::Private::rpcErrorOccured(mtpRequestId requestId, const RPCFailHan } bool Instance::Private::hasAuthorization() { - return (AuthSession::Current() != nullptr); + return AuthSession::Exists(); } void Instance::Private::importDone(const MTPauth_Authorization &result, mtpRequestId requestId) { diff --git a/Telegram/SourceFiles/overviewwidget.cpp b/Telegram/SourceFiles/overviewwidget.cpp index d32d6374b..4ff7dec87 100644 --- a/Telegram/SourceFiles/overviewwidget.cpp +++ b/Telegram/SourceFiles/overviewwidget.cpp @@ -42,6 +42,8 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "history/history_service_layout.h" #include "media/media_audio.h" #include "observer_peer.h" +#include "auth_session.h" +#include "storage/file_download.h" // flick scroll taken from http://qt-project.org/doc/qt-4.8/demos-embedded-anomaly-src-flickcharm-cpp.html @@ -59,7 +61,7 @@ OverviewInner::OverviewInner(OverviewWidget *overview, Ui::ScrollArea *scroll, P , _cancelSearch(this, st::dialogsCancelSearch) , _itemsToBeLoaded(LinksOverviewPerPage * 2) , _width(st::windowMinWidth) { - subscribe(FileDownload::ImageLoaded(), [this] { update(); }); + subscribe(AuthSession::Current().downloader()->taskFinished(), [this] { update(); }); subscribe(Global::RefItemRemoved(), [this](HistoryItem *item) { itemRemoved(item); }); @@ -1917,7 +1919,7 @@ void OverviewWidget::clear() { } void OverviewWidget::onScroll() { - MTP::clearLoaderPriorities(); + AuthSession::Current().downloader()->clearPriorities(); int32 preloadThreshold = _scroll->height() * 5; bool needToPreload = false; do { diff --git a/Telegram/SourceFiles/passcodewidget.cpp b/Telegram/SourceFiles/passcodewidget.cpp index 50d185018..2a5dff84b 100644 --- a/Telegram/SourceFiles/passcodewidget.cpp +++ b/Telegram/SourceFiles/passcodewidget.cpp @@ -72,7 +72,7 @@ void PasscodeWidget::onSubmit() { cSetPasscodeBadTries(0); Messenger::Instance().startMtp(); - if (AuthSession::Current()) { + if (AuthSession::Exists()) { App::wnd()->setupMain(); } else { App::wnd()->setupIntro(); diff --git a/Telegram/SourceFiles/platform/linux/main_window_linux.cpp b/Telegram/SourceFiles/platform/linux/main_window_linux.cpp index 7e380d289..65f661fd4 100644 --- a/Telegram/SourceFiles/platform/linux/main_window_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/main_window_linux.cpp @@ -164,8 +164,10 @@ static gboolean _trayIconCheck(gpointer/* pIn*/) { if (Libs::gtk_status_icon_is_embedded(_trayIcon)) { trayIconChecked = true; cSetSupportTray(true); + if (Global::started()) { + Global::RefWorkMode().setForced(Global::WorkMode().value(), true); + } if (App::wnd()) { - App::wnd()->psUpdateWorkmode(); Notify::unreadCounterUpdated(); App::wnd()->updateTrayMenu(); } @@ -196,7 +198,7 @@ void MainWindow::initHook() { } bool MainWindow::hasTrayIcon() const { - return trayIcon || ((useAppIndicator || (useStatusIcon && trayIconChecked)) && (cWorkMode() != dbiwmWindowOnly)); + return trayIcon || ((useAppIndicator || (useStatusIcon && trayIconChecked)) && (Global::WorkMode().value() != dbiwmWindowOnly)); } void MainWindow::psStatusIconCheck() { @@ -272,10 +274,10 @@ void MainWindow::psSetupTrayIcon() { } } -void MainWindow::psUpdateWorkmode() { +void MainWindow::workmodeUpdated(DBIWorkMode mode) { if (!cSupportTray()) return; - if (cWorkMode() == dbiwmWindowOnly) { + if (mode == dbiwmWindowOnly) { if (noQtTrayIcon) { if (useAppIndicator) { Libs::app_indicator_set_status(_trayIndicator, APP_INDICATOR_STATUS_PASSIVE); @@ -372,10 +374,6 @@ void MainWindow::updateIconCounters() { } } -bool MainWindow::psHasNativeNotifications() { - return Notifications::Supported(); -} - void MainWindow::LibsLoaded() { noQtTrayIcon = !DesktopEnvironment::TryQtTrayIcon(); tryAppIndicator = !DesktopEnvironment::PreferAppIndicatorTrayIcon(); @@ -518,7 +516,7 @@ void MainWindow::psCreateTrayIcon() { Libs::g_idle_add((GSourceFunc)_trayIconCheck, 0); _psCheckStatusIconTimer.start(100); } else { - psUpdateWorkmode(); + workmodeUpdated(Global::WorkMode().value()); } } @@ -555,7 +553,7 @@ void MainWindow::psFirstShow() { if ((cLaunchMode() == LaunchModeAutoStart && cStartMinimized()) || cStartInTray()) { setWindowState(Qt::WindowMinimized); - if (cWorkMode() == dbiwmTrayOnly || cWorkMode() == dbiwmWindowAndTray) { + if (Global::WorkMode().value() == dbiwmTrayOnly || Global::WorkMode().value() == dbiwmWindowAndTray) { hide(); } else { show(); @@ -577,9 +575,6 @@ void MainWindow::psUpdateSysMenu(Qt::WindowState state) { void MainWindow::psUpdateMargins() { } -void MainWindow::psFlash() { -} - MainWindow::~MainWindow() { if (_trayIcon) { Libs::g_object_unref(_trayIcon); diff --git a/Telegram/SourceFiles/platform/linux/main_window_linux.h b/Telegram/SourceFiles/platform/linux/main_window_linux.h index 830d4438f..d23f6ac07 100644 --- a/Telegram/SourceFiles/platform/linux/main_window_linux.h +++ b/Telegram/SourceFiles/platform/linux/main_window_linux.h @@ -35,16 +35,9 @@ public: void psUpdateSysMenu(Qt::WindowState state); void psUpdateMargins(); - void psFlash(); - void psNotifySettingGot(); - - void psUpdateWorkmode(); - void psRefreshTaskbarIcon() { } - bool psHasNativeNotifications(); - virtual QImage iconWithCounter(int size, int count, style::color bg, style::color fg, bool smallIcon) = 0; static void LibsLoaded(); @@ -63,6 +56,8 @@ protected: bool hasTrayIcon() const override; + void workmodeUpdated(DBIWorkMode mode) override; + QSystemTrayIcon *trayIcon = nullptr; QMenu *trayIconMenu = nullptr; QImage icon256, iconbig256; diff --git a/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp b/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp index bdef8d83d..5f83de52a 100644 --- a/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp @@ -24,13 +24,12 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "platform/linux/linux_libnotify.h" #include "platform/linux/linux_libs.h" #include "lang.h" +#include "core/task_queue.h" namespace Platform { namespace Notifications { namespace { -NeverFreedPointer ManagerInstance; - bool LibNotifyLoaded() { return (Libs::notify_init != nullptr) && (Libs::notify_uninit != nullptr) @@ -100,10 +99,10 @@ QString escapeHtml(const QString &text) { class NotificationData { public: - NotificationData(const QString &title, const QString &body, const QStringList &capabilities, PeerId peerId, MsgId msgId) + NotificationData(const std::shared_ptr &guarded, const QString &title, const QString &body, const QStringList &capabilities, PeerId peerId, MsgId msgId) : _data(Libs::notify_notification_new(title.toUtf8().constData(), body.toUtf8().constData(), nullptr)) { if (valid()) { - init(capabilities, peerId, msgId); + init(guarded, capabilities, peerId, msgId); } } bool valid() const { @@ -158,7 +157,7 @@ public: } private: - void init(const QStringList &capabilities, PeerId peerId, MsgId msgId) { + void init(const std::shared_ptr &guarded, const QStringList &capabilities, PeerId peerId, MsgId msgId) { if (capabilities.contains(qsl("append"))) { Libs::notify_notification_set_hint_string(_data, "append", "true"); } else if (capabilities.contains(qsl("x-canonical-append"))) { @@ -169,22 +168,20 @@ private: auto signalHandler = G_CALLBACK(NotificationData::notificationClosed); auto signalName = "closed"; auto signalDataFreeMethod = &NotificationData::notificationDataFreeClosure; - auto signalData = new NotificationDataStruct(peerId, msgId); + auto signalData = new NotificationDataStruct(guarded, peerId, msgId); _handlerId = Libs::g_signal_connect_helper(signalReceiver, signalName, signalHandler, signalData, signalDataFreeMethod); Libs::notify_notification_set_timeout(_data, Libs::NOTIFY_EXPIRES_DEFAULT); - if (auto manager = ManagerInstance.data()) { - if (manager->hasActionsSupport()) { - auto label = lang(lng_notification_reply).toUtf8(); - auto actionReceiver = _data; - auto actionHandler = &NotificationData::notificationClicked; - auto actionLabel = label.constData(); - auto actionName = "default"; - auto actionDataFreeMethod = &NotificationData::notificationDataFree; - auto actionData = new NotificationDataStruct(peerId, msgId); - Libs::notify_notification_add_action(actionReceiver, actionName, actionLabel, actionHandler, actionData, actionDataFreeMethod); - } + if ((*guarded)->hasActionsSupport()) { + auto label = lang(lng_notification_reply).toUtf8(); + auto actionReceiver = _data; + auto actionHandler = &NotificationData::notificationClicked; + auto actionLabel = label.constData(); + auto actionName = "default"; + auto actionDataFreeMethod = &NotificationData::notificationDataFree; + auto actionData = new NotificationDataStruct(guarded, peerId, msgId); + Libs::notify_notification_add_action(actionReceiver, actionName, actionLabel, actionHandler, actionData, actionDataFreeMethod); } } @@ -194,12 +191,23 @@ private: } struct NotificationDataStruct { - NotificationDataStruct(PeerId peerId, MsgId msgId) : peerId(peerId), msgId(msgId) { + NotificationDataStruct(const std::shared_ptr &guarded, PeerId peerId, MsgId msgId) + : weak(guarded) + , peerId(peerId) + , msgId(msgId) { } + std::weak_ptr weak; PeerId peerId = 0; MsgId msgId = 0; }; + static void performOnMainQueue(NotificationDataStruct *data, base::lambda_once task) { + base::TaskQueue::Main().Put([weak = data->weak, task = std::move(task)]() mutable { + if (auto strong = weak.lock()) { + task(*strong); + } + }); + } static void notificationDataFree(gpointer data) { auto notificationData = static_cast(data); delete notificationData; @@ -211,15 +219,15 @@ private: static void notificationClosed(Libs::NotifyNotification *notification, gpointer data) { auto closedReason = Libs::notify_notification_get_closed_reason(notification); auto notificationData = static_cast(data); - if (auto manager = ManagerInstance.data()) { - manager->clearNotification(notificationData->peerId, notificationData->msgId); - } + performOnMainQueue(notificationData, [peerId = notificationData->peerId, msgId = notificationData->msgId](Manager *manager) { + manager->clearNotification(peerId, msgId); + }); } static void notificationClicked(Libs::NotifyNotification *notification, char *action, gpointer data) { auto notificationData = static_cast(data); - if (auto manager = ManagerInstance.data()) { - manager->notificationActivated(notificationData->peerId, notificationData->msgId); - } + performOnMainQueue(notificationData, [peerId = notificationData->peerId, msgId = notificationData->msgId](Manager *manager) { + manager->notificationActivated(peerId, msgId); + }); } Libs::NotifyNotification *_data = nullptr; @@ -229,47 +237,72 @@ private: using Notification = QSharedPointer; -} // namespace - -void Start() { - if (LibNotifyLoaded()) { - if (Libs::notify_is_initted() || Libs::notify_init("Telegram Desktop")) { - ManagerInstance.createIfNull(); - if (!ManagerInstance->init()) { - ManagerInstance.clear(); - LOG(("LibNotify Error: manager failed to init!")); - } - } else { - LOG(("LibNotify Error: failed to init!")); - } +QString GetServerName() { + if (!LibNotifyLoaded()) { + return QString(); } + if (!Libs::notify_is_initted() && !Libs::notify_init("Telegram Desktop")) { + LOG(("LibNotify Error: failed to init!")); + return QString(); + } + + gchar *name = nullptr; + auto guard = base::scope_guard([&name] { + if (name) Libs::g_free(name); + }); + + if (!Libs::notify_get_server_info(&name, nullptr, nullptr, nullptr)) { + LOG(("LibNotify Error: could not get server name!")); + return QString(); + } + if (!name) { + LOG(("LibNotify Error: successfully got empty server name!")); + return QString(); + } + + auto result = QString::fromUtf8(static_cast(name)); + LOG(("Notifications Server: %1").arg(result)); + + return result; } -Window::Notifications::Manager *GetManager() { - if (Global::started() && Global::NativeNotifications()) { - return ManagerInstance.data(); +auto LibNotifyServerName = QString(); + +} // namespace + +bool Supported() { + static auto Checked = false; + if (!Checked) { + Checked = true; + LibNotifyServerName = GetServerName(); + } + + return !LibNotifyServerName.isEmpty(); +} + +std::unique_ptr Create(Window::Notifications::System *system) { + if (Global::NativeNotifications() && Supported()) { + return std::make_unique(system); } return nullptr; } -bool Supported() { - return ManagerInstance.data() != nullptr; -} - void Finish() { - if (GetManager()) { - ManagerInstance.reset(); - Libs::notify_uninit(); + if (Libs::notify_is_initted && Libs::notify_uninit) { + if (Libs::notify_is_initted()) { + Libs::notify_uninit(); + } } } -class Manager::Impl { +class Manager::Private { public: using Type = Window::Notifications::CachedUserpics::Type; - Impl(Type type) : _cachedUserpics(type) { + explicit Private(Type type) + : _cachedUserpics(type) { } - bool init(); + void init(Manager *manager); void showNotification(PeerData *peer, MsgId msgId, const QString &title, const QString &subtitle, const QString &msg, bool hideNameAndPhoto, bool hideReplyButton); void clearAll(); @@ -283,6 +316,8 @@ public: return _actionsSupported; } + ~Private(); + private: QString escapeNotificationText(const QString &text) const; void showNextNotification(); @@ -309,9 +344,13 @@ private: bool _markupSupported = false; bool _poorSupported = false; + std::shared_ptr _guarded; + }; -bool Manager::Impl::init() { +void Manager::Private::init(Manager *manager) { + _guarded = std::make_shared(manager); + if (auto capabilities = Libs::notify_get_server_caps()) { for (auto capability = capabilities; capability; capability = capability->next) { auto capabilityText = QString::fromUtf8(static_cast(capability->data)); @@ -331,32 +370,19 @@ bool Manager::Impl::init() { // Unity and other Notify OSD users handle desktop notifications // extremely poor, even without the ability to close() them. - gchar *name = nullptr; - if (Libs::notify_get_server_info(&name, nullptr, nullptr, nullptr)) { - if (name) { - _serverName = QString::fromUtf8(static_cast(name)); - Libs::g_free(name); - - LOG(("Notifications Server: %1").arg(_serverName)); - if (_serverName == qstr("notify-osd")) { -// _poorSupported = true; - _actionsSupported = false; - } - } else { - LOG(("LibNotify Error: successfully got empty server name!")); - } - } else { - LOG(("LibNotify Error: could not get server name!")); + _serverName = LibNotifyServerName; + t_assert(!_serverName.isEmpty()); + if (_serverName == qstr("notify-osd")) { +// _poorSupported = true; + _actionsSupported = false; } - - return !_serverName.isEmpty(); } -QString Manager::Impl::escapeNotificationText(const QString &text) const { +QString Manager::Private::escapeNotificationText(const QString &text) const { return _markupSupported ? escapeHtml(text) : text; } -void Manager::Impl::showNotification(PeerData *peer, MsgId msgId, const QString &title, const QString &subtitle, const QString &msg, bool hideNameAndPhoto, bool hideReplyButton) { +void Manager::Private::showNotification(PeerData *peer, MsgId msgId, const QString &title, const QString &subtitle, const QString &msg, bool hideNameAndPhoto, bool hideReplyButton) { auto titleText = escapeNotificationText(title); auto subtitleText = escapeNotificationText(subtitle); auto msgText = escapeNotificationText(msg); @@ -376,7 +402,7 @@ void Manager::Impl::showNotification(PeerData *peer, MsgId msgId, const QString showNextNotification(); } -void Manager::Impl::showNextNotification() { +void Manager::Private::showNextNotification() { // Show only one notification at a time in Unity / Notify OSD. if (_poorSupported) { for (auto b = _notifications.begin(); !_notifications.isEmpty() && b->isEmpty();) { @@ -401,7 +427,7 @@ void Manager::Impl::showNextNotification() { auto peerId = data.peer->id; auto msgId = data.msgId; - auto notification = MakeShared(data.title, data.body, _capabilities, peerId, msgId); + auto notification = MakeShared(_guarded, data.title, data.body, _capabilities, peerId, msgId); if (!notification->valid()) { return; } @@ -438,7 +464,7 @@ void Manager::Impl::showNextNotification() { } } -void Manager::Impl::clearAll() { +void Manager::Private::clearAll() { _queuedNotifications.clear(); auto temp = base::take(_notifications); @@ -449,7 +475,7 @@ void Manager::Impl::clearAll() { } } -void Manager::Impl::clearFromHistory(History *history) { +void Manager::Private::clearFromHistory(History *history) { for (auto i = _queuedNotifications.begin(); i != _queuedNotifications.end();) { if (i->peer == history->peer) { i = _queuedNotifications.erase(i); @@ -471,7 +497,7 @@ void Manager::Impl::clearFromHistory(History *history) { showNextNotification(); } -void Manager::Impl::clearNotification(PeerId peerId, MsgId msgId) { +void Manager::Private::clearNotification(PeerId peerId, MsgId msgId) { auto i = _notifications.find(peerId); if (i != _notifications.cend()) { i.value().remove(msgId); @@ -483,37 +509,39 @@ void Manager::Impl::clearNotification(PeerId peerId, MsgId msgId) { showNextNotification(); } -Manager::Manager() : _impl(std::make_unique(Impl::Type::Rounded)) { +Manager::Private::~Private() { + clearAll(); } -bool Manager::init() { - return _impl->init(); +Manager::Manager(Window::Notifications::System *system) : NativeManager(system) +, _private(std::make_unique(Private::Type::Rounded)) { + _private->init(this); } void Manager::clearNotification(PeerId peerId, MsgId msgId) { - _impl->clearNotification(peerId, msgId); + _private->clearNotification(peerId, msgId); } bool Manager::hasPoorSupport() const { - return _impl->hasPoorSupport(); + return _private->hasPoorSupport(); } bool Manager::hasActionsSupport() const { - return _impl->hasActionsSupport(); + return _private->hasActionsSupport(); } Manager::~Manager() = default; void Manager::doShowNativeNotification(PeerData *peer, MsgId msgId, const QString &title, const QString &subtitle, const QString &msg, bool hideNameAndPhoto, bool hideReplyButton) { - _impl->showNotification(peer, msgId, title, subtitle, msg, hideNameAndPhoto, hideReplyButton); + _private->showNotification(peer, msgId, title, subtitle, msg, hideNameAndPhoto, hideReplyButton); } void Manager::doClearAllFast() { - _impl->clearAll(); + _private->clearAll(); } void Manager::doClearFromHistory(History *history) { - _impl->clearFromHistory(history); + _private->clearFromHistory(history); } } // namespace Notifications diff --git a/Telegram/SourceFiles/platform/linux/notifications_manager_linux.h b/Telegram/SourceFiles/platform/linux/notifications_manager_linux.h index b4241e17f..cd4642d13 100644 --- a/Telegram/SourceFiles/platform/linux/notifications_manager_linux.h +++ b/Telegram/SourceFiles/platform/linux/notifications_manager_linux.h @@ -36,11 +36,14 @@ inline bool SkipToast() { return false; } +inline void FlashBounce() { +} + +void Finish(); + class Manager : public Window::Notifications::NativeManager { public: - Manager(); - - bool init(); + Manager(Window::Notifications::System *system); void clearNotification(PeerId peerId, MsgId msgId); bool hasPoorSupport() const; @@ -54,9 +57,8 @@ protected: void doClearFromHistory(History *history) override; private: - class Impl; - friend class Impl; - std::unique_ptr _impl; + class Private; + const std::unique_ptr _private; }; diff --git a/Telegram/SourceFiles/platform/linux/specific_linux.cpp b/Telegram/SourceFiles/platform/linux/specific_linux.cpp index 113e881d3..df4534f4a 100644 --- a/Telegram/SourceFiles/platform/linux/specific_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/specific_linux.cpp @@ -23,6 +23,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "mainwidget.h" #include "mainwindow.h" #include "platform/linux/file_utilities_linux.h" +#include "platform/platform_notifications_manager.h" #include "storage/localstorage.h" #include @@ -370,6 +371,8 @@ void start() { } void finish() { + Notifications::Finish(); + delete _psEventFilter; _psEventFilter = nullptr; } diff --git a/Telegram/SourceFiles/platform/mac/main_window_mac.h b/Telegram/SourceFiles/platform/mac/main_window_mac.h index 064a85c9c..1858b040e 100644 --- a/Telegram/SourceFiles/platform/mac/main_window_mac.h +++ b/Telegram/SourceFiles/platform/mac/main_window_mac.h @@ -36,25 +36,20 @@ public: void psUpdateSysMenu(Qt::WindowState state); void psUpdateMargins(); - void psFlash(); - - void psUpdateWorkmode(); - void psRefreshTaskbarIcon() { } bool psFilterNativeEvent(void *event); - bool psHasNativeNotifications() { - return !(QSysInfo::macVersion() < QSysInfo::MV_10_8); - } - virtual QImage iconWithCounter(int size, int count, style::color bg, style::color fg, bool smallIcon) = 0; int getCustomTitleHeight() const { return _customTitleHeight; } + // It is placed here while the window handles activeSpaceDidChange event. + void customNotificationCreated(QWidget *notification); + ~MainWindow(); class Private; @@ -88,6 +83,8 @@ protected: void updateGlobalMenuHook() override; + void workmodeUpdated(DBIWorkMode mode) override; + QSystemTrayIcon *trayIcon = nullptr; QMenu *trayIconMenu = nullptr; QImage icon256, iconbig256; @@ -109,9 +106,16 @@ private: void updateTitleCounter(); void updateIconCounters(); + class CustomNotificationHandle; + friend class CustomNotificationHandle; + void customNotificationDestroyed(CustomNotificationHandle *handle); + void activateCustomNotifications(); + friend class Private; std::unique_ptr _private; + std::set _customNotifications; + mutable bool psIdle; mutable QTimer psIdleTimer; diff --git a/Telegram/SourceFiles/platform/mac/main_window_mac.mm b/Telegram/SourceFiles/platform/mac/main_window_mac.mm index f9379c539..5bbed25d3 100644 --- a/Telegram/SourceFiles/platform/mac/main_window_mac.mm +++ b/Telegram/SourceFiles/platform/mac/main_window_mac.mm @@ -56,7 +56,6 @@ public: Private(MainWindow *window); void setWindowBadge(const QString &str); - void startBounce(); void enableShadow(WId winId); @@ -64,6 +63,7 @@ public: void willEnterFullScreen(); void willExitFullScreen(); + void activateCustomNotifications(); void initCustomTitle(NSWindow *window, NSView *view); @@ -82,6 +82,25 @@ private: }; +class MainWindow::CustomNotificationHandle : public QObject { +public: + CustomNotificationHandle(QWidget *parent) : QObject(parent) { + } + + void activate() { + auto widget = static_cast(parent()); + NSWindow *wnd = [reinterpret_cast(widget->winId()) window]; + [wnd orderFront:wnd]; + } + + ~CustomNotificationHandle() { + if (auto window = App::wnd()) { + window->customNotificationDestroyed(this); + } + } + +}; + } // namespace Platform @implementation MainWindowObserver { @@ -98,11 +117,7 @@ MainWindow::Private *_private; } - (void) activeSpaceDidChange:(NSNotification *)aNotification { - if (auto manager = Window::Notifications::Default::GetManager()) { - manager->enumerateNotifications([](QWidget *widget) { - objc_activateWnd(widget->winId()); - }); - } + _private->activateCustomNotifications(); } - (void) darkModeChanged:(NSNotification *)aNotification { @@ -157,10 +172,6 @@ void MainWindow::Private::setWindowBadge(const QString &str) { } } -void MainWindow::Private::startBounce() { - [NSApp requestUserAttention:NSInformationalRequest]; -} - void MainWindow::Private::initCustomTitle(NSWindow *window, NSView *view) { [window setStyleMask:[window styleMask] | NSFullSizeContentViewWindowMask]; [window setTitlebarAppearsTransparent:YES]; @@ -191,6 +202,10 @@ void MainWindow::Private::willExitFullScreen() { _public->setTitleVisible(true); } +void MainWindow::Private::activateCustomNotifications() { + _public->activateCustomNotifications(); +} + void MainWindow::Private::enableShadow(WId winId) { // [[(NSView*)winId window] setStyleMask:NSBorderlessWindowMask]; // [[(NSView*)winId window] setHasShadow:YES]; @@ -299,18 +314,15 @@ void MainWindow::psSetupTrayIcon() { trayIcon->show(); } -void MainWindow::psUpdateWorkmode() { +void MainWindow::workmodeUpdated(DBIWorkMode mode) { psSetupTrayIcon(); - if (cWorkMode() == dbiwmWindowOnly) { + if (mode == dbiwmWindowOnly) { if (trayIcon) { trayIcon->setContextMenu(0); delete trayIcon; trayIcon = nullptr; } } - if (auto manager = Platform::Notifications::GetNativeManager()) { - manager->updateDelegate(); - } } void _placeCounter(QImage &img, int size, int count, style::color bg, style::color color) { @@ -400,7 +412,7 @@ void MainWindow::psFirstShow() { if ((cLaunchMode() == LaunchModeAutoStart && cStartMinimized()) || cStartInTray()) { setWindowState(Qt::WindowMinimized); - if (cWorkMode() == dbiwmTrayOnly || cWorkMode() == dbiwmWindowAndTray) { + if (Global::WorkMode().value() == dbiwmTrayOnly || Global::WorkMode().value() == dbiwmWindowAndTray) { hide(); } else { show(); @@ -514,6 +526,20 @@ void MainWindow::psUpdateSysMenu(Qt::WindowState state) { void MainWindow::psUpdateMargins() { } +void MainWindow::customNotificationCreated(QWidget *notification) { + _customNotifications.insert(object_ptr(notification)); +} + +void MainWindow::customNotificationDestroyed(CustomNotificationHandle *handle) { + _customNotifications.erase(handle); +} + +void MainWindow::activateCustomNotifications() { + for (auto handle : _customNotifications) { + handle->activate(); + } +} + void MainWindow::updateGlobalMenuHook() { if (!App::wnd() || !positionInited()) return; @@ -552,10 +578,6 @@ void MainWindow::updateGlobalMenuHook() { _forceDisabled(psShowTelegram, App::wnd()->isActive()); } -void MainWindow::psFlash() { - return _private->startBounce(); -} - bool MainWindow::psFilterNativeEvent(void *event) { return _private->filterNativeEvent(event); } diff --git a/Telegram/SourceFiles/platform/mac/notifications_manager_mac.h b/Telegram/SourceFiles/platform/mac/notifications_manager_mac.h index a0466e4fe..9c6883689 100644 --- a/Telegram/SourceFiles/platform/mac/notifications_manager_mac.h +++ b/Telegram/SourceFiles/platform/mac/notifications_manager_mac.h @@ -25,9 +25,6 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org namespace Platform { namespace Notifications { -class Manager; -Manager *GetNativeManager(); - inline bool SkipAudio() { return false; } @@ -38,10 +35,7 @@ inline bool SkipToast() { class Manager : public Window::Notifications::NativeManager { public: - Manager(); - - void updateDelegate(); - + Manager(Window::Notifications::System *system); ~Manager(); protected: @@ -50,14 +44,10 @@ protected: void doClearFromHistory(History *history) override; private: - class Impl; - std::unique_ptr _impl; + class Private; + const std::unique_ptr _private; }; -inline Window::Notifications::Manager *GetManager() { - return GetNativeManager(); -} - } // namespace Notifications } // namespace Platform diff --git a/Telegram/SourceFiles/platform/mac/notifications_manager_mac.mm b/Telegram/SourceFiles/platform/mac/notifications_manager_mac.mm index 7b2fd9ca3..8f30b5ee1 100644 --- a/Telegram/SourceFiles/platform/mac/notifications_manager_mac.mm +++ b/Telegram/SourceFiles/platform/mac/notifications_manager_mac.mm @@ -23,6 +23,8 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "platform/platform_specific.h" #include "platform/mac/mac_utilities.h" #include "styles/style_window.h" +#include "mainwindow.h" +#include "core/task_queue.h" #include @@ -93,22 +95,19 @@ NSImage *qt_mac_create_nsimage(const QPixmap &pm); namespace Platform { namespace Notifications { -void Start() { - if (cPlatform() != dbipMacOld) { - ManagerInstance.createIfNull(); - } -} - -Manager *GetNativeManager() { - return ManagerInstance.data(); -} - bool Supported() { - return ManagerInstance.data() != nullptr; + return (cPlatform() != dbipMacOld); } -void Finish() { - ManagerInstance.clear(); +std::unique_ptr Create(Window::Notifications::System *system) { + if (Supported()) { + return std::make_unique(system); + } + return nullptr; +} + +void FlashBounce() { + [NSApp requestUserAttention:NSInformationalRequest]; } void CustomNotificationShownHook(QWidget *widget) { @@ -116,27 +115,38 @@ void CustomNotificationShownHook(QWidget *widget) { objc_holdOnTop(widget->winId()); widget->show(); psShowOverAll(widget, false); + if (auto window = App::wnd()) { + window->customNotificationCreated(widget); + } } -class Manager::Impl { +class Manager::Private : public QObject, private base::Subscriber { public: - Impl(); + Private(); void showNotification(PeerData *peer, MsgId msgId, const QString &title, const QString &subtitle, const QString &msg, bool hideNameAndPhoto, bool hideReplyButton); void clearAll(); void clearFromHistory(History *history); void updateDelegate(); - ~Impl(); + ~Private(); private: NotificationDelegate *_delegate; }; -Manager::Impl::Impl() : _delegate([[NotificationDelegate alloc] init]) { +Manager::Private::Private() : _delegate([[NotificationDelegate alloc] init]) { + updateDelegate(); + subscribe(Global::RefWorkMode(), [this](DBIWorkMode mode) { + // We need to update the delegate _after_ the tray icon change was done in Qt. + // Because Qt resets the delegate. + base::TaskQueue::Main().Put(base::lambda_guarded(this, [this] { + updateDelegate(); + })); + }); } -void Manager::Impl::showNotification(PeerData *peer, MsgId msgId, const QString &title, const QString &subtitle, const QString &msg, bool hideNameAndPhoto, bool hideReplyButton) { +void Manager::Private::showNotification(PeerData *peer, MsgId msgId, const QString &title, const QString &subtitle, const QString &msg, bool hideNameAndPhoto, bool hideReplyButton) { @autoreleasepool { NSUserNotification *notification = [[[NSUserNotification alloc] init] autorelease]; @@ -168,7 +178,7 @@ void Manager::Impl::showNotification(PeerData *peer, MsgId msgId, const QString } } -void Manager::Impl::clearAll() { +void Manager::Private::clearAll() { NSUserNotificationCenter *center = [NSUserNotificationCenter defaultUserNotificationCenter]; NSArray *notificationsList = [center deliveredNotifications]; for (id notification in notificationsList) { @@ -182,7 +192,7 @@ void Manager::Impl::clearAll() { [center removeAllDeliveredNotifications]; } -void Manager::Impl::clearFromHistory(History *history) { +void Manager::Private::clearFromHistory(History *history) { unsigned long long peerId = history->peer->id; NSUserNotificationCenter *center = [NSUserNotificationCenter defaultUserNotificationCenter]; @@ -199,34 +209,32 @@ void Manager::Impl::clearFromHistory(History *history) { } } -void Manager::Impl::updateDelegate() { +void Manager::Private::updateDelegate() { NSUserNotificationCenter *center = [NSUserNotificationCenter defaultUserNotificationCenter]; [center setDelegate:_delegate]; } -Manager::Impl::~Impl() { +Manager::Private::~Private() { + clearAll(); [_delegate release]; } -Manager::Manager() : _impl(std::make_unique()) { -} - -void Manager::updateDelegate() { - _impl->updateDelegate(); +Manager::Manager(Window::Notifications::System *system) : NativeManager(system) +, _private(std::make_unique()) { } Manager::~Manager() = default; void Manager::doShowNativeNotification(PeerData *peer, MsgId msgId, const QString &title, const QString &subtitle, const QString &msg, bool hideNameAndPhoto, bool hideReplyButton) { - _impl->showNotification(peer, msgId, title, subtitle, msg, hideNameAndPhoto, hideReplyButton); + _private->showNotification(peer, msgId, title, subtitle, msg, hideNameAndPhoto, hideReplyButton); } void Manager::doClearAllFast() { - _impl->clearAll(); + _private->clearAll(); } void Manager::doClearFromHistory(History *history) { - _impl->clearFromHistory(history); + _private->clearFromHistory(history); } } // namespace Notifications diff --git a/Telegram/SourceFiles/platform/mac/specific_mac_p.h b/Telegram/SourceFiles/platform/mac/specific_mac_p.h index 9691b7e90..cdad7ab96 100644 --- a/Telegram/SourceFiles/platform/mac/specific_mac_p.h +++ b/Telegram/SourceFiles/platform/mac/specific_mac_p.h @@ -24,7 +24,6 @@ void objc_holdOnTop(WId winId); bool objc_darkMode(); void objc_showOverAll(WId winId, bool canFocus = true); void objc_bringToBack(WId winId); -void objc_activateWnd(WId winId); void objc_debugShowAlert(const QString &str); void objc_outputDebugString(const QString &str); diff --git a/Telegram/SourceFiles/platform/mac/specific_mac_p.mm b/Telegram/SourceFiles/platform/mac/specific_mac_p.mm index e176727e5..ca625ce49 100644 --- a/Telegram/SourceFiles/platform/mac/specific_mac_p.mm +++ b/Telegram/SourceFiles/platform/mac/specific_mac_p.mm @@ -208,11 +208,6 @@ void objc_bringToBack(WId winId) { [wnd setLevel:NSModalPanelWindowLevel]; } -void objc_activateWnd(WId winId) { - NSWindow *wnd = [reinterpret_cast(winId) window]; - [wnd orderFront:wnd]; -} - bool objc_handleMediaKeyEvent(void *ev) { auto e = reinterpret_cast(ev); diff --git a/Telegram/SourceFiles/platform/platform_notifications_manager.h b/Telegram/SourceFiles/platform/platform_notifications_manager.h index 924423308..50b8e0af3 100644 --- a/Telegram/SourceFiles/platform/platform_notifications_manager.h +++ b/Telegram/SourceFiles/platform/platform_notifications_manager.h @@ -29,10 +29,9 @@ void CustomNotificationShownHook(QWidget *widget); bool SkipAudio(); bool SkipToast(); -void Start(); -Window::Notifications::Manager *GetManager(); bool Supported(); -void Finish(); +std::unique_ptr Create(Window::Notifications::System *system); +void FlashBounce(); } // namespace Notifications } // namespace Platform diff --git a/Telegram/SourceFiles/platform/win/main_window_win.cpp b/Telegram/SourceFiles/platform/win/main_window_win.cpp index 69a43d5b2..56206f6f2 100644 --- a/Telegram/SourceFiles/platform/win/main_window_win.cpp +++ b/Telegram/SourceFiles/platform/win/main_window_win.cpp @@ -703,8 +703,8 @@ void MainWindow::showTrayTooltip() { } } -void MainWindow::psUpdateWorkmode() { - switch (cWorkMode()) { +void MainWindow::workmodeUpdated(DBIWorkMode mode) { + switch (mode) { case dbiwmWindowAndTray: { psSetupTrayIcon(); HWND psOwner = (HWND)GetWindowLong(ps_hWnd, GWL_HWNDPARENT); @@ -800,10 +800,6 @@ void MainWindow::initHook() { setWindowIcon(wndIcon); } -bool MainWindow::psHasNativeNotifications() { - return Notifications::Supported(); -} - Q_DECLARE_METATYPE(QMargins); void MainWindow::psFirstShow() { _psShadowWindows.init(st::windowShadowFg->c); @@ -821,7 +817,7 @@ void MainWindow::psFirstShow() { if ((cLaunchMode() == LaunchModeAutoStart && cStartMinimized() && !App::passcoded()) || cStartInTray()) { setWindowState(Qt::WindowMinimized); - if (cWorkMode() == dbiwmTrayOnly || cWorkMode() == dbiwmWindowAndTray) { + if (Global::WorkMode().value() == dbiwmTrayOnly || Global::WorkMode().value() == dbiwmWindowAndTray) { hide(); } else { show(); @@ -934,18 +930,6 @@ void MainWindow::psUpdateMargins() { } } -void MainWindow::psFlash() { - if (GetForegroundWindow() == ps_hWnd) return; - - FLASHWINFO info; - info.cbSize = sizeof(info); - info.hwnd = ps_hWnd; - info.dwFlags = FLASHW_ALL; - info.dwTimeout = 0; - info.uCount = 1; - FlashWindowEx(&info); -} - HWND MainWindow::psHwnd() const { return ps_hWnd; } diff --git a/Telegram/SourceFiles/platform/win/main_window_win.h b/Telegram/SourceFiles/platform/win/main_window_win.h index 62d4ecd06..d718c6ed8 100644 --- a/Telegram/SourceFiles/platform/win/main_window_win.h +++ b/Telegram/SourceFiles/platform/win/main_window_win.h @@ -43,15 +43,8 @@ public: void psUpdateSysMenu(Qt::WindowState state); void psUpdateMargins(); - void psFlash(); - void psNotifySettingGot(); - - void psUpdateWorkmode(); - void psRefreshTaskbarIcon(); - bool psHasNativeNotifications(); - virtual QImage iconWithCounter(int size, int count, style::color bg, style::color fg, bool smallIcon) = 0; static UINT TaskbarCreatedMsgId() { @@ -108,6 +101,8 @@ protected: void showTrayTooltip() override; + void workmodeUpdated(DBIWorkMode mode) override; + QTimer psUpdatedPositionTimer; private: diff --git a/Telegram/SourceFiles/platform/win/notifications_manager_win.cpp b/Telegram/SourceFiles/platform/win/notifications_manager_win.cpp index 16450627a..1e4dca9e3 100644 --- a/Telegram/SourceFiles/platform/win/notifications_manager_win.cpp +++ b/Telegram/SourceFiles/platform/win/notifications_manager_win.cpp @@ -25,6 +25,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "platform/win/windows_event_filter.h" #include "platform/win/windows_dlls.h" #include "mainwindow.h" +#include "core/task_queue.h" #include #include @@ -48,22 +49,6 @@ namespace Platform { namespace Notifications { namespace { -NeverFreedPointer ManagerInstance; - -ComPtr _notificationManager; -ComPtr _notifier; -ComPtr _notificationFactory; - -struct NotificationPtr { - NotificationPtr() { - } - NotificationPtr(const ComPtr &ptr) : p(ptr) { - } - ComPtr p; -}; -using Notifications = QMap>; -Notifications _notifications; - class StringReferenceWrapper { public: StringReferenceWrapper(_In_reads_(length) PCWSTR stringRef, _In_ UINT32 length) throw() { @@ -137,15 +122,6 @@ bool init() { if (!SUCCEEDED(Dlls::SetCurrentProcessExplicitAppUserModelID(appUserModelId))) { return false; } - if (!SUCCEEDED(wrap_GetActivationFactory(StringReferenceWrapper(RuntimeClass_Windows_UI_Notifications_ToastNotificationManager).Get(), &_notificationManager))) { - return false; - } - if (!SUCCEEDED(_notificationManager->CreateToastNotifierWithId(StringReferenceWrapper(appUserModelId, wcslen(appUserModelId)).Get(), &_notifier))) { - return false; - } - if (!SUCCEEDED(wrap_GetActivationFactory(StringReferenceWrapper(RuntimeClass_Windows_UI_Notifications_ToastNotification).Get(), &_notificationFactory))) { - return false; - } return true; } @@ -235,16 +211,27 @@ typedef ABI::Windows::Foundation::ITypedEventHandler { public: - ToastEventHandler::ToastEventHandler(const PeerId &peer, MsgId msg) : _ref(1), _peerId(peer), _msgId(msg) { + // We keep a weak pointer to a member field of native notifications manager. + ToastEventHandler::ToastEventHandler(const std::shared_ptr &guarded, const PeerId &peer, MsgId msg) + : _peerId(peer) + , _msgId(msg) + , _weak(guarded) { } - ~ToastEventHandler() { + ~ToastEventHandler() = default; + + void performOnMainQueue(base::lambda_once task) { + base::TaskQueue::Main().Put([weak = _weak, task = std::move(task)]() mutable { + if (auto strong = weak.lock()) { + task(*strong); + } + }); } // DesktopToastActivatedEventHandler IFACEMETHODIMP Invoke(_In_ IToastNotification *sender, _In_ IInspectable* args) { - if (auto manager = ManagerInstance.data()) { - manager->notificationActivated(_peerId, _msgId); - } + performOnMainQueue([peerId = _peerId, msgId = _msgId](Manager *manager) { + manager->notificationActivated(peerId, msgId); + }); return S_OK; } @@ -258,9 +245,9 @@ public: case ToastDismissalReason_UserCanceled: case ToastDismissalReason_TimedOut: default: - if (auto manager = ManagerInstance.data()) { - manager->clearNotification(_peerId, _msgId); - } + performOnMainQueue([peerId = _peerId, msgId = _msgId](Manager *manager) { + manager->clearNotification(peerId, msgId); + }); break; } } @@ -269,21 +256,23 @@ public: // DesktopToastFailedEventHandler IFACEMETHODIMP Invoke(_In_ IToastNotification *sender, _In_ IToastFailedEventArgs *e) { - if (auto manager = ManagerInstance.data()) { - manager->clearNotification(_peerId, _msgId); - } + performOnMainQueue([peerId = _peerId, msgId = _msgId](Manager *manager) { + manager->clearNotification(peerId, msgId); + }); return S_OK; } // IUnknown IFACEMETHODIMP_(ULONG) AddRef() { - return InterlockedIncrement(&_ref); + return InterlockedIncrement(&_refCount); } IFACEMETHODIMP_(ULONG) Release() { - ULONG l = InterlockedDecrement(&_ref); - if (l == 0) delete this; - return l; + auto refCount = InterlockedDecrement(&_refCount); + if (refCount == 0) { + delete this; + } + return refCount; } IFACEMETHODIMP QueryInterface(_In_ REFIID riid, _COM_Outptr_ void **ppv) { @@ -306,41 +295,61 @@ public: } private: - ULONG _ref; - PeerId _peerId; - MsgId _msgId; + ULONG _refCount = 0; + PeerId _peerId = 0; + MsgId _msgId = 0; + std::weak_ptr _weak; }; -} // namespace +auto Checked = false; +auto InitSucceeded = false; -void Start() { - if (init()) { - ManagerInstance.createIfNull(); - } +void Check() { + InitSucceeded = init(); } -Window::Notifications::Manager *GetManager() { - if (Global::started() && Global::NativeNotifications()) { - return ManagerInstance.data(); +} // namespace + +bool Supported() { + if (!Checked) { + Checked = true; + Check(); + } + return InitSucceeded; +} + +std::unique_ptr Create(Window::Notifications::System *system) { + if (Global::NativeNotifications() && Supported()) { + auto result = std::make_unique(system); + if (result->init()) { + return std::move(result); + } } return nullptr; } -bool Supported() { - return ManagerInstance.data() != nullptr; +void FlashBounce() { + auto window = App::wnd(); + if (!window || GetForegroundWindow() == window->psHwnd()) { + return; + } + + FLASHWINFO info; + info.cbSize = sizeof(info); + info.hwnd = window->psHwnd(); + info.dwFlags = FLASHW_ALL; + info.dwTimeout = 0; + info.uCount = 1; + FlashWindowEx(&info); } -void Finish() { - ManagerInstance.clear(); -} - -class Manager::Impl { +class Manager::Private { public: using Type = Window::Notifications::CachedUserpics::Type; - Impl(Type type) : _cachedUserpics(type) { - } + explicit Private(Manager *instance, Type type); + bool init(); bool showNotification(PeerData *peer, MsgId msgId, const QString &title, const QString &subtitle, const QString &msg, bool hideNameAndPhoto, bool hideReplyButton); void clearAll(); @@ -349,21 +358,60 @@ public: void afterNotificationActivated(PeerId peerId, MsgId msgId); void clearNotification(PeerId peerId, MsgId msgId); - ~Impl(); + ~Private(); private: Window::Notifications::CachedUserpics _cachedUserpics; + std::shared_ptr _guarded; + + ComPtr _notificationManager; + ComPtr _notifier; + ComPtr _notificationFactory; + + struct NotificationPtr { + NotificationPtr() { + } + NotificationPtr(const ComPtr &ptr) : p(ptr) { + } + + ComPtr p; + }; + QMap> _notifications; + }; -Manager::Impl::~Impl() { +Manager::Private::Private(Manager *instance, Type type) +: _guarded(std::make_shared(instance)) +, _cachedUserpics(type) { +} + +bool Manager::Private::init() { + if (!SUCCEEDED(wrap_GetActivationFactory(StringReferenceWrapper(RuntimeClass_Windows_UI_Notifications_ToastNotificationManager).Get(), &_notificationManager))) { + return false; + } + + auto appUserModelId = AppUserModelId::getId(); + if (!SUCCEEDED(_notificationManager->CreateToastNotifierWithId(StringReferenceWrapper(appUserModelId, wcslen(appUserModelId)).Get(), &_notifier))) { + return false; + } + + if (!SUCCEEDED(wrap_GetActivationFactory(StringReferenceWrapper(RuntimeClass_Windows_UI_Notifications_ToastNotification).Get(), &_notificationFactory))) { + return false; + } + return true; +} + +Manager::Private::~Private() { + clearAll(); + _notifications.clear(); if (_notificationManager) _notificationManager.Reset(); if (_notifier) _notifier.Reset(); if (_notificationFactory) _notificationFactory.Reset(); } -void Manager::Impl::clearAll() { +void Manager::Private::clearAll() { if (!_notifier) return; auto temp = base::take(_notifications); @@ -374,7 +422,7 @@ void Manager::Impl::clearAll() { } } -void Manager::Impl::clearFromHistory(History *history) { +void Manager::Private::clearFromHistory(History *history) { if (!_notifier) return; auto i = _notifications.find(history->peer->id); @@ -388,17 +436,17 @@ void Manager::Impl::clearFromHistory(History *history) { } } -void Manager::Impl::beforeNotificationActivated(PeerId peerId, MsgId msgId) { +void Manager::Private::beforeNotificationActivated(PeerId peerId, MsgId msgId) { clearNotification(peerId, msgId); } -void Manager::Impl::afterNotificationActivated(PeerId peerId, MsgId msgId) { +void Manager::Private::afterNotificationActivated(PeerId peerId, MsgId msgId) { if (auto window = App::wnd()) { SetForegroundWindow(window->psHwnd()); } } -void Manager::Impl::clearNotification(PeerId peerId, MsgId msgId) { +void Manager::Private::clearNotification(PeerId peerId, MsgId msgId) { auto i = _notifications.find(peerId); if (i != _notifications.cend()) { i.value().remove(msgId); @@ -408,7 +456,7 @@ void Manager::Impl::clearNotification(PeerId peerId, MsgId msgId) { } } -bool Manager::Impl::showNotification(PeerData *peer, MsgId msgId, const QString &title, const QString &subtitle, const QString &msg, bool hideNameAndPhoto, bool hideReplyButton) { +bool Manager::Private::showNotification(PeerData *peer, MsgId msgId, const QString &title, const QString &subtitle, const QString &msg, bool hideNameAndPhoto, bool hideReplyButton) { if (!_notificationManager || !_notifier || !_notificationFactory) return false; ComPtr toastXml; @@ -475,7 +523,7 @@ bool Manager::Impl::showNotification(PeerData *peer, MsgId msgId, const QString if (!SUCCEEDED(hr)) return false; EventRegistrationToken activatedToken, dismissedToken, failedToken; - ComPtr eventHandler(new ToastEventHandler(peer->id, msgId)); + ComPtr eventHandler(new ToastEventHandler(_guarded, peer->id, msgId)); hr = toast->add_Activated(eventHandler.Get(), &activatedToken); if (!SUCCEEDED(hr)) return false; @@ -510,33 +558,38 @@ bool Manager::Impl::showNotification(PeerData *peer, MsgId msgId, const QString return true; } -Manager::Manager() : _impl(std::make_unique(Impl::Type::Rounded)) { +Manager::Manager(Window::Notifications::System *system) : NativeManager(system) +, _private(std::make_unique(this, Private::Type::Rounded)) { +} + +bool Manager::init() { + return _private->init(); } void Manager::clearNotification(PeerId peerId, MsgId msgId) { - _impl->clearNotification(peerId, msgId); + _private->clearNotification(peerId, msgId); } Manager::~Manager() = default; void Manager::doShowNativeNotification(PeerData *peer, MsgId msgId, const QString &title, const QString &subtitle, const QString &msg, bool hideNameAndPhoto, bool hideReplyButton) { - _impl->showNotification(peer, msgId, title, subtitle, msg, hideNameAndPhoto, hideReplyButton); + _private->showNotification(peer, msgId, title, subtitle, msg, hideNameAndPhoto, hideReplyButton); } void Manager::doClearAllFast() { - _impl->clearAll(); + _private->clearAll(); } void Manager::doClearFromHistory(History *history) { - _impl->clearFromHistory(history); + _private->clearFromHistory(history); } void Manager::onBeforeNotificationActivated(PeerId peerId, MsgId msgId) { - _impl->beforeNotificationActivated(peerId, msgId); + _private->beforeNotificationActivated(peerId, msgId); } void Manager::onAfterNotificationActivated(PeerId peerId, MsgId msgId) { - _impl->afterNotificationActivated(peerId, msgId); + _private->afterNotificationActivated(peerId, msgId); } namespace { diff --git a/Telegram/SourceFiles/platform/win/notifications_manager_win.h b/Telegram/SourceFiles/platform/win/notifications_manager_win.h index e7d5ba134..5171b10c4 100644 --- a/Telegram/SourceFiles/platform/win/notifications_manager_win.h +++ b/Telegram/SourceFiles/platform/win/notifications_manager_win.h @@ -30,7 +30,9 @@ inline void CustomNotificationShownHook(QWidget *widget) { class Manager : public Window::Notifications::NativeManager { public: - Manager(); + Manager(Window::Notifications::System *system); + + bool init(); void clearNotification(PeerId peerId, MsgId msgId); @@ -44,8 +46,8 @@ protected: void onAfterNotificationActivated(PeerId peerId, MsgId msgId) override; private: - class Impl; - std::unique_ptr _impl; + class Private; + const std::unique_ptr _private; }; diff --git a/Telegram/SourceFiles/profile/profile_block_peer_list.cpp b/Telegram/SourceFiles/profile/profile_block_peer_list.cpp index 36b11dc69..586d162f6 100644 --- a/Telegram/SourceFiles/profile/profile_block_peer_list.cpp +++ b/Telegram/SourceFiles/profile/profile_block_peer_list.cpp @@ -24,6 +24,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "ui/widgets/popup_menu.h" #include "styles/style_profile.h" #include "styles/style_widgets.h" +#include "auth_session.h" namespace Profile { @@ -38,7 +39,7 @@ PeerListWidget::PeerListWidget(QWidget *parent, PeerData *peer, const QString &t , _removeText(removeText) , _removeWidth(st::normalFont->width(_removeText)) { setMouseTracking(true); - subscribe(FileDownload::ImageLoaded(), [this] { update(); }); + subscribe(AuthSession::CurrentDownloaderTaskFinished(), [this] { update(); }); } int PeerListWidget::resizeGetHeight(int newWidth) { diff --git a/Telegram/SourceFiles/profile/profile_userpic_button.cpp b/Telegram/SourceFiles/profile/profile_userpic_button.cpp index e046a0ac7..6311d97af 100644 --- a/Telegram/SourceFiles/profile/profile_userpic_button.cpp +++ b/Telegram/SourceFiles/profile/profile_userpic_button.cpp @@ -22,7 +22,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "styles/style_profile.h" #include "observer_peer.h" -#include "storage/file_download.h" +#include "auth_session.h" namespace Profile { @@ -41,7 +41,7 @@ UserpicButton::UserpicButton(QWidget *parent, PeerData *peer, int size) : Abstra subscribe(Notify::PeerUpdated(), Notify::PeerUpdatedHandler(observeEvents, [this](const Notify::PeerUpdate &update) { notifyPeerUpdated(update); })); - subscribe(FileDownload::ImageLoaded(), [this] { + subscribe(AuthSession::CurrentDownloaderTaskFinished(), [this] { if (_waiting && _peer->userpicLoaded()) { _waiting = false; startNewPhotoShowing(); diff --git a/Telegram/SourceFiles/settings.cpp b/Telegram/SourceFiles/settings.cpp index 602c34906..56afa777c 100644 --- a/Telegram/SourceFiles/settings.cpp +++ b/Telegram/SourceFiles/settings.cpp @@ -55,7 +55,6 @@ bool gAutoUpdate = true; TWindowPos gWindowPos; LaunchMode gLaunchMode = LaunchModeNormal; bool gSupportTray = true; -DBIWorkMode gWorkMode = dbiwmWindowAndTray; bool gSeenTrayTooltip = false; bool gRestartingUpdate = false, gRestarting = false, gRestartingToSettings = false, gWriteProtected = false; int32 gLastUpdateCheck = 0; diff --git a/Telegram/SourceFiles/settings.h b/Telegram/SourceFiles/settings.h index 7bf7ade91..64505e9c1 100644 --- a/Telegram/SourceFiles/settings.h +++ b/Telegram/SourceFiles/settings.h @@ -101,7 +101,6 @@ struct TWindowPos { }; DeclareSetting(TWindowPos, WindowPos); DeclareSetting(bool, SupportTray); -DeclareSetting(DBIWorkMode, WorkMode); DeclareSetting(bool, SeenTrayTooltip); DeclareSetting(bool, RestartingUpdate); DeclareSetting(bool, Restarting); diff --git a/Telegram/SourceFiles/settings/settings_general_widget.cpp b/Telegram/SourceFiles/settings/settings_general_widget.cpp index a8055b4e9..074cec216 100644 --- a/Telegram/SourceFiles/settings/settings_general_widget.cpp +++ b/Telegram/SourceFiles/settings/settings_general_widget.cpp @@ -184,9 +184,10 @@ void GeneralWidget::refreshControls() { #endif // !TDESKTOP_DISABLE_AUTOUPDATE if (cPlatform() == dbipWindows || cSupportTray()) { - addChildRow(_enableTrayIcon, marginSmall, lang(lng_settings_workmode_tray), SLOT(onEnableTrayIcon()), (cWorkMode() == dbiwmTrayOnly || cWorkMode() == dbiwmWindowAndTray)); + auto workMode = Global::WorkMode().value(); + addChildRow(_enableTrayIcon, marginSmall, lang(lng_settings_workmode_tray), SLOT(onEnableTrayIcon()), (workMode == dbiwmTrayOnly || workMode == dbiwmWindowAndTray)); if (cPlatform() == dbipWindows) { - addChildRow(_enableTaskbarIcon, marginLarge, lang(lng_settings_workmode_window), SLOT(onEnableTaskbarIcon()), (cWorkMode() == dbiwmWindowOnly || cWorkMode() == dbiwmWindowAndTray)); + addChildRow(_enableTaskbarIcon, marginLarge, lang(lng_settings_workmode_window), SLOT(onEnableTaskbarIcon()), (workMode == dbiwmWindowOnly || workMode == dbiwmWindowAndTray)); #ifndef OS_WIN_STORE addChildRow(_autoStart, marginSmall, lang(lng_settings_auto_start), SLOT(onAutoStart()), cAutoStart()); @@ -276,12 +277,11 @@ void GeneralWidget::onEnableTaskbarIcon() { } void GeneralWidget::updateWorkmode() { - DBIWorkMode newMode = (_enableTrayIcon->checked() && (!_enableTaskbarIcon || _enableTaskbarIcon->checked())) ? dbiwmWindowAndTray : (_enableTrayIcon->checked() ? dbiwmTrayOnly : dbiwmWindowOnly); - if (cWorkMode() != newMode && (newMode == dbiwmWindowAndTray || newMode == dbiwmTrayOnly)) { + auto newMode = (_enableTrayIcon->checked() && (!_enableTaskbarIcon || _enableTaskbarIcon->checked())) ? dbiwmWindowAndTray : (_enableTrayIcon->checked() ? dbiwmTrayOnly : dbiwmWindowOnly); + if (Global::WorkMode().value() != newMode && (newMode == dbiwmWindowAndTray || newMode == dbiwmTrayOnly)) { cSetSeenTrayTooltip(false); } - cSetWorkMode(newMode); - App::wnd()->psUpdateWorkmode(); + Global::RefWorkMode().set(newMode); Local::writeSettings(); } diff --git a/Telegram/SourceFiles/settings/settings_notifications_widget.cpp b/Telegram/SourceFiles/settings/settings_notifications_widget.cpp index 20e97211f..79249548f 100644 --- a/Telegram/SourceFiles/settings/settings_notifications_widget.cpp +++ b/Telegram/SourceFiles/settings/settings_notifications_widget.cpp @@ -30,18 +30,24 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "window/notifications_manager.h" #include "boxes/notifications_box.h" #include "platform/platform_notifications_manager.h" +#include "auth_session.h" namespace Settings { +namespace { + +using ChangeType = Window::Notifications::ChangeType; + +} // namespace NotificationsWidget::NotificationsWidget(QWidget *parent, UserData *self) : BlockWidget(parent, self, lang(lng_settings_section_notify)) { createControls(); - subscribe(Global::RefNotifySettingsChanged(), [this](Notify::ChangeType type) { - if (type == Notify::ChangeType::DesktopEnabled) { + subscribe(AuthSession::Current().notifications()->settingsChanged(), [this](ChangeType type) { + if (type == ChangeType::DesktopEnabled) { desktopEnabledUpdated(); - } else if (type == Notify::ChangeType::ViewParams) { + } else if (type == ChangeType::ViewParams) { viewParamUpdated(); - } else if (type == Notify::ChangeType::SoundEnabled) { + } else if (type == ChangeType::SoundEnabled) { _playSound->setChecked(Global::SoundNotify()); } }); @@ -95,7 +101,7 @@ void NotificationsWidget::onDesktopNotifications() { } Global::SetDesktopNotify(_desktopNotifications->checked()); Local::writeUserSettings(); - Global::RefNotifySettingsChanged().notify(Notify::ChangeType::DesktopEnabled); + AuthSession::Current().notifications()->settingsChanged().notify(ChangeType::DesktopEnabled); } void NotificationsWidget::desktopEnabledUpdated() { @@ -125,7 +131,7 @@ void NotificationsWidget::onShowSenderName() { } Global::SetNotifyView(viewParam); Local::writeUserSettings(); - Global::RefNotifySettingsChanged().notify(Notify::ChangeType::ViewParams); + AuthSession::Current().notifications()->settingsChanged().notify(ChangeType::ViewParams); } void NotificationsWidget::onShowMessagePreview() { @@ -143,7 +149,7 @@ void NotificationsWidget::onShowMessagePreview() { Global::SetNotifyView(viewParam); Local::writeUserSettings(); - Global::RefNotifySettingsChanged().notify(Notify::ChangeType::ViewParams); + AuthSession::Current().notifications()->settingsChanged().notify(ChangeType::ViewParams); } void NotificationsWidget::viewParamUpdated() { @@ -159,10 +165,11 @@ void NotificationsWidget::onNativeNotifications() { return; } - Window::Notifications::GetManager()->clearAllFast(); Global::SetNativeNotifications(_nativeNotifications->checked()); Local::writeUserSettings(); + AuthSession::Current().notifications()->createManager(); + if (Global::NativeNotifications()) { _advanced->slideUp(); } else { @@ -181,13 +188,13 @@ void NotificationsWidget::onPlaySound() { Global::SetSoundNotify(_playSound->checked()); Local::writeUserSettings(); - Global::RefNotifySettingsChanged().notify(Notify::ChangeType::SoundEnabled); + AuthSession::Current().notifications()->settingsChanged().notify(ChangeType::SoundEnabled); } void NotificationsWidget::onIncludeMuted() { Global::SetIncludeMuted(_includeMuted->checked()); Local::writeUserSettings(); - Global::RefNotifySettingsChanged().notify(Notify::ChangeType::IncludeMuted); + AuthSession::Current().notifications()->settingsChanged().notify(ChangeType::IncludeMuted); } } // namespace Settings diff --git a/Telegram/SourceFiles/shortcuts.cpp b/Telegram/SourceFiles/shortcuts.cpp index 5d2ddcf2e..9cf0de4c8 100644 --- a/Telegram/SourceFiles/shortcuts.cpp +++ b/Telegram/SourceFiles/shortcuts.cpp @@ -46,7 +46,7 @@ bool lock_telegram() { bool minimize_telegram() { if (auto w = App::wnd()) { - if (cWorkMode() == dbiwmTrayOnly) { + if (Global::WorkMode().value() == dbiwmTrayOnly) { w->minimizeToTray(); } else { w->setWindowState(Qt::WindowMinimized); diff --git a/Telegram/SourceFiles/stickers/emoji_pan.cpp b/Telegram/SourceFiles/stickers/emoji_pan.cpp index 8e3d4b6f3..ed4f5b80e 100644 --- a/Telegram/SourceFiles/stickers/emoji_pan.cpp +++ b/Telegram/SourceFiles/stickers/emoji_pan.cpp @@ -37,6 +37,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "mainwindow.h" #include "apiwrap.h" #include "mainwidget.h" +#include "auth_session.h" namespace internal { namespace { @@ -719,7 +720,7 @@ StickerPanInner::StickerPanInner(QWidget *parent) : TWidget(parent) _updateInlineItems.setSingleShot(true); connect(&_updateInlineItems, SIGNAL(timeout()), this, SLOT(onUpdateInlineItems())); - subscribe(FileDownload::ImageLoaded(), [this] { + subscribe(AuthSession::CurrentDownloaderTaskFinished(), [this] { update(); readVisibleSets(); }); diff --git a/Telegram/SourceFiles/storage/file_download.cpp b/Telegram/SourceFiles/storage/file_download.cpp index dcccc98df..fdf6aa5e0 100644 --- a/Telegram/SourceFiles/storage/file_download.cpp +++ b/Telegram/SourceFiles/storage/file_download.cpp @@ -25,10 +25,18 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "messenger.h" #include "storage/localstorage.h" #include "platform/platform_file_utilities.h" +#include "auth_session.h" + +namespace Storage { + +void Downloader::clearPriorities() { + ++_priority; +} + +} // namespace Storage namespace { -int32 GlobalPriority = 1; struct DataRequested { DataRequested() { memset(v, 0, sizeof(v)); @@ -37,16 +45,15 @@ struct DataRequested { }; QMap DataRequestedMap; -// Debug flag to find out how we end up crashing. -std::set MustNotDestroy; - -} +} // namespace struct FileLoaderQueue { - FileLoaderQueue(int32 limit) : queries(0), limit(limit), start(0), end(0) { + FileLoaderQueue(int32 limit) : limit(limit) { } - int32 queries, limit; - FileLoader *start, *end; + int queries = 0; + int limit = 0; + FileLoader *start = nullptr; + FileLoader *end = nullptr; }; namespace { @@ -64,7 +71,8 @@ namespace { } FileLoader::FileLoader(const QString &toFile, int32 size, LocationType locationType, LoadToCacheSetting toCache, LoadFromCloudSetting fromCloud, bool autoLoading) -: _autoLoading(autoLoading) +: _downloader(AuthSession::Current().downloader()) +, _autoLoading(autoLoading) , _file(toFile) , _fname(toFile) , _toCache(toCache) @@ -158,8 +166,6 @@ void FileLoader::pause() { } FileLoader::~FileLoader() { - t_assert(MustNotDestroy.find(this) == MustNotDestroy.cend()); - if (_localTaskId) { Local::cancelTask(_localTaskId); } @@ -197,11 +203,9 @@ void FileLoader::localLoaded(const StorageImageSaved &result, const QByteArray & _fileIsOpen = false; Platform::File::PostprocessDownloaded(QFileInfo(_file).absoluteFilePath()); } - FileDownload::ImageLoaded().notify(); + _downloader->taskFinished().notify(); - MustNotDestroy.insert(this); emit progress(this); - MustNotDestroy.erase(this); loadNext(); } @@ -224,45 +228,46 @@ void FileLoader::start(bool loadFirst, bool prior) { } } + auto currentPriority = _downloader->currentPriority(); FileLoader *before = 0, *after = 0; if (prior) { - if (_inQueue && _priority == GlobalPriority) { + if (_inQueue && _priority == currentPriority) { if (loadFirst) { if (!_prev) return startLoading(loadFirst, prior); before = _queue->start; } else { - if (!_next || _next->_priority < GlobalPriority) return startLoading(loadFirst, prior); + if (!_next || _next->_priority < currentPriority) return startLoading(loadFirst, prior); after = _next; - while (after->_next && after->_next->_priority == GlobalPriority) { + while (after->_next && after->_next->_priority == currentPriority) { after = after->_next; } } } else { - _priority = GlobalPriority; + _priority = currentPriority; if (loadFirst) { if (_inQueue && !_prev) return startLoading(loadFirst, prior); before = _queue->start; } else { if (_inQueue) { - if (_next && _next->_priority == GlobalPriority) { + if (_next && _next->_priority == currentPriority) { after = _next; - } else if (_prev && _prev->_priority < GlobalPriority) { + } else if (_prev && _prev->_priority < currentPriority) { before = _prev; - while (before->_prev && before->_prev->_priority < GlobalPriority) { + while (before->_prev && before->_prev->_priority < currentPriority) { before = before->_prev; } } else { return startLoading(loadFirst, prior); } } else { - if (_queue->start && _queue->start->_priority == GlobalPriority) { + if (_queue->start && _queue->start->_priority == currentPriority) { after = _queue->start; } else { before = _queue->start; } } if (after) { - while (after->_next && after->_next->_priority == GlobalPriority) { + while (after->_next && after->_next->_priority == currentPriority) { after = after->_next; } } @@ -270,9 +275,9 @@ void FileLoader::start(bool loadFirst, bool prior) { } } else { if (loadFirst) { - if (_inQueue && (!_prev || _prev->_priority == GlobalPriority)) return startLoading(loadFirst, prior); + if (_inQueue && (!_prev || _prev->_priority == currentPriority)) return startLoading(loadFirst, prior); before = _prev; - while (before->_prev && before->_prev->_priority != GlobalPriority) { + while (before->_prev && before->_prev->_priority != currentPriority) { before = before->_prev; } } else { @@ -330,13 +335,11 @@ void FileLoader::cancel(bool fail) { _fname = QString(); _file.setFileName(_fname); - MustNotDestroy.insert(this); if (fail) { emit failed(this, started); } else { emit progress(this); } - MustNotDestroy.erase(this); loadNext(); } @@ -553,12 +556,10 @@ void mtpFileLoader::partLoaded(int32 offset, const MTPupload_File &result, mtpRe } } if (_finished) { - FileDownload::ImageLoaded().notify(); + _downloader->taskFinished().notify(); } - MustNotDestroy.insert(this); emit progress(this); - MustNotDestroy.erase(this); loadNext(); } @@ -609,9 +610,7 @@ bool mtpFileLoader::tryLoadLocal() { } } - MustNotDestroy.insert(this); emit progress(this); - MustNotDestroy.erase(this); if (_localStatus != LocalNotTried) { return _finished; @@ -690,11 +689,9 @@ void webFileLoader::onFinished(const QByteArray &data) { if (_localStatus == LocalNotFound || _localStatus == LocalFailed) { Local::writeWebFile(_url, _data); } - FileDownload::ImageLoaded().notify(); + _downloader->taskFinished().notify(); - MustNotDestroy.insert(this); emit progress(this); - MustNotDestroy.erase(this); loadNext(); } @@ -1103,22 +1100,3 @@ void WebLoadMainManager::error(webFileLoader *loader) { loader->onError(); } } - -namespace MTP { - void clearLoaderPriorities() { - ++GlobalPriority; - } -} - -namespace FileDownload { -namespace { - -base::Observable ImageLoadedObservable; - -} // namespace - -base::Observable &ImageLoaded() { - return ImageLoadedObservable; -} - -} // namespace FileDownload diff --git a/Telegram/SourceFiles/storage/file_download.h b/Telegram/SourceFiles/storage/file_download.h index b3e6b4a9d..783bbeef5 100644 --- a/Telegram/SourceFiles/storage/file_download.h +++ b/Telegram/SourceFiles/storage/file_download.h @@ -21,20 +21,29 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #pragma once #include "core/observer.h" +#include "storage/localimageloader.h" // for TaskId -namespace MTP { - void clearLoaderPriorities(); -} +namespace Storage { -enum LocationType { - UnknownFileLocation = 0, - // 1, 2, etc are used as "version" value in mediaKey() method. +class Downloader final { +public: + int currentPriority() const { + return _priority; + } + void clearPriorities(); + + base::Observable &taskFinished() { + return _taskFinishedObservable; + } + +private: + base::Observable _taskFinishedObservable; + int _priority = 1; - DocumentFileLocation = 0x4e45abe9, // mtpc_inputDocumentFileLocation - AudioFileLocation = 0x74dc404d, // mtpc_inputAudioFileLocation - VideoFileLocation = 0x3d0364ec, // mtpc_inputVideoFileLocation }; +} // namespace Storage + struct StorageImageSaved { StorageImageSaved() = default; explicit StorageImageSaved(const QByteArray &data) : data(data) { @@ -52,17 +61,6 @@ enum LocalLoadStatus { LocalFailed, }; -using TaskId = void*; // no interface, just id - -enum LoadFromCloudSetting { - LoadFromCloudOrLocal, - LoadFromLocalOnly, -}; -enum LoadToCacheSetting { - LoadToFileOnly, - LoadToCacheAsWell, -}; - class mtpFileLoader; class webFileLoader; @@ -129,6 +127,7 @@ signals: protected: void readImage(const QSize &shrinkBox) const; + Storage::Downloader *_downloader = nullptr; FileLoader *_prev = nullptr; FileLoader *_next = nullptr; int _priority = 0; @@ -338,9 +337,3 @@ static WebLoadManager * const FinishedWebLoadManager = SharedMemoryLocation &ImageLoaded(); - -} // namespace FileDownload diff --git a/Telegram/SourceFiles/storage/localimageloader.h b/Telegram/SourceFiles/storage/localimageloader.h index 642795e5c..9d6955384 100644 --- a/Telegram/SourceFiles/storage/localimageloader.h +++ b/Telegram/SourceFiles/storage/localimageloader.h @@ -99,6 +99,8 @@ struct SendMediaReady { }; +using TaskId = void*; // no interface, just id + class Task { public: virtual void process() = 0; // is executed in a separate thread diff --git a/Telegram/SourceFiles/storage/localstorage.cpp b/Telegram/SourceFiles/storage/localstorage.cpp index 0b23bb843..75e2bcf7c 100644 --- a/Telegram/SourceFiles/storage/localstorage.cpp +++ b/Telegram/SourceFiles/storage/localstorage.cpp @@ -1088,11 +1088,14 @@ bool _readSetting(quint32 blockId, QDataStream &stream, int version, ReadSetting stream >> v; if (!_checkStreamStatus(stream)) return false; - switch (v) { - case dbiwmTrayOnly: cSetWorkMode(dbiwmTrayOnly); break; - case dbiwmWindowOnly: cSetWorkMode(dbiwmWindowOnly); break; - default: cSetWorkMode(dbiwmWindowAndTray); break; + auto newMode = [v] { + switch (v) { + case dbiwmTrayOnly: return dbiwmTrayOnly; + case dbiwmWindowOnly: return dbiwmWindowOnly; + }; + return dbiwmWindowAndTray; }; + Global::RefWorkMode().set(newMode()); } break; case dbiConnectionType: { @@ -2269,7 +2272,7 @@ void writeSettings() { data.stream << quint32(dbiAutoStart) << qint32(cAutoStart()); data.stream << quint32(dbiStartMinimized) << qint32(cStartMinimized()); data.stream << quint32(dbiSendToMenu) << qint32(cSendToMenu()); - data.stream << quint32(dbiWorkMode) << qint32(cWorkMode()); + data.stream << quint32(dbiWorkMode) << qint32(Global::WorkMode().value()); data.stream << quint32(dbiSeenTrayTooltip) << qint32(cSeenTrayTooltip()); data.stream << quint32(dbiAutoUpdate) << qint32(cAutoUpdate()); data.stream << quint32(dbiLastUpdateCheck) << qint32(cLastUpdateCheck()); @@ -2731,7 +2734,7 @@ public: qint32 legacyTypeField = 0; stream >> first >> second >> legacyTypeField >> data; } - void clearInMap() { + void clearInMap() override { StorageMap::iterator j = _imagesMap.find(_location); if (j != _imagesMap.cend() && j->first == _key) { clearKey(_key, FileOption::User); diff --git a/Telegram/SourceFiles/storage/localstorage.h b/Telegram/SourceFiles/storage/localstorage.h index 9a13bcfba..0b3b3c399 100644 --- a/Telegram/SourceFiles/storage/localstorage.h +++ b/Telegram/SourceFiles/storage/localstorage.h @@ -21,6 +21,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #pragma once #include "core/basic_types.h" +#include "storage/file_download.h" namespace Window { namespace Theme { diff --git a/Telegram/SourceFiles/structs.cpp b/Telegram/SourceFiles/structs.cpp index 49630edb0..9b537d8bb 100644 --- a/Telegram/SourceFiles/structs.cpp +++ b/Telegram/SourceFiles/structs.cpp @@ -37,6 +37,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "window/themes/window_theme.h" #include "auth_session.h" #include "messenger.h" +#include "storage/file_download.h" namespace { diff --git a/Telegram/SourceFiles/structs.h b/Telegram/SourceFiles/structs.h index 674e3a608..a8bee40c4 100644 --- a/Telegram/SourceFiles/structs.h +++ b/Telegram/SourceFiles/structs.h @@ -20,6 +20,34 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org */ #pragma once +using MediaKey = QPair; + +inline uint64 mediaMix32To64(int32 a, int32 b) { + return (uint64(*reinterpret_cast(&a)) << 32) | uint64(*reinterpret_cast(&b)); +} + +enum LocationType { + UnknownFileLocation = 0, + // 1, 2, etc are used as "version" value in mediaKey() method. + + DocumentFileLocation = 0x4e45abe9, // mtpc_inputDocumentFileLocation + AudioFileLocation = 0x74dc404d, // mtpc_inputAudioFileLocation + VideoFileLocation = 0x3d0364ec, // mtpc_inputVideoFileLocation +}; + +// Old method, should not be used anymore. +//inline MediaKey mediaKey(LocationType type, int32 dc, const uint64 &id) { +// return MediaKey(mediaMix32To64(type, dc), id); +//} +// New method when version was introduced, type is not relevant anymore (all files are Documents). +inline MediaKey mediaKey(LocationType type, int32 dc, const uint64 &id, int32 version) { + return (version > 0) ? MediaKey(mediaMix32To64(version, dc), id) : MediaKey(mediaMix32To64(type, dc), id); +} + +inline StorageKey mediaKey(const MTPDfileLocation &location) { + return storageKey(location.vdc_id.v, location.vvolume_id.v, location.vlocal_id.v); +} + typedef int32 UserId; typedef int32 ChatId; typedef int32 ChannelId; diff --git a/Telegram/SourceFiles/ui/images.cpp b/Telegram/SourceFiles/ui/images.cpp index 497e75769..b255a05b1 100644 --- a/Telegram/SourceFiles/ui/images.cpp +++ b/Telegram/SourceFiles/ui/images.cpp @@ -883,6 +883,10 @@ void RemoteImage::setData(QByteArray &bytes, const QByteArray &bytesFormat) { _forgot = false; } +bool RemoteImage::amLoading() const { + return _loader && _loader != CancelledFileLoader; +} + void RemoteImage::automaticLoad(const HistoryItem *item) { if (loaded()) return; diff --git a/Telegram/SourceFiles/ui/images.h b/Telegram/SourceFiles/ui/images.h index d54ac1098..f8f5e31ae 100644 --- a/Telegram/SourceFiles/ui/images.h +++ b/Telegram/SourceFiles/ui/images.h @@ -20,7 +20,18 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org */ #pragma once -#include "storage/file_download.h" +class FileLoader; +class mtpFileLoader; + +enum LoadFromCloudSetting { + LoadFromCloudOrLocal, + LoadFromLocalOnly, +}; + +enum LoadToCacheSetting { + LoadToFileOnly, + LoadToCacheAsWell, +}; enum class ImageRoundRadius { None, @@ -317,9 +328,7 @@ protected: private: mutable FileLoader *_loader = nullptr; - bool amLoading() const { - return _loader && _loader != CancelledFileLoader; - } + bool amLoading() const; void doCheckload() const; void destroyLoaderDelayed(FileLoader *newValue = nullptr) const; @@ -328,7 +337,6 @@ private: class StorageImage : public RemoteImage { public: - StorageImage(const StorageImageLocation &location, int32 size = 0); StorageImage(const StorageImageLocation &location, QByteArray &bytes); @@ -511,19 +519,3 @@ inline bool operator==(const FileLocation &a, const FileLocation &b) { inline bool operator!=(const FileLocation &a, const FileLocation &b) { return !(a == b); } - -typedef QPair MediaKey; -inline uint64 mediaMix32To64(int32 a, int32 b) { - return (uint64(*reinterpret_cast(&a)) << 32) | uint64(*reinterpret_cast(&b)); -} -// Old method, should not be used anymore. -//inline MediaKey mediaKey(LocationType type, int32 dc, const uint64 &id) { -// return MediaKey(mediaMix32To64(type, dc), id); -//} -// New method when version was introduced, type is not relevant anymore (all files are Documents). -inline MediaKey mediaKey(LocationType type, int32 dc, const uint64 &id, int32 version) { - return (version > 0) ? MediaKey(mediaMix32To64(version, dc), id) : MediaKey(mediaMix32To64(type, dc), id); -} -inline StorageKey mediaKey(const MTPDfileLocation &location) { - return storageKey(location.vdc_id.v, location.vvolume_id.v, location.vlocal_id.v); -} diff --git a/Telegram/SourceFiles/window/main_window.cpp b/Telegram/SourceFiles/window/main_window.cpp index dd9bc57b5..2f03e1c6b 100644 --- a/Telegram/SourceFiles/window/main_window.cpp +++ b/Telegram/SourceFiles/window/main_window.cpp @@ -43,6 +43,7 @@ MainWindow::MainWindow() : QWidget() } }); subscribe(Global::RefUnreadCounterUpdate(), [this] { updateUnreadCounter(); }); + subscribe(Global::RefWorkMode(), [this](DBIWorkMode mode) { workmodeUpdated(mode); }); _isActiveTimer->setSingleShot(true); connect(_isActiveTimer, SIGNAL(timeout()), this, SLOT(updateIsActiveByTimer())); @@ -53,7 +54,7 @@ bool MainWindow::hideNoQuit() { hideMediaview(); return true; } - if (cWorkMode() == dbiwmTrayOnly || cWorkMode() == dbiwmWindowAndTray) { + if (Global::WorkMode().value() == dbiwmTrayOnly || Global::WorkMode().value() == dbiwmWindowAndTray) { if (minimizeToTray()) { Ui::showChatsList(); return true; diff --git a/Telegram/SourceFiles/window/main_window.h b/Telegram/SourceFiles/window/main_window.h index cf7c19456..e2e77560d 100644 --- a/Telegram/SourceFiles/window/main_window.h +++ b/Telegram/SourceFiles/window/main_window.h @@ -127,6 +127,9 @@ protected: virtual void showTrayTooltip() { } + virtual void workmodeUpdated(DBIWorkMode mode) { + } + virtual void updateControlsGeometry(); // This one is overriden in Windows for historical reasons. diff --git a/Telegram/SourceFiles/window/notifications_manager.cpp b/Telegram/SourceFiles/window/notifications_manager.cpp index 4ce8d61c2..5cf8e131a 100644 --- a/Telegram/SourceFiles/window/notifications_manager.cpp +++ b/Telegram/SourceFiles/window/notifications_manager.cpp @@ -25,25 +25,332 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "lang.h" #include "mainwindow.h" #include "mainwidget.h" +#include "apiwrap.h" +#include "auth_session.h" namespace Window { namespace Notifications { -void Start() { - Default::Start(); - Platform::Notifications::Start(); +System::System(AuthSession *session) : _authSession(session) { + createManager(); + + _waitTimer.setTimeoutHandler([this] { + showNext(); + }); + + subscribe(settingsChanged(), [this](ChangeType type) { + if (type == ChangeType::DesktopEnabled) { + App::wnd()->updateTrayMenu(); + clearAll(); + } else if (type == ChangeType::ViewParams) { + updateAll(); + } else if (type == ChangeType::IncludeMuted) { + Notify::unreadCounterUpdated(); + } + }); } -Manager *GetManager() { - if (auto result = Platform::Notifications::GetManager()) { - return result; +void System::createManager() { + _manager = Platform::Notifications::Create(this); + if (!_manager) { + _manager = std::make_unique(this); } - return Default::GetManager(); } -void Finish() { - Platform::Notifications::Finish(); - Default::Finish(); +void System::schedule(History *history, HistoryItem *item) { + if (App::quitting() || !history->currentNotification() || !App::api()) return; + + PeerData *notifyByFrom = (!history->peer->isUser() && item->mentionsMe()) ? item->from() : 0; + + if (item->isSilent()) { + history->popNotification(item); + return; + } + + bool haveSetting = (history->peer->notify != UnknownNotifySettings); + if (haveSetting) { + if (history->peer->notify != EmptyNotifySettings && history->peer->notify->mute > unixtime()) { + if (notifyByFrom) { + haveSetting = (item->from()->notify != UnknownNotifySettings); + if (haveSetting) { + if (notifyByFrom->notify != EmptyNotifySettings && notifyByFrom->notify->mute > unixtime()) { + history->popNotification(item); + return; + } + } else { + App::api()->requestNotifySetting(notifyByFrom); + } + } else { + history->popNotification(item); + return; + } + } + } else { + if (notifyByFrom && notifyByFrom->notify == UnknownNotifySettings) { + App::api()->requestNotifySetting(notifyByFrom); + } + App::api()->requestNotifySetting(history->peer); + } + if (!item->notificationReady()) { + haveSetting = false; + } + + int delay = item->Has() ? 500 : 100, t = unixtime(); + auto ms = getms(true); + bool isOnline = App::main()->lastWasOnline(), otherNotOld = ((cOtherOnline() * 1000LL) + Global::OnlineCloudTimeout() > t * 1000LL); + bool otherLaterThanMe = (cOtherOnline() * 1000LL + (ms - App::main()->lastSetOnline()) > t * 1000LL); + if (!isOnline && otherNotOld && otherLaterThanMe) { + delay = Global::NotifyCloudDelay(); + } else if (cOtherOnline() >= t) { + delay = Global::NotifyDefaultDelay(); + } + + auto when = ms + delay; + _whenAlerts[history].insert(when, notifyByFrom); + if (Global::DesktopNotify() && !Platform::Notifications::SkipToast()) { + auto &whenMap = _whenMaps[history]; + if (whenMap.constFind(item->id) == whenMap.cend()) { + whenMap.insert(item->id, when); + } + + auto &addTo = haveSetting ? _waiters : _settingWaiters; + auto it = addTo.constFind(history); + if (it == addTo.cend() || it->when > when) { + addTo.insert(history, Waiter(item->id, when, notifyByFrom)); + } + } + if (haveSetting) { + if (!_waitTimer.isActive() || _waitTimer.remainingTime() > delay) { + _waitTimer.start(delay); + } + } +} + +void System::clearAll() { + _manager->clearAll(); + + for (auto i = _whenMaps.cbegin(), e = _whenMaps.cend(); i != e; ++i) { + i.key()->clearNotifications(); + } + _whenMaps.clear(); + _whenAlerts.clear(); + _waiters.clear(); + _settingWaiters.clear(); +} + +void System::clearFromHistory(History *history) { + _manager->clearFromHistory(history); + + history->clearNotifications(); + _whenMaps.remove(history); + _whenAlerts.remove(history); + _waiters.remove(history); + _settingWaiters.remove(history); + + _waitTimer.stop(); + showNext(); +} + +void System::clearFromItem(HistoryItem *item) { + _manager->clearFromItem(item); +} + +void System::clearAllFast() { + _manager->clearAllFast(); + + _whenMaps.clear(); + _whenAlerts.clear(); + _waiters.clear(); + _settingWaiters.clear(); +} + +void System::checkDelayed() { + int32 t = unixtime(); + for (auto i = _settingWaiters.begin(); i != _settingWaiters.end();) { + auto history = i.key(); + bool loaded = false, muted = false; + if (history->peer->notify != UnknownNotifySettings) { + if (history->peer->notify == EmptyNotifySettings || history->peer->notify->mute <= t) { + loaded = true; + } else if (PeerData *from = i.value().notifyByFrom) { + if (from->notify != UnknownNotifySettings) { + if (from->notify == EmptyNotifySettings || from->notify->mute <= t) { + loaded = true; + } else { + loaded = muted = true; + } + } + } else { + loaded = muted = true; + } + } + if (loaded) { + if (HistoryItem *item = App::histItemById(history->channelId(), i.value().msg)) { + if (!item->notificationReady()) { + loaded = false; + } + } else { + muted = true; + } + } + if (loaded) { + if (!muted) { + _waiters.insert(i.key(), i.value()); + } + i = _settingWaiters.erase(i); + } else { + ++i; + } + } + _waitTimer.stop(); + showNext(); +} + +void System::showNext() { + if (App::quitting()) return; + + auto ms = getms(true), nextAlert = 0LL; + bool alert = false; + int32 now = unixtime(); + for (auto i = _whenAlerts.begin(); i != _whenAlerts.end();) { + while (!i.value().isEmpty() && i.value().begin().key() <= ms) { + NotifySettingsPtr n = i.key()->peer->notify, f = i.value().begin().value() ? i.value().begin().value()->notify : UnknownNotifySettings; + while (!i.value().isEmpty() && i.value().begin().key() <= ms + 500) { // not more than one sound in 500ms from one peer - grouping + i.value().erase(i.value().begin()); + } + if (n == EmptyNotifySettings || (n != UnknownNotifySettings && n->mute <= now)) { + alert = true; + } else if (f == EmptyNotifySettings || (f != UnknownNotifySettings && f->mute <= now)) { // notify by from() + alert = true; + } + } + if (i.value().isEmpty()) { + i = _whenAlerts.erase(i); + } else { + if (!nextAlert || nextAlert > i.value().begin().key()) { + nextAlert = i.value().begin().key(); + } + ++i; + } + } + if (alert) { + Platform::Notifications::FlashBounce(); + App::playSound(); + } + + if (_waiters.isEmpty() || !Global::DesktopNotify() || Platform::Notifications::SkipToast()) { + if (nextAlert) { + _waitTimer.start(nextAlert - ms); + } + return; + } + + while (true) { + auto next = 0LL; + 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) { + auto j = _whenMaps.find(history); + if (j == _whenMaps.end()) { + history->clearNotifications(); + i = _waiters.erase(i); + 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(); + break; + } + history->skipNotification(); + } while (history->currentNotification()); + } + if (!history->currentNotification()) { + _whenMaps.remove(history); + i = _waiters.erase(i); + continue; + } + auto when = i.value().when; + if (!notifyItem || next > when) { + next = when; + notifyItem = history->currentNotification(); + notifyHistory = history; + } + ++i; + } + if (notifyItem) { + if (next > ms) { + if (nextAlert && nextAlert < next) { + next = nextAlert; + nextAlert = 0; + } + _waitTimer.start(next - ms); + break; + } else { + HistoryItem *fwd = notifyItem->Has() ? notifyItem : nullptr; // forwarded notify grouping + int32 fwdCount = 1; + + auto ms = getms(true); + History *history = notifyItem->history(); + auto j = _whenMaps.find(history); + if (j == _whenMaps.cend()) { + history->clearNotifications(); + } else { + HistoryItem *nextNotify = 0; + do { + history->skipNotification(); + if (!history->hasNotification()) { + break; + } + + j.value().remove((fwd ? fwd : notifyItem)->id); + do { + auto k = j.value().constFind(history->currentNotification()->id); + if (k != j.value().cend()) { + nextNotify = history->currentNotification(); + _waiters.insert(notifyHistory, Waiter(k.key(), k.value(), 0)); + break; + } + history->skipNotification(); + } while (history->hasNotification()); + if (nextNotify) { + if (fwd) { + HistoryItem *nextFwd = nextNotify->Has() ? nextNotify : nullptr; + if (nextFwd && fwd->author() == nextFwd->author() && qAbs(int64(nextFwd->date.toTime_t()) - int64(fwd->date.toTime_t())) < 2) { + fwd = nextFwd; + ++fwdCount; + } else { + nextNotify = nullptr; + } + } else { + nextNotify = nullptr; + } + } + } while (nextNotify); + } + + _manager->showNotification(notifyItem, fwdCount); + + if (!history->hasNotification()) { + _waiters.remove(history); + _whenMaps.remove(history); + continue; + } + } + } else { + break; + } + } + if (nextAlert) { + _waitTimer.start(nextAlert - ms); + } +} + +void System::updateAll() { + _manager->updateAll(); } Manager::DisplayOptions Manager::getNotificationOptions(HistoryItem *item) { @@ -66,7 +373,7 @@ void Manager::notificationActivated(PeerId peerId, MsgId msgId) { #endif if (App::passcoded()) { window->setInnerFocus(); - window->notifyClear(); + system()->clearAll(); } else { auto tomsg = !history->peer->isUser() && (msgId > 0); if (tomsg) { @@ -76,7 +383,7 @@ void Manager::notificationActivated(PeerId peerId, MsgId msgId) { } } Ui::showPeerHistory(history, tomsg ? msgId : ShowAtUnreadMsgId); - window->notifyClear(history); + system()->clearFromHistory(history); } } onAfterNotificationActivated(peerId, msgId); diff --git a/Telegram/SourceFiles/window/notifications_manager.h b/Telegram/SourceFiles/window/notifications_manager.h index f479e35c9..bd318481a 100644 --- a/Telegram/SourceFiles/window/notifications_manager.h +++ b/Telegram/SourceFiles/window/notifications_manager.h @@ -20,17 +20,100 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org */ #pragma once +class AuthSession; + +namespace Platform { +namespace Notifications { +class Manager; +} // namespace Notifications +} // namespace Platform + +namespace Window { +namespace Notifications { + +enum class ChangeType { + SoundEnabled, + IncludeMuted, + DesktopEnabled, + ViewParams, + MaxCount, + Corner, + DemoIsShown, +}; + +} // namespace Notifications +} // namespace Window + +namespace base { + +template <> +struct custom_is_fast_copy_type : public std::true_type { +}; + +} // namespace base + namespace Window { namespace Notifications { class Manager; -void Start(); -Manager *GetManager(); -void Finish(); +class System final : private base::Subscriber { +public: + System(AuthSession *session); + + void createManager(); + + void checkDelayed(); + void schedule(History *history, HistoryItem *item); + void clearFromHistory(History *history); + void clearFromItem(HistoryItem *item); + void clearAll(); + void clearAllFast(); + void updateAll(); + + base::Observable &settingsChanged() { + return _settingsChanged; + } + + AuthSession *authSession() { + return _authSession; + } + +private: + void showNext(); + + AuthSession *_authSession = nullptr; + + QMap> _whenMaps; + + struct Waiter { + Waiter(MsgId msg, TimeMs when, PeerData *notifyByFrom) + : msg(msg) + , when(when) + , notifyByFrom(notifyByFrom) { + } + MsgId msg; + TimeMs when; + PeerData *notifyByFrom; + }; + using Waiters = QMap; + Waiters _waiters; + Waiters _settingWaiters; + SingleTimer _waitTimer; + + QMap> _whenAlerts; + + std::unique_ptr _manager; + + base::Observable _settingsChanged; + +}; class Manager { public: + Manager(System *system) : _system(system) { + } + void showNotification(HistoryItem *item, int forwardedCount) { doShowNotification(item, forwardedCount); } @@ -63,6 +146,10 @@ public: virtual ~Manager() = default; protected: + System *system() const { + return _system; + } + virtual void doUpdateAll() = 0; virtual void doShowNotification(HistoryItem *item, int forwardedCount) = 0; virtual void doClearAll() = 0; @@ -74,10 +161,15 @@ protected: virtual void onAfterNotificationActivated(PeerId peerId, MsgId msgId) { } +private: + System *_system = nullptr; + }; class NativeManager : public Manager { protected: + using Manager::Manager; + void doUpdateAll() override { doClearAllFast(); } diff --git a/Telegram/SourceFiles/window/notifications_manager_default.cpp b/Telegram/SourceFiles/window/notifications_manager_default.cpp index 41f543dbb..2bfaefc19 100644 --- a/Telegram/SourceFiles/window/notifications_manager_default.cpp +++ b/Telegram/SourceFiles/window/notifications_manager_default.cpp @@ -31,14 +31,14 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "styles/style_dialogs.h" #include "styles/style_boxes.h" #include "styles/style_window.h" +#include "storage/file_download.h" +#include "auth_session.h" namespace Window { namespace Notifications { namespace Default { namespace { -NeverFreedPointer ManagerInstance; - int notificationMaxHeight() { return st::notifyMinHeight + st::notifyReplyArea.heightMax + st::notifyBorderWidth; } @@ -59,32 +59,24 @@ internal::Widget::Direction notificationShiftDirection() { } // namespace -void Start() { - ManagerInstance.createIfNull(); +std::unique_ptr Create(System *system) { + return std::make_unique(system); } -Manager *GetManager() { - return ManagerInstance.data(); -} - -void Finish() { - ManagerInstance.clear(); -} - -Manager::Manager() { - subscribe(FileDownload::ImageLoaded(), [this] { - for_const (auto notification, _notifications) { +Manager::Manager(System *system) : Notifications::Manager(system) { + subscribe(system->authSession()->downloader()->taskFinished(), [this] { + for_const (auto ¬ification, _notifications) { notification->updatePeerPhoto(); } }); - subscribe(Global::RefNotifySettingsChanged(), [this](Notify::ChangeType change) { + subscribe(system->settingsChanged(), [this](ChangeType change) { settingsChanged(change); }); _inputCheckTimer.setTimeoutHandler([this] { checkLastInput(); }); } bool Manager::hasReplyingNotification() const { - for_const (auto notification, _notifications) { + for_const (auto ¬ification, _notifications) { if (notification->isReplying()) { return true; } @@ -92,20 +84,20 @@ bool Manager::hasReplyingNotification() const { return false; } -void Manager::settingsChanged(Notify::ChangeType change) { - if (change == Notify::ChangeType::Corner) { +void Manager::settingsChanged(ChangeType change) { + if (change == ChangeType::Corner) { auto startPosition = notificationStartPosition(); auto shiftDirection = notificationShiftDirection(); - for_const (auto notification, _notifications) { + for_const (auto ¬ification, _notifications) { notification->updatePosition(startPosition, shiftDirection); } if (_hideAll) { _hideAll->updatePosition(startPosition, shiftDirection); } - } else if (change == Notify::ChangeType::MaxCount) { + } else if (change == ChangeType::MaxCount) { int allow = Global::NotificationsCount(); for (int i = _notifications.size(); i != 0;) { - auto notification = _notifications[--i]; + auto ¬ification = _notifications[--i]; if (notification->isUnlinked()) continue; if (--allow < 0) { notification->unlinkHistory(); @@ -116,14 +108,14 @@ void Manager::settingsChanged(Notify::ChangeType change) { showNextFromQueue(); } } - } else if (change == Notify::ChangeType::DemoIsShown) { + } else if (change == ChangeType::DemoIsShown) { auto demoIsShown = Global::NotificationsDemoIsShown(); _demoMasterOpacity.start([this] { demoMasterOpacityCallback(); }, demoIsShown ? 1. : 0., demoIsShown ? 0. : 1., st::notifyFastAnim); } } void Manager::demoMasterOpacityCallback() { - for_const (auto notification, _notifications) { + for_const (auto ¬ification, _notifications) { notification->updateOpacity(); } if (_hideAll) { @@ -138,7 +130,7 @@ float64 Manager::demoMasterOpacity() const { void Manager::checkLastInput() { auto replying = hasReplyingNotification(); auto waiting = false; - for_const (auto notification, _notifications) { + for_const (auto ¬ification, _notifications) { if (!notification->checkLastInput(replying)) { waiting = true; } @@ -151,7 +143,7 @@ void Manager::checkLastInput() { void Manager::startAllHiding() { if (!hasReplyingNotification()) { int notHidingCount = 0; - for_const (auto notification, _notifications) { + for_const (auto ¬ification, _notifications) { if (notification->isShowing()) { ++notHidingCount; } else { @@ -166,7 +158,7 @@ void Manager::startAllHiding() { } void Manager::stopAllHiding() { - for_const (auto notification, _notifications) { + for_const (auto ¬ification, _notifications) { notification->stopHiding(); } if (_hideAll) { @@ -175,46 +167,52 @@ void Manager::stopAllHiding() { } void Manager::showNextFromQueue() { - if (!_queuedNotifications.isEmpty()) { - int count = Global::NotificationsCount(); - for_const (auto notification, _notifications) { - if (notification->isUnlinked()) continue; - --count; - } - if (count > 0) { - auto startPosition = notificationStartPosition(); - auto startShift = 0; - auto shiftDirection = notificationShiftDirection(); - do { - auto queued = _queuedNotifications.front(); - _queuedNotifications.pop_front(); - - auto notification = std::make_unique( - queued.history, - queued.peer, - queued.author, - queued.item, - queued.forwardedCount, - startPosition, startShift, shiftDirection); - Platform::Notifications::CustomNotificationShownHook(notification.get()); - _notifications.push_back(notification.release()); - --count; - } while (count > 0 && !_queuedNotifications.isEmpty()); - - _positionsOutdated = true; - checkLastInput(); + auto guard = base::scope_guard([this] { + if (_positionsOutdated) { + moveWidgets(); } + }); + if (_queuedNotifications.empty()) { + return; } - if (_positionsOutdated) { - moveWidgets(); + int count = Global::NotificationsCount(); + for_const (auto ¬ification, _notifications) { + if (notification->isUnlinked()) continue; + --count; } + if (count <= 0) { + return; + } + + auto startPosition = notificationStartPosition(); + auto startShift = 0; + auto shiftDirection = notificationShiftDirection(); + do { + auto queued = _queuedNotifications.front(); + _queuedNotifications.pop_front(); + + auto notification = std::make_unique( + this, + queued.history, + queued.peer, + queued.author, + queued.item, + queued.forwardedCount, + startPosition, startShift, shiftDirection); + Platform::Notifications::CustomNotificationShownHook(notification.get()); + _notifications.push_back(std::move(notification)); + --count; + } while (count > 0 && !_queuedNotifications.empty()); + + _positionsOutdated = true; + checkLastInput(); } void Manager::moveWidgets() { auto shift = st::notifyDeltaY; int lastShift = 0, lastShiftCurrent = 0, count = 0; for (int i = _notifications.size(); i != 0;) { - auto notification = _notifications[--i]; + auto ¬ification = _notifications[--i]; if (notification->isUnlinked()) continue; notification->changeShift(shift); @@ -226,10 +224,10 @@ void Manager::moveWidgets() { ++count; } - if (count > 1 || !_queuedNotifications.isEmpty()) { + if (count > 1 || !_queuedNotifications.empty()) { auto deltaY = st::notifyHideAllHeight + st::notifyDeltaY; if (!_hideAll) { - _hideAll = new HideAllButton(notificationStartPosition(), lastShiftCurrent, notificationShiftDirection()); + _hideAll = std::make_unique(this, notificationStartPosition(), lastShiftCurrent, notificationShiftDirection()); } _hideAll->changeShift(lastShift); _hideAll->stopHiding(); @@ -243,10 +241,12 @@ void Manager::changeNotificationHeight(Notification *notification, int newHeight if (!deltaHeight) return; notification->addToHeight(deltaHeight); - auto index = _notifications.indexOf(notification); - if (index > 0) { - for (int i = 0; i != index; ++i) { - auto notification = _notifications[i]; + auto it = std::find_if(_notifications.cbegin(), _notifications.cend(), [notification](auto &item) { + return (item.get() == notification); + }); + if (it != _notifications.cend()) { + for (auto i = _notifications.cbegin(); i != it; ++i) { + auto ¬ification = *i; if (notification->isUnlinked()) continue; notification->addToShift(deltaHeight); @@ -266,22 +266,21 @@ void Manager::unlinkFromShown(Notification *remove) { showNextFromQueue(); } -void Manager::removeFromShown(Notification *remove) { - if (remove) { - auto index = _notifications.indexOf(remove); - if (index >= 0) { - _notifications.removeAt(index); +void Manager::removeWidget(internal::Widget *remove) { + if (remove == _hideAll.get()) { + _hideAll.reset(); + } else if (remove) { + auto it = std::find_if(_notifications.cbegin(), _notifications.cend(), [remove](auto &item) { + return item.get() == remove; + }); + if (it != _notifications.cend()) { + _notifications.erase(it); _positionsOutdated = true; } } showNextFromQueue(); } -void Manager::removeHideAll(HideAllButton *remove) { - if (remove == _hideAll) { - _hideAll = nullptr; - } -} void Manager::doShowNotification(HistoryItem *item, int forwardedCount) { _queuedNotifications.push_back(QueuedNotification(item, forwardedCount)); showNextFromQueue(); @@ -289,7 +288,7 @@ void Manager::doShowNotification(HistoryItem *item, int forwardedCount) { void Manager::doClearAll() { _queuedNotifications.clear(); - for_const (auto notification, _notifications) { + for_const (auto ¬ification, _notifications) { notification->unlinkHistory(); } showNextFromQueue(); @@ -297,11 +296,8 @@ void Manager::doClearAll() { void Manager::doClearAllFast() { _queuedNotifications.clear(); - auto notifications = base::take(_notifications); - for_const (auto notification, notifications) { - delete notification; - } - delete base::take(_hideAll); + base::take(_notifications); + base::take(_hideAll); } void Manager::doClearFromHistory(History *history) { @@ -312,7 +308,7 @@ void Manager::doClearFromHistory(History *history) { ++i; } } - for_const (auto notification, _notifications) { + for_const (auto ¬ification, _notifications) { if (notification->unlinkHistory(history)) { _positionsOutdated = true; } @@ -321,20 +317,18 @@ void Manager::doClearFromHistory(History *history) { } void Manager::doClearFromItem(HistoryItem *item) { - for (auto i = 0, queuedCount = _queuedNotifications.size(); i != queuedCount; ++i) { - if (_queuedNotifications[i].item == item) { - _queuedNotifications.removeAt(i); - break; - } - } - for_const (auto notification, _notifications) { + _queuedNotifications.erase(std::remove_if(_queuedNotifications.begin(), _queuedNotifications.end(), [item](auto &queued) { + return (queued.item == item); + }), _queuedNotifications.cend()); + + for_const (auto ¬ification, _notifications) { // Calls unlinkFromShown() -> showNextFromQueue() notification->itemRemoved(item); } } void Manager::doUpdateAll() { - for_const (auto notification, _notifications) { + for_const (auto ¬ification, _notifications) { notification->updateNotifyDisplay(); } } @@ -345,7 +339,8 @@ Manager::~Manager() { namespace internal { -Widget::Widget(QPoint startPosition, int shift, Direction shiftDirection) : TWidget(nullptr) +Widget::Widget(Manager *manager, QPoint startPosition, int shift, Direction shiftDirection) : TWidget(nullptr) +, _manager(manager) , _startPosition(startPosition) , _direction(shiftDirection) , a_shift(shift) @@ -364,12 +359,10 @@ void Widget::destroyDelayed() { if (_deleted) return; _deleted = true; - // Ubuntu has a lag if deleteLater() called immediately. -#if defined Q_OS_LINUX32 || defined Q_OS_LINUX64 - QTimer::singleShot(1000, [this] { delete this; }); -#else // Q_OS_LINUX32 || Q_OS_LINUX64 - deleteLater(); -#endif // Q_OS_LINUX32 || Q_OS_LINUX64 + // Ubuntu has a lag if a fully transparent widget is destroyed immediately. + App::CallDelayed(1000, this, [this] { + manager()->removeWidget(this); + }); } void Widget::opacityAnimationCallback() { @@ -411,9 +404,7 @@ void Widget::hideAnimated(float64 duration, const anim::transition &func) { } void Widget::updateOpacity() { - if (auto manager = ManagerInstance.data()) { - setWindowOpacity(_a_opacity.current(_hiding ? 0. : 1.) * manager->demoMasterOpacity()); - } + setWindowOpacity(_a_opacity.current(_hiding ? 0. : 1.) * _manager->demoMasterOpacity()); } void Widget::changeShift(int top) { @@ -469,7 +460,7 @@ void Background::paintEvent(QPaintEvent *e) { p.fillRect(st::notifyBorderWidth, height() - st::notifyBorderWidth, width() - 2 * st::notifyBorderWidth, st::notifyBorderWidth, st::notifyBorder); } -Notification::Notification(History *history, PeerData *peer, PeerData *author, HistoryItem *msg, int forwardedCount, QPoint startPosition, int shift, Direction shiftDirection) : Widget(startPosition, shift, shiftDirection) +Notification::Notification(Manager *manager, History *history, PeerData *peer, PeerData *author, HistoryItem *msg, int forwardedCount, QPoint startPosition, int shift, Direction shiftDirection) : Widget(manager, startPosition, shift, shiftDirection) , _history(history) , _peer(peer) , _author(author) @@ -711,9 +702,7 @@ bool Notification::canReply() const { } void Notification::unlinkHistoryInManager() { - if (auto manager = ManagerInstance.data()) { - manager->unlinkFromShown(this); - } + manager()->unlinkFromShown(this); } void Notification::toggleActionButtons(bool visible) { @@ -766,19 +755,15 @@ void Notification::showReplyField() { void Notification::sendReply() { if (!_history) return; - if (auto manager = ManagerInstance.data()) { - auto peerId = _history->peer->id; - auto msgId = _item ? _item->id : ShowAtUnreadMsgId; - manager->notificationReplied(peerId, msgId, _replyArea->getLastText()); + auto peerId = _history->peer->id; + auto msgId = _item ? _item->id : ShowAtUnreadMsgId; + manager()->notificationReplied(peerId, msgId, _replyArea->getLastText()); - manager->startAllHiding(); - } + manager()->startAllHiding(); } void Notification::changeHeight(int newHeight) { - if (auto manager = ManagerInstance.data()) { - manager->changeNotificationHeight(this, newHeight); - } + manager()->changeNotificationHeight(this, newHeight); } bool Notification::unlinkHistory(History *history) { @@ -793,9 +778,7 @@ bool Notification::unlinkHistory(History *history) { void Notification::enterEventHook(QEvent *e) { if (!_history) return; - if (auto manager = ManagerInstance.data()) { - manager->stopAllHiding(); - } + manager()->stopAllHiding(); if (!_replyArea && canReply()) { toggleActionButtons(true); } @@ -803,9 +786,7 @@ void Notification::enterEventHook(QEvent *e) { void Notification::leaveEventHook(QEvent *e) { if (!_history) return; - if (auto manager = ManagerInstance.data()) { - manager->startAllHiding(); - } + manager()->startAllHiding(); toggleActionButtons(false); } @@ -821,11 +802,9 @@ void Notification::mousePressEvent(QMouseEvent *e) { unlinkHistoryInManager(); } else { e->ignore(); - if (auto manager = ManagerInstance.data()) { - auto peerId = _history->peer->id; - auto msgId = _item ? _item->id : ShowAtUnreadMsgId; - manager->notificationActivated(peerId, msgId); - } + auto peerId = _history->peer->id; + auto msgId = _item ? _item->id : ShowAtUnreadMsgId; + manager()->notificationActivated(peerId, msgId); } } @@ -850,13 +829,7 @@ void Notification::onHideByTimer() { startHiding(); } -Notification::~Notification() { - if (auto manager = ManagerInstance.data()) { - manager->removeFromShown(this); - } -} - -HideAllButton::HideAllButton(QPoint startPosition, int shift, Direction shiftDirection) : Widget(startPosition, shift, shiftDirection) { +HideAllButton::HideAllButton(Manager *manager, QPoint startPosition, int shift, Direction shiftDirection) : Widget(manager, startPosition, shift, shiftDirection) { setCursor(style::cur_pointer); auto position = computePosition(st::notifyHideAllHeight); @@ -885,12 +858,6 @@ void HideAllButton::stopHiding() { hideStop(); } -HideAllButton::~HideAllButton() { - if (auto manager = ManagerInstance.data()) { - manager->removeHideAll(this); - } -} - void HideAllButton::enterEventHook(QEvent *e) { _mouseOver = true; update(); @@ -908,9 +875,7 @@ void HideAllButton::mousePressEvent(QMouseEvent *e) { void HideAllButton::mouseReleaseEvent(QMouseEvent *e) { auto mouseDown = base::take(_mouseDown); if (mouseDown && _mouseOver) { - if (auto manager = ManagerInstance.data()) { - manager->clearAll(); - } + manager()->clearAll(); } } diff --git a/Telegram/SourceFiles/window/notifications_manager_default.h b/Telegram/SourceFiles/window/notifications_manager_default.h index b75cbfb06..7417c8114 100644 --- a/Telegram/SourceFiles/window/notifications_manager_default.h +++ b/Telegram/SourceFiles/window/notifications_manager_default.h @@ -39,18 +39,15 @@ class HideAllButton; } // namespace internal class Manager; - -void Start(); -Manager *GetManager(); -void Finish(); +std::unique_ptr Create(System *system); class Manager : public Notifications::Manager, private base::Subscriber { public: - Manager(); + Manager(System *system); template void enumerateNotifications(Method method) { - for_const (auto notification, _notifications) { + for_const (auto ¬ification, _notifications) { method(notification); } } @@ -73,25 +70,24 @@ private: void showNextFromQueue(); void unlinkFromShown(Notification *remove); - void removeFromShown(Notification *remove); - void removeHideAll(HideAllButton *remove); void startAllHiding(); void stopAllHiding(); void checkLastInput(); + void removeWidget(internal::Widget *remove); + float64 demoMasterOpacity() const; void demoMasterOpacityCallback(); void moveWidgets(); void changeNotificationHeight(Notification *widget, int newHeight); - void settingsChanged(Notify::ChangeType change); + void settingsChanged(ChangeType change); bool hasReplyingNotification() const; - using Notifications = QList; - Notifications _notifications; + std::vector> _notifications; - HideAllButton *_hideAll = nullptr; + std::unique_ptr _hideAll; bool _positionsOutdated = false; SingleTimer _inputCheckTimer; @@ -111,8 +107,7 @@ private: HistoryItem *item; int forwardedCount; }; - using QueuedNotifications = QList; - QueuedNotifications _queuedNotifications; + std::deque _queuedNotifications; Animation _demoMasterOpacity; @@ -126,7 +121,7 @@ public: Up, Down, }; - Widget(QPoint startPosition, int shift, Direction shiftDirection); + Widget(Manager *manager, QPoint startPosition, int shift, Direction shiftDirection); bool isShowing() const { return _a_opacity.animating() && !_hiding; @@ -149,6 +144,11 @@ protected: virtual void updateGeometry(int x, int y, int width, int height); +protected: + Manager *manager() const { + return _manager; + } + private: void opacityAnimationCallback(); void destroyDelayed(); @@ -156,6 +156,8 @@ private: void hideAnimated(float64 duration, const anim::transition &func); void step_shift(float64 ms, bool timer); + Manager *_manager = nullptr; + bool _hiding = false; bool _deleted = false; Animation _a_opacity; @@ -180,7 +182,7 @@ class Notification : public Widget { Q_OBJECT public: - Notification(History *history, PeerData *peer, PeerData *author, HistoryItem *item, int forwardedCount, QPoint startPosition, int shift, Direction shiftDirection); + Notification(Manager *manager, History *history, PeerData *peer, PeerData *author, HistoryItem *item, int forwardedCount, QPoint startPosition, int shift, Direction shiftDirection); void startHiding(); void stopHiding(); @@ -200,8 +202,6 @@ public: bool unlinkHistory(History *history = nullptr); bool checkLastInput(bool hasReplyingNotifications); - ~Notification(); - protected: void enterEventHook(QEvent *e) override; void leaveEventHook(QEvent *e) override; @@ -260,14 +260,12 @@ private: class HideAllButton : public Widget { public: - HideAllButton(QPoint startPosition, int shift, Direction shiftDirection); + HideAllButton(Manager *manager, QPoint startPosition, int shift, Direction shiftDirection); void startHiding(); void startHidingFast(); void stopHiding(); - ~HideAllButton(); - protected: void enterEventHook(QEvent *e) override; void leaveEventHook(QEvent *e) override; diff --git a/Telegram/SourceFiles/window/notifications_utilities.cpp b/Telegram/SourceFiles/window/notifications_utilities.cpp index 739efde86..bf91b2190 100644 --- a/Telegram/SourceFiles/window/notifications_utilities.cpp +++ b/Telegram/SourceFiles/window/notifications_utilities.cpp @@ -112,7 +112,13 @@ void CachedUserpics::onClear() { CachedUserpics::~CachedUserpics() { if (_someSavedFlag) { - psDeleteDir(cWorkingDir() + qsl("tdata/temp")); + TimeMs result = 0; + for_const (auto &item, _images) { + QFile(item.path).remove(); + } + +// This works about 1200ms on Windows for a folder with one image O_o +// psDeleteDir(cWorkingDir() + qsl("tdata/temp")); } } diff --git a/Telegram/SourceFiles/window/window_main_menu.cpp b/Telegram/SourceFiles/window/window_main_menu.cpp index 03ee5f4b2..f59569592 100644 --- a/Telegram/SourceFiles/window/window_main_menu.cpp +++ b/Telegram/SourceFiles/window/window_main_menu.cpp @@ -30,6 +30,7 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org #include "boxes/aboutbox.h" #include "lang.h" #include "core/click_handler_types.h" +#include "auth_session.h" namespace Window { @@ -70,7 +71,7 @@ MainMenu::MainMenu(QWidget *parent) : TWidget(parent) _version->setLink(1, MakeShared(qsl("https://desktop.telegram.org/changelog"))); _version->setLink(2, MakeShared([] { Ui::show(Box()); })); - subscribe(FileDownload::ImageLoaded(), [this] { update(); }); + subscribe(AuthSession::CurrentDownloaderTaskFinished(), [this] { update(); }); subscribe(Global::RefConnectionTypeChanged(), [this] { updateConnectionState(); }); updateConnectionState(); }