diff --git a/Telegram/SourceFiles/data/data_session.cpp b/Telegram/SourceFiles/data/data_session.cpp index 1de327455..7626ab991 100644 --- a/Telegram/SourceFiles/data/data_session.cpp +++ b/Telegram/SourceFiles/data/data_session.cpp @@ -1188,6 +1188,14 @@ rpl::producer> Session::viewLayoutChanged() const { return _viewLayoutChanges.events(); } +void Session::notifyUnreadItemAdded(not_null item) { + _unreadItemAdded.fire_copy(item); +} + +rpl::producer> Session::unreadItemAdded() const { + return _unreadItemAdded.events(); +} + void Session::changeMessageId(ChannelId channel, MsgId wasId, MsgId nowId) { const auto list = messagesListForInsert(channel); auto i = list->find(wasId); diff --git a/Telegram/SourceFiles/data/data_session.h b/Telegram/SourceFiles/data/data_session.h index 0fe5b4c7f..17e6eb8a1 100644 --- a/Telegram/SourceFiles/data/data_session.h +++ b/Telegram/SourceFiles/data/data_session.h @@ -192,6 +192,8 @@ public: [[nodiscard]] rpl::producer> itemLayoutChanged() const; void notifyViewLayoutChange(not_null view); [[nodiscard]] rpl::producer> viewLayoutChanged() const; + void notifyUnreadItemAdded(not_null item); + [[nodiscard]] rpl::producer> unreadItemAdded() const; void requestItemRepaint(not_null item); [[nodiscard]] rpl::producer> itemRepaintRequest() const; void requestViewRepaint(not_null view); @@ -841,6 +843,7 @@ private: rpl::event_stream _itemIdChanges; rpl::event_stream> _itemLayoutChanges; rpl::event_stream> _viewLayoutChanges; + rpl::event_stream> _unreadItemAdded; rpl::event_stream> _itemRepaintRequest; rpl::event_stream> _viewRepaintRequest; rpl::event_stream> _itemResizeRequest; diff --git a/Telegram/SourceFiles/history/history.cpp b/Telegram/SourceFiles/history/history.cpp index c177d5c65..f00ef46a8 100644 --- a/Telegram/SourceFiles/history/history.cpp +++ b/Telegram/SourceFiles/history/history.cpp @@ -1242,19 +1242,28 @@ void History::newItemAdded(not_null item) { from->madeAction(item->date()); } item->contributeToSlowmode(); - if (item->out()) { + if (item->showNotification()) { + _notifications.push_back(item); + owner().notifyUnreadItemAdded(item); + const auto stillShow = item->showNotification(); + if (stillShow) { + session().notifications().schedule(item); + if (!item->out() && item->unread()) { + if (unreadCountKnown()) { + setUnreadCount(unreadCount() + 1); + } else { + session().api().requestDialogEntry(this); + } + } + } + } else if (item->out()) { destroyUnreadBar(); - if (!item->unread()) { - outboxRead(item); - } - } else if (item->unread()) { - if (!isChannel() || peer->asChannel()->amIn()) { - _notifications.push_back(item); - App::main()->newUnreadMsg(this, item); - } } else { inboxRead(item); } + if (item->out() && !item->unread()) { + outboxRead(item); + } if (!folderKnown()) { session().api().requestDialogEntry(this); } diff --git a/Telegram/SourceFiles/history/history_item.cpp b/Telegram/SourceFiles/history/history_item.cpp index 07082e6f9..2c17f3bfc 100644 --- a/Telegram/SourceFiles/history/history_item.cpp +++ b/Telegram/SourceFiles/history/history_item.cpp @@ -367,6 +367,12 @@ void HistoryItem::addLogEntryOriginal( content); } +PeerData *HistoryItem::specialNotificationPeer() const { + return (mentionsMe() && !_history->peer->isUser()) + ? from().get() + : nullptr; +} + UserData *HistoryItem::viaBot() const { if (const auto via = Get()) { return via->bot; @@ -383,7 +389,22 @@ UserData *HistoryItem::getMessageBot() const { bot = history()->peer->asUser(); } return (bot && bot->isBot()) ? bot : nullptr; -}; +} + +bool HistoryItem::isHistoryEntry() const { + return IsServerMsgId(id) + || (_clientFlags & MTPDmessage_ClientFlag::f_local_history_entry); +} + +bool HistoryItem::isFromScheduled() const { + return isHistoryEntry() + && (_flags & MTPDmessage::Flag::f_from_scheduled); +} + +bool HistoryItem::isScheduled() const { + return !isHistoryEntry() + && (_flags & MTPDmessage::Flag::f_from_scheduled); +} void HistoryItem::destroy() { _history->owner().destroyMessage(this); @@ -737,6 +758,14 @@ bool HistoryItem::unread() const { return (_clientFlags & MTPDmessage_ClientFlag::f_clientside_unread); } +bool HistoryItem::showNotification() const { + const auto channel = _history->peer->asChannel(); + if (channel && !channel->amIn()) { + return false; + } + return out() ? isFromScheduled() : unread(); +} + void HistoryItem::markClientSideAsRead() { _clientFlags &= ~MTPDmessage_ClientFlag::f_clientside_unread; } diff --git a/Telegram/SourceFiles/history/history_item.h b/Telegram/SourceFiles/history/history_item.h index 60d66dec8..bb31f99d5 100644 --- a/Telegram/SourceFiles/history/history_item.h +++ b/Telegram/SourceFiles/history/history_item.h @@ -75,28 +75,20 @@ public: virtual MsgId dependencyMsgId() const { return 0; } - virtual bool notificationReady() const { + [[nodiscard]] virtual bool notificationReady() const { return true; } + [[nodiscard]] PeerData *specialNotificationPeer() const; virtual void applyGroupAdminChanges( const base::flat_set &changes) { } - UserData *viaBot() const; - UserData *getMessageBot() const; + [[nodiscard]] UserData *viaBot() const; + [[nodiscard]] UserData *getMessageBot() const; + [[nodiscard]] bool isHistoryEntry() const; + [[nodiscard]] bool isFromScheduled() const; + [[nodiscard]] bool isScheduled() const; - [[nodiscard]] bool isHistoryEntry() const { - return IsServerMsgId(id) - || (_clientFlags & MTPDmessage_ClientFlag::f_local_history_entry); - } - [[nodiscard]] bool isFromScheduled() const { - return isHistoryEntry() - && (_flags & MTPDmessage::Flag::f_from_scheduled); - } - [[nodiscard]] bool isScheduled() const { - return !isHistoryEntry() - && (_flags & MTPDmessage::Flag::f_from_scheduled); - } void addLogEntryOriginal( WebPageId localId, const QString &label, @@ -123,6 +115,7 @@ public: return _flags & MTPDmessage::Flag::f_out; } [[nodiscard]] bool unread() const; + [[nodiscard]] bool showNotification() const; void markClientSideAsRead(); [[nodiscard]] bool mentionsMe() const; [[nodiscard]] bool isUnreadMention() const; diff --git a/Telegram/SourceFiles/history/history_message.cpp b/Telegram/SourceFiles/history/history_message.cpp index 38d407238..7c37ce8e7 100644 --- a/Telegram/SourceFiles/history/history_message.cpp +++ b/Telegram/SourceFiles/history/history_message.cpp @@ -1302,7 +1302,12 @@ void HistoryMessage::dependencyItemRemoved(HistoryItem *dependency) { } QString HistoryMessage::notificationHeader() const { - return (!_history->peer->isUser() && !isPost()) ? from()->name : QString(); + if (out() && isFromScheduled()) { + return tr::lng_from_you(tr::now); + } else if (!_history->peer->isUser() && !isPost()) { + return App::peerName(from()); + } + return QString(); } std::unique_ptr HistoryMessage::createView( diff --git a/Telegram/SourceFiles/history/history_widget.cpp b/Telegram/SourceFiles/history/history_widget.cpp index df53b525f..aea513731 100644 --- a/Telegram/SourceFiles/history/history_widget.cpp +++ b/Telegram/SourceFiles/history/history_widget.cpp @@ -455,6 +455,12 @@ HistoryWidget::HistoryWidget( update(); } }); + + session().data().unreadItemAdded( + ) | rpl::start_with_next([=](not_null item) { + unreadMessageAdded(item); + }, lifetime()); + session().data().itemRemoved( ) | rpl::start_with_next([=](not_null item) { itemRemoved(item); @@ -2225,34 +2231,28 @@ void HistoryWidget::destroyUnreadBar() { if (_migrated) _migrated->destroyUnreadBar(); } -void HistoryWidget::newUnreadMsg( - not_null history, - not_null item) { - if (_history == history) { - // If we get here in non-resized state we can't rely on results of - // doWeReadServerHistory() and mark chat as read. - // If we receive N messages being not at bottom: - // - on first message we set unreadcount += 1, firstUnreadMessage. - // - on second we get wrong doWeReadServerHistory() and read both. - session().data().sendHistoryChangeNotifications(); +void HistoryWidget::unreadMessageAdded(not_null item) { + if (_history != item->history()) { + return; + } - if (_scroll->scrollTop() + 1 > _scroll->scrollTopMax()) { - destroyUnreadBar(); - } - if (App::wnd()->doWeReadServerHistory()) { - if (item->isUnreadMention() && !item->isUnreadMedia()) { - session().api().markMediaRead(item); - } - session().api().readServerHistoryForce(history); - return; - } + // If we get here in non-resized state we can't rely on results of + // doWeReadServerHistory() and mark chat as read. + // If we receive N messages being not at bottom: + // - on first message we set unreadcount += 1, firstUnreadMessage. + // - on second we get wrong doWeReadServerHistory() and read both. + session().data().sendHistoryChangeNotifications(); + + if (_scroll->scrollTop() + 1 > _scroll->scrollTopMax()) { + destroyUnreadBar(); } - session().notifications().schedule(history, item); - if (history->unreadCountKnown()) { - history->setUnreadCount(history->unreadCount() + 1); - } else { - session().api().requestDialogEntry(history); + if (!App::wnd()->doWeReadServerHistory()) { + return; } + if (item->isUnreadMention() && !item->isUnreadMedia()) { + session().api().markMediaRead(item); + } + session().api().readServerHistoryForce(_history); } void HistoryWidget::historyToDown(History *history) { diff --git a/Telegram/SourceFiles/history/history_widget.h b/Telegram/SourceFiles/history/history_widget.h index 6d6f0f824..7d281e45e 100644 --- a/Telegram/SourceFiles/history/history_widget.h +++ b/Telegram/SourceFiles/history/history_widget.h @@ -124,9 +124,6 @@ public: void firstLoadMessages(); void delayedShowAt(MsgId showAtMsgId); - void newUnreadMsg( - not_null history, - not_null item); void historyToDown(History *history); QRect historyRect() const; @@ -423,6 +420,7 @@ private: void historyDownAnimationFinish(); void unreadMentionsAnimationFinish(); void sendButtonClicked(); + void unreadMessageAdded(not_null item); bool canSendFiles(not_null data) const; bool confirmSendingFiles( diff --git a/Telegram/SourceFiles/mainwidget.cpp b/Telegram/SourceFiles/mainwidget.cpp index 62fb273db..4e30489f0 100644 --- a/Telegram/SourceFiles/mainwidget.cpp +++ b/Telegram/SourceFiles/mainwidget.cpp @@ -2198,12 +2198,6 @@ void MainWidget::dialogsToUp() { _dialogs->jumpToTop(); } -void MainWidget::newUnreadMsg( - not_null history, - not_null item) { - _history->newUnreadMsg(history, item); -} - void MainWidget::markActiveHistoryAsRead() { if (const auto activeHistory = _history->history()) { session().api().readServerHistory(activeHistory); diff --git a/Telegram/SourceFiles/mainwidget.h b/Telegram/SourceFiles/mainwidget.h index 7f4aee12d..dc82b283a 100644 --- a/Telegram/SourceFiles/mainwidget.h +++ b/Telegram/SourceFiles/mainwidget.h @@ -146,9 +146,6 @@ public: bool deleteChannelFailed(const RPCError &error); void historyToDown(History *hist); void dialogsToUp(); - void newUnreadMsg( - not_null history, - not_null item); void markActiveHistoryAsRead(); PeerData *peer(); diff --git a/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp b/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp index 05c3bf965..f6c59869e 100644 --- a/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp +++ b/Telegram/SourceFiles/platform/linux/notifications_manager_linux.cpp @@ -304,7 +304,14 @@ public: void init(Manager *manager); - void showNotification(PeerData *peer, MsgId msgId, const QString &title, const QString &subtitle, const QString &msg, bool hideNameAndPhoto, bool hideReplyButton); + void showNotification( + not_null peer, + MsgId msgId, + const QString &title, + const QString &subtitle, + const QString &msg, + bool hideNameAndPhoto, + bool hideReplyButton); void clearAll(); void clearFromHistory(History *history); void clearNotification(PeerId peerId, MsgId msgId); @@ -384,7 +391,14 @@ QString Manager::Private::escapeNotificationText(const QString &text) const { return _markupSupported ? escapeHtml(text) : text; } -void Manager::Private::showNotification(PeerData *peer, MsgId msgId, const QString &title, const QString &subtitle, const QString &msg, bool hideNameAndPhoto, bool hideReplyButton) { +void Manager::Private::showNotification( + not_null 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); @@ -537,8 +551,22 @@ bool Manager::hasActionsSupport() const { Manager::~Manager() = default; -void Manager::doShowNativeNotification(PeerData *peer, MsgId msgId, const QString &title, const QString &subtitle, const QString &msg, bool hideNameAndPhoto, bool hideReplyButton) { - _private->showNotification(peer, msgId, title, subtitle, msg, hideNameAndPhoto, hideReplyButton); +void Manager::doShowNativeNotification( + not_null peer, + MsgId msgId, + const QString &title, + const QString &subtitle, + const QString &msg, + bool hideNameAndPhoto, + bool hideReplyButton) { + _private->showNotification( + peer, + msgId, + title, + subtitle, + msg, + hideNameAndPhoto, + hideReplyButton); } void Manager::doClearAllFast() { diff --git a/Telegram/SourceFiles/platform/linux/notifications_manager_linux.h b/Telegram/SourceFiles/platform/linux/notifications_manager_linux.h index e8532f909..32bfc06ef 100644 --- a/Telegram/SourceFiles/platform/linux/notifications_manager_linux.h +++ b/Telegram/SourceFiles/platform/linux/notifications_manager_linux.h @@ -36,7 +36,14 @@ public: ~Manager(); protected: - void doShowNativeNotification(PeerData *peer, MsgId msgId, const QString &title, const QString &subtitle, const QString &msg, bool hideNameAndPhoto, bool hideReplyButton) override; + void doShowNativeNotification( + not_null peer, + MsgId msgId, + const QString &title, + const QString &subtitle, + const QString &msg, + bool hideNameAndPhoto, + bool hideReplyButton) override; void doClearAllFast() override; void doClearFromHistory(History *history) override; diff --git a/Telegram/SourceFiles/platform/mac/notifications_manager_mac.h b/Telegram/SourceFiles/platform/mac/notifications_manager_mac.h index c85f6deab..0c19b1693 100644 --- a/Telegram/SourceFiles/platform/mac/notifications_manager_mac.h +++ b/Telegram/SourceFiles/platform/mac/notifications_manager_mac.h @@ -22,7 +22,14 @@ public: ~Manager(); protected: - void doShowNativeNotification(PeerData *peer, MsgId msgId, const QString &title, const QString &subtitle, const QString &msg, bool hideNameAndPhoto, bool hideReplyButton) override; + void doShowNativeNotification( + not_null peer, + MsgId msgId, + const QString &title, + const QString &subtitle, + const QString &msg, + bool hideNameAndPhoto, + bool hideReplyButton) override; void doClearAllFast() override; void doClearFromHistory(History *history) override; diff --git a/Telegram/SourceFiles/platform/mac/notifications_manager_mac.mm b/Telegram/SourceFiles/platform/mac/notifications_manager_mac.mm index 00a393b41..dc88b7c8c 100644 --- a/Telegram/SourceFiles/platform/mac/notifications_manager_mac.mm +++ b/Telegram/SourceFiles/platform/mac/notifications_manager_mac.mm @@ -160,7 +160,14 @@ class Manager::Private : public QObject, private base::Subscriber { public: Private(Manager *manager); - void showNotification(PeerData *peer, MsgId msgId, const QString &title, const QString &subtitle, const QString &msg, bool hideNameAndPhoto, bool hideReplyButton); + void showNotification( + not_null peer, + MsgId msgId, + const QString &title, + const QString &subtitle, + const QString &msg, + bool hideNameAndPhoto, + bool hideReplyButton); void clearAll(); void clearFromHistory(History *history); void updateDelegate(); @@ -208,7 +215,14 @@ Manager::Private::Private(Manager *manager) }); } -void Manager::Private::showNotification(PeerData *peer, MsgId msgId, const QString &title, const QString &subtitle, const QString &msg, bool hideNameAndPhoto, bool hideReplyButton) { +void Manager::Private::showNotification( + not_null peer, + MsgId msgId, + const QString &title, + const QString &subtitle, + const QString &msg, + bool hideNameAndPhoto, + bool hideReplyButton) { @autoreleasepool { NSUserNotification *notification = [[[NSUserNotification alloc] init] autorelease]; @@ -329,8 +343,22 @@ Manager::Manager(Window::Notifications::System *system) : NativeManager(system) Manager::~Manager() = default; -void Manager::doShowNativeNotification(PeerData *peer, MsgId msgId, const QString &title, const QString &subtitle, const QString &msg, bool hideNameAndPhoto, bool hideReplyButton) { - _private->showNotification(peer, msgId, title, subtitle, msg, hideNameAndPhoto, hideReplyButton); +void Manager::doShowNativeNotification( + not_null peer, + MsgId msgId, + const QString &title, + const QString &subtitle, + const QString &msg, + bool hideNameAndPhoto, + bool hideReplyButton) { + _private->showNotification( + peer, + msgId, + title, + subtitle, + msg, + hideNameAndPhoto, + hideReplyButton); } void Manager::doClearAllFast() { diff --git a/Telegram/SourceFiles/platform/win/notifications_manager_win.cpp b/Telegram/SourceFiles/platform/win/notifications_manager_win.cpp index f88fec4c4..c49cf7108 100644 --- a/Telegram/SourceFiles/platform/win/notifications_manager_win.cpp +++ b/Telegram/SourceFiles/platform/win/notifications_manager_win.cpp @@ -342,7 +342,14 @@ public: 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); + bool showNotification( + not_null peer, + MsgId msgId, + const QString &title, + const QString &subtitle, + const QString &msg, + bool hideNameAndPhoto, + bool hideReplyButton); void clearAll(); void clearFromHistory(History *history); void beforeNotificationActivated(PeerId peerId, MsgId msgId); @@ -447,13 +454,24 @@ void Manager::Private::clearNotification(PeerId peerId, MsgId msgId) { } } -bool Manager::Private::showNotification(PeerData *peer, MsgId msgId, const QString &title, const QString &subtitle, const QString &msg, bool hideNameAndPhoto, bool hideReplyButton) { +bool Manager::Private::showNotification( + not_null peer, + MsgId msgId, + const QString &title, + const QString &subtitle, + const QString &msg, + bool hideNameAndPhoto, + bool hideReplyButton) { if (!_notificationManager || !_notifier || !_notificationFactory) return false; ComPtr toastXml; bool withSubtitle = !subtitle.isEmpty(); - HRESULT hr = _notificationManager->GetTemplateContent(withSubtitle ? ToastTemplateType_ToastImageAndText04 : ToastTemplateType_ToastImageAndText02, &toastXml); + HRESULT hr = _notificationManager->GetTemplateContent( + (withSubtitle + ? ToastTemplateType_ToastImageAndText04 + : ToastTemplateType_ToastImageAndText02), + &toastXml); if (!SUCCEEDED(hr)) return false; hr = SetAudioSilent(toastXml.Get()); @@ -560,8 +578,22 @@ void Manager::clearNotification(PeerId peerId, MsgId msgId) { Manager::~Manager() = default; -void Manager::doShowNativeNotification(PeerData *peer, MsgId msgId, const QString &title, const QString &subtitle, const QString &msg, bool hideNameAndPhoto, bool hideReplyButton) { - _private->showNotification(peer, msgId, title, subtitle, msg, hideNameAndPhoto, hideReplyButton); +void Manager::doShowNativeNotification( + not_null peer, + MsgId msgId, + const QString &title, + const QString &subtitle, + const QString &msg, + bool hideNameAndPhoto, + bool hideReplyButton) { + _private->showNotification( + peer, + msgId, + title, + subtitle, + msg, + hideNameAndPhoto, + hideReplyButton); } void Manager::doClearAllFast() { diff --git a/Telegram/SourceFiles/platform/win/notifications_manager_win.h b/Telegram/SourceFiles/platform/win/notifications_manager_win.h index 766a86676..fb4e34322 100644 --- a/Telegram/SourceFiles/platform/win/notifications_manager_win.h +++ b/Telegram/SourceFiles/platform/win/notifications_manager_win.h @@ -23,7 +23,14 @@ public: ~Manager(); protected: - void doShowNativeNotification(PeerData *peer, MsgId msgId, const QString &title, const QString &subtitle, const QString &msg, bool hideNameAndPhoto, bool hideReplyButton) override; + void doShowNativeNotification( + not_null peer, + MsgId msgId, + const QString &title, + const QString &subtitle, + const QString &msg, + bool hideNameAndPhoto, + bool hideReplyButton) override; void doClearAllFast() override; void doClearFromHistory(History *history) override; void onBeforeNotificationActivated(PeerId peerId, MsgId msgId) override; diff --git a/Telegram/SourceFiles/ui/text/text.cpp b/Telegram/SourceFiles/ui/text/text.cpp index 99d27b60c..4a46dab03 100644 --- a/Telegram/SourceFiles/ui/text/text.cpp +++ b/Telegram/SourceFiles/ui/text/text.cpp @@ -3322,7 +3322,9 @@ IsolatedEmoji String::toIsolatedEmoji() const { auto index = 0; for (const auto &block : _blocks) { const auto type = block->type(); - if (type == TextBlockTEmoji) { + if (block->lnkIndex()) { + return IsolatedEmoji(); + } else if (type == TextBlockTEmoji) { result.items[index++] = static_cast(block.get())->emoji; } else if (type != TextBlockTSkip) { return IsolatedEmoji(); diff --git a/Telegram/SourceFiles/window/notifications_manager.cpp b/Telegram/SourceFiles/window/notifications_manager.cpp index e0cd14741..d2cb7de85 100644 --- a/Telegram/SourceFiles/window/notifications_manager.cpp +++ b/Telegram/SourceFiles/window/notifications_manager.cpp @@ -61,45 +61,58 @@ void System::createManager() { } } -void System::schedule( - not_null history, - not_null item) { - if (App::quitting() - || !history->currentNotification() - || !Main::Session::Exists()) return; - - const auto notifyBy = (!history->peer->isUser() && item->mentionsMe()) - ? item->from().get() - : nullptr; +System::SkipState System::skipNotification( + not_null item) const { + const auto history = item->history(); + const auto notifyBy = item->specialNotificationPeer(); + if (App::quitting() || !history->currentNotification()) { + return { SkipState::Skip }; + } + const auto scheduled = item->out() && item->isFromScheduled(); history->owner().requestNotifySettings(history->peer); if (notifyBy) { history->owner().requestNotifySettings(notifyBy); } - auto haveSetting = !history->owner().notifyMuteUnknown(history->peer); - if (haveSetting && history->owner().notifyIsMuted(history->peer)) { - if (notifyBy) { - haveSetting = !history->owner().notifyMuteUnknown(notifyBy); - if (haveSetting) { - if (history->owner().notifyIsMuted(notifyBy)) { - history->popNotification(item); - return; - } - } - } else { - history->popNotification(item); - return; - } + + if (history->owner().notifyMuteUnknown(history->peer)) { + return { SkipState::Unknown, item->isSilent() }; + } else if (!history->owner().notifyIsMuted(history->peer)) { + return { SkipState::DontSkip, item->isSilent() }; + } else if (!notifyBy) { + return { + scheduled ? SkipState::DontSkip : SkipState::Skip, + item->isSilent() || scheduled + }; + } else if (history->owner().notifyMuteUnknown(notifyBy)) { + return { SkipState::Unknown, item->isSilent() }; + } else if (!history->owner().notifyIsMuted(notifyBy)) { + return { SkipState::DontSkip, item->isSilent() }; + } else { + return { + scheduled ? SkipState::DontSkip : SkipState::Skip, + item->isSilent() || scheduled + }; } - if (!item->notificationReady()) { - haveSetting = false; +} + +void System::schedule(not_null item) { + const auto history = item->history(); + const auto skip = skipNotification(item); + if (skip.value == SkipState::Skip) { + history->popNotification(item); + return; } + const auto notifyBy = item->specialNotificationPeer(); + const auto ready = (skip.value != SkipState::Unknown) + && item->notificationReady(); auto delay = item->Has() ? 500 : 100; - auto t = base::unixtime::now(); - auto ms = crl::now(); - bool isOnline = App::main()->lastWasOnline(), otherNotOld = ((cOtherOnline() * 1000LL) + Global::OnlineCloudTimeout() > t * 1000LL); - bool otherLaterThanMe = (cOtherOnline() * 1000LL + (ms - App::main()->lastSetOnline()) > t * 1000LL); + const auto t = base::unixtime::now(); + const auto ms = crl::now(); + const bool isOnline = App::main()->lastWasOnline(); + const auto otherNotOld = ((cOtherOnline() * 1000LL) + Global::OnlineCloudTimeout() > t * 1000LL); + const bool otherLaterThanMe = (cOtherOnline() * 1000LL + (ms - App::main()->lastSetOnline()) > t * 1000LL); if (!isOnline && otherNotOld && otherLaterThanMe) { delay = Global::NotifyCloudDelay(); } else if (cOtherOnline() >= t) { @@ -107,7 +120,7 @@ void System::schedule( } auto when = ms + delay; - if (!item->isSilent()) { + if (!skip.silent) { _whenAlerts[history].insert(when, notifyBy); } if (Global::DesktopNotify() && !Platform::Notifications::SkipToast()) { @@ -116,13 +129,13 @@ void System::schedule( whenMap.insert(item->id, when); } - auto &addTo = haveSetting ? _waiters : _settingWaiters; - auto it = addTo.constFind(history); + auto &addTo = ready ? _waiters : _settingWaiters; + const auto it = addTo.constFind(history); if (it == addTo.cend() || it->when > when) { addTo.insert(history, Waiter(item->id, when, notifyBy)); } } - if (haveSetting) { + if (ready) { if (!_waitTimer.isActive() || _waitTimer.remainingTime() > delay) { _waitTimer.callOnce(delay); } @@ -524,9 +537,12 @@ void Manager::notificationReplied( } } -void NativeManager::doShowNotification(HistoryItem *item, int forwardedCount) { +void NativeManager::doShowNotification( + not_null item, + int forwardedCount) { const auto options = getNotificationOptions(item); + const auto scheduled = (item->out() && item->isFromScheduled()); const auto title = options.hideNameAndPhoto ? qsl("Telegram Desktop") : item->history()->peer->name; const auto subtitle = options.hideNameAndPhoto ? QString() : item->notificationHeader(); const auto text = options.hideMessageText @@ -539,7 +555,7 @@ void NativeManager::doShowNotification(HistoryItem *item, int forwardedCount) { item->history()->peer, item->id, title, - subtitle, + scheduled ? WrapFromScheduled(subtitle) : subtitle, text, options.hideNameAndPhoto, options.hideReplyButton); @@ -547,5 +563,9 @@ void NativeManager::doShowNotification(HistoryItem *item, int forwardedCount) { System::~System() = default; +QString WrapFromScheduled(const QString &text) { + return QString::fromUtf8("\xF0\x9F\x93\x85 ") + text; +} + } // namespace Notifications } // namespace Window diff --git a/Telegram/SourceFiles/window/notifications_manager.h b/Telegram/SourceFiles/window/notifications_manager.h index 1b6eb0693..b0cb185fd 100644 --- a/Telegram/SourceFiles/window/notifications_manager.h +++ b/Telegram/SourceFiles/window/notifications_manager.h @@ -62,7 +62,7 @@ public: void createManager(); void checkDelayed(); - void schedule(not_null history, not_null item); + void schedule(not_null item); void clearFromHistory(History *history); void clearFromItem(HistoryItem *item); void clearAll(); @@ -80,6 +80,18 @@ public: ~System(); private: + struct SkipState { + enum Value { + Unknown, + Skip, + DontSkip + }; + Value value = Value::Unknown; + bool silent = false; + }; + + SkipState skipNotification(not_null item) const; + void showNext(); void showGrouped(); void ensureSoundCreated(); @@ -122,7 +134,9 @@ public: explicit Manager(not_null system) : _system(system) { } - void showNotification(HistoryItem *item, int forwardedCount) { + void showNotification( + not_null item, + int forwardedCount) { doShowNotification(item, forwardedCount); } void updateAll() { @@ -162,7 +176,9 @@ protected: } virtual void doUpdateAll() = 0; - virtual void doShowNotification(HistoryItem *item, int forwardedCount) = 0; + virtual void doShowNotification( + not_null item, + int forwardedCount) = 0; virtual void doClearAll() = 0; virtual void doClearAllFast() = 0; virtual void doClearFromItem(HistoryItem *item) = 0; @@ -193,11 +209,22 @@ protected: } void doClearFromItem(HistoryItem *item) override { } - void doShowNotification(HistoryItem *item, int forwardedCount) override; + void doShowNotification( + not_null item, + int forwardedCount) override; - virtual void doShowNativeNotification(PeerData *peer, MsgId msgId, const QString &title, const QString &subtitle, const QString &msg, bool hideNameAndPhoto, bool hideReplyButton) = 0; + virtual void doShowNativeNotification( + not_null peer, + MsgId msgId, + const QString &title, + const QString &subtitle, + const QString &msg, + bool hideNameAndPhoto, + bool hideReplyButton) = 0; }; +QString WrapFromScheduled(const QString &text); + } // namespace Notifications } // namespace Window diff --git a/Telegram/SourceFiles/window/notifications_manager_default.cpp b/Telegram/SourceFiles/window/notifications_manager_default.cpp index c6c2131bd..cafb34912 100644 --- a/Telegram/SourceFiles/window/notifications_manager_default.cpp +++ b/Telegram/SourceFiles/window/notifications_manager_default.cpp @@ -12,6 +12,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "lang/lang_keys.h" #include "ui/widgets/buttons.h" #include "ui/widgets/input_fields.h" +#include "ui/text_options.h" #include "dialogs/dialogs_layout.h" #include "window/themes/window_theme.h" #include "styles/style_dialogs.h" @@ -66,13 +67,14 @@ Manager::Manager(System *system) } Manager::QueuedNotification::QueuedNotification( - not_null item - , int forwardedCount) + not_null item, + int forwardedCount) : history(item->history()) , peer(history->peer) -, author((!peer->isUser() && !item->isPost()) ? item->author().get() : nullptr) +, author(item->notificationHeader()) , item((forwardedCount < 2) ? item.get() : nullptr) -, forwardedCount(forwardedCount) { +, forwardedCount(forwardedCount) +, fromScheduled(item->out() && item->isFromScheduled()) { } QPixmap Manager::hiddenUserpicPlaceholder() const { @@ -206,7 +208,10 @@ void Manager::showNextFromQueue() { queued.author, queued.item, queued.forwardedCount, - startPosition, startShift, shiftDirection); + queued.fromScheduled, + startPosition, + startShift, + shiftDirection); _notifications.push_back(std::move(notification)); --count; } while (count > 0 && !_queuedNotifications.empty()); @@ -289,7 +294,9 @@ void Manager::removeWidget(internal::Widget *remove) { showNextFromQueue(); } -void Manager::doShowNotification(HistoryItem *item, int forwardedCount) { +void Manager::doShowNotification( + not_null item, + int forwardedCount) { _queuedNotifications.emplace_back(item, forwardedCount); showNextFromQueue(); } @@ -353,7 +360,12 @@ Manager::~Manager() { namespace internal { -Widget::Widget(Manager *manager, QPoint startPosition, int shift, Direction shiftDirection) : TWidget(nullptr) +Widget::Widget( + not_null manager, + QPoint startPosition, + int shift, + Direction shiftDirection) +: TWidget(nullptr) , _manager(manager) , _startPosition(startPosition) , _direction(shiftDirection) @@ -501,12 +513,13 @@ void Background::paintEvent(QPaintEvent *e) { } Notification::Notification( - Manager *manager, - History *history, - PeerData *peer, - PeerData *author, - HistoryItem *msg, + not_null manager, + not_null history, + not_null peer, + const QString &author, + HistoryItem *item, int forwardedCount, + bool fromScheduled, QPoint startPosition, int shift, Direction shiftDirection) @@ -515,8 +528,9 @@ Notification::Notification( , _history(history) , _peer(peer) , _author(author) -, _item(msg) +, _item(item) , _forwardedCount(forwardedCount) +, _fromScheduled(fromScheduled) , _close(this, st::notifyClose) , _reply(this, tr::lng_notification_reply(), st::defaultBoxButton) { subscribe(Lang::Current().updated(), [this] { refreshLang(); }); @@ -683,35 +697,53 @@ void Notification::updateNotifyDisplay() { } if (!options.hideMessageText) { - const HistoryItem *textCachedFor = nullptr; - Ui::Text::String itemTextCache(itemWidth); - QRect r(st::notifyPhotoPos.x() + st::notifyPhotoSize + st::notifyTextLeft, st::notifyItemTop + st::msgNameFont->height, itemWidth, 2 * st::dialogsTextFont->height); - if (_item) { - auto active = false, selected = false; - _item->drawInDialog( - p, - r, - active, - selected, - HistoryItem::DrawInDialog::Normal, - textCachedFor, - itemTextCache); - } else if (_forwardedCount > 1) { - p.setFont(st::dialogsTextFont); - if (_author) { - itemTextCache.setText(st::dialogsTextStyle, _author->name); - p.setPen(st::dialogsTextFgService); - itemTextCache.drawElided(p, r.left(), r.top(), r.width()); - r.setTop(r.top() + st::dialogsTextFont->height); - } - p.setPen(st::dialogsTextFg); - p.drawText(r.left(), r.top() + st::dialogsTextFont->ascent, tr::lng_forward_messages(tr::now, lt_count, _forwardedCount)); - } + auto itemTextCache = Ui::Text::String(itemWidth); + auto r = QRect( + st::notifyPhotoPos.x() + st::notifyPhotoSize + st::notifyTextLeft, + st::notifyItemTop + st::msgNameFont->height, + itemWidth, + 2 * st::dialogsTextFont->height); + p.setTextPalette(st::dialogsTextPalette); + p.setPen(st::dialogsTextFg); + p.setFont(st::dialogsTextFont); + const auto text = _item + ? _item->inDialogsText(HistoryItem::DrawInDialog::Normal) + : ((!_author.isEmpty() + ? textcmdLink(1, _author) + : QString()) + + (_forwardedCount > 1 + ? ('\n' + tr::lng_forward_messages( + tr::now, + lt_count, + _forwardedCount)) + : QString())); + const auto Options = TextParseOptions{ + TextParseRichText + | (_forwardedCount > 1 ? TextParseMultiline : 0), + 0, + 0, + Qt::LayoutDirectionAuto, + }; + itemTextCache.setText( + st::dialogsTextStyle, + _fromScheduled ? WrapFromScheduled(text) : text, + Options); + itemTextCache.drawElided( + p, + r.left(), + r.top(), + r.width(), + r.height() / st::dialogsTextFont->height); + p.restoreTextPalette(); } else { - static QString notifyText = st::dialogsTextFont->elided(tr::lng_notification_preview(tr::now), itemWidth); p.setFont(st::dialogsTextFont); p.setPen(st::dialogsTextFgService); - p.drawText(st::notifyPhotoPos.x() + st::notifyPhotoSize + st::notifyTextLeft, st::notifyItemTop + st::msgNameFont->height + st::dialogsTextFont->ascent, notifyText); + p.drawText( + st::notifyPhotoPos.x() + st::notifyPhotoSize + st::notifyTextLeft, + st::notifyItemTop + st::msgNameFont->height + st::dialogsTextFont->ascent, + st::dialogsTextFont->elided( + tr::lng_notification_preview(tr::now), + itemWidth)); } p.setPen(st::dialogsNameFg); @@ -839,7 +871,7 @@ void Notification::changeHeight(int newHeight) { } bool Notification::unlinkHistory(History *history) { - auto unlink = _history && (history == _history || !history); + const auto unlink = _history && (history == _history || !history); if (unlink) { hideFast(); _history = nullptr; @@ -897,7 +929,12 @@ void Notification::stopHiding() { Widget::hideStop(); } -HideAllButton::HideAllButton(Manager *manager, QPoint startPosition, int shift, Direction shiftDirection) : Widget(manager, startPosition, shift, shiftDirection) { +HideAllButton::HideAllButton( + not_null manager, + QPoint startPosition, + int shift, + Direction shiftDirection) +: Widget(manager, startPosition, shift, shiftDirection) { setCursor(style::cur_pointer); auto position = computePosition(st::notifyHideAllHeight); diff --git a/Telegram/SourceFiles/window/notifications_manager_default.h b/Telegram/SourceFiles/window/notifications_manager_default.h index da3f7a62e..a1dce2869 100644 --- a/Telegram/SourceFiles/window/notifications_manager_default.h +++ b/Telegram/SourceFiles/window/notifications_manager_default.h @@ -52,7 +52,9 @@ private: QPixmap hiddenUserpicPlaceholder() const; void doUpdateAll() override; - void doShowNotification(HistoryItem *item, int forwardedCount) override; + void doShowNotification( + not_null item, + int forwardedCount) override; void doClearAll() override; void doClearAllFast() override; void doClearFromHistory(History *history) override; @@ -87,9 +89,10 @@ private: not_null history; not_null peer; - PeerData *author; - HistoryItem *item; - int forwardedCount; + QString author; + HistoryItem *item = nullptr; + int forwardedCount = 0; + bool fromScheduled = false; }; std::deque _queuedNotifications; @@ -107,7 +110,11 @@ public: Up, Down, }; - Widget(Manager *manager, QPoint startPosition, int shift, Direction shiftDirection); + Widget( + not_null manager, + QPoint startPosition, + int shift, + Direction shiftDirection); bool isShowing() const { return _a_opacity.animating() && !_hiding; @@ -131,7 +138,7 @@ protected: virtual void updateGeometry(int x, int y, int width, int height); protected: - Manager *manager() const { + not_null manager() const { return _manager; } @@ -142,7 +149,7 @@ private: void hideAnimated(float64 duration, const anim::transition &func); bool shiftAnimationCallback(crl::time now); - Manager *_manager = nullptr; + const not_null _manager; bool _hiding = false; bool _deleted = false; @@ -168,12 +175,13 @@ protected: class Notification : public Widget { public: Notification( - Manager *manager, - History *history, - PeerData *peer, - PeerData *author, + not_null manager, + not_null history, + not_null peer, + const QString &author, HistoryItem *item, int forwardedCount, + bool fromScheduled, QPoint startPosition, int shift, Direction shiftDirection); @@ -228,11 +236,12 @@ private: crl::time _started; - History *_history; - PeerData *_peer; - PeerData *_author; - HistoryItem *_item; - int _forwardedCount; + History *_history = nullptr; + PeerData *_peer = nullptr; + QString _author; + HistoryItem *_item = nullptr; + int _forwardedCount = 0; + bool _fromScheduled = false; object_ptr _close; object_ptr _reply; object_ptr _background = { nullptr }; @@ -250,7 +259,11 @@ private: class HideAllButton : public Widget { public: - HideAllButton(Manager *manager, QPoint startPosition, int shift, Direction shiftDirection); + HideAllButton( + not_null manager, + QPoint startPosition, + int shift, + Direction shiftDirection); void startHiding(); void startHidingFast();