diff --git a/Telegram/SourceFiles/platform/win/notifications_manager_win.cpp b/Telegram/SourceFiles/platform/win/notifications_manager_win.cpp index 6a7d808b7..852f07d38 100644 --- a/Telegram/SourceFiles/platform/win/notifications_manager_win.cpp +++ b/Telegram/SourceFiles/platform/win/notifications_manager_win.cpp @@ -21,6 +21,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "main/main_session.h" #include "mainwindow.h" #include "windows_quiethours_h.h" +#include "styles/style_chat.h" #include @@ -32,7 +33,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include #include #include -#include HICON qt_pixmapToWinHICON(const QPixmap &); @@ -48,6 +48,43 @@ namespace Notifications { #ifndef __MINGW32__ namespace { +constexpr auto kNotificationTemplate = LR"( + + + + + + + + + + + + + + +)"; + +constexpr auto kNotificationTemplateSmall = LR"( + + + + + + + + + + +)"; + bool init() { if (!IsWindows8OrGreater()) { return false; @@ -100,6 +137,28 @@ void SetImageSrc(const XmlDocument &toastXml, const std::wstring &path) { L"file:///" + path); } +// Throws. +void SetReplyIconSrc(const XmlDocument &toastXml, const std::wstring &path) { + const auto nodeList = toastXml.GetElementsByTagName(L"action"); + const auto attributes = nodeList.Item(0).Attributes(); + return SetNodeValueString( + toastXml, + attributes.GetNamedItem(L"imageUri"), + L"file:///" + path); +} + +// Throws. +void SetReplyPlaceholder( + const XmlDocument &toastXml, + const std::wstring &placeholder) { + const auto nodeList = toastXml.GetElementsByTagName(L"input"); + const auto attributes = nodeList.Item(0).Attributes(); + return SetNodeValueString( + toastXml, + attributes.GetNamedItem(L"placeHolderContent"), + placeholder); +} + auto Checked = false; auto InitSucceeded = false; @@ -340,8 +399,10 @@ private: const QString &msg, bool hideNameAndPhoto, bool hideReplyButton); + [[nodiscard]] std::wstring ensureSendButtonIcon(); Window::Notifications::CachedUserpics _cachedUserpics; + std::wstring _sendButtonIconPath; std::shared_ptr _guarded; ToastNotifier _notifier = nullptr; @@ -469,6 +530,15 @@ bool Manager::Private::showNotification( }).value_or(false); } +std::wstring Manager::Private::ensureSendButtonIcon() { + if (_sendButtonIconPath.empty()) { + const auto path = cWorkingDir() + u"tdata/temp/fast_reply.png"_q; + st::historySendIcon.instance(Qt::white, 300).save(path, "PNG"); + _sendButtonIconPath = path.toStdWString(); + } + return _sendButtonIconPath; +} + bool Manager::Private::showNotificationInTryCatch( not_null peer, std::shared_ptr &userpicView, @@ -479,17 +549,35 @@ bool Manager::Private::showNotificationInTryCatch( bool hideNameAndPhoto, bool hideReplyButton) { const auto withSubtitle = !subtitle.isEmpty(); - const auto toastXml = ToastNotificationManager::GetTemplateContent( - (withSubtitle - ? ToastTemplateType::ToastImageAndText04 - : ToastTemplateType::ToastImageAndText02)); - SetAudioSilent(toastXml); + auto toastXml = XmlDocument(); + const auto modern = Platform::IsWindows10OrGreater(); + if (modern) { + toastXml.LoadXml(hideReplyButton + ? kNotificationTemplateSmall + : kNotificationTemplate); + } else { + toastXml = ToastNotificationManager::GetTemplateContent( + (withSubtitle + ? ToastTemplateType::ToastImageAndText04 + : ToastTemplateType::ToastImageAndText02)); + SetAudioSilent(toastXml); + } const auto userpicKey = hideNameAndPhoto ? InMemoryKey() : peer->userpicUniqueKey(userpicView); - const auto userpicPath = _cachedUserpics.get(userpicKey, peer, userpicView); - const auto userpicPathWide = QDir::toNativeSeparators(userpicPath).toStdWString(); + const auto userpicPath = _cachedUserpics.get( + userpicKey, + peer, + userpicView); + const auto userpicPathWide = QDir::toNativeSeparators( + userpicPath).toStdWString(); + if (modern && !hideReplyButton) { + SetReplyIconSrc(toastXml, ensureSendButtonIcon()); + SetReplyPlaceholder( + toastXml, + tr::lng_message_ph(tr::now).toStdWString()); + } SetImageSrc(toastXml, userpicPathWide); @@ -525,30 +613,52 @@ bool Manager::Private::showNotificationInTryCatch( .full = key, .msgId = msgId }; + auto toast = ToastNotification(toastXml); const auto token1 = toast.Activated([=]( const ToastNotification &sender, - const winrt::Windows::Foundation::IInspectable &args) { - performOnMainQueue([notificationId](Manager *manager) { - manager->notificationActivated(notificationId); - }); + const winrt::Windows::Foundation::IInspectable &object) { + if (const auto args = object.try_as()) { + const auto arguments = args.Arguments(); + const auto userInput = args.UserInput(); + if (arguments == L"action=reply") { + const auto reply = userInput.TryLookup(L"fastReply"); + const auto data = reply.try_as>(); + auto text = data + ? QString::fromWCharArray(data.GetString().c_str()) + : QString(); + if (text.indexOf(QChar('\n')) < 0) { + text.replace(QChar('\r'), QChar('\n')); + } + performOnMainQueue([notificationId, text](Manager *manager) { + manager->notificationReplied(notificationId, { text }); + }); + } else { + performOnMainQueue([notificationId](Manager *manager) { + manager->notificationActivated(notificationId); + }); + } + } else { + performOnMainQueue([notificationId](Manager *manager) { + manager->notificationActivated(notificationId); + }); + } }); const auto token2 = toast.Dismissed([=]( const ToastNotification &sender, const ToastDismissedEventArgs &args) { - base::WinRT::Try([&] { - switch (args.Reason()) { - case ToastDismissalReason::ApplicationHidden: - case ToastDismissalReason::TimedOut: // Went to Action Center. - break; - case ToastDismissalReason::UserCanceled: - default: - performOnMainQueue([notificationId](Manager *manager) { - manager->clearNotification(notificationId); - }); - break; - } - }); + const auto reason = args.Reason(); + switch (reason) { + case ToastDismissalReason::ApplicationHidden: + case ToastDismissalReason::TimedOut: // Went to Action Center. + break; + case ToastDismissalReason::UserCanceled: + default: + performOnMainQueue([notificationId](Manager *manager) { + manager->clearNotification(notificationId); + }); + break; + } }); const auto token3 = toast.Failed([=]( const auto &sender,