data_notify_settings.cpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674
  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. #include "data/notify/data_notify_settings.h"
  8. #include "apiwrap.h"
  9. #include "api/api_ringtones.h"
  10. #include "base/unixtime.h"
  11. #include "core/application.h"
  12. #include "data/data_changes.h"
  13. #include "data/data_channel.h"
  14. #include "data/data_chat.h"
  15. #include "data/data_document.h"
  16. #include "data/data_file_origin.h"
  17. #include "data/data_peer.h"
  18. #include "data/data_forum.h"
  19. #include "data/data_forum_topic.h"
  20. #include "data/data_session.h"
  21. #include "data/data_user.h"
  22. #include "main/main_session.h"
  23. #include "history/history.h"
  24. #include "main/main_session.h"
  25. #include "window/notifications_manager.h"
  26. namespace Data {
  27. namespace {
  28. constexpr auto kMaxNotifyCheckDelay = 24 * 3600 * crl::time(1000);
  29. [[nodiscard]] bool MutedFromUntil(TimeId until, crl::time *changesIn) {
  30. const auto now = base::unixtime::now();
  31. const auto result = (until > now) ? (until - now) : 0;
  32. if (changesIn) {
  33. *changesIn = (result > 0)
  34. ? std::min(result * crl::time(1000), kMaxNotifyCheckDelay)
  35. : kMaxNotifyCheckDelay;
  36. }
  37. return (result > 0);
  38. }
  39. [[nodiscard]] bool SkipAddException(not_null<PeerData*> peer) {
  40. if (const auto user = peer->asUser()) {
  41. return user->isInaccessible() || user->isSelf();
  42. } else if (const auto chat = peer->asChat()) {
  43. return chat->isDeactivated() || chat->isForbidden();
  44. } else if (const auto channel = peer->asChannel()) {
  45. return channel->isForbidden();
  46. }
  47. return false;
  48. }
  49. } // namespace
  50. DefaultNotify DefaultNotifyType(not_null<const PeerData*> peer) {
  51. return peer->isUser()
  52. ? DefaultNotify::User
  53. : (peer->isChat() || peer->isMegagroup())
  54. ? DefaultNotify::Group
  55. : DefaultNotify::Broadcast;
  56. }
  57. MTPInputNotifyPeer DefaultNotifyToMTP(DefaultNotify type) {
  58. switch (type) {
  59. case DefaultNotify::User: return MTP_inputNotifyUsers();
  60. case DefaultNotify::Group: return MTP_inputNotifyChats();
  61. case DefaultNotify::Broadcast: return MTP_inputNotifyBroadcasts();
  62. }
  63. Unexpected("Default notify type in sendNotifySettingsUpdates");
  64. }
  65. NotifySettings::NotifySettings(not_null<Session*> owner)
  66. : _owner(owner)
  67. , _unmuteByFinishedTimer([=] { unmuteByFinished(); }) {
  68. }
  69. void NotifySettings::request(not_null<PeerData*> peer) {
  70. if (peer->notify().settingsUnknown()) {
  71. peer->session().api().requestNotifySettings(
  72. MTP_inputNotifyPeer(peer->input));
  73. }
  74. if (defaultSettings(peer).settingsUnknown()) {
  75. peer->session().api().requestNotifySettings(peer->isUser()
  76. ? MTP_inputNotifyUsers()
  77. : (peer->isChat() || peer->isMegagroup())
  78. ? MTP_inputNotifyChats()
  79. : MTP_inputNotifyBroadcasts());
  80. }
  81. }
  82. void NotifySettings::request(not_null<Thread*> thread) {
  83. if (const auto topic = thread->asTopic()) {
  84. if (topic->notify().settingsUnknown()) {
  85. topic->session().api().requestNotifySettings(
  86. MTP_inputNotifyForumTopic(
  87. topic->channel()->input,
  88. MTP_int(topic->rootId())));
  89. }
  90. }
  91. request(thread->peer());
  92. }
  93. void NotifySettings::apply(
  94. const MTPNotifyPeer &notifyPeer,
  95. const MTPPeerNotifySettings &settings) {
  96. notifyPeer.match([&](const MTPDnotifyUsers &) {
  97. apply(DefaultNotify::User, settings);
  98. }, [&](const MTPDnotifyChats &) {
  99. apply(DefaultNotify::Group, settings);
  100. }, [&](const MTPDnotifyBroadcasts &) {
  101. apply(DefaultNotify::Broadcast, settings);
  102. }, [&](const MTPDnotifyPeer &data) {
  103. apply(peerFromMTP(data.vpeer()), settings);
  104. }, [&](const MTPDnotifyForumTopic &data) {
  105. apply(peerFromMTP(data.vpeer()), data.vtop_msg_id().v, settings);
  106. });
  107. }
  108. void NotifySettings::apply(
  109. const MTPInputNotifyPeer &notifyPeer,
  110. const MTPPeerNotifySettings &settings) {
  111. const auto peerFromInput = [&](const MTPInputPeer &peer) {
  112. return peer.match([&](const MTPDinputPeerSelf &) {
  113. return _owner->session().userPeerId();
  114. }, [](const MTPDinputPeerUser &data) {
  115. return peerFromUser(data.vuser_id());
  116. }, [](const MTPDinputPeerChat &data) {
  117. return peerFromChat(data.vchat_id());
  118. }, [](const MTPDinputPeerChannel &data) {
  119. return peerFromChannel(data.vchannel_id());
  120. }, [](const MTPDinputPeerUserFromMessage &data) -> PeerId {
  121. Unexpected("From message peer in NotifySettings::apply.");
  122. }, [](const MTPDinputPeerChannelFromMessage &data) -> PeerId {
  123. Unexpected("From message peer in NotifySettings::apply.");
  124. }, [](const MTPDinputPeerEmpty &) -> PeerId {
  125. Unexpected("Empty peer in NotifySettings::apply.");
  126. });
  127. };
  128. notifyPeer.match([&](const MTPDinputNotifyUsers &) {
  129. apply(DefaultNotify::User, settings);
  130. }, [&](const MTPDinputNotifyChats &) {
  131. apply(DefaultNotify::Group, settings);
  132. }, [&](const MTPDinputNotifyBroadcasts &) {
  133. apply(DefaultNotify::Broadcast, settings);
  134. }, [&](const MTPDinputNotifyPeer &data) {
  135. apply(peerFromInput(data.vpeer()), settings);
  136. }, [&](const MTPDinputNotifyForumTopic &data) {
  137. apply(peerFromInput(data.vpeer()), data.vtop_msg_id().v, settings);
  138. });
  139. }
  140. void NotifySettings::apply(
  141. DefaultNotify type,
  142. const MTPPeerNotifySettings &settings) {
  143. if (defaultValue(type).settings.change(settings)) {
  144. updateLocal(type);
  145. Core::App().notifications().checkDelayed();
  146. }
  147. }
  148. void NotifySettings::apply(
  149. PeerId peerId,
  150. const MTPPeerNotifySettings &settings) {
  151. if (const auto peer = _owner->peerLoaded(peerId)) {
  152. apply(peer, settings);
  153. }
  154. }
  155. void NotifySettings::apply(
  156. not_null<PeerData*> peer,
  157. const MTPPeerNotifySettings &settings) {
  158. if (peer->notify().change(settings)) {
  159. updateException(peer);
  160. updateLocal(peer);
  161. Core::App().notifications().checkDelayed();
  162. }
  163. }
  164. void NotifySettings::apply(
  165. PeerId peerId,
  166. MsgId topicRootId,
  167. const MTPPeerNotifySettings &settings) {
  168. if (const auto peer = _owner->peerLoaded(peerId)) {
  169. if (const auto topic = peer->forumTopicFor(topicRootId)) {
  170. apply(topic, settings);
  171. }
  172. }
  173. }
  174. void NotifySettings::apply(
  175. not_null<ForumTopic*> topic,
  176. const MTPPeerNotifySettings &settings) {
  177. if (topic->notify().change(settings)) {
  178. updateLocal(topic);
  179. Core::App().notifications().checkDelayed();
  180. }
  181. }
  182. void NotifySettings::update(
  183. not_null<Thread*> thread,
  184. MuteValue muteForSeconds,
  185. std::optional<bool> silentPosts,
  186. std::optional<NotifySound> sound,
  187. std::optional<bool> storiesMuted) {
  188. if (thread->notify().change(
  189. muteForSeconds,
  190. silentPosts,
  191. sound,
  192. storiesMuted)) {
  193. if (const auto history = thread->asHistory()) {
  194. updateException(history->peer);
  195. }
  196. updateLocal(thread);
  197. thread->session().api().updateNotifySettingsDelayed(thread);
  198. }
  199. }
  200. void NotifySettings::resetToDefault(not_null<Thread*> thread) {
  201. // Duplicated in clearExceptions(type) and resetToDefault(peer).
  202. if (thread->notify().resetToDefault()) {
  203. if (const auto history = thread->asHistory()) {
  204. updateException(history->peer);
  205. }
  206. updateLocal(thread);
  207. thread->session().api().updateNotifySettingsDelayed(thread);
  208. Core::App().notifications().checkDelayed();
  209. }
  210. }
  211. void NotifySettings::update(
  212. not_null<PeerData*> peer,
  213. MuteValue muteForSeconds,
  214. std::optional<bool> silentPosts,
  215. std::optional<NotifySound> sound,
  216. std::optional<bool> storiesMuted) {
  217. if (peer->notify().change(
  218. muteForSeconds,
  219. silentPosts,
  220. sound,
  221. storiesMuted)) {
  222. updateException(peer);
  223. updateLocal(peer);
  224. peer->session().api().updateNotifySettingsDelayed(peer);
  225. }
  226. }
  227. void NotifySettings::resetToDefault(not_null<PeerData*> peer) {
  228. // Duplicated in clearExceptions(type) and resetToDefault(thread).
  229. if (peer->notify().resetToDefault()) {
  230. updateException(peer);
  231. updateLocal(peer);
  232. peer->session().api().updateNotifySettingsDelayed(peer);
  233. Core::App().notifications().checkDelayed();
  234. }
  235. }
  236. void NotifySettings::forumParentMuteUpdated(not_null<Forum*> forum) {
  237. forum->enumerateTopics([&](not_null<ForumTopic*> topic) {
  238. if (!topic->notify().settingsUnknown()) {
  239. updateLocal(topic);
  240. }
  241. });
  242. }
  243. auto NotifySettings::defaultValue(DefaultNotify type)
  244. -> DefaultValue & {
  245. const auto index = static_cast<int>(type);
  246. Assert(index >= 0 && index < base::array_size(_defaultValues));
  247. return _defaultValues[index];
  248. }
  249. auto NotifySettings::defaultValue(DefaultNotify type) const
  250. -> const DefaultValue & {
  251. const auto index = static_cast<int>(type);
  252. Assert(index >= 0 && index < base::array_size(_defaultValues));
  253. return _defaultValues[index];
  254. }
  255. const PeerNotifySettings &NotifySettings::defaultSettings(
  256. not_null<const PeerData*> peer) const {
  257. return defaultSettings(DefaultNotifyType(peer));
  258. }
  259. const PeerNotifySettings &NotifySettings::defaultSettings(
  260. DefaultNotify type) const {
  261. return defaultValue(type).settings;
  262. }
  263. bool NotifySettings::isMuted(DefaultNotify type) const {
  264. if (const auto until = defaultSettings(type).muteUntil()) {
  265. return MutedFromUntil(*until, nullptr);
  266. }
  267. return true;
  268. }
  269. void NotifySettings::defaultUpdate(
  270. DefaultNotify type,
  271. MuteValue muteForSeconds,
  272. std::optional<bool> silentPosts,
  273. std::optional<NotifySound> sound,
  274. std::optional<bool> storiesMuted) {
  275. auto &settings = defaultValue(type).settings;
  276. if (settings.change(muteForSeconds, silentPosts, sound, storiesMuted)) {
  277. updateLocal(type);
  278. _owner->session().api().updateNotifySettingsDelayed(type);
  279. }
  280. }
  281. void NotifySettings::updateLocal(not_null<Thread*> thread) {
  282. const auto topic = thread->asTopic();
  283. if (!topic) {
  284. return updateLocal(thread->peer());
  285. }
  286. auto changesIn = crl::time(0);
  287. const auto muted = isMuted(topic, &changesIn);
  288. topic->setMuted(muted);
  289. if (muted) {
  290. auto &lifetime = _mutedTopics.emplace(
  291. topic,
  292. rpl::lifetime()).first->second;
  293. topic->destroyed() | rpl::start_with_next([=] {
  294. _mutedTopics.erase(topic);
  295. }, lifetime);
  296. unmuteByFinishedDelayed(changesIn);
  297. Core::App().notifications().clearIncomingFromTopic(topic);
  298. } else {
  299. _mutedTopics.erase(topic);
  300. }
  301. cacheSound(topic->notify().sound());
  302. }
  303. void NotifySettings::updateLocal(not_null<PeerData*> peer) {
  304. const auto history = _owner->historyLoaded(peer->id);
  305. auto changesIn = crl::time(0);
  306. const auto muted = isMuted(peer, &changesIn);
  307. const auto changeInHistory = history && (history->muted() != muted);
  308. if (changeInHistory) {
  309. history->setMuted(muted);
  310. // Notification already sent.
  311. } else {
  312. peer->session().changes().peerUpdated(
  313. peer,
  314. PeerUpdate::Flag::Notifications);
  315. }
  316. if (muted) {
  317. _mutedPeers.emplace(peer);
  318. unmuteByFinishedDelayed(changesIn);
  319. if (history) {
  320. Core::App().notifications().clearIncomingFromHistory(history);
  321. }
  322. } else {
  323. _mutedPeers.erase(peer);
  324. }
  325. cacheSound(peer->notify().sound());
  326. }
  327. void NotifySettings::cacheSound(DocumentId id) {
  328. cacheSound(_owner->document(id));
  329. }
  330. void NotifySettings::cacheSound(not_null<DocumentData*> document) {
  331. if (document->isNull()) {
  332. return;
  333. }
  334. const auto view = document->createMediaView();
  335. _ringtones.views.emplace(document->id, view);
  336. document->forceToCache(true);
  337. document->save(FileOriginRingtones(), QString());
  338. }
  339. void NotifySettings::cacheSound(const std::optional<NotifySound> &sound) {
  340. if (!sound || !sound->id) {
  341. return;
  342. } else if (const auto doc = _owner->document(sound->id); !doc->isNull()) {
  343. cacheSound(doc);
  344. return;
  345. }
  346. _ringtones.pendingIds.push_back(sound->id);
  347. if (_ringtones.pendingLifetime) {
  348. return;
  349. }
  350. // Not requested yet.
  351. _owner->session().api().ringtones().listUpdates(
  352. ) | rpl::start_with_next([=] {
  353. for (const auto id : base::take(_ringtones.pendingIds)) {
  354. cacheSound(id);
  355. }
  356. _ringtones.pendingLifetime.destroy();
  357. }, _ringtones.pendingLifetime);
  358. _owner->session().api().ringtones().requestList();
  359. }
  360. void NotifySettings::updateLocal(DefaultNotify type) {
  361. defaultValue(type).updates.fire({});
  362. const auto goodForUpdate = [&](
  363. not_null<const PeerData*> peer,
  364. const PeerNotifySettings &settings) {
  365. auto &peers = peer->notify();
  366. return !peers.settingsUnknown()
  367. && ((!peers.muteUntil() && settings.muteUntil())
  368. || (!peers.silentPosts() && settings.silentPosts())
  369. || (!peers.sound() && settings.sound()));
  370. };
  371. const auto callback = [&](not_null<PeerData*> peer) {
  372. if (goodForUpdate(peer, defaultSettings(type))) {
  373. updateLocal(peer);
  374. }
  375. };
  376. switch (type) {
  377. case DefaultNotify::User: _owner->enumerateUsers(callback); break;
  378. case DefaultNotify::Group: _owner->enumerateGroups(callback); break;
  379. case DefaultNotify::Broadcast:
  380. _owner->enumerateBroadcasts(callback);
  381. break;
  382. }
  383. cacheSound(defaultValue(type).settings.sound());
  384. }
  385. std::shared_ptr<DocumentMedia> NotifySettings::lookupRingtone(
  386. DocumentId id) const {
  387. if (!id) {
  388. return nullptr;
  389. }
  390. const auto it = _ringtones.views.find(id);
  391. return (it == end(_ringtones.views)) ? nullptr : it->second;
  392. }
  393. void NotifySettings::unmuteByFinishedDelayed(crl::time delay) {
  394. accumulate_min(delay, kMaxNotifyCheckDelay);
  395. if (!_unmuteByFinishedTimer.isActive()
  396. || _unmuteByFinishedTimer.remainingTime() > delay) {
  397. _unmuteByFinishedTimer.callOnce(delay);
  398. }
  399. }
  400. void NotifySettings::unmuteByFinished() {
  401. auto changesInMin = crl::time(0);
  402. for (auto i = begin(_mutedPeers); i != end(_mutedPeers);) {
  403. const auto history = _owner->historyLoaded((*i)->id);
  404. auto changesIn = crl::time(0);
  405. const auto muted = isMuted(*i, &changesIn);
  406. if (history) {
  407. history->setMuted(muted);
  408. }
  409. if (muted) {
  410. if (!changesInMin || changesInMin > changesIn) {
  411. changesInMin = changesIn;
  412. }
  413. ++i;
  414. } else {
  415. i = _mutedPeers.erase(i);
  416. }
  417. }
  418. for (auto i = begin(_mutedTopics); i != end(_mutedTopics);) {
  419. auto changesIn = crl::time(0);
  420. const auto topic = i->first;
  421. const auto muted = isMuted(topic, &changesIn);
  422. topic->setMuted(muted);
  423. if (muted) {
  424. if (!changesInMin || changesInMin > changesIn) {
  425. changesInMin = changesIn;
  426. }
  427. ++i;
  428. } else {
  429. i = _mutedTopics.erase(i);
  430. }
  431. }
  432. if (changesInMin) {
  433. unmuteByFinishedDelayed(changesInMin);
  434. }
  435. }
  436. bool NotifySettings::isMuted(
  437. not_null<const Thread*> thread,
  438. crl::time *changesIn) const {
  439. const auto topic = thread->asTopic();
  440. const auto until = topic ? topic->notify().muteUntil() : std::nullopt;
  441. return until
  442. ? MutedFromUntil(*until, changesIn)
  443. : isMuted(thread->peer(), changesIn);
  444. }
  445. bool NotifySettings::isMuted(not_null<const Thread*> thread) const {
  446. return isMuted(thread, nullptr);
  447. }
  448. NotifySound NotifySettings::sound(not_null<const Thread*> thread) const {
  449. const auto topic = thread->asTopic();
  450. const auto sound = topic ? topic->notify().sound() : std::nullopt;
  451. return sound ? *sound : this->sound(thread->peer());
  452. }
  453. bool NotifySettings::muteUnknown(not_null<const Thread*> thread) const {
  454. const auto topic = thread->asTopic();
  455. return (topic && topic->notify().settingsUnknown())
  456. || ((!topic || !topic->notify().muteUntil().has_value())
  457. && muteUnknown(thread->peer()));
  458. }
  459. bool NotifySettings::soundUnknown(not_null<const Thread*> thread) const {
  460. const auto topic = thread->asTopic();
  461. return (topic && topic->notify().settingsUnknown())
  462. || ((!topic || !topic->notify().sound().has_value())
  463. && soundUnknown(topic->channel()));
  464. }
  465. bool NotifySettings::isMuted(
  466. not_null<const PeerData*> peer,
  467. crl::time *changesIn) const {
  468. if (const auto until = peer->notify().muteUntil()) {
  469. return MutedFromUntil(*until, changesIn);
  470. } else if (const auto until = defaultSettings(peer).muteUntil()) {
  471. return MutedFromUntil(*until, changesIn);
  472. }
  473. return true;
  474. }
  475. bool NotifySettings::isMuted(not_null<const PeerData*> peer) const {
  476. return isMuted(peer, nullptr);
  477. }
  478. bool NotifySettings::silentPosts(not_null<const PeerData*> peer) const {
  479. if (const auto silent = peer->notify().silentPosts()) {
  480. return *silent;
  481. } else if (const auto silent = defaultSettings(peer).silentPosts()) {
  482. return *silent;
  483. }
  484. return false;
  485. }
  486. NotifySound NotifySettings::sound(not_null<const PeerData*> peer) const {
  487. // Explicitly ignore a notify sound for Saved Messages
  488. // to follow the global notify sound.
  489. if (const auto sound = peer->notify().sound(); !peer->isSelf() && sound) {
  490. return *sound;
  491. } else if (const auto sound = defaultSettings(peer).sound()) {
  492. return *sound;
  493. }
  494. return {};
  495. }
  496. bool NotifySettings::muteUnknown(not_null<const PeerData*> peer) const {
  497. return peer->notify().settingsUnknown()
  498. || (!peer->notify().muteUntil().has_value()
  499. && defaultSettings(peer).settingsUnknown());
  500. }
  501. bool NotifySettings::silentPostsUnknown(
  502. not_null<const PeerData*> peer) const {
  503. return peer->notify().settingsUnknown()
  504. || (!peer->notify().silentPosts().has_value()
  505. && defaultSettings(peer).settingsUnknown());
  506. }
  507. bool NotifySettings::soundUnknown(not_null<const PeerData*> peer) const {
  508. return peer->notify().settingsUnknown()
  509. || (!peer->notify().sound().has_value()
  510. && defaultSettings(peer).settingsUnknown());
  511. }
  512. bool NotifySettings::settingsUnknown(not_null<const PeerData*> peer) const {
  513. return muteUnknown(peer)
  514. || silentPostsUnknown(peer)
  515. || soundUnknown(peer);
  516. }
  517. bool NotifySettings::settingsUnknown(not_null<const Thread*> thread) const {
  518. const auto topic = thread->asTopic();
  519. return muteUnknown(thread)
  520. || soundUnknown(thread)
  521. || (!topic && silentPostsUnknown(thread->peer()));
  522. }
  523. rpl::producer<> NotifySettings::defaultUpdates(DefaultNotify type) const {
  524. return defaultValue(type).updates.events();
  525. }
  526. rpl::producer<> NotifySettings::defaultUpdates(
  527. not_null<const PeerData*> peer) const {
  528. return defaultUpdates(peer->isUser()
  529. ? DefaultNotify::User
  530. : (peer->isChat() || peer->isMegagroup())
  531. ? DefaultNotify::Group
  532. : DefaultNotify::Broadcast);
  533. }
  534. void NotifySettings::loadExceptions() {
  535. for (auto i = 0; i != kDefaultNotifyTypes; ++i) {
  536. if (_exceptionsRequestId[i]) {
  537. continue;
  538. }
  539. const auto type = static_cast<DefaultNotify>(i);
  540. const auto api = &_owner->session().api();
  541. const auto requestId = api->request(MTPaccount_GetNotifyExceptions(
  542. MTP_flags(MTPaccount_GetNotifyExceptions::Flag::f_peer),
  543. DefaultNotifyToMTP(type)
  544. )).done([=](const MTPUpdates &result) {
  545. api->applyUpdates(result);
  546. }).send();
  547. _exceptionsRequestId[i] = requestId;
  548. }
  549. }
  550. void NotifySettings::updateException(not_null<PeerData*> peer) {
  551. const auto type = DefaultNotifyType(peer);
  552. const auto index = static_cast<int>(type);
  553. const auto exception = peer->notify().muteUntil().has_value();
  554. if (!exception) {
  555. if (_exceptions[index].remove(peer)) {
  556. exceptionsUpdated(type);
  557. }
  558. } else if (SkipAddException(peer)) {
  559. return;
  560. } else if (_exceptions[index].emplace(peer).second) {
  561. exceptionsUpdated(type);
  562. }
  563. }
  564. void NotifySettings::exceptionsUpdated(DefaultNotify type) {
  565. if (!ranges::contains(_exceptionsUpdatesScheduled, true)) {
  566. crl::on_main(&_owner->session(), [=] {
  567. const auto scheduled = base::take(_exceptionsUpdatesScheduled);
  568. for (auto i = 0; i != kDefaultNotifyTypes; ++i) {
  569. if (scheduled[i]) {
  570. _exceptionsUpdates.fire(static_cast<DefaultNotify>(i));
  571. }
  572. }
  573. });
  574. }
  575. _exceptionsUpdatesScheduled[static_cast<int>(type)] = true;
  576. _exceptionsUpdatesRealtime.fire_copy(type);
  577. }
  578. rpl::producer<DefaultNotify> NotifySettings::exceptionsUpdates() const {
  579. return _exceptionsUpdates.events();
  580. }
  581. auto NotifySettings::exceptionsUpdatesRealtime() const
  582. -> rpl::producer<DefaultNotify> {
  583. return _exceptionsUpdatesRealtime.events();
  584. }
  585. const base::flat_set<not_null<PeerData*>> &NotifySettings::exceptions(
  586. DefaultNotify type) const {
  587. const auto index = static_cast<int>(type);
  588. Assert(index >= 0 && index < kDefaultNotifyTypes);
  589. return _exceptions[index];
  590. }
  591. void NotifySettings::clearExceptions(DefaultNotify type) {
  592. const auto index = static_cast<int>(type);
  593. const auto list = base::take(_exceptions[index]);
  594. if (list.empty()) {
  595. return;
  596. }
  597. for (const auto &peer : list) {
  598. // Duplicated in resetToDefault(peer / thread).
  599. if (peer->notify().resetToDefault()) {
  600. updateLocal(peer);
  601. peer->session().api().updateNotifySettingsDelayed(peer);
  602. }
  603. }
  604. Core::App().notifications().checkDelayed();
  605. exceptionsUpdated(type);
  606. }
  607. } // namespace Data