notifications_manager.h 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441
  1. /*
  2. This file is part of Telegram Desktop,
  3. the official desktop application for the Telegram messaging service.
  4. For license and copyright information please follow this link:
  5. https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
  6. */
  7. #pragma once
  8. #include "data/data_message_reaction_id.h"
  9. #include "base/timer.h"
  10. #include "base/type_traits.h"
  11. #include "media/audio/media_audio_local_cache.h"
  12. class History;
  13. namespace Data {
  14. class Session;
  15. class ForumTopic;
  16. class Thread;
  17. struct ItemNotification;
  18. enum class ItemNotificationType;
  19. } // namespace Data
  20. namespace Ui {
  21. struct PeerUserpicView;
  22. } // namespace Ui
  23. namespace Main {
  24. class Session;
  25. } // namespace Main
  26. namespace Platform {
  27. namespace Notifications {
  28. class Manager;
  29. } // namespace Notifications
  30. } // namespace Platform
  31. namespace Media::Audio {
  32. class Track;
  33. } // namespace Media::Audio
  34. namespace Window {
  35. class SessionController;
  36. } // namespace Window
  37. namespace Window::Notifications {
  38. enum class ManagerType {
  39. Dummy,
  40. Default,
  41. Native,
  42. };
  43. enum class ChangeType {
  44. SoundEnabled,
  45. FlashBounceEnabled,
  46. IncludeMuted,
  47. CountMessages,
  48. DesktopEnabled,
  49. ViewParams,
  50. MaxCount,
  51. Corner,
  52. DemoIsShown,
  53. DemoIsHidden,
  54. };
  55. } // namespace Window::Notifications
  56. namespace base {
  57. template <>
  58. struct custom_is_fast_copy_type<Window::Notifications::ChangeType> : std::true_type {
  59. };
  60. } // namespace base
  61. namespace base::options {
  62. template <typename Type>
  63. class option;
  64. using toggle = option<bool>;
  65. } // namespace base::options
  66. namespace Window::Notifications {
  67. extern const char kOptionGNotification[];
  68. extern base::options::toggle OptionGNotification;
  69. class Manager;
  70. class System final {
  71. public:
  72. System();
  73. ~System();
  74. [[nodiscard]] Main::Session *findSession(uint64 sessionId) const;
  75. void createManager();
  76. void setManager(Fn<std::unique_ptr<Manager>()> create);
  77. void checkDelayed();
  78. void schedule(Data::ItemNotification notification);
  79. void clearFromTopic(not_null<Data::ForumTopic*> topic);
  80. void clearFromHistory(not_null<History*> history);
  81. void clearIncomingFromTopic(not_null<Data::ForumTopic*> topic);
  82. void clearIncomingFromHistory(not_null<History*> history);
  83. void clearFromSession(not_null<Main::Session*> session);
  84. void clearFromItem(not_null<HistoryItem*> item);
  85. void clearAll();
  86. void clearAllFast();
  87. void updateAll();
  88. [[nodiscard]] rpl::producer<ChangeType> settingsChanged() const;
  89. void notifySettingsChanged(ChangeType type);
  90. void playSound(not_null<Main::Session*> session, DocumentId id);
  91. [[nodiscard]] QByteArray lookupSoundBytes(
  92. not_null<Data::Session*> owner,
  93. DocumentId id);
  94. [[nodiscard]] rpl::lifetime &lifetime() {
  95. return _lifetime;
  96. }
  97. private:
  98. struct Waiter;
  99. struct SkipState {
  100. enum Value {
  101. Unknown,
  102. Skip,
  103. DontSkip
  104. };
  105. Value value = Value::Unknown;
  106. bool silent = false;
  107. };
  108. struct NotificationInHistoryKey {
  109. NotificationInHistoryKey(Data::ItemNotification notification);
  110. NotificationInHistoryKey(
  111. MsgId messageId,
  112. Data::ItemNotificationType type);
  113. MsgId messageId = 0;
  114. Data::ItemNotificationType type = Data::ItemNotificationType();
  115. friend inline auto operator<=>(
  116. NotificationInHistoryKey a,
  117. NotificationInHistoryKey b) = default;
  118. };
  119. struct Timing {
  120. crl::time delay = 0;
  121. crl::time when = 0;
  122. };
  123. struct ReactionNotificationId {
  124. FullMsgId itemId;
  125. uint64 sessionId = 0;
  126. friend inline bool operator<(
  127. ReactionNotificationId a,
  128. ReactionNotificationId b) {
  129. return std::pair(a.itemId, a.sessionId)
  130. < std::pair(b.itemId, b.sessionId);
  131. }
  132. };
  133. void clearForThreadIf(Fn<bool(not_null<Data::Thread*>)> predicate);
  134. [[nodiscard]] SkipState skipNotification(
  135. Data::ItemNotification notification) const;
  136. [[nodiscard]] SkipState computeSkipState(
  137. Data::ItemNotification notification) const;
  138. [[nodiscard]] Timing countTiming(
  139. not_null<Data::Thread*> thread,
  140. crl::time minimalDelay) const;
  141. [[nodiscard]] bool skipReactionNotification(
  142. not_null<HistoryItem*> item) const;
  143. void showNext();
  144. void showGrouped();
  145. void ensureSoundCreated();
  146. [[nodiscard]] not_null<Media::Audio::Track*> lookupSound(
  147. not_null<Data::Session*> owner,
  148. DocumentId id);
  149. void registerThread(not_null<Data::Thread*> thread);
  150. base::flat_map<
  151. not_null<Data::Thread*>,
  152. base::flat_map<NotificationInHistoryKey, crl::time>> _whenMaps;
  153. base::flat_map<not_null<Data::Thread*>, Waiter> _waiters;
  154. base::flat_map<not_null<Data::Thread*>, Waiter> _settingWaiters;
  155. base::Timer _waitTimer;
  156. base::Timer _waitForAllGroupedTimer;
  157. base::flat_map<
  158. not_null<Data::Thread*>,
  159. base::flat_map<crl::time, PeerData*>> _whenAlerts;
  160. mutable base::flat_map<
  161. ReactionNotificationId,
  162. crl::time> _sentReactionNotifications;
  163. std::unique_ptr<Manager> _manager;
  164. rpl::event_stream<ChangeType> _settingsChanged;
  165. std::unique_ptr<Media::Audio::Track> _soundTrack;
  166. base::flat_map<
  167. DocumentId,
  168. std::unique_ptr<Media::Audio::Track>> _customSoundTracks;
  169. base::flat_map<
  170. not_null<Data::ForumTopic*>,
  171. rpl::lifetime> _watchedTopics;
  172. int _lastForwardedCount = 0;
  173. uint64 _lastHistorySessionId = 0;
  174. FullMsgId _lastHistoryItemId;
  175. std::optional<DocumentId> _lastSoundId;
  176. rpl::lifetime _lifetime;
  177. };
  178. class Manager {
  179. public:
  180. struct ContextId {
  181. uint64 sessionId = 0;
  182. PeerId peerId = 0;
  183. MsgId topicRootId = 0;
  184. friend inline auto operator<=>(
  185. const ContextId&,
  186. const ContextId&) = default;
  187. };
  188. struct NotificationId {
  189. ContextId contextId;
  190. MsgId msgId = 0;
  191. friend inline auto operator<=>(
  192. const NotificationId&,
  193. const NotificationId&) = default;
  194. };
  195. struct NotificationFields {
  196. not_null<HistoryItem*> item;
  197. int forwardedCount = 0;
  198. PeerData *reactionFrom = nullptr;
  199. Data::ReactionId reactionId;
  200. std::optional<DocumentId> soundId;
  201. };
  202. explicit Manager(not_null<System*> system) : _system(system) {
  203. }
  204. void showNotification(NotificationFields fields) {
  205. doShowNotification(std::move(fields));
  206. }
  207. void updateAll() {
  208. doUpdateAll();
  209. }
  210. void clearAll() {
  211. doClearAll();
  212. }
  213. void clearAllFast() {
  214. doClearAllFast();
  215. }
  216. void clearFromItem(not_null<HistoryItem*> item) {
  217. doClearFromItem(item);
  218. }
  219. void clearFromTopic(not_null<Data::ForumTopic*> topic) {
  220. doClearFromTopic(topic);
  221. }
  222. void clearFromHistory(not_null<History*> history) {
  223. doClearFromHistory(history);
  224. }
  225. void clearFromSession(not_null<Main::Session*> session) {
  226. doClearFromSession(session);
  227. }
  228. void notificationActivated(
  229. NotificationId id,
  230. const TextWithTags &draft = {});
  231. void notificationReplied(NotificationId id, const TextWithTags &reply);
  232. struct DisplayOptions {
  233. bool hideNameAndPhoto : 1 = false;
  234. bool hideMessageText : 1 = false;
  235. bool hideMarkAsRead : 1 = false;
  236. bool hideReplyButton : 1 = false;
  237. bool spoilerLoginCode : 1 = false;
  238. };
  239. [[nodiscard]] DisplayOptions getNotificationOptions(
  240. HistoryItem *item,
  241. Data::ItemNotificationType type) const;
  242. [[nodiscard]] static TextWithEntities ComposeReactionEmoji(
  243. not_null<Main::Session*> session,
  244. const Data::ReactionId &reaction);
  245. [[nodiscard]] static TextWithEntities ComposeReactionNotification(
  246. not_null<HistoryItem*> item,
  247. const Data::ReactionId &reaction,
  248. bool hideContent);
  249. [[nodiscard]] TextWithEntities addTargetAccountName(
  250. TextWithEntities title,
  251. not_null<Main::Session*> session);
  252. [[nodiscard]] QString addTargetAccountName(
  253. const QString &title,
  254. not_null<Main::Session*> session);
  255. [[nodiscard]] virtual ManagerType type() const = 0;
  256. [[nodiscard]] bool skipToast() const {
  257. return doSkipToast();
  258. }
  259. void maybePlaySound(Fn<void()> playSound) {
  260. doMaybePlaySound(std::move(playSound));
  261. }
  262. void maybeFlashBounce(Fn<void()> flashBounce) {
  263. doMaybeFlashBounce(std::move(flashBounce));
  264. }
  265. virtual ~Manager() = default;
  266. protected:
  267. [[nodiscard]] not_null<System*> system() const {
  268. return _system;
  269. }
  270. virtual void doUpdateAll() = 0;
  271. virtual void doShowNotification(NotificationFields &&fields) = 0;
  272. virtual void doClearAll() = 0;
  273. virtual void doClearAllFast() = 0;
  274. virtual void doClearFromItem(not_null<HistoryItem*> item) = 0;
  275. virtual void doClearFromTopic(not_null<Data::ForumTopic*> topic) = 0;
  276. virtual void doClearFromHistory(not_null<History*> history) = 0;
  277. virtual void doClearFromSession(not_null<Main::Session*> session) = 0;
  278. [[nodiscard]] virtual bool doSkipToast() const = 0;
  279. virtual void doMaybePlaySound(Fn<void()> playSound) = 0;
  280. virtual void doMaybeFlashBounce(Fn<void()> flashBounce) = 0;
  281. [[nodiscard]] virtual bool forceHideDetails() const {
  282. return false;
  283. }
  284. virtual void onBeforeNotificationActivated(NotificationId id) {
  285. }
  286. virtual void onAfterNotificationActivated(
  287. NotificationId id,
  288. not_null<SessionController*> window) {
  289. }
  290. [[nodiscard]] virtual QString accountNameSeparator();
  291. private:
  292. void openNotificationMessage(
  293. not_null<History*> history,
  294. MsgId messageId);
  295. const not_null<System*> _system;
  296. };
  297. class NativeManager : public Manager {
  298. public:
  299. [[nodiscard]] ManagerType type() const override {
  300. return ManagerType::Native;
  301. }
  302. using NotificationSound = Media::Audio::LocalSound;
  303. struct NotificationInfo {
  304. not_null<PeerData*> peer;
  305. MsgId topicRootId = 0;
  306. MsgId itemId = 0;
  307. QString title;
  308. QString subtitle;
  309. QString message;
  310. Fn<NotificationSound()> sound;
  311. DisplayOptions options;
  312. };
  313. protected:
  314. using Manager::Manager;
  315. void doUpdateAll() override {
  316. doClearAllFast();
  317. }
  318. void doClearAll() override {
  319. doClearAllFast();
  320. }
  321. void doShowNotification(NotificationFields &&fields) override;
  322. bool forceHideDetails() const override;
  323. virtual void doShowNativeNotification(
  324. NotificationInfo &&info,
  325. Ui::PeerUserpicView &userpicView) = 0;
  326. private:
  327. Media::Audio::LocalCache _localSoundCache;
  328. };
  329. class DummyManager : public NativeManager {
  330. public:
  331. using NativeManager::NativeManager;
  332. [[nodiscard]] ManagerType type() const override {
  333. return ManagerType::Dummy;
  334. }
  335. protected:
  336. void doShowNativeNotification(
  337. NotificationInfo &&info,
  338. Ui::PeerUserpicView &userpicView) override {
  339. }
  340. void doClearAllFast() override {
  341. }
  342. void doClearFromItem(not_null<HistoryItem*> item) override {
  343. }
  344. void doClearFromTopic(not_null<Data::ForumTopic*> topic) override {
  345. }
  346. void doClearFromHistory(not_null<History*> history) override {
  347. }
  348. void doClearFromSession(not_null<Main::Session*> session) override {
  349. }
  350. bool doSkipToast() const override {
  351. return false;
  352. }
  353. void doMaybePlaySound(Fn<void()> playSound) override {
  354. playSound();
  355. }
  356. void doMaybeFlashBounce(Fn<void()> flashBounce) override {
  357. flashBounce();
  358. }
  359. };
  360. [[nodiscard]] QString WrapFromScheduled(const QString &text);
  361. } // namespace Window::Notifications