Notifications management moved to AuthSession.

Also implemented Global::WorkMode() as an base::Variable.
This commit is contained in:
John Preston 2017-03-04 22:36:59 +03:00
parent b14ba398e6
commit 81790b2271
80 changed files with 1299 additions and 1152 deletions

View file

@ -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;
}

View file

@ -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<HistoryItem*> 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();

View file

@ -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();

View file

@ -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<Storage::Downloader>())
, _notifications(std::make_unique<Window::Notifications::System>(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<void> &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;

View file

@ -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<void> &CurrentDownloaderTaskFinished();
Window::Notifications::System *notifications() {
return _notifications.get();
}
~AuthSession();
private:
UserId _userId = 0;
const std::unique_ptr<Storage::Downloader> _downloader;
const std::unique_ptr<Window::Notifications::System> _notifications;
};

View file

@ -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();
}

View file

@ -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();

View file

@ -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();
}
}

View file

@ -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;

View file

@ -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();

View file

@ -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;

View file

@ -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();
}
}

View file

@ -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<int32*>(hashData.data());
if (hashDataInts[0] != AuthSession::CurrentUserId()) {
if (!AuthSession::Exists() || hashDataInts[0] != AuthSession::CurrentUserId()) {
Ui::show(Box<InformBox>(lang(lng_share_wrong_user)));
return;
}

View file

@ -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();
});

View file

@ -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);

View file

@ -176,7 +176,7 @@ struct vtable_once_impl<Lambda, false, Return, Args...> : public vtable_base<Ret
vtable_once_impl() : Parent(
&bad_construct_copy,
&vtable_once_impl::construct_move_other_method,
&bad_const_call<Args...>,
&bad_const_call<Return, Args...>,
&vtable_once_impl::call_method,
&vtable_once_impl::destruct_method) {
}

View file

@ -376,7 +376,7 @@ public:
void setForced(parameter_type<Type> newValue, bool sync = false) {
_value = newValue;
_observable.notify(_value, sync);
changed().notify(_value, sync);
}
void set(parameter_type<Type> newValue, bool sync = false) {
@ -388,16 +388,16 @@ public:
template <typename Callback>
void process(Callback callback, bool sync = false) {
callback(_value);
_observable.notify(_value, sync);
changed().notify(_value, sync);
}
Observable<Type> &observable() {
return _observable;
Observable<Type> &changed() {
return _changed;
}
private:
Type _value;
Observable<Type> _observable;
Observable<Type> _changed;
};
@ -416,12 +416,12 @@ protected:
template <typename Type, typename Lambda>
int subscribe(base::Variable<Type> &variable, Lambda &&handler) {
return subscribe(variable.observable(), std::forward<Lambda>(handler));
return subscribe(variable.changed(), std::forward<Lambda>(handler));
}
template <typename Type, typename Lambda>
int subscribe(base::Variable<Type> *variable, Lambda &&handler) {
return subscribe(variable->observable(), std::forward<Lambda>(handler));
return subscribe(variable->changed(), std::forward<Lambda>(handler));
}
void unsubscribe(int index) {

View file

@ -20,30 +20,14 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
*/
#include "core/task_queue.h"
#include <thread>
#include <condition_variable>
namespace base {
namespace {
auto MainThreadId = QThread::currentThreadId();
const auto MaxThreadsCount = qMax(QThread::idealThreadCount(), 2);
template <typename Lambda>
class Thread : public QThread {
public:
Thread(Lambda code) : _code(std::move(code)) {
}
void run() override {
_code();
}
private:
Lambda _code;
};
template <typename Lambda>
object_ptr<Thread<Lambda>> MakeThread(Lambda code) {
return object_ptr<Thread<Lambda>>(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<TaskThreadPool> &Instance();
static const std::shared_ptr<TaskThreadPool> &Instance();
void AddQueueTask(TaskQueue *queue, Task &&task);
void RemoveQueue(TaskQueue *queue);
@ -84,16 +68,15 @@ public:
~TaskThreadPool();
private:
void ThreadFunction();
std::vector<object_ptr<QThread>> threads_;
QMutex queues_mutex_;
std::vector<std::thread> 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<std::mutex> 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<std::mutex> 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<std::mutex> 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> &TaskQueue::TaskThreadPool::Instance() { // static
static auto Pool = MakeShared<TaskThreadPool>(Private());
const std::shared_ptr<TaskQueue::TaskThreadPool> &TaskQueue::TaskThreadPool::Instance() { // static
static auto Pool = std::make_shared<TaskThreadPool>(Private());
return Pool;
}
@ -259,7 +241,7 @@ void TaskQueue::TaskThreadPool::ThreadFunction() {
while (true) {
Task task;
{
QMutexLocker lock(&queues_mutex_);
std::unique_lock<std::mutex> 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<std::mutex> 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<std::mutex> 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;

View file

@ -20,6 +20,9 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
*/
#pragma once
#include <mutex>
#include <memory>
namespace base {
using Task = lambda_once<void()>;
@ -70,11 +73,11 @@ private:
const Priority priority_;
std::deque<Task> 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<TaskThreadPool> weak_thread_pool_;
std::weak_ptr<TaskThreadPool> weak_thread_pool_;
class TaskQueueList;

View file

@ -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) {

View file

@ -686,7 +686,6 @@ struct Data {
int NotificationsCount = 3;
Notify::ScreenCorner NotificationsCorner = Notify::ScreenCorner::BottomRight;
bool NotificationsDemoIsShown = false;
base::Observable<Notify::ChangeType> NotifySettingsChanged;
DBIConnectionType ConnectionType = dbictAuto;
bool TryIPv6 = (cPlatform() == dbipWindows) ? false : true;
@ -699,6 +698,8 @@ struct Data {
bool LocalPasscode = false;
base::Observable<void> LocalPasscodeChanged;
base::Variable<DBIWorkMode> WorkMode = { dbiwmWindowAndTray };
base::Observable<HistoryItem*> ItemRemoved;
base::Observable<void> UnreadCounterUpdate;
base::Observable<void> 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<Notify::ChangeType>, 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<void>, LocalPasscodeChanged);
DefineRefVar(Global, base::Variable<DBIWorkMode>, WorkMode);
DefineRefVar(Global, base::Observable<HistoryItem*>, ItemRemoved);
DefineRefVar(Global, base::Observable<void>, UnreadCounterUpdate);
DefineRefVar(Global, base::Observable<void>, PeerChooseCancel);

View file

@ -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<Notify::ChangeType> : 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<Notify::ChangeType>, NotifySettingsChanged);
DeclareVar(DBIConnectionType, ConnectionType);
DeclareVar(bool, TryIPv6);
@ -408,6 +390,8 @@ DeclareVar(int, AutoLock);
DeclareVar(bool, LocalPasscode);
DeclareRefVar(base::Observable<void>, LocalPasscodeChanged);
DeclareRefVar(base::Variable<DBIWorkMode>, WorkMode);
DeclareRefVar(base::Observable<HistoryItem*>, ItemRemoved);
DeclareRefVar(base::Observable<void>, UnreadCounterUpdate);
DeclareRefVar(base::Observable<void>, PeerChooseCancel);

View file

@ -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;
}

View file

@ -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();

View file

@ -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) {

View file

@ -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);
}

View file

@ -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<uint64>();
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<HistoryMessage*>(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<HistoryMessage*>(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<HistoryPhoto*>(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;

View file

@ -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<InformBox>("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.

View file

@ -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) {

View file

@ -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<Window::SectionMemento> &&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<QAction*(const QStrin
}
void MainWidget::onViewsIncrement() {
if (!App::main() || !AuthSession::Current()) return;
for (ViewsIncrement::iterator i = _viewsToIncrement.begin(); i != _viewsToIncrement.cend();) {
if (_viewsIncrementRequests.contains(i.key())) {
++i;
@ -3628,8 +3624,6 @@ bool MainWidget::failDifference(const RPCError &error) {
}
void MainWidget::onGetDifferenceTimeByPts() {
if (!AuthSession::Current()) return;
auto now = getms(true), wait = 0LL;
if (_getDifferenceTimeByPts) {
if (_getDifferenceTimeByPts > 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<MTPInputUser>(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<MTPUser>(1, user))) {
self->loadUserpic();
if (auto user = App::feedUsers(MTP_vector<MTPUser>(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<MTPUser> &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<MTPUser> &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();

View file

@ -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<MTPUser> &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<MTPUser> &users);
void saveSectionInStack();
bool _started = false;

View file

@ -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<MTPInputUser>(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<HistoryMessageForwarded>() ? 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<HistoryMessageForwarded>() ? 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<HistoryMessageForwarded>() ? 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;

View file

@ -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<MsgId, TimeMs>;
using NotifyWhenMaps = QMap<History*, NotifyWhenMap>;
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<History*, NotifyWaiter>;
NotifyWaiters _notifyWaiters;
NotifyWaiters _notifySettingWaiters;
SingleTimer _notifyWaitTimer;
using NotifyWhenAlert = QMap<TimeMs, PeerData*>;
using NotifyWhenAlerts = QMap<History*, NotifyWhenAlert>;
NotifyWhenAlerts _notifyWhenAlerts;
};
class PreLaunchWindow : public TWidget {

View file

@ -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;

View file

@ -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<AuthSession>(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();

View file

@ -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<void> &authSessionChanged() {
return _authSessionChanged;
}
FileUploader *uploader();
void uploadProfilePhoto(const QImage &tosend, const PeerId &peerId);
@ -145,6 +148,7 @@ private:
std::unique_ptr<MTP::Instance> _mtproto;
std::unique_ptr<MTP::Instance> _mtprotoForKeysDestroy;
std::unique_ptr<AuthSession> _authSession;
base::Observable<void> _authSessionChanged;
SingleDelayedCall _delayedLoadersDestroyer;
std::vector<std::unique_ptr<FileLoader>> _delayedDestroyedLoaders;

View file

@ -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) {

View file

@ -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 {

View file

@ -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();

View file

@ -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);

View file

@ -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;

View file

@ -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<Manager> 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<Manager*> &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<Manager*> &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<Manager*> &guarded, PeerId peerId, MsgId msgId)
: weak(guarded)
, peerId(peerId)
, msgId(msgId) {
}
std::weak_ptr<Manager*> weak;
PeerId peerId = 0;
MsgId msgId = 0;
};
static void performOnMainQueue(NotificationDataStruct *data, base::lambda_once<void(Manager *manager)> 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<NotificationDataStruct*>(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<NotificationDataStruct*>(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<NotificationDataStruct*>(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<NotificationData>;
} // 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<const char*>(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<Window::Notifications::Manager> Create(Window::Notifications::System *system) {
if (Global::NativeNotifications() && Supported()) {
return std::make_unique<Manager>(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<Manager*> _guarded;
};
bool Manager::Impl::init() {
void Manager::Private::init(Manager *manager) {
_guarded = std::make_shared<Manager*>(manager);
if (auto capabilities = Libs::notify_get_server_caps()) {
for (auto capability = capabilities; capability; capability = capability->next) {
auto capabilityText = QString::fromUtf8(static_cast<const char*>(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<const char*>(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<NotificationData>(data.title, data.body, _capabilities, peerId, msgId);
auto notification = MakeShared<NotificationData>(_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>(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>(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

View file

@ -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> _impl;
class Private;
const std::unique_ptr<Private> _private;
};

View file

@ -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 <sys/stat.h>
@ -370,6 +371,8 @@ void start() {
}
void finish() {
Notifications::Finish();
delete _psEventFilter;
_psEventFilter = nullptr;
}

View file

@ -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> _private;
std::set<CustomNotificationHandle*> _customNotifications;
mutable bool psIdle;
mutable QTimer psIdleTimer;

View file

@ -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<QWidget*>(parent());
NSWindow *wnd = [reinterpret_cast<NSView *>(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<CustomNotificationHandle>(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);
}

View file

@ -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> _impl;
class Private;
const std::unique_ptr<Private> _private;
};
inline Window::Notifications::Manager *GetManager() {
return GetNativeManager();
}
} // namespace Notifications
} // namespace Platform

View file

@ -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 <Cocoa/Cocoa.h>
@ -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<Window::Notifications::Manager> Create(Window::Notifications::System *system) {
if (Supported()) {
return std::make_unique<Manager>(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<Impl>()) {
}
void Manager::updateDelegate() {
_impl->updateDelegate();
Manager::Manager(Window::Notifications::System *system) : NativeManager(system)
, _private(std::make_unique<Private>()) {
}
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

View file

@ -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);

View file

@ -208,11 +208,6 @@ void objc_bringToBack(WId winId) {
[wnd setLevel:NSModalPanelWindowLevel];
}
void objc_activateWnd(WId winId) {
NSWindow *wnd = [reinterpret_cast<NSView *>(winId) window];
[wnd orderFront:wnd];
}
bool objc_handleMediaKeyEvent(void *ev) {
auto e = reinterpret_cast<NSEvent*>(ev);

View file

@ -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<Window::Notifications::Manager> Create(Window::Notifications::System *system);
void FlashBounce();
} // namespace Notifications
} // namespace Platform

View file

@ -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;
}

View file

@ -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:

View file

@ -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 <Shobjidl.h>
#include <shellapi.h>
@ -48,22 +49,6 @@ namespace Platform {
namespace Notifications {
namespace {
NeverFreedPointer<Manager> ManagerInstance;
ComPtr<IToastNotificationManagerStatics> _notificationManager;
ComPtr<IToastNotifier> _notifier;
ComPtr<IToastNotificationFactory> _notificationFactory;
struct NotificationPtr {
NotificationPtr() {
}
NotificationPtr(const ComPtr<IToastNotification> &ptr) : p(ptr) {
}
ComPtr<IToastNotification> p;
};
using Notifications = QMap<PeerId, QMap<MsgId, NotificationPtr>>;
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<ToastNotification*, ToastFa
class ToastEventHandler : public Implements<DesktopToastActivatedEventHandler, DesktopToastDismissedEventHandler, DesktopToastFailedEventHandler> {
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<Manager*> &guarded, const PeerId &peer, MsgId msg)
: _peerId(peer)
, _msgId(msg)
, _weak(guarded) {
}
~ToastEventHandler() {
~ToastEventHandler() = default;
void performOnMainQueue(base::lambda_once<void(Manager *manager)> 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<Manager*> _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<Window::Notifications::Manager> Create(Window::Notifications::System *system) {
if (Global::NativeNotifications() && Supported()) {
auto result = std::make_unique<Manager>(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<Manager*> _guarded;
ComPtr<IToastNotificationManagerStatics> _notificationManager;
ComPtr<IToastNotifier> _notifier;
ComPtr<IToastNotificationFactory> _notificationFactory;
struct NotificationPtr {
NotificationPtr() {
}
NotificationPtr(const ComPtr<IToastNotification> &ptr) : p(ptr) {
}
ComPtr<IToastNotification> p;
};
QMap<PeerId, QMap<MsgId, NotificationPtr>> _notifications;
};
Manager::Impl::~Impl() {
Manager::Private::Private(Manager *instance, Type type)
: _guarded(std::make_shared<Manager*>(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<IXmlDocument> 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<ToastEventHandler> eventHandler(new ToastEventHandler(peer->id, msgId));
ComPtr<ToastEventHandler> 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>(Impl::Type::Rounded)) {
Manager::Manager(Window::Notifications::System *system) : NativeManager(system)
, _private(std::make_unique<Private>(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 {

View file

@ -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> _impl;
class Private;
const std::unique_ptr<Private> _private;
};

View file

@ -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) {

View file

@ -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();

View file

@ -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;

View file

@ -101,7 +101,6 @@ struct TWindowPos {
};
DeclareSetting(TWindowPos, WindowPos);
DeclareSetting(bool, SupportTray);
DeclareSetting(DBIWorkMode, WorkMode);
DeclareSetting(bool, SeenTrayTooltip);
DeclareSetting(bool, RestartingUpdate);
DeclareSetting(bool, Restarting);

View file

@ -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();
}

View file

@ -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

View file

@ -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);

View file

@ -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();
});

View file

@ -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<int32, DataRequested> DataRequestedMap;
// Debug flag to find out how we end up crashing.
std::set<FileLoader*> 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<void> ImageLoadedObservable;
} // namespace
base::Observable<void> &ImageLoaded() {
return ImageLoadedObservable;
}
} // namespace FileDownload

View file

@ -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<void> &taskFinished() {
return _taskFinishedObservable;
}
private:
base::Observable<void> _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<WebL
void reinitWebLoadManager();
void stopWebLoadManager();
namespace FileDownload {
base::Observable<void> &ImageLoaded();
} // namespace FileDownload

View file

@ -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

View file

@ -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);

View file

@ -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 {

View file

@ -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 {

View file

@ -20,6 +20,34 @@ Copyright (c) 2014-2017 John Preston, https://desktop.telegram.org
*/
#pragma once
using MediaKey = QPair<uint64, uint64>;
inline uint64 mediaMix32To64(int32 a, int32 b) {
return (uint64(*reinterpret_cast<uint32*>(&a)) << 32) | uint64(*reinterpret_cast<uint32*>(&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;

View file

@ -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;

View file

@ -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<uint64, uint64> MediaKey;
inline uint64 mediaMix32To64(int32 a, int32 b) {
return (uint64(*reinterpret_cast<uint32*>(&a)) << 32) | uint64(*reinterpret_cast<uint32*>(&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);
}

View file

@ -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;

View file

@ -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.

View file

@ -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<Default::Manager>(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<HistoryMessageForwarded>() ? 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<HistoryMessageForwarded>() ? 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<HistoryMessageForwarded>() ? 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);

View file

@ -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<Window::Notifications::ChangeType> : 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<ChangeType> &settingsChanged() {
return _settingsChanged;
}
AuthSession *authSession() {
return _authSession;
}
private:
void showNext();
AuthSession *_authSession = nullptr;
QMap<History*, QMap<MsgId, TimeMs>> _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<History*, Waiter>;
Waiters _waiters;
Waiters _settingWaiters;
SingleTimer _waitTimer;
QMap<History*, QMap<TimeMs, PeerData*>> _whenAlerts;
std::unique_ptr<Manager> _manager;
base::Observable<ChangeType> _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();
}

View file

@ -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<Manager> 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<Manager> Create(System *system) {
return std::make_unique<Manager>(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 &notification, _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 &notification, _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 &notification, _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 &notification = _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 &notification, _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 &notification, _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 &notification, _notifications) {
if (notification->isShowing()) {
++notHidingCount;
} else {
@ -166,7 +158,7 @@ void Manager::startAllHiding() {
}
void Manager::stopAllHiding() {
for_const (auto notification, _notifications) {
for_const (auto &notification, _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<Notification>(
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 &notification, _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<Notification>(
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 &notification = _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<HideAllButton>(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 &notification = *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 &notification, _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 &notification, _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 &notification, _notifications) {
// Calls unlinkFromShown() -> showNextFromQueue()
notification->itemRemoved(item);
}
}
void Manager::doUpdateAll() {
for_const (auto notification, _notifications) {
for_const (auto &notification, _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();
}
}

View file

@ -39,18 +39,15 @@ class HideAllButton;
} // namespace internal
class Manager;
void Start();
Manager *GetManager();
void Finish();
std::unique_ptr<Manager> Create(System *system);
class Manager : public Notifications::Manager, private base::Subscriber {
public:
Manager();
Manager(System *system);
template <typename Method>
void enumerateNotifications(Method method) {
for_const (auto notification, _notifications) {
for_const (auto &notification, _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<Notification*>;
Notifications _notifications;
std::vector<std::unique_ptr<Notification>> _notifications;
HideAllButton *_hideAll = nullptr;
std::unique_ptr<HideAllButton> _hideAll;
bool _positionsOutdated = false;
SingleTimer _inputCheckTimer;
@ -111,8 +107,7 @@ private:
HistoryItem *item;
int forwardedCount;
};
using QueuedNotifications = QList<QueuedNotification>;
QueuedNotifications _queuedNotifications;
std::deque<QueuedNotification> _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;

View file

@ -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"));
}
}

View file

@ -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<UrlClickHandler>(qsl("https://desktop.telegram.org/changelog")));
_version->setLink(2, MakeShared<LambdaClickHandler>([] { Ui::show(Box<AboutBox>()); }));
subscribe(FileDownload::ImageLoaded(), [this] { update(); });
subscribe(AuthSession::CurrentDownloaderTaskFinished(), [this] { update(); });
subscribe(Global::RefConnectionTypeChanged(), [this] { updateConnectionState(); });
updateConnectionState();
}