data_peer_values.cpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624
  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/data_peer_values.h"
  8. #include "lang/lang_keys.h"
  9. #include "data/data_channel.h"
  10. #include "data/data_chat.h"
  11. #include "data/data_user.h"
  12. #include "data/data_changes.h"
  13. #include "data/data_forum_topic.h"
  14. #include "data/data_session.h"
  15. #include "data/data_message_reactions.h"
  16. #include "main/main_session.h"
  17. #include "main/main_app_config.h"
  18. #include "ui/image/image_prepare.h"
  19. #include "base/unixtime.h"
  20. namespace Data {
  21. namespace {
  22. constexpr auto kMinOnlineChangeTimeout = crl::time(1000);
  23. constexpr auto kMaxOnlineChangeTimeout = 86400 * crl::time(1000);
  24. constexpr auto kSecondsInDay = 86400;
  25. int OnlinePhraseChangeInSeconds(LastseenStatus status, TimeId now) {
  26. const auto till = status.onlineTill();
  27. if (till > now) {
  28. return till - now;
  29. } else if (status.isHidden()) {
  30. return std::numeric_limits<int>::max();
  31. }
  32. const auto minutes = (now - till) / 60;
  33. if (minutes < 60) {
  34. return (minutes + 1) * 60 - (now - till);
  35. }
  36. const auto hours = (now - till) / 3600;
  37. if (hours < 12) {
  38. return (hours + 1) * 3600 - (now - till);
  39. }
  40. const auto nowFull = base::unixtime::parse(now);
  41. const auto tomorrow = nowFull.date().addDays(1).startOfDay();
  42. return std::max(static_cast<TimeId>(nowFull.secsTo(tomorrow)), 0);
  43. }
  44. std::optional<QString> OnlineTextSpecial(not_null<UserData*> user) {
  45. if (user->isNotificationsUser()) {
  46. return tr::lng_status_service_notifications(tr::now);
  47. } else if (user->isSupport()) {
  48. return tr::lng_status_support(tr::now);
  49. } else if (user->isBot()) {
  50. if (const auto count = user->botInfo->activeUsers) {
  51. return tr::lng_bot_status_users(
  52. tr::now,
  53. lt_count_decimal,
  54. count);
  55. }
  56. return tr::lng_status_bot(tr::now);
  57. } else if (user->isServiceUser()) {
  58. return tr::lng_status_support(tr::now);
  59. }
  60. return std::nullopt;
  61. }
  62. std::optional<QString> OnlineTextCommon(LastseenStatus status, TimeId now) {
  63. if (status.isOnline(now)) {
  64. return tr::lng_status_online(tr::now);
  65. } else if (status.isLongAgo()) {
  66. return tr::lng_status_offline(tr::now);
  67. } else if (status.isRecently()) {
  68. return tr::lng_status_recently(tr::now);
  69. } else if (status.isWithinWeek()) {
  70. return tr::lng_status_last_week(tr::now);
  71. } else if (status.isWithinMonth()) {
  72. return tr::lng_status_last_month(tr::now);
  73. } else if (status.isHidden()) {
  74. return tr::lng_status_recently(tr::now);
  75. }
  76. return std::nullopt;
  77. }
  78. [[nodiscard]] int UniqueReactionsLimit(not_null<Main::AppConfig*> config) {
  79. return config->get<int>("reactions_uniq_max", 11);
  80. }
  81. } // namespace
  82. inline auto AdminRightsValue(not_null<ChannelData*> channel) {
  83. return channel->adminRightsValue();
  84. }
  85. inline auto AdminRightsValue(
  86. not_null<ChannelData*> channel,
  87. ChatAdminRights mask) {
  88. return FlagsValueWithMask(AdminRightsValue(channel), mask);
  89. }
  90. inline auto AdminRightValue(
  91. not_null<ChannelData*> channel,
  92. ChatAdminRight flag) {
  93. return SingleFlagValue(AdminRightsValue(channel), flag);
  94. }
  95. inline auto AdminRightsValue(not_null<ChatData*> chat) {
  96. return chat->adminRightsValue();
  97. }
  98. inline auto AdminRightsValue(
  99. not_null<ChatData*> chat,
  100. ChatAdminRights mask) {
  101. return FlagsValueWithMask(AdminRightsValue(chat), mask);
  102. }
  103. inline auto AdminRightValue(
  104. not_null<ChatData*> chat,
  105. ChatAdminRight flag) {
  106. return SingleFlagValue(AdminRightsValue(chat), flag);
  107. }
  108. inline auto RestrictionsValue(not_null<ChannelData*> channel) {
  109. return channel->restrictionsValue();
  110. }
  111. inline auto RestrictionsValue(
  112. not_null<ChannelData*> channel,
  113. ChatRestrictions mask) {
  114. return FlagsValueWithMask(RestrictionsValue(channel), mask);
  115. }
  116. inline auto RestrictionValue(
  117. not_null<ChannelData*> channel,
  118. ChatRestriction flag) {
  119. return SingleFlagValue(RestrictionsValue(channel), flag);
  120. }
  121. inline auto DefaultRestrictionsValue(not_null<ChannelData*> channel) {
  122. return channel->defaultRestrictionsValue();
  123. }
  124. inline auto DefaultRestrictionsValue(
  125. not_null<ChannelData*> channel,
  126. ChatRestrictions mask) {
  127. return FlagsValueWithMask(DefaultRestrictionsValue(channel), mask);
  128. }
  129. inline auto DefaultRestrictionValue(
  130. not_null<ChannelData*> channel,
  131. ChatRestriction flag) {
  132. return SingleFlagValue(DefaultRestrictionsValue(channel), flag);
  133. }
  134. inline auto DefaultRestrictionsValue(not_null<ChatData*> chat) {
  135. return chat->defaultRestrictionsValue();
  136. }
  137. inline auto DefaultRestrictionsValue(
  138. not_null<ChatData*> chat,
  139. ChatRestrictions mask) {
  140. return FlagsValueWithMask(DefaultRestrictionsValue(chat), mask);
  141. }
  142. inline auto DefaultRestrictionValue(
  143. not_null<ChatData*> chat,
  144. ChatRestriction flag) {
  145. return SingleFlagValue(DefaultRestrictionsValue(chat), flag);
  146. }
  147. // Duplicated in CanSendAnyOf().
  148. [[nodiscard]] rpl::producer<bool> CanSendAnyOfValue(
  149. not_null<Thread*> thread,
  150. ChatRestrictions rights,
  151. bool forbidInForums) {
  152. if (const auto topic = thread->asTopic()) {
  153. using Flag = ChannelDataFlag;
  154. const auto mask = Flag()
  155. | Flag::Left
  156. | Flag::JoinToWrite
  157. | Flag::HasLink
  158. | Flag::Forbidden
  159. | Flag::Creator;
  160. const auto channel = topic->channel();
  161. return rpl::combine(
  162. PeerFlagsValue(channel.get(), mask),
  163. RestrictionsValue(channel, rights),
  164. DefaultRestrictionsValue(channel, rights),
  165. AdminRightsValue(channel, ChatAdminRight::ManageTopics),
  166. topic->session().changes().topicFlagsValue(
  167. topic,
  168. TopicUpdate::Flag::Closed),
  169. [=](
  170. ChannelDataFlags flags,
  171. ChatRestrictions sendRestriction,
  172. ChatRestrictions defaultSendRestriction,
  173. auto,
  174. auto) {
  175. const auto notAmInFlags = Flag::Left | Flag::Forbidden;
  176. const auto allowed = !(flags & notAmInFlags)
  177. || ((flags & Flag::HasLink)
  178. && !(flags & Flag::JoinToWrite));
  179. return allowed
  180. && ((flags & Flag::Creator)
  181. || (!sendRestriction && !defaultSendRestriction))
  182. && (!topic->closed() || topic->canToggleClosed());
  183. });
  184. }
  185. return CanSendAnyOfValue(thread->peer(), rights, forbidInForums);
  186. }
  187. // Duplicated in CanSendAnyOf().
  188. [[nodiscard]] rpl::producer<bool> CanSendAnyOfValue(
  189. not_null<PeerData*> peer,
  190. ChatRestrictions rights,
  191. bool forbidInForums) {
  192. if (const auto user = peer->asUser()) {
  193. if (user->isRepliesChat() || user->isVerifyCodes()) {
  194. return rpl::single(false);
  195. }
  196. using namespace rpl::mappers;
  197. const auto other = rights & ~(ChatRestriction::SendVoiceMessages
  198. | ChatRestriction::SendVideoMessages);
  199. auto allowedAny = PeerFlagsValue(
  200. user,
  201. (UserDataFlag::Deleted | UserDataFlag::RequiresPremiumToWrite)
  202. ) | rpl::map([=](UserDataFlags flags) {
  203. return (flags & UserDataFlag::Deleted)
  204. ? rpl::single(false)
  205. : !(flags & UserDataFlag::RequiresPremiumToWrite)
  206. ? rpl::single(true)
  207. : AmPremiumValue(&user->session());
  208. }) | rpl::flatten_latest();
  209. if (other) {
  210. return allowedAny;
  211. }
  212. const auto mask = UserDataFlag::VoiceMessagesForbidden;
  213. return rpl::combine(
  214. std::move(allowedAny),
  215. PeerFlagValue(user, mask),
  216. _1 && !_2);
  217. } else if (const auto chat = peer->asChat()) {
  218. const auto mask = ChatDataFlag()
  219. | ChatDataFlag::Deactivated
  220. | ChatDataFlag::Forbidden
  221. | ChatDataFlag::Left
  222. | ChatDataFlag::Creator;
  223. return rpl::combine(
  224. PeerFlagsValue(chat, mask),
  225. AdminRightsValue(chat),
  226. DefaultRestrictionsValue(chat, rights),
  227. [rights](
  228. ChatDataFlags flags,
  229. Data::Flags<ChatAdminRights>::Change adminRights,
  230. ChatRestrictions defaultSendRestrictions) {
  231. const auto amOutFlags = ChatDataFlag()
  232. | ChatDataFlag::Deactivated
  233. | ChatDataFlag::Forbidden
  234. | ChatDataFlag::Left;
  235. return !(flags & amOutFlags)
  236. && ((flags & ChatDataFlag::Creator)
  237. || (adminRights.value != ChatAdminRights(0))
  238. || (rights & ~defaultSendRestrictions));
  239. });
  240. } else if (const auto channel = peer->asChannel()) {
  241. using Flag = ChannelDataFlag;
  242. const auto mask = Flag()
  243. | Flag::Left
  244. | Flag::Forum
  245. | Flag::JoinToWrite
  246. | Flag::HasLink
  247. | Flag::Forbidden
  248. | Flag::Creator
  249. | Flag::Broadcast;
  250. return rpl::combine(
  251. PeerFlagsValue(channel, mask),
  252. AdminRightValue(
  253. channel,
  254. ChatAdminRight::PostMessages),
  255. channel->unrestrictedByBoostsValue(),
  256. RestrictionsValue(channel, rights),
  257. DefaultRestrictionsValue(channel, rights),
  258. [=](
  259. ChannelDataFlags flags,
  260. bool postMessagesRight,
  261. bool unrestrictedByBoosts,
  262. ChatRestrictions sendRestriction,
  263. ChatRestrictions defaultSendRestriction) {
  264. const auto notAmInFlags = Flag::Left | Flag::Forbidden;
  265. const auto forumRestriction = forbidInForums
  266. && (flags & Flag::Forum);
  267. const auto allowed = !(flags & notAmInFlags)
  268. || ((flags & Flag::HasLink)
  269. && !(flags & Flag::JoinToWrite));
  270. const auto restricted = sendRestriction
  271. | (defaultSendRestriction && !unrestrictedByBoosts);
  272. return allowed
  273. && !forumRestriction
  274. && (postMessagesRight
  275. || (flags & Flag::Creator)
  276. || (!(flags & Flag::Broadcast)
  277. && (rights & ~restricted)));
  278. });
  279. }
  280. Unexpected("Peer type in Data::CanSendAnyOfValue.");
  281. }
  282. // This is duplicated in PeerData::canPinMessages().
  283. rpl::producer<bool> CanPinMessagesValue(not_null<PeerData*> peer) {
  284. using namespace rpl::mappers;
  285. if (const auto user = peer->asUser()) {
  286. return PeerFlagsValue(
  287. user,
  288. UserDataFlag::CanPinMessages
  289. ) | rpl::map(_1 != UserDataFlag(0));
  290. } else if (const auto chat = peer->asChat()) {
  291. const auto mask = 0
  292. | ChatDataFlag::Deactivated
  293. | ChatDataFlag::Forbidden
  294. | ChatDataFlag::Left
  295. | ChatDataFlag::Creator;
  296. return rpl::combine(
  297. PeerFlagsValue(chat, mask),
  298. AdminRightValue(chat, ChatAdminRight::PinMessages),
  299. DefaultRestrictionValue(chat, ChatRestriction::PinMessages),
  300. [](
  301. ChatDataFlags flags,
  302. bool adminRightAllows,
  303. bool defaultRestriction) {
  304. const auto amOutFlags = 0
  305. | ChatDataFlag::Deactivated
  306. | ChatDataFlag::Forbidden
  307. | ChatDataFlag::Left;
  308. return !(flags & amOutFlags)
  309. && ((flags & ChatDataFlag::Creator)
  310. || adminRightAllows
  311. || !defaultRestriction);
  312. });
  313. } else if (const auto megagroup = peer->asMegagroup()) {
  314. if (megagroup->amCreator()) {
  315. return rpl::single(true);
  316. }
  317. return rpl::combine(
  318. AdminRightValue(megagroup, ChatAdminRight::PinMessages),
  319. DefaultRestrictionValue(megagroup, ChatRestriction::PinMessages),
  320. PeerFlagsValue(
  321. megagroup,
  322. ChannelDataFlag::Username | ChannelDataFlag::Location),
  323. megagroup->restrictionsValue()
  324. ) | rpl::map([=](
  325. bool adminRightAllows,
  326. bool defaultRestriction,
  327. ChannelDataFlags usernameOrLocation,
  328. Data::Flags<ChatRestrictions>::Change restrictions) {
  329. return adminRightAllows
  330. || (!usernameOrLocation
  331. && !defaultRestriction
  332. && !(restrictions.value & ChatRestriction::PinMessages));
  333. });
  334. } else if (const auto channel = peer->asChannel()) {
  335. if (channel->amCreator()) {
  336. return rpl::single(true);
  337. }
  338. return AdminRightValue(channel, ChatAdminRight::EditMessages);
  339. }
  340. Unexpected("Peer type in CanPinMessagesValue.");
  341. }
  342. rpl::producer<bool> CanManageGroupCallValue(not_null<PeerData*> peer) {
  343. const auto flag = ChatAdminRight::ManageCall;
  344. if (const auto chat = peer->asChat()) {
  345. return chat->amCreator()
  346. ? (rpl::single(true) | rpl::type_erased())
  347. : AdminRightValue(chat, flag);
  348. } else if (const auto channel = peer->asChannel()) {
  349. return channel->amCreator()
  350. ? (rpl::single(true) | rpl::type_erased())
  351. : AdminRightValue(channel, flag);
  352. }
  353. return rpl::single(false);
  354. }
  355. rpl::producer<bool> PeerPremiumValue(not_null<PeerData*> peer) {
  356. const auto user = peer->asUser();
  357. if (!user) {
  358. return rpl::single(false);
  359. }
  360. return user->flagsValue(
  361. ) | rpl::filter([=](UserData::Flags::Change change) {
  362. return (change.diff & UserDataFlag::Premium);
  363. }) | rpl::map([=] {
  364. return user->isPremium();
  365. });
  366. }
  367. rpl::producer<bool> AmPremiumValue(not_null<Main::Session*> session) {
  368. return PeerPremiumValue(session->user());
  369. }
  370. TimeId SortByOnlineValue(not_null<UserData*> user, TimeId now) {
  371. if (user->isServiceUser() || user->isBot()) {
  372. return -1;
  373. }
  374. const auto lastseen = user->lastseen();
  375. if (const auto till = lastseen.onlineTill()) {
  376. return till;
  377. } else if (lastseen.isRecently()) {
  378. return now - 3 * kSecondsInDay;
  379. } else if (lastseen.isWithinWeek()) {
  380. return now - 7 * kSecondsInDay;
  381. } else if (lastseen.isWithinMonth()) {
  382. return now - 30 * kSecondsInDay;
  383. } else {
  384. return 0;
  385. }
  386. }
  387. crl::time OnlineChangeTimeout(Data::LastseenStatus status, TimeId now) {
  388. const auto result = OnlinePhraseChangeInSeconds(status, now);
  389. Assert(result >= 0);
  390. return std::clamp(
  391. result * crl::time(1000),
  392. kMinOnlineChangeTimeout,
  393. kMaxOnlineChangeTimeout);
  394. }
  395. crl::time OnlineChangeTimeout(not_null<UserData*> user, TimeId now) {
  396. if (user->isServiceUser() || user->isBot()) {
  397. return kMaxOnlineChangeTimeout;
  398. }
  399. return OnlineChangeTimeout(user->lastseen(), now);
  400. }
  401. QString OnlineText(Data::LastseenStatus status, TimeId now) {
  402. if (const auto common = OnlineTextCommon(status, now)) {
  403. return *common;
  404. }
  405. const auto till = status.onlineTill();
  406. Assert(till > 0);
  407. const auto minutes = (now - till) / 60;
  408. if (!minutes) {
  409. return tr::lng_status_lastseen_now(tr::now);
  410. } else if (minutes < 60) {
  411. return tr::lng_status_lastseen_minutes(tr::now, lt_count, minutes);
  412. }
  413. const auto hours = (now - till) / 3600;
  414. if (hours < 12) {
  415. return tr::lng_status_lastseen_hours(tr::now, lt_count, hours);
  416. }
  417. const auto onlineFull = base::unixtime::parse(till);
  418. const auto nowFull = base::unixtime::parse(now);
  419. const auto locale = QLocale();
  420. if (onlineFull.date() == nowFull.date()) {
  421. const auto onlineTime = locale.toString(onlineFull.time(), QLocale::ShortFormat);
  422. return tr::lng_status_lastseen_today(tr::now, lt_time, onlineTime);
  423. } else if (onlineFull.date().addDays(1) == nowFull.date()) {
  424. const auto onlineTime = locale.toString(onlineFull.time(), QLocale::ShortFormat);
  425. return tr::lng_status_lastseen_yesterday(tr::now, lt_time, onlineTime);
  426. }
  427. const auto date = locale.toString(onlineFull.date(), QLocale::ShortFormat);
  428. return tr::lng_status_lastseen_date(tr::now, lt_date, date);
  429. }
  430. QString OnlineText(not_null<UserData*> user, TimeId now) {
  431. if (const auto special = OnlineTextSpecial(user)) {
  432. return *special;
  433. }
  434. return OnlineText(user->lastseen(), now);
  435. }
  436. QString OnlineTextFull(not_null<UserData*> user, TimeId now) {
  437. if (const auto special = OnlineTextSpecial(user)) {
  438. return *special;
  439. } else if (const auto common = OnlineTextCommon(user->lastseen(), now)) {
  440. return *common;
  441. }
  442. const auto till = user->lastseen().onlineTill();
  443. const auto onlineFull = base::unixtime::parse(till);
  444. const auto nowFull = base::unixtime::parse(now);
  445. const auto locale = QLocale();
  446. if (onlineFull.date() == nowFull.date()) {
  447. const auto onlineTime = locale.toString(onlineFull.time(), QLocale::ShortFormat);
  448. return tr::lng_status_lastseen_today(tr::now, lt_time, onlineTime);
  449. } else if (onlineFull.date().addDays(1) == nowFull.date()) {
  450. const auto onlineTime = locale.toString(onlineFull.time(), QLocale::ShortFormat);
  451. return tr::lng_status_lastseen_yesterday(tr::now, lt_time, onlineTime);
  452. }
  453. const auto date = locale.toString(onlineFull.date(), QLocale::ShortFormat);
  454. const auto time = locale.toString(onlineFull.time(), QLocale::ShortFormat);
  455. return tr::lng_status_lastseen_date_time(tr::now, lt_date, date, lt_time, time);
  456. }
  457. bool OnlineTextActive(not_null<UserData*> user, TimeId now) {
  458. return !user->isServiceUser()
  459. && !user->isBot()
  460. && user->lastseen().isOnline(now);
  461. }
  462. bool IsUserOnline(not_null<UserData*> user, TimeId now) {
  463. if (!now) {
  464. now = base::unixtime::now();
  465. }
  466. return OnlineTextActive(user, now);
  467. }
  468. bool ChannelHasActiveCall(not_null<ChannelData*> channel) {
  469. return (channel->flags() & ChannelDataFlag::CallNotEmpty);
  470. }
  471. bool ChannelHasSubscriptionUntilDate(ChannelData *channel) {
  472. return channel && channel->subscriptionUntilDate() > 0;
  473. }
  474. rpl::producer<QImage> PeerUserpicImageValue(
  475. not_null<PeerData*> peer,
  476. int size,
  477. std::optional<int> radius) {
  478. return [=](auto consumer) {
  479. auto result = rpl::lifetime();
  480. struct State {
  481. Ui::PeerUserpicView view;
  482. rpl::lifetime waiting;
  483. InMemoryKey key = {};
  484. bool empty = true;
  485. Fn<void()> push;
  486. };
  487. const auto state = result.make_state<State>();
  488. state->push = [=] {
  489. const auto key = peer->userpicUniqueKey(state->view);
  490. const auto loading = Ui::PeerUserpicLoading(state->view);
  491. if (loading && !state->waiting) {
  492. peer->session().downloaderTaskFinished(
  493. ) | rpl::start_with_next(state->push, state->waiting);
  494. } else if (!loading && state->waiting) {
  495. state->waiting.destroy();
  496. }
  497. if (!state->empty && (loading || key == state->key)) {
  498. return;
  499. }
  500. state->key = key;
  501. state->empty = false;
  502. consumer.put_next(
  503. PeerData::GenerateUserpicImage(
  504. peer,
  505. state->view,
  506. size,
  507. radius));
  508. };
  509. peer->session().changes().peerFlagsValue(
  510. peer,
  511. PeerUpdate::Flag::Photo
  512. ) | rpl::start_with_next(state->push, result);
  513. return result;
  514. };
  515. }
  516. const AllowedReactions &PeerAllowedReactions(not_null<PeerData*> peer) {
  517. if (const auto chat = peer->asChat()) {
  518. return chat->allowedReactions();
  519. } else if (const auto channel = peer->asChannel()) {
  520. return channel->allowedReactions();
  521. } else {
  522. static const auto result = AllowedReactions{
  523. .type = AllowedReactionsType::All,
  524. };
  525. return result;
  526. }
  527. }
  528. rpl::producer<AllowedReactions> PeerAllowedReactionsValue(
  529. not_null<PeerData*> peer) {
  530. return peer->session().changes().peerFlagsValue(
  531. peer,
  532. Data::PeerUpdate::Flag::Reactions
  533. ) | rpl::map([=]{
  534. return PeerAllowedReactions(peer);
  535. });
  536. }
  537. int UniqueReactionsLimit(not_null<PeerData*> peer) {
  538. if (const auto channel = peer->asChannel()) {
  539. if (const auto limit = channel->allowedReactions().maxCount) {
  540. return limit;
  541. }
  542. } else if (const auto chat = peer->asChat()) {
  543. if (const auto limit = chat->allowedReactions().maxCount) {
  544. return limit;
  545. }
  546. }
  547. return UniqueReactionsLimit(&peer->session().appConfig());
  548. }
  549. rpl::producer<int> UniqueReactionsLimitValue(
  550. not_null<PeerData*> peer) {
  551. auto configValue = peer->session().appConfig().value(
  552. ) | rpl::map([config = &peer->session().appConfig()] {
  553. return UniqueReactionsLimit(config);
  554. }) | rpl::distinct_until_changed();
  555. if (const auto channel = peer->asChannel()) {
  556. return rpl::combine(
  557. PeerAllowedReactionsValue(peer),
  558. std::move(configValue)
  559. ) | rpl::map([=](const auto &allowedReactions, int limit) {
  560. return allowedReactions.maxCount
  561. ? allowedReactions.maxCount
  562. : limit;
  563. });
  564. } else if (const auto chat = peer->asChat()) {
  565. return rpl::combine(
  566. PeerAllowedReactionsValue(peer),
  567. std::move(configValue)
  568. ) | rpl::map([=](const auto &allowedReactions, int limit) {
  569. return allowedReactions.maxCount
  570. ? allowedReactions.maxCount
  571. : limit;
  572. });
  573. }
  574. return configValue;
  575. }
  576. } // namespace Data